ZLDiscord v1.2 — Documentation


Introduction

ZLDiscord is a universal Paper 1.16.5–1.21.x plugin bridging Minecraft with Discord. Built-in template engine, LongPoll bot, custom commands, voting, DataStore and full PlaceholderAPI support.


Installation

  1. Copy ZoLiryzik.jar to plugins/
  2. Start LongPoll bot: node longpoll-server.js &
  3. Restart server (stop → start, no /reload)
  4. Configure config.yml: Discord Token, Guild ID, roles
  5. Connect LongPoll: /zlongpoll create demo http://localhost:17523/poll secret 25

Commands

/zevent — Voting

/zevent "title" "command" "duration" "channel_id"
  • title — embed title (supports %placeholder%)
  • command — console command on ✅ win
  • duration5s, 10m, 1h
  • channel_id — Discord text channel ID

When the timer runs out, ✅ / ❌ are counted. If ✅ wins, the command executes.

/zcc — Custom Commands

/zcc list             — list commands
/zcc run <name>       — execute
/zcc create <name>    — create (\n for newline)
/zcc remove <name>    — remove
/zcc reload           — reload

/zlink / /zunlink — Discord Linking

/zlink — get code in Minecraft
/unlink — unlink in Discord (bot DMs)

/zembed — Embed Message

/zembed "name" "title" "description" "channel_id" [image] [thumbnail] [footer] [footer_icon]

Sends embed to Discord, saves messageId. PAPI

/zembedmanage — Embed Management

/zembedmanage delete <name>
/zembedmanage edit_title <name> "new title"
/zembedmanage edit_description <name> "new description"

/zattr — DataStore

/zattr get <bot|member|player> <key> [player]
/zattr set <bot|member|player> <key> <value> [player]
/zattr inc|dec <...> <key> <amount> [player]
/zattr list <bot|member|player> [player]

/zlongpoll — LongPoll

/zlongpoll create <name> <url> <key> [wait]
/zlongpoll list
/zlongpoll remove <name>
/zlongpoll reload

Discord Commands

Prefix Text commands in Discord use the fixed prefix z! (e.g. z!hello, z!info). It cannot be changed.

  • /zevent — create a vote (embed-creator roles)
  • !embed — button to create Embed
  • /link <code> — link account (bot DMs)
  • /unlink — unlink (bot DMs)
  • Slash commands from custom_commands.yml register automatically
  • Text commands from custom_commands.yml use z! prefix (e.g. z!warn, z!report)

Custom Commands

File custom_commands.yml:

commands:
  hello:
    file: "commands/hello.yml"
    description: "Greeting"
    cooldown: 10
    enabled: true
    scope: all              # all / minecraft / discord
    permission: zoliryzik.hello
    permission-message: "&cNo permission"
    command_mc: hello
    command_ds: hello        # Discord prefix z! is fixed — z!hello, cannot be changed
    aliases_mc: ["h"]
    aliases_ds: ["h"]
    discord-roles: ["role_id_1", "role_id_2"]

Nested folders supported: commands/lp/lp.yml

hello.yml example

<~ args[0] ~>, hello from the server!

fly.yml example (conditions, arguments, PAPI)

<set target = args[0]>
<require target != null return "Specify player name">
<set mode = args[1]>
<if mode == null or mode == "">
  <set mode = "toggle">
</if>
<if mode == "on">
  <do mc.command("zflytoggle " + target + " on")>
  Fly &aenabled
<elseif mode == "off">
  <do mc.command("zflytoggle " + target + " off")>
  Fly &cdisabled
<else>
  <do mc.command("zflytoggle " + target)>
  Fly toggled
</if>

Template Engine

Variables

VariableScopeDescription
argsalwaysList of arguments
_args_rawalwaysRaw argument string
_cmd_namealwaysCommand name
_langalwaysLanguage: ru/en
mc_onlinealwaysOnline players
mc_max_playersalwaysMax players
mc_tpsalwaysTPS
mc_motdalwaysMOTD
_player_nameMinecraftPlayer name
_player_uuidMinecraftUUID
_player_worldMinecraftWorld
_discord_member_idDiscordMember ID
_discord_member_nameDiscordMember name
_discord_member_nickDiscordNickname
_discord_channel_idDiscordChannel ID
_discord_channel_nameDiscordChannel name
_discord_guild_idDiscordGuild ID
_discord_guild_nameDiscordGuild name
_mentioned_user_idDiscordMentioned user ID
_mentioned_user_nameDiscordMentioned user name

Tags

TagDescription
<~ expr ~>Print expression value
<set var = expr>Assign variable
<if cond>...<else>...</if>Condition
<for var in list>...</for>Loop
<do action(...)>Execute action
<require cond return "msg">Guard with error
<return "msg">Early return
<global var = expr>Global variable
<# comment #>Comment

Functions

String: lower, upper, capitalize, trim, title, replace(a,b,s), split(sep,s), length, format, after(N)

Checks: defined, empty, not, number, iterable, even, odd, contains, startsWith, endsWith, typeof

Math: abs, round, number_format, random(min,max), random_hex, random_uuid

Collections: first, last, reverse, slice, sort, shuffle, batch, concat, join, keys, values, default

PAPI: papi("placeholder" [, player])

DataStore: getattribute(key), member.getattribute(key), player.getattribute(key), bot.getattribute(key), find_player_by_session(id)

HTTP: http_get(url), http_post(url, body|key,val...), http_put, http_delete, http_patch, http_code(resp), http_body(resp), http_header(name,val), http_bearer(token), json_parse(str)

Actions (<do>)

