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
- Copy
ZoLiryzik.jartoplugins/ - Start LongPoll bot:
node longpoll-server.js & - Restart server (stop → start, no /reload)
- Configure
config.yml: Discord Token, Guild ID, roles - 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
- duration —
5s,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.ymlregister automatically - Text commands from
custom_commands.ymlusez!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
| Variable | Scope | Description |
|---|---|---|
args | always | List of arguments |
_args_raw | always | Raw argument string |
_cmd_name | always | Command name |
_lang | always | Language: ru/en |
mc_online | always | Online players |
mc_max_players | always | Max players |
mc_tps | always | TPS |
mc_motd | always | MOTD |
_player_name | Minecraft | Player name |
_player_uuid | Minecraft | UUID |
_player_world | Minecraft | World |
_discord_member_id | Discord | Member ID |
_discord_member_name | Discord | Member name |
_discord_member_nick | Discord | Nickname |
_discord_channel_id | Discord | Channel ID |
_discord_channel_name | Discord | Channel name |
_discord_guild_id | Discord | Guild ID |
_discord_guild_name | Discord | Guild name |
_mentioned_user_id | Discord | Mentioned user ID |
_mentioned_user_name | Discord | Mentioned user name |
Tags
| Tag | Description |
|---|---|
<~ 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>)
| Action | Description |
|---|---|
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.
| Method | Endpoint | Body |
|---|---|---|
| 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
| Variable | Description |
|---|---|
_event | Map with all fields |
_event.type | Event type |
_event.player, ... | Fields from JSON |
_conn_name | Connection 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
| Placeholder | Description |
|---|---|
%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
| Variable | Description |
|---|---|
DISCORD_TOKEN | Discord bot token |
LONGPOLL_KEY | API key (default: secret) |
LONGPOLL_PORT | Port (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.