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

Lua 5.5 · API v0.1

Обзор

Скрипты — это небольшие программы на Lua, которые расширяют возможности вашего сервера. Они выполняются в изолированном процессе-песочнице Lua 5.5, полностью отделённом от бота. Скрипт никогда не обращается к базе данных, файловой системе или другим серверам. Любая возможность доступна только через API хоста из белого списка, который передаёт управление обратно боту по IPC.

Одна песочница на скрипт

Нет загруженного скрипта — нет и процесса.

Вызовы хоста с ограничением частоты

Вызовы bot:* и msg:* проверяются на права ботом, а не Lua.

Встроенные функции в приоритете

АвтоМод, приветствия и тикеты работают всегда. Скрипты дополняют их, но не заменяют.

Ошибки не выходят за рамки

Упавший скрипт выгружается или помещается в карантин, а бот продолжает работать.

Редактора в браузере нет. Пишите .lua в обычном редакторе и загружайте файл на странице «Скрипты» вашего сервера.

Начало работы

  1. 1

    Напишите файл .lua локально

    VS Code, Neovim или любой другой редактор.
  2. 2

    Проверьте его локально (необязательно)

    script_runner --check file.lua выводит check: ok.
  3. 3

    Загрузите его

    Откройте страницу «Скрипты» вашего сервера и вставьте код или загрузите файл .lua (до 64 КиБ).
  4. 4

    Пробный запуск

    Проверяет размер, кодировку, байткод и запрещённые API, ничего не выполняя.
  5. 5

    Активируйте

    Дождитесь одобрения версии персоналом, затем нажмите «Активировать». Бот загрузит её в течение нескольких секунд.
  6. 6

    Укажите канал журнала

    Туда будут приходить вывод bot:log и уведомления об ошибках.

Локальный CLI

Раннер — тот же бинарный файл, который использует бот.

  • script_runner --check file.lua

    Статическая проверка синтаксиса и запрещённых API без запуска. Выводит 'check: ok'.

  • script_runner --eval 'code'

    Запускает фрагмент в песочнице локально. Ошибки выводятся с типом и сообщением.

  • script_runner --self-test

    Запускает самотесты песочницы, протокола и сокета.

Жизненный цикл и проверка

Скрипты версионируются. Версия фиксируется при отправке и идентифицируется по SHA-256. Активировать можно только одобренные версии, и только активные версии загружаются ботом.

  1. 1

    Написание

    Создайте файл .lua локально в VS Code, Neovim или любом другом редакторе. Редактора в браузере намеренно нет.
  2. 2

    Пробный запуск / загрузка

    Вставьте код или загрузите файл .lua на странице «Скрипты» сервера (≤ 64 КиБ, без двоичных данных). Пробный запуск проверяет размер, кодировку, байткод и запрещённые API, ничего не выполняя.
  3. 3

    Проверка

    Каждый новый скрипт и каждая новая версия начинаются со статуса «ожидает». Персонал одобряет или отклоняет конкретную ревизию кода, прежде чем она сможет запуститься.
  4. 4

    Активация

    Вы активируете одобренную версию. Активация версии, ожидающей проверки или отклонённой, запрещена. Ботом загружаются только активные версии.
  5. 5

    Запуск

    Бот загружает версию (код + SHA-256, проверяется раннером) в изолированную песочницу Lua 5.5. События доставляются только обработчикам, зарегистрированным через bot:on.
  6. 6

    Обновление / откат

    Загрузите новую версию (снова в статусе «ожидает») или активируйте более старую одобренную версию для отката. KV-хранилище сохраняется.
  7. 7

    Отключение / удаление

    Отключение немедленно останавливает загрузку, «Отключить все» затрагивает все активные скрипты сервера. Удаление убирает скрипт, его версии, KV и ошибки.

Статус скрипта