ActionDescription
mc.command("cmd")Execute command (console/player)
mc.broadcast("msg")Broadcast (& for colors)
mc.give("player","item")Give item
discord.send(channelId,"text")Send to Discord
discord.embed(...)Embed with fields, buttons, reactions
discord.role_add/give(memberId, roleId)Add role
discord.role_remove/delete(memberId, roleId)Remove role
bot.setstatus("online/dnd/idle")Bot status
bot.setstatustext("text")Bot status text

Chain API

guild.getChannel("id").createEmbed().withTitle("...").withColor("#hex").withField("n","v",true).send()

Bot Status

File bot_status.yml:

enabled: true
type: "playing"     # playing / watching / listening / competing / custom
text: "with the server"

LongPoll Bot API

Node.js bot at http://localhost:17523. Auth: Authorization: Bearer secret.

MethodEndpointBody
POST/event{"type":"...", ...}
POST/api/config{"key":"value"}
GET/api/script?type=X
POST/api/script?type=X{"script":"..."}

LongPoll Scripts

Path: longpoll/papca/<connection>/<type>.yml

VariableDescription
_eventMap with all fields
_event.typeEvent type
_event.player, ...Fields from JSON
_conn_nameConnection name

Important Use <do action(...)>, NOT <action(...)>.

donate.yml example

<if !defined(_event.nickname)>
  <do mc.command("say", "Donate: no nickname in event")>
<else>
  <set _nick = _event.nickname>
  <set _amount = _event.amount>
  <do mc.command("give " + _nick + " diamond " + (_amount / 100))>
  <if _lang == "ru">
    <do mc.broadcast("&a🎉 Player " + _nick + " donated &e" + _amount + " &aand got " + (_amount / 100) + " diamonds!")>
  <else>
    <do mc.broadcast("&a🎉 Player " + _nick + " donated &e" + _amount + " &aand got " + (_amount / 100) + " diamonds!")>
  </if>
  <do player.getAttribute("donations").increment(_amount)>
</if>

PlaceholderAPI

  • Player — resolves for command sender
  • Console — resolves for first online player
  • papi("x", "Nick") — target any player by name
PlaceholderDescription
%zoliryzik_in_vc%Voice channel status: "In a voice channel" / "Not in a voice channel" / "Not linked"
%zoliryzik_attr_member_<key>%Discord member attribute from DataStore by key
%zoliryzik_attr_player_<key>%Player attribute from DataStore by key
%zoliryzik_attr_bot_<key>%Global bot attribute from DataStore by key

Configuration

Loading...

Environment Variables

VariableDescription
DISCORD_TOKENDiscord bot token
LONGPOLL_KEYAPI key (default: secret)
LONGPOLL_PORTPort (default: 17523)

Script Examples

Examples for sending events to the LongPoll bot from external scripts.

Python

import requests, json

URL = "http://localhost:17523/event"
HEADERS = {
    "Authorization": "Bearer secret",
    "Content-Type": "application/json"
}

# Send donate event
event = {
    "type": "donate",
    "nickname": "Player123",
    "amount": 1500,
    "product": "Diamond Pack"
}
r = requests.post(URL, headers=HEADERS, json=event)
print("Status:", r.status_code, r.json())

# Send kick event
event = {
    "type": "kick",
    "nickname": "Griefer",
    "reason": "Spam",
    "moderator": "Admin"
}
r = requests.post(URL, headers=HEADERS, json=event)
print("Status:", r.status_code, r.json())

Node.js

const URL = "http://localhost:17523/event";
const HEADERS = {
    "Authorization": "Bearer secret",
    "Content-Type": "application/json"
};

// Send donate event
fetch(URL, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
        type: "donate",
        nickname: "Player123",
        amount: 1500,
        product: "Diamond Pack"
    })
}).then(r => r.json()).then(console.log);

// Send kick event
fetch(URL, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
        type: "kick",
        nickname: "Griefer",
        reason: "Spam",
        moderator: "Admin"
    })
}).then(r => r.json()).then(console.log);

curl

curl http://localhost:17523/event -X POST \
  -H "Authorization: Bearer secret" \
  -H "Content-Type: application/json" \
  -d '{"type":"donate","nickname":"Player123","amount":1500}'

curl http://localhost:17523/event -X POST \
  -H "Authorization: Bearer secret" \
  -H "Content-Type: application/json" \
  -d '{"type":"kick","nickname":"Griefer","reason":"Spam","moderator":"Admin"}'

Examples

LP command from Minecraft

<set _ = http_header("Authorization", "Bearer secret")>
<set _type = args[0]>
<set _resp = http_post("http://localhost:17523/event",
  "type", _type, "player", _player_name, "world", _player_world,
  "cmd", _cmd_name, "args", _args_raw)>
<do mc.broadcast("&a[LP] &f" + _type + " &a✓")>

Donate notification

<if _event.amount >= 5000>
  <do mc.broadcast("&a&l⭐ Mega-donate from &e" + _event.nickname)>
<else>
  <do mc.broadcast("&a❤ " + _event.nickname + " donated &e" + _event.amount)>
</if>

Player Info (info.yml)

<set target = args[0]>
<if target == null or target == "">
  <set target = _player_name>
</if>
📊 Info about <~ target ~>
├ Level: <~ papi("%level%", target) ~>
├ Health: <~ papi("%player_health%", target) ~>
├ World: <~ papi("%player_world%", target) ~>
└ Online: <~ mc_online ~>/<~ mc_max_players ~>

Report (Discord command)

<set target = args[0]>
<set reason = after(1)>
<require target != null and reason != "" return "Specify offender ID/mention and reason">
Report on <~ target ~>:
<~ reason ~>
<do discord.send(_discord_channel_id, format("%s, Your report has been submitted!", _discord_member_nick))>

Diagnostics

/zdiag (ZDiag) — 12 system tests: bot, channels, roles, LongPoll, attributes, HTTP, templates, PAPI, permissions.