ZLDiscord v — Документация


Введение

ZLDiscord — универсальный плагин для Paper 1.16.5–1.21.x, интегрирующий Minecraft с Discord. Встроенный шаблонизатор, LongPoll-бот, кастомные команды, голосования, DataStore и полная поддержка PlaceholderAPI.


Установка

  1. Скопировать ZoLiryzik.jar в plugins/
  2. Запустить LongPoll-бота: node longpoll-server.js &
  3. Перезапустить сервер (stop → start, не /reload)
  4. Настроить config.yml: Discord Token, Guild ID, роли
  5. Подключить LongPoll: /zlongpoll create demo http://localhost:17523/poll secret 25

v1.4 База данных SQLite с авто-миграцией — настройка не требуется. Безопасная перезагрузка плагина через /zreload без перезапуска сервера.


Команды

/zevent — Голосование

/zevent "Название" "команда" "время" "channel_id" [route] 
[resolve-now]
  • Название — заголовок embed (поддерживает %placeholder%)
  • команда — Minecraft-команда консоли при победе ✅
  • время — длительность: 5s, 10m, 1h
  • channel_id — ID текстового канала Discord

После таймера подсчитываются ✅ / ❌. При победе ✅ — выполняется команда.

  • route — сервер для выполнения: player / main / имя сервера | только в дискорд сервере
  • resolve-nowtrue (по умолч.) / false. false = плейсхолдеры резолвятся при завершении голосования, а не при создании

/zcc — Кастомные команды

/zcc list             — список команд
/zcc run <name>       — выполнить
/zcc create <name>    — создать (\n для новой строки)
/zcc remove <name>    — удалить
/zcc reload           — перезагрузить
/zcc info <name>      — инфо о команде (шаблон, кулдаун)
/zcc prefix discord <p> — изменить префикс Discord
/zcc prefix mc <p>      — изменить префикс MC

/zlink / /zunlink — Привязка Discord

/zlink — получить код в Minecraft
/unlink — отвязать в Discord (ЛС бота)

/zembed — Embed-сообщение

/zembed "имя" "заголовок" "описание" "channel_id" [картинка] [миниатюра] [футер] [иконка_футера]

Создаёт embed в Discord, сохраняет messageId. PAPI

/zembedmanage — Управление embed

/zembedmanage delete <имя>
/zembedmanage edit_title <имя> "новый заголовок"
/zembedmanage edit_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

/zstatus — Статус бота

/zstatus                    — показать статус
/zstatus set <text>         — текст статуса
/zstatus settype <type>     — тип: playing/watching/listening/competing
/zstatus reload             — перезагрузить bot_status.yml
/zstatus interval <sec>     — интервал смены статуса

v1.4 /zsync — Групповая синхронизация

/zsync reload    — перезагрузить group_sync.yml
/zsync status    — показать статус синхронизации

Синхронизирует роли Discord с группами LuckPerms. Назначает одну роль Discord на основе группы игрока в LuckPerms (с учётом приоритета). Настройка в group_sync.yml.

v1.4 /zprefix — Префикс команд

/zprefix discord <prefix>  — изменить префикс Discord slash-команд
/zprefix mc <prefix>      — изменить префикс MC команд

Меняет префикс команд Minecraft (z по умолчанию) и Discord (z! по умолчанию). Настройки в config.ymlminecraft-command-prefix, discord-command-prefix, discord-slash-command-prefix.

v1.4 /zreload — Перезагрузка

/zreload

Безопасная перезагрузка конфигурации плагина без перезапуска сервера. Перечитывает config.yml, кастомные команды и LongPoll подключения.

v1.4 Управление ролями Discord

/rolegive <member> <role>     — выдать роль участнику
/roleremove <member> <role>  — забрать роль у участника
/rolemenu                       — открыть меню выбора ролей
/roletoggle [member] [role]     — переключить роль
  • /rolemenu — открывает панель выбора ролей в Discord (настройка в config.yml)
  • /roletoggle — если роль есть — убирает, если нет — выдаёт