Отображается значком у каждого скрипта.

  • черновик

    Создан, активной версии пока нет.

  • ожидает

    Ожидает чего-либо (например, первого одобрения) — как черновик никогда не запускается.

  • активен

    Одобренная версия активирована и загружена ботом.

  • отклонён

    Персонал отклонил скрипт; он не будет загружен.

  • отключён

    Отключён вручную (вами, персоналом, «Отключить все» или из-за снятия квоты) — выгружен.

  • в карантине

    Автоматическое отключение после злоупотребления лимитами (лавина запросов, переполнение очереди, повторные сбои). Выгружен и требует внимания персонала.

Проверка версии

Каждая ревизия кода проходит проверку персоналом.

  • ожидает

    Персонал ещё не принял решение. Активировать нельзя.

  • одобрена

    Можно активировать. Новые загрузки установленных скриптов из магазина обходят очередь (версия записи уже одобрена).

  • отклонена

    Персонал отклонил эту ревизию кода. Вместо этого загрузите новую версию.

Квота: 2 активных скрипта на сервер бесплатно, 10 с Premium. Если квота уменьшается, самые старые активные скрипты сверх неё отключаются автоматически, а вы получаете уведомление в канале журнала скриптов.

Обработчики и события

Регистрируйте обработчики с помощью bot:on(event, fn). Скрипт сообщает о зарегистрированных событиях при загрузке, и ему доставляются только они. Неизвестные имена событий приводят к ошибке загрузки, а на одно событие скрипт может зарегистрировать не более 8 обработчиков.

luaLua
bot:on("message_create", function(msg)
  if msg.content == "!ping" then
    msg:reply("pong")
  end
end)

События, связанные с сообщениями (message_*, automod_check), передают объект сообщения с методами, см. Объект сообщения.

Вызовы хоста передают управление: пока обработчик ждёт bot:kv_get и аналогичные вызовы, второе событие в этом же скрипте не выполняется. Новые события ждут в очереди из 32 элементов, при переполнении самое старое событие отбрасывается, а постоянный поток событий отправляет скрипт в карантин.

guild_id всегда добавляется в данные. События доставляются только обработчикам, зарегистрированным через bot:on. Неизвестные имена событий приводят к ошибке загрузки с ошибкой политики. События voice_state нет.

Данные событий

В каждом наборе данных есть guild_id. Поля — таблицы в нижнем регистре в стиле snake_case.

  • message_create

    id, guild_id, channel_id, author_id, author, content, attachments, referenced_id

    Срабатывает до тегов и автоответчика. Вложения: до 8 записей { id, filename, size }. Если ответа нет, referenced_id равен 0.

  • message_update

    same as message_create

  • message_delete

    channel_id, id

  • message_delete_bulk

    channel_id, ids

    ids — массив идентификаторов удалённых сообщений.

  • guild_member_add

    user_id

    Аккаунты ботов пропускаются.

  • guild_member_remove

    user_id

  • guild_member_update

    user_id

  • reaction_add

    channel_id, id, author_id, user_id, emoji, emoji_id

    Реакции ботов пропускаются. emoji — название, для эмодзи Unicode emoji_id равен 0.

  • reaction_remove

    channel_id, id, author_id, user_id, emoji, emoji_id

  • channel_create

    id, name, type

  • channel_update

    id, name, type

  • channel_delete

    id, name

  • role_create

    id, name

  • role_update

    id, name

  • role_delete

    id, name

  • thread_create

    id, name

  • thread_update

    id, name

  • thread_delete

    id

  • invite_create

    code, uses

  • invite_delete

    code

  • guild_ban_add

    user_id

  • guild_ban_remove

    user_id

  • welcome_before

    user_id, username

    Срабатывает до встроенного приветственного сообщения. Только для входа не-ботов.

  • goodbye_before

    user_id

    Срабатывает до встроенного прощального сообщения.

  • ticket_created

    ticket_id, user_id, channel_id

  • automod_check

    content, channel_id, author_id, id

    Срабатывает до встроенного АвтоМода. msg:escalate() удаляет сообщение обрабатываемого события; встроенный запрет всегда приоритетнее; msg:allow() ничего не делает.

  • webhooks_update

    channel_id

  • emoji_update

    count, ids

    ids ограничен 8 элементами.

  • tick

    unix

    Каждые 60 секунд, доставляется только скриптам, зарегистрировавшим bot:on("tick"). unix — время Unix.

API хоста

Эти вызовы передают управление боту по IPC. Лимиты частоты на скрипт: 30 вызовов хоста / 10 с плюс 5 отправок в Discord / 10 с (reply, channel_send, react, edit, delete, pin, escalate, http_get). Чтение из кэша, например member_get и guild_get, никогда не обращается к Discord напрямую.

  • bot:log(msg)

    Записывает в канал журнала скриптов и журнал ошибок. Учитывается в лимите 30 вызовов / 10 с.

  • bot:kv_get(key)

    Возвращает сохранённое значение JSON или nil. Ключи: 1–64 символа [A-Za-z0-9_-].

  • bot:kv_set(key, value)

    Сохраняет до 8 КиБ JSON. 256 ключей на скрипт, 1 МиБ на скрипт (8 МиБ с Premium), 8 МиБ на сервер.

  • bot:kv_del(key)

    Удаляет один ключ.

  • bot:kv_keys()

    Возвращает до 256 ключей в отсортированном виде.

  • bot:kv_incr(key, delta?)

    Увеличивает сохранённое число JSON, по умолчанию на 1; приращение ограничено ±1 000 000 000.

  • bot:db_exec(sql, binds?)

    Выполняет один запрос на запись или изменение схемы (CREATE TABLE/INDEX, INSERT, UPDATE, DELETE, DROP) в базе данных скриптов сервера. Необязательные параметры: массив из nil, boolean, number или string, до 8. Возвращает { changes, last_insert_rowid }. Проблемы с квотой, занятостью или лимитами возвращают { error, kind } вместо исключения; запрещённые запросы вызывают ошибку политики.

  • bot:db_query(sql, binds?)

    Выполняет один SELECT с необязательными параметрами и возвращает до 50 строк — таблиц с ключами по именам столбцов. Всегда используйте LIMIT. При ошибке возвращает { error, kind }, поэтому проверяйте rows.error.

  • bot:http_get(url)

    Только HTTPS GET. Хост должен входить в белый список сервера. Возвращает { status, body, truncated }; тело ограничено 16 КиБ. Время ожидания не учитывается в 100 мс реального времени Lua.

  • bot:channel_send(channel_id, text, embed?)

    Отправка в любой канал собственного сервера, в который бот может писать. Правила содержимого те же, что и у msg:reply.

  • bot:channel_get(channel_id)

    Чтение из кэша: { id, name, type, parent_id }. Только собственный сервер.

  • bot:member_get(user_id)

    Чтение из кэша: { id, nick, bot, roles (до 20) }.

  • bot:role_get(role_id)

    Чтение из кэша: { id, name, position, color }.

  • bot:guild_get()

    Кэш собственного сервера: { id, name, owner_id, member_count }.

  • bot:self()

    Пользователь-бот: { id, username, bot }.

  • bot:has_role(user_id, role_id)

    Чтение из кэша, возвращает { has: true|false }.

  • bot:channels()

    Список каналов сервера из кэша (максимум 50).

  • bot:roles()

    Список ролей сервера из кэша (максимум 50).

  • bot:now()

    Время Unix в секундах.

  • bot:id()

    UUID этого скрипта. Не передаёт управление и не учитывается в лимитах частоты.

  • bot:message_get(channel_id, message_id)

    Чтение из кэша сообщений бота (без обращения к сети).

  • bot:can_send(channel_id)

    Возвращает { ok: true|false } — может ли бот отправлять сообщения в этот канал.

Что принимает bot:http_get

  • Только HTTPS

    URL с http:// отклоняются. Без userinfo (user:pass@host).

  • Белый список сервера

    Хост должен входить в HTTP-белый список сервера (страница «Скрипты», до 32 хостов). Вы управляете им самостоятельно.

  • Никаких инфраструктурных хостов

    IP-адреса, localhost, *.local и *.internal всегда запрещены.

  • Ответ

    { status, body, truncated }, тело ограничено 16 КиБ. Тайм-аут 3 с. Использует общий лимит Discord — 5 за 10 с.