Команды в Discord

Prefix Текстовые команды в Discord используют префикс z! по умолчанию. Меняется в config.ymldiscord-command-prefix.

  • /zevent — создать голосование (роли embed-creator)
  • z!embed — кнопка для создания Embed
  • /link <code> — привязать (ЛС бота)
  • /unlink — отвязать (ЛС бота)
  • /zcc reload — перезагрузить кастомные команды
  • /zcc list — список кастомных команд
  • /zcc prefix — управление префиксом
  • Slash-команды из custom_commands.yml регистрируются автоматически
  • Текстовые команды из custom_commands.yml — через префикс z! (например, z!warn, z!report)

Пермишены

ПермишенОписание
zoliryzik.link v1.4 zoliryzik.zlinkПривязка аккаунта
zoliryzik.unlink v1.4 zoliryzik.zunlinkОтвязка аккаунта
zoliryzik.event v1.4 zoliryzik.useСоздание голосования
zoliryzik.embed v1.4 zoliryzik.zembedСоздание embed
zoliryzik.embed_manage v1.4 zoliryzik.zembed_manageУправление embed
zoliryzik.ccКастомные команды
zoliryzik.attr v1.4 zoliryzik.attr.getDataStore атрибуты
zoliryzik.statusСтатус бота
zoliryzik.longpollLongPoll подключения
zoliryzik.syncСинхронизация ролей
zoliryzik.prefixИзменение префикса
zoliryzik.reloadПерезагрузка плагина
zoliryzik.rolegiveВыдача роли Discord
zoliryzik.roleremoveЗабрать роль Discord
zoliryzik.rolemenuМеню выбора ролей
zoliryzik.roletoggleПереключение роли
zoliryzik.adminАдмин-доступ (все команды)

Кастомные команды

Файл custom_commands.yml:

commands:
  hello:
    file: "commands/hello.yml"
    description: "Приветствие"
    cooldown: 10
    enabled: true
    scope: all              # all / minecraft / discord
    permission: zoliryzik.hello
    permission-message: "&cНет прав"
    command_mc: hello        # имя в майнкрафте
    command_ds: hello        # имя в дискорде
    aliases_mc: ["h"]
    aliases_ds: ["h"]
    discord-roles: ["role_id_1", "role_id_2"]

Поддерживается вложенность папок: commands/lp/lp.yml

Пример hello.yml

<~ args[0] ~>, привет с сервера!

Пример fly.yml (условия, аргументы, PAPI)

<set target = args[0]>
<require target != null return "Укажи ник игрока">
<set mode = args[1]>
<if mode == null or mode == "">
  <set mode = "toggle">
</if>
<if mode == "on">
  <do mc.command("zflytoggle " + target + " on")>
  <~ target ~>: полёт &aвключён
<elseif mode == "off">
  <do mc.command("zflytoggle " + target + " off")>
  <~ target ~>: полёт &cвыключен
<else>
  <do mc.command("zflytoggle " + target)>
  <~ target ~>: полёт переключён
</if>

Система шаблонов

Переменные

ПеременнаяГдеОписание
argsвсегдаList аргументов
_args_rawвсегдаСтрока аргументов
_cmd_nameвсегдаИмя команды
_langвсегдаЯзык: ru/en
mc_onlineвсегдаИгроков онлайн
mc_max_playersвсегдаМакс. игроков
mc_tpsвсегдаTPS
mc_motdвсегдаMOTD
_player_nameMinecraftИмя игрока
_player_uuidMinecraftUUID
_player_worldMinecraftМир
_discord_member_idDiscordID участника
_discord_member_nameDiscordИмя
_discord_member_nickDiscordНикнейм
_discord_channel_idDiscordID канала
_discord_channel_nameDiscordНазвание канала
_discord_guild_idDiscordID гильдии
_discord_guild_nameDiscordНазвание гильдии
_mentioned_user_idDiscordID упомянутого
_mentioned_user_nameDiscordИмя упомянутого