Объект сообщения

События сообщений передают таблицу msg и поля данных. Методы действуют только на сообщение обрабатываемого события — получать и изменять произвольные сообщения нельзя.

Правила содержимого для всех методов: текст от 1 до 2000 байт, не более одного эмбеда { title, description, color }, упоминания разрешаются только для автора события, @everyone и @here удаляются. Без файлов и стикеров.

  • msg:reply(text, embed?)

    Отвечает в канале события. Упоминания разрешаются только для автора события; @everyone / @here удаляются. Текст 1–2000 байт, не более одного эмбеда.

  • msg:react(emoji)

    Добавляет реакцию к сообщению события.

  • msg:unreact(emoji)

    Удаляет собственную реакцию скрипта.

  • msg:edit(text, embed?)

    Редактирует сообщение обрабатываемого события. Правила содержимого те же, что и для ответа.

  • msg:delete()

    Удаляет сообщение обрабатываемого события.

  • msg:pin() / msg:unpin()

    Закрепляет или открепляет сообщение обрабатываемого события.

  • msg:escalate()

    Только для automod_check: удаляет сообщение обрабатываемого события (дополнительное действие с ограничением частоты). Решения встроенного АвтоМода всегда приоритетнее.

  • msg:allow()

    Только для automod_check: помечает сообщение как разрешённое. Это не отменяет встроенный запрет — фактически это пустая метка.

Лимиты и квоты

Все лимиты хранятся в одном файле конфигурации, общем для бота и раннера. Они применяются во время выполнения, а не только описаны здесь. Указаны текущие значения по умолчанию.

  • Язык

    Lua 5.5, только текстовые фрагменты (байткод отклоняется). Без io/os.file/require/load/debug/coroutine.

  • Вычисления

    ~100 мс реального времени на обработчик, 10 000 000 инструкций (хук подсчёта).

  • Память

    2 МБ на скрипт бесплатно, 8 МБ с Premium.

  • Размер кода

    64 КиБ UTF-8 .lua, без NUL-байтов и байткода.

  • Вызовы хоста

    30 за 10 с на скрипт (включая bot:log).

  • Отправки в Discord

    5 за 10 с на скрипт (reply, channel_send, react/unreact, edit, delete, pin/unpin, escalate, http_get). 8 отказов за 10 с → карантин.

  • Обработчики

    Не более 8 на событие на скрипт. Неизвестные имена событий отклоняются при загрузке.

  • Очередь событий

    Глубина 32 на скрипт, при переполнении удаляется самое старое событие. 8+ потерь за 10 с → карантин.

  • HTTP

    HTTPS + белый список сервера, тайм-аут 3 с, тело 16 КиБ, без IP/localhost/внутренних хостов, без userinfo.

  • KV

    Ключи 1–64 [A-Za-z0-9_-], значения ≤ 8 КиБ JSON, 256 ключей/скрипт, 1 МиБ/скрипт (8 МиБ с Premium), 8 МиБ/сервер.

  • База данных

    Один файл SQLite на сервер, общий для всех его скриптов: 1 МиБ (8 МиБ с Premium), один запрос за вызов, SQL ≤ 2 КиБ, ≤ 8 параметров, ≤ 50 строк, 20 мс на запрос, 64 таблицы и индекса. Без ATTACH, PRAGMA, транзакций, триггеров, представлений и WITH. Каждый скрипт на сервере может читать и удалять любую таблицу.

  • Активные скрипты

    2 на сервер бесплатно, 10 с Premium. Лишние самые старые активные скрипты отключаются автоматически.

  • Версии

    Количество версий на скрипт не ограничено; каждая новая версия ожидает одобрения персоналом.

  • Упоминания

    Только автор события. @everyone / @here удаляются. Без файлов и стикеров.

  • Изоляция

    3 сбоя (тайм-аут/память/политика/выполнение) → отдельный изолированный воркер (максимум 4), иначе карантин и выгрузка.

  • Журнал ошибок

    Последние 24 ч видны в панели, записи удаляются через 7 дней.

  • Запрещено

    Без voice_state, без API mod_* (бан/кик/тайм-аут), без доступа между серверами, без базы данных/файловой системы, без монетизации.

  • АвтоМод

    Встроенный АвтоМод работает первым и всегда приоритетнее скриптов.

Валидация

Перед сохранением версии бэкенд выполняет статические проверки. Пробный запуск делает то же самое без сохранения. Сама песочница — вторая линия защиты: запрещённых глобальных объектов во время выполнения просто нет.

  • Размер / кодировка

    Не пустой, ≤ 64 КиБ, UTF-8 без NUL-байтов.

  • Байткод

    Всё, что начинается с сигнатуры байткода Lua, отклоняется.

  • Запрещённые API

    Статическое сканирование отклоняет: load(, loadfile, dofile, require(, os.execute, io.open, debug., string.dump, loadstring, getfenv, setfenv, package., coroutine., ffi., jit.

  • Песочница времени выполнения

    Даже если что-то пройдёт статическое сканирование, песочница просто не предоставляет эти глобальные объекты — вызов завершится ошибкой политики.

Доступные встроенные функции: стандартная библиотека Lua без доступа к файлам. string, table, math, tostring, tonumber, type, pairs, ipairs, os.time, os.clock, os.difftime и bot.

Ошибки и карантин

Каждая ошибка обработчика записывается с указанием типа и отображается на странице скрипта (за последние 24 ч). Записи удаляются через 7 дней.

  • runtime

    Ошибка Lua в вашем обработчике (обращение к nil, неверные аргументы, …). Сообщение содержит текст ошибки Lua.

  • timeout

    Обработчик превысил 100 мс реального времени или истёк тайм-аут IPC-вызова (1 с).

  • memory

    Скрипт превысил лимит памяти.

  • policy

    Скрипт попытался сделать что-то недопустимое: неизвестное событие, слишком много обработчиков, запрещённый API, слишком большие данные.

Повторные нарушения приводят к автоматической эскалации: 3 сбоя (тайм-аут, память, политика, выполнение) переносят скрипт в отдельный изолированный воркер. Если нарушения продолжаются или нет свободного изолированного слота (максимум 4), скрипт помещается в карантин: выгружается и помечается в панели. Лавина запросов (8 отклонённых отправок в Discord или 8 сброшенных событий очереди за 10 с) отправляет в карантин немедленно. Вы получаете уведомление в канале журнала скриптов или в личных сообщениях.

Со стороны эксплуатации: бота можно глобально приостановить аварийным выключателем без перезапуска, а сигнал scripts:updated перезагружает изменённые версии в течение нескольких секунд. Оба действия доступны только персоналу.

Магазин

Магазин позволяет публично делиться скриптами и устанавливать скрипты с других серверов. И публикация, и установка создают копии. Установка записи создаёт независимый скрипт на вашем сервере и не остаётся связанной с кодом автора.

  • Публикация

    Требуются активный скрипт с одобренной версией, описание (10–512 символов) и 1–8 тегов ([a-z0-9-], 1–24 символа). Перед первой публикацией необходимо один раз принять условия магазина.

  • Установка

    При установке запись копируется на ваш сервер как отдельный скрипт (черновик, заранее одобренная версия, собственное имя с суффиксом, если оно занято). KV не копируется. Счётчик установок записи увеличивается.

  • Снятие с публикации

    Удаляет запись из магазина. Существующие копии остаются и продолжают работать; они просто больше не получают обновлений.

  • Никаких денег

    Продажи, платный доступ, ссылки на пожертвования/криптовалюту и подобная монетизация в описании или коде отклоняются при публикации и могут привести к блокировке скрипта.

  • Жалобы

    Любой, кто имеет доступ к серверу, может пожаловаться на запись, указав причину. Персонал видит очередь открытых жалоб и закрывает их после проверки.

  • Лимиты

    Список магазина показывает до 50 записей на запрос (поиск, тег, сортировка по установкам или новизне). На установки действует обычная квота скриптов сервера.

Примеры

Выберите пример, чтобы увидеть его код.