Теги

ТегОписание
<~ expr ~>Вывести значение
<set var = expr>Присвоить переменную
<if cond>...<else>...</if>Условие
<for var in list>...</for>Цикл
<do action(...)>Выполнить действие
<require cond return "msg">Проверка с ошибкой
<return "msg">Выход с сообщением
<global var = expr>Глобальная переменная
<# comment #>Комментарий

v1.4 В условиях <if> теперь поддерживаются скобки: <if (a == 1 or b == 2) and c == 3>

Функции

Строки: lower, upper, capitalize, trim, title, replace(a,b,s), split(sep,s), length, format, after(N)

Проверки: defined, empty, not, number, iterable, even, odd, contains, startsWith, endsWith, typeof

Математика: abs, round, number_format, random(min,max), random_hex, random_uuid

Коллекции: 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)

Действия (<do>)

ДействиеОписание
mc.command("cmd")Выполнить команду (консоль/игрок)
mc.broadcast("msg")Отправить в чат (& для цвета)
mc.give("player","item")Выдать предмет
discord.send(channelId,"text")Сообщение в Discord
discord.embed(...)Embed (title, description, color, fields, buttons, reactions)
discord.role_add/give(memberId, roleId)Добавить роль
discord.role_remove/delete(memberId, roleId)Удалить роль
discord.auto_delete()Авто-удаление сообщения с командой
discord.open_modal("name")Открыть модальное окно
bot.setstatus("online/dnd/idle")Статус бота
bot.setstatustext("текст")Текст статуса

Chain API

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

Статус бота

Файл bot_status.yml:

enabled: true
type: "playing"     # playing / watching / listening / competing / custom
text: "с сервером"

LongPoll Bot API

Node.js-бот на http://localhost:17523. Аутентификация: Authorization: Bearer secret.

МетодEndpointТело
POST/event{"type":"...", ...}
POST/api/config{"key":"value"}
GET/api/script?type=X
POST/api/script?type=X{"script":"..."}

LongPoll скрипты

Путь: longpoll/papca/<connection>/<type>.yml

ПеременнаяОписание
_eventMap со всеми полями
_event.typeТип события
_event.player, ...Поля из JSON
_conn_nameИмя подключения

Важно Только <do action(...)>, НЕ <action(...)>.

Пример donate.yml

<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🎉 Игрок " + _nick + " задонатил &e" + _amount + " &aи получил " + (_amount / 100) + " алмазов!")>
  <else>
    <do mc.broadcast("&a🎉 Player " + _nick + " donated &e" + _amount + " &aand got " + (_amount / 100) + " diamonds!")>
  </if>
  <do player.getAttribute("donations").increment(_amount)>
</if>

PlaceholderAPI

  • Игрок — от имени отправителя
  • Консоль — от первого онлайн
  • papi("x", "Nick") — для любого игрока
  • lang_*.yml%placeholder% работает напрямую в сообщениях команд (getMessage(path, sender))
ПлейсхолдерОписание
%zoliryzik_in_vc%Статус в голосовом канале: "В голосовом чате" / "Не в голосовом чате" / "Не привязан"
%zoliryzik_attr_member_<key>%Атрибут Discord-участника из DataStore по ключу
%zoliryzik_attr_player_<key>%Атрибут игрока из DataStore по ключу
%zoliryzik_attr_bot_<key>%Глобальный атрибут бота из DataStore по ключу

v1.4 Проверка ролей

ПлейсхолдерОписание
%zoliryzik_role_<role_id>%"true" если игрок имеет ЛЮБУЮ из указанных ролей (OR). Несколько ID через запятую.
%zoliryzik_roles_<role_id>%"true" если игрок имеет ВСЕ указанные роли (AND). Несколько ID через запятую.

Пример: %zoliryzik_role_123456,789012% — true если есть роль 123456 ИЛИ 789012.

Пример: %zoliryzik_roles_123456,789012% — true если есть роль 123456 И 789012.


Конфигурация

Загрузка...

Переменные окружения

ПеременнаяОписание
DISCORD_TOKENТокен Discord бота
LONGPOLL_KEYКлюч API (умолч. secret)
LONGPOLL_PORTПорт (умолч. 17523)

GroupSync — Синхронизация ролей

Файл group_sync.yml. Синхронизирует группы LuckPerms с ролями Discord.

  • Приоритет: снизу вверх. Последняя роль = высший приоритет.
  • Игрок может иметь несколько групп LP, но в Discord — только одну роль (наивысший приоритет).
  • sync-on-discord-edit: true — при ручной смене роли в Discord обновляется группа LP.
enabled: true
sync-on-discord-edit: true
group-roles:
  default: "role_id_1"     # низший приоритет
  vip: "role_id_2"
  premium: "role_id_3"
  admin: "role_id_4"       # высший приоритет

Reactions — Реакции

Файл reactions.yml. Автоматические действия при добавлении/удалении реакций.

reactions:
  welcome:
    type: "reaction_add"
    channel_id: "000000000000000000"
    message_id: "any"
    emoji: "✅"
    role_id: "000000000000000000"
    remove_on_unreact: true

  upvote:
    type: "reaction_add"
    channel_id: "any"
    message_id: "any"
    emoji: "⭐"
    command: "mc.command('say '+_discord_member_name+' gave a star')"
  • typereaction_add или reaction_remove
  • channel_id — ID канала или "any"
  • message_id — ID сообщения или "any"
  • role_id — роль для выдачи/удаления
  • command — шаблон команды для выполнения
  • remove_on_unreact — убрать роль при снятии реакции

Modals — Модальные окна

Файл modals.yml. Discord Modal — всплывающее окно с полями ввода.

modals:
  report:
    title: "Submit Report"
    command: "report"
    fields:
      - label: "Player Name"
        style: "short"
        placeholder: "Enter nickname"
        required: true
        min_length: 1
        max_length: 32
      - label: "Reason"
        style: "paragraph"
        placeholder: "Describe the reason"
        required: true
        min_length: 10
        max_length: 500

Вызывается из шаблона: <do discord.open_modal("report")>

  • styleshort (одна строка) / paragraph (много строк)
  • Поля доступны в шаблоне как {field_label}

Banned Commands — Чёрный список

Файл banned_commands.yml.

Два отдельных списка запрещённых команд:

zevent-blacklist

Модераторы и ивенторы не смогут создать голосование для этих команд.

zevent-blacklist:
  - stop
  - restart
  - shutdown
  - reload
  - plugman
  - op
  - deop
  - ban
  - kick
  - gamemode

mc-command-blacklist

Команды, которые нельзя выполнить через <do mc.command(...)>. Защита от злоупотребления шаблонизатором.

mc-command-blacklist:
  # Серверные команды
  - stop
  - restart
  - shutdown
  - reload
  - plugman

  # Права игроков
  - op
  - deop
  - ban
  - pardon
  - ban-ip
  - pardon-ip

  # Управление сервером
  - whitelist
  - save-all
  - save-off
  - save-on
  - force-upgrade
  - version
  - plugins
  - help

  # Геймод и читы
  - gamemode
  - gm
  - gmc
  - gms
  - gma
  - gmsp
  - effect
  - enchant
  - tp
  - teleport
  - tphere
  - tppos
  - spawnpoint
  - setworldspawn
  - time
  - weather
  - difficulty
  - xp

  # Опасные операции
  - execute
  - run
  - as
  - data
  - datapack
  - function
  - macro

Кастомные плейсхолдеры

Файл placeholders.yml. Создание собственных плейсхолдеров %zoliryzik_<name>%.

placeholders:
  server_type:
    description: "Тип сервера"
    value: "Survival"

  online_info:
    description: "Информация об онлайне"
    template: "&a❤ &f<~ mc_online ~>/<~ mc_max_players ~>"

  player_guild_name:
    description: "Имя гильдии игрока"
    template: "<~ papi('%some_plugin_guild%', _player_name) ~>"
    requires_player: true
  • value — статическое значение
  • template — динамический шаблон (поддерживает <~ ~>)
  • requires_player — привязка к игроку из контекста

Примеры скриптов

Примеры для отправки событий в LongPoll-бот из внешних скриптов.

Python

import requests, json

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

# Отправить событие donate
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())

# Отправить событие kick
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"
};

// Отправить событие donate
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);

// Отправить событие kick
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"}'

Примеры

LP команда из 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-уведомление

<if _event.amount >= 5000>
  <do mc.broadcast("&a&l⭐ Мега-донат от &e" + _event.nickname)>
<else>
  <do mc.broadcast("&a❤ " + _event.nickname + " задонатил &e" + _event.amount)>
</if>

Информация об игроке (info.yml)

<set target = args[0]>
<if target == null or target == "">
  <set target = _player_name>
</if>
📊 Информация о <~ target ~>
├ Уровень: <~ papi("%level%", target) ~>
├ Здоровье: <~ papi("%player_health%", target) ~>
├ Мир: <~ papi("%player_world%", target) ~>
└ Онлайн: <~ mc_online ~>/<~ mc_max_players ~>

Report (Discord-команда)

<set target = args[0]>
<set reason = after(1)>
<require target != null and reason != "" return "Укажи ID или упоминание нарушителя и причину">
Был отправлен репорт на участника <~ target ~>:
<~ reason ~>
<do discord.send(_discord_channel_id, format("%s, Ваш репорт успешно отправлен!", _discord_member_nick))>

Телепортация по заявке (Discord)

<set who = args[0]>
<require who != null return "Укажи ник игрока">
<require hasRole("MODERATOR_ROLE_ID") return "Нет прав на телепортацию">
<do mc.command("tp " + _player_name + " " + who)>
<do discord.send(_discord_channel_id,
  "⚡ " + _player_name + " телепортировался к " + who)>

Бан через Discord

<require hasRole("ADMIN_ROLE_ID") return "Только для администрации">
<set player = args[0]>
<set reason = after(1)>
<require player != null return "Укажи игрока">
<require reason != "" return "Укажи причину бана">
<do mc.command("ban " + player + " " + reason)>
<do discord.send(_discord_channel_id,
  "🔨 " + player + " забанен. Причина: " + reason)>

Важно hasRole() проверяет роли Discord, работает только в Discord-командах. Для Minecraft используйте стандартные пермишены.

Список онлайн (из Discord)

Сейчас на сервере: <~ mc_online ~>/<~ mc_max_players ~>
TPS: <~ mc_tps ~>

Рассылка по каналам

<set msg = after(0)>
<require msg != "" return "Напиши сообщение после команды">
<do discord.send("CHANNEL_ID_1", msg)>
<do discord.send("CHANNEL_ID_2", msg)>
<do mc.broadcast("&a📢 Объявление отправлено в Discord")>

Проверка привязки перед выдачей

<set linked = getattribute("linked_" + _player_uuid)>
<if linked == null or linked == "">
  <do mc.command("msg " + _player_name + " Привяжи аккаунт: /zlink")>
<else>
  <do mc.command("give " + _player_name + " diamond 1")>
  <do mc.broadcast("&a" + _player_name + " получил алмаз за привязку!")>
</if>

Статус бота по таймеру

В bot_status.yml можно использовать плейсхолдеры:

enabled: true
type: "playing"
text: "на сервере | <~ mc_online ~> игроков"