Zeny

Doku

Lua 5.5 · API v0.1

Überblick

Skripte sind kleine Lua-Programme, die deinen Server erweitern. Sie laufen in einem isolierten Lua-5.5-Sandbox-Prozess, völlig getrennt vom Bot. Ein Skript greift nie auf die Datenbank, das Dateisystem oder andere Server zu. Jede Fähigkeit läuft über eine freigegebene Host-API, die per IPC an den Bot zurückgibt.

Eine Sandbox pro Skript

Ohne geladenes Skript existiert kein Prozess.

Host-Aufrufe mit Ratenlimit

bot:*- und msg:*-Aufrufe prüft der Bot auf Berechtigung, nicht Lua.

Native Funktionen gewinnen

AutoMod, Willkommensnachrichten und Tickets laufen immer. Skripte ergänzen, sie ersetzen nichts.

Fehler bleiben eingegrenzt

Ein abstürzendes Skript wird entladen oder unter Quarantäne gestellt, der Bot läuft weiter.

Es gibt keinen Editor im Browser. Schreibe .lua in einem richtigen Editor und lade die Datei auf der Skripte-Seite deines Servers hoch.

Erste Schritte

  1. 1

    Eine .lua-Datei lokal schreiben

    VS Code, Neovim oder ein beliebiger Editor.
  2. 2

    Lokal prüfen (optional)

    script_runner --check file.lua gibt check: ok aus.
  3. 3

    Hochladen

    Öffne die Skripte-Seite deines Servers und füge den Code ein oder lade die .lua-Datei hoch (bis 64 KiB).
  4. 4

    Testlauf

    Prüft Größe, Kodierung, Bytecode und verbotene APIs, ohne etwas auszuführen.
  5. 5

    Aktivieren

    Warte auf die Freigabe der Version durch das Team und klicke dann auf Aktivieren. Der Bot lädt sie innerhalb von Sekunden.
  6. 6

    Log-Kanal festlegen

    Dort erscheinen bot:log-Ausgaben und Fehlermeldungen.

Lokale CLI

Der Runner ist dieselbe Datei, die auch der Bot nutzt.

  • script_runner --check file.lua

    Statische Syntax-/Verbotene-API-Prüfung ohne Ausführung. Gibt „check: ok“ aus.

  • script_runner --eval 'code'

    Führt ein Snippet lokal in der Sandbox aus. Fehler geben Art + Meldung aus.

  • script_runner --self-test

    Führt die Selbsttests für Sandbox, Protokoll und Socket aus.

Ablauf & Prüfung

Skripte sind versioniert. Eine Version wird beim Einreichen eingefroren und über ihren SHA-256 identifiziert. Nur freigegebene Versionen lassen sich aktivieren, und nur aktive Versionen lädt der Bot.

  1. 1

    Schreiben

    Erstelle die .lua-Datei lokal in VS Code, Neovim oder einem beliebigen Editor. Einen Editor im Browser gibt es bewusst nicht.
  2. 2

    Testlauf / Upload

    Füge den Code ein oder lade die .lua-Datei auf der Skripte-Seite des Servers hoch (≤ 64 KiB, kein Binärformat). Der Testlauf prüft Größe, Kodierung, Bytecode und verbotene APIs, ohne etwas auszuführen.
  3. 3

    Prüfung

    Jedes neue Skript und jede neue Version beginnt als ausstehend. Das Team gibt die genaue Code-Revision frei oder lehnt sie ab, bevor sie laufen kann.
  4. 4

    Aktivieren

    Du aktivierst eine freigegebene Version. Das Aktivieren einer ausstehenden oder abgelehnten Version wird verweigert. Der Bot lädt nur aktive Versionen.
  5. 5

    Ausführen

    Der Bot lädt die Version (Code + SHA-256, vom Runner verifiziert) in eine isolierte Lua-5.5-Sandbox. Events werden nur an Handler verteilt, die mit bot:on registriert sind.
  6. 6

    Update / Rollback

    Lade eine neue Version hoch (wieder ausstehend) oder aktiviere eine ältere freigegebene Version für einen Rollback. Der KV-Speicher bleibt erhalten.
  7. 7

    Deaktivieren / Löschen

    Deaktivieren stoppt das Laden sofort, „Alle deaktivieren“ trifft jedes aktive Skript des Servers. Löschen entfernt das Skript samt Versionen, KV und Fehlern.

Skript-Status

Wird bei jedem Skript als Badge angezeigt.

  • draft

    Erstellt, noch keine aktive Version.

  • pending

    Wartet auf etwas (z. B. die erste Freigabe), läuft nie als Entwurf.

  • active

    Eine freigegebene Version ist aktiviert und wird vom Bot geladen.

  • rejected

    Vom Team abgelehnt; das Skript wird nicht geladen.

  • disabled

    Manuell deaktiviert (du, das Team, „Alle deaktivieren“ oder Kontingent-Entfernung), entladen.

  • quarantined

    Automatische Abschaltung nach Limit-Missbrauch (Ratenüberflutung, Warteschlangen-Überflutung, wiederholte Fehler). Entladen und braucht die Aufmerksamkeit des Teams.

Versionsprüfung

Jede Code-Revision wird vom Team geprüft.

  • pending

    Das Team hat noch nicht entschieden. Kann nicht aktiviert werden.

  • approved

    Darf aktiviert werden. Neue Uploads installierter Store-Skripte überspringen die Warteschlange (die Version des Eintrags war bereits freigegeben).

  • rejected

    Das Team hat diese Code-Revision abgelehnt. Lade stattdessen eine neue Version hoch.

Kontingent: 2 aktive Skripte pro Server kostenlos, 10 mit Premium. Sinkt das Kontingent, werden die ältesten aktiven Skripte darüber automatisch deaktiviert und du wirst im Skript-Log-Kanal benachrichtigt.

Handler & Events

Registriere Handler mit bot:on(event, fn). Das Skript meldet seine registrierten Events beim Laden, und nur diese Events werden ihm zugestellt. Unbekannte Event-Namen lassen das Laden fehlschlagen, und ein Skript darf höchstens 8 Handler pro Event registrieren.

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

Nachrichtenähnliche Events (message_*, automod_check) übergeben ein Message-Objekt mit Methoden, siehe Message-Objekt.

Host-Aufrufe geben die Kontrolle ab: Während ein Handler auf bot:kv_get und ähnliche Aufrufe wartet, läuft auf demselben Skript kein zweites Event. Neue Events warten in einer Warteschlange mit 32 Plätzen, bei Überlauf fällt das älteste Event weg, und dauerhafte Überflutung stellt das Skript unter Quarantäne.

guild_id wird immer in die Payload eingefügt. Events werden nur an Handler zugestellt, die mit bot:on registriert sind. Unbekannte Event-Namen lassen das Laden mit einem Policy-Fehler fehlschlagen. Es gibt kein voice_state-Event.

Event-Payloads

Jede Payload enthält guild_id. Die Felder sind Tabellen in Kleinbuchstaben mit snake_case.

  • message_create

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

    Wird vor Tags und Auto-Respondern ausgelöst. Anhänge: bis zu 8 Einträge { id, filename, size }. referenced_id ist 0, wenn es keine Antwort-Referenz gibt.

  • message_update

    same as message_create

  • message_delete

    channel_id, id

  • message_delete_bulk

    channel_id, ids

    ids ist ein Array der gelöschten Nachrichten-IDs.

  • guild_member_add

    user_id

    Bot-Accounts werden übersprungen.

  • guild_member_remove

    user_id

  • guild_member_update

    user_id

  • reaction_add

    channel_id, id, author_id, user_id, emoji, emoji_id

    Reaktionen von Bots werden übersprungen. emoji ist der Name, emoji_id ist 0 bei Unicode-Emojis.

  • 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

    Wird vor der nativen Willkommensnachricht ausgelöst. Nur Beitritte von Nicht-Bots.

  • goodbye_before

    user_id

    Wird vor der nativen Abschiedsnachricht ausgelöst.

  • ticket_created

    ticket_id, user_id, channel_id

  • automod_check

    content, channel_id, author_id, id

    Wird vor dem nativen AutoMod ausgelöst. msg:escalate() löscht die laufende Nachricht; ein natives Ablehnen gewinnt immer; msg:allow() hat keine Wirkung.

  • webhooks_update

    channel_id

  • emoji_update

    count, ids

    ids auf 8 begrenzt.

  • tick

    unix

    Alle 60 Sekunden, nur an Skripte zugestellt, die bot:on("tick") registriert haben. unix ist die Unix-Zeit.

Host-APIs

Diese Aufrufe geben per IPC an den Bot ab. Ratenlimits pro Skript: 30 Host-Aufrufe / 10 s sowie 5 Discord-Sendungen / 10 s (reply, channel_send, react, edit, delete, pin, escalate, http_get). Cache-Lesezugriffe wie member_get und guild_get erreichen Discord nie direkt.

  • bot:log(msg)

    Schreibt in den Skript-Log-Kanal und das Fehlerprotokoll. Zählt zum Limit von 30 Aufrufen / 10 s.

  • bot:kv_get(key)

    Gibt den gespeicherten JSON-Wert oder nil zurück. Keys: 1–64 Zeichen [A-Za-z0-9_-].

  • bot:kv_set(key, value)

    Speichert bis zu 8 KiB JSON. 256 Keys pro Skript, 1 MiB pro Skript (8 MiB Premium), 8 MiB pro Server.

  • bot:kv_del(key)

    Löscht einen Key.

  • bot:kv_keys()

    Gibt bis zu 256 Keys sortiert zurück.

  • bot:kv_incr(key, delta?)

    Erhöht eine gespeicherte JSON-Zahl, Standard-Delta 1, Delta auf ±1.000.000.000 begrenzt.

  • bot:db_exec(sql, binds?)

    Führt ein Schreib- oder Schema-Statement (CREATE TABLE/INDEX, INSERT, UPDATE, DELETE, DROP) auf der Skript-Datenbank des Servers aus. Optionale Binds: Array aus nil, boolean, number oder string, bis zu 8. Gibt { changes, last_insert_rowid } zurück. Quota-, Busy- oder Limit-Probleme liefern { error, kind } statt eines Fehlers; verbotene Statements lösen einen Policy-Fehler aus.

  • bot:db_query(sql, binds?)

    Führt ein SELECT mit optionalen Binds aus und liefert bis zu 50 Zeilen, jede als Tabelle mit Spaltennamen als Schlüssel. Immer LIMIT verwenden. Bei einem Fehler kommt { error, kind } zurück, also rows.error prüfen.

  • bot:http_get(url)

    Nur HTTPS-GET. Der Host muss auf der Allowlist des Servers stehen. Gibt { status, body, truncated } zurück; der Body ist auf 16 KiB begrenzt. Die Wartezeit zählt nicht zu den 100 ms Lua-Laufzeit.

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

    Sendet in jeden Kanal des eigenen Servers, in dem der Bot schreiben darf. Es gelten dieselben Inhaltsregeln wie bei msg:reply.

  • bot:channel_get(channel_id)

    Cache-Lesezugriff: { id, name, type, parent_id }. Nur der eigene Server.

  • bot:member_get(user_id)

    Cache-Lesezugriff: { id, nick, bot, roles (bis zu 20) }.

  • bot:role_get(role_id)

    Cache-Lesezugriff: { id, name, position, color }.

  • bot:guild_get()

    Cache des eigenen Servers: { id, name, owner_id, member_count }.

  • bot:self()

    Der Bot-Nutzer: { id, username, bot }.

  • bot:has_role(user_id, role_id)

    Cache-Lesezugriff, gibt { has: true|false } zurück.

  • bot:channels()

    Cache-Liste der Kanäle des Servers (max. 50).

  • bot:roles()

    Cache-Liste der Rollen des Servers (max. 50).

  • bot:now()

    Unix-Zeit in Sekunden.

  • bot:id()

    Die UUID dieses Skripts. Gibt die Kontrolle nicht ab und zählt nicht zu den Ratenlimits.

  • bot:message_get(channel_id, message_id)

    Cache-Lesezugriff auf den Nachrichten-Cache des Bots (kein Netzwerkabruf).

  • bot:can_send(channel_id)

    Gibt { ok: true|false } zurück, ob der Bot in diesem Kanal senden darf.

Was bot:http_get akzeptiert

  • Nur HTTPS

    http://-URLs werden abgelehnt. Keine Userinfo (user:pass@host).

  • Allowlist des Servers

    Der Host muss auf der HTTP-Allowlist des Servers stehen (Skripte-Seite, bis zu 32 Hosts). Du verwaltest sie selbst.

  • Keine Infrastruktur-Hosts

    IP-Adressen, localhost, *.local und *.internal werden immer abgelehnt.

  • Antwort

    { status, body, truncated }, Body auf 16 KiB begrenzt. Timeout 3 s. Teilt sich das Discord-Kontingent von 5 pro 10 s.

Message-Objekt

Nachrichten-Events übergeben eine msg-Tabelle plus die Payload-Felder. Die Methoden wirken nur auf die Event-Nachricht selbst, beliebige Nachrichten kannst du nicht abrufen und bearbeiten.

Inhaltsregeln überall: Text 1 bis 2000 Bytes, höchstens ein Embed { title, description, color }, Erwähnungen gelten nur für den Autor des Events, @everyone und @here werden entfernt. Keine Dateien, keine Sticker.

  • msg:reply(text, embed?)

    Antwortet im Kanal des Events. Erwähnungen gelten nur für den Autor des Events; @everyone / @here werden entfernt. Text 1–2000 Bytes, höchstens ein Embed.

  • msg:react(emoji)

    Fügt der Event-Nachricht eine Reaktion hinzu.

  • msg:unreact(emoji)

    Entfernt die eigene Reaktion des Skripts.

  • msg:edit(text, embed?)

    Bearbeitet die laufende Nachricht. Es gelten dieselben Inhaltsregeln wie bei reply.

  • msg:delete()

    Löscht die laufende Nachricht.

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

    Hängt die laufende Nachricht an oder löst sie.

  • msg:escalate()

    Nur bei automod_check: löscht die laufende Nachricht (Zusatzaktion mit Ratenlimit). Entscheidungen des nativen AutoMod gewinnen immer.

  • msg:allow()

    Nur bei automod_check: markiert die Nachricht als erlaubt. Ein natives Ablehnen lässt sich damit nicht aufheben, es ist praktisch eine wirkungslose Markierung.

Limits & Kontingente

Alle Limits stehen in einer Konfigurationsdatei, die Bot und Runner gemeinsam nutzen. Sie werden zur Laufzeit durchgesetzt und nicht nur hier dokumentiert. Die Zahlen sind die aktuellen Standardwerte.

  • Sprache

    Lua 5.5, nur Text-Chunks (Bytecode wird abgelehnt). Kein io/os.file/require/load/debug/coroutine.

  • Rechenzeit

    ~100 ms Laufzeit pro Handler, 10.000.000 Instruktionen (Count-Hook).

  • Speicher

    2 MB pro Skript kostenlos, 8 MB Premium.

  • Code-Größe

    64 KiB UTF-8 .lua, keine NUL-Bytes, kein Bytecode.

  • Host-Aufrufe

    30 pro 10 s und Skript (bot:log eingeschlossen).

  • Discord-Sendungen

    5 pro 10 s und Skript (reply, channel_send, react/unreact, edit, delete, pin/unpin, escalate, http_get). 8 Ablehnungen in 10 s → Quarantäne.

  • Handler

    Max. 8 pro Event und Skript. Unbekannte Event-Namen werden beim Laden abgelehnt.

  • Event-Warteschlange

    32 Plätze pro Skript, bei Überlauf fällt das älteste Event weg. 8+ verworfene Events in 10 s → Quarantäne.

  • HTTP

    HTTPS + Server-Allowlist, Timeout 3 s, Body 16 KiB, keine IP-/localhost-/internen Hosts, keine Userinfo.

  • KV

    Keys 1–64 [A-Za-z0-9_-], Werte ≤ 8 KiB JSON, 256 Keys/Skript, 1 MiB/Skript (8 MiB Premium), 8 MiB/Server.

  • Datenbank

    Eine SQLite-Datei pro Server, geteilt von allen Skripten: 1 MiB (8 MiB Premium), ein Statement pro Aufruf, SQL ≤ 2 KiB, ≤ 8 Binds, ≤ 50 Zeilen, 20 ms pro Statement, 64 Tabellen und Indizes. Kein ATTACH, PRAGMA, keine Transaktionen, Trigger, Views oder WITH. Jedes Skript des Servers kann jede Tabelle lesen und löschen.

  • Aktive Skripte

    2 pro Server kostenlos, 10 Premium. Überzählige, älteste aktive Skripte werden automatisch deaktiviert.

  • Versionen

    Unbegrenzt viele pro Skript; jede neue Version bleibt ausstehend, bis das Team sie freigibt.

  • Erwähnungen

    Nur der Autor des Events. @everyone / @here werden entfernt. Keine Dateien, keine Sticker.

  • Isolation

    3 Fehler (Timeout/Speicher/Policy/Runtime) → eigener isolierter Worker (max. 4), sonst Quarantäne + Entladen.

  • Fehlerprotokoll

    Die letzten 24 h sind im Dashboard sichtbar, Einträge werden nach 7 Tagen gelöscht.

  • Verboten

    Kein voice_state, keine mod_*-APIs (Bann/Kick/Timeout), kein serverübergreifender Zugriff, keine Datenbank/kein Dateisystem, keine Monetarisierung.

  • AutoMod

    Das native AutoMod läuft zuerst und gewinnt immer gegen Skripte.

Validierung

Bevor eine Version gespeichert wird, führt das Backend statische Prüfungen aus. Der Testlauf macht dasselbe, ohne zu speichern. Die Sandbox selbst ist die zweite Verteidigungslinie: Verbotene Globals existieren zur Laufzeit schlicht nicht.

  • Größe / Kodierung

    Nicht leer, ≤ 64 KiB, UTF-8 ohne NUL-Bytes.

  • Bytecode

    Alles, was mit der Lua-Bytecode-Signatur beginnt, wird abgelehnt.

  • Verbotene APIs

    Der statische Scan lehnt ab: load(, loadfile, dofile, require(, os.execute, io.open, debug., string.dump, loadstring, getfenv, setfenv, package., coroutine., ffi., jit.

  • Laufzeit-Sandbox

    Selbst wenn etwas den statischen Scan passiert, stellt die Sandbox diese Globals schlicht nicht bereit. Der Aufruf schlägt mit einem Policy-Fehler fehl.

Verfügbare Built-ins: die Lua-Standardbibliothek ohne Dateizugriff. string, table, math, tostring, tonumber, type, pairs, ipairs, os.time, os.clock, os.difftime und bot.

Fehler & Quarantäne

Jeder Handler-Fehler wird mit einer Art erfasst und auf der Skript-Detailseite angezeigt (letzte 24 h). Einträge werden nach 7 Tagen gelöscht.

  • runtime

    Ein Lua-Fehler in deinem Handler (nil-Zugriff, falsche Argumente, …). Die Meldung enthält den Lua-Fehlertext.

  • timeout

    Der Handler hat die 100 ms Laufzeit überschritten oder ein IPC-Aufruf lief in ein Timeout (1 s).

  • memory

    Das Skript hat sein Speicherlimit überschritten.

  • policy

    Das Skript hat etwas Unerlaubtes versucht: unbekanntes Event, zu viele Handler, nicht erlaubte API, zu große Payload.

Wiederholter Missbrauch eskaliert automatisch: 3 Fehler (Timeout, Speicher, Policy, Runtime) verschieben das Skript in einen eigenen isolierten Worker. Hält das Fehlverhalten an oder ist kein isolierter Platz frei (max. 4), wird das Skript unter Quarantäne gestellt: entladen und im Dashboard markiert. Ratenüberflutung (8 abgelehnte Discord-Sendungen oder 8 verworfene Events in 10 s) führt sofort zur Quarantäne. Du wirst im Skript-Log-Kanal oder per DM benachrichtigt.

Betriebsseite: Der Bot lässt sich über einen Kill-Switch ohne Neustart global pausieren, und ein scripts:updated-Signal lädt geänderte Versionen innerhalb von Sekunden neu. Beides sind Team-Aktionen.

Store

Im Store kannst du Skripte öffentlich teilen und Skripte anderer Server installieren. Veröffentlichen und Installieren erzeugen jeweils Kopien. Wer einen Eintrag installiert, erhält ein eigenständiges Skript auf seinem Server, das nicht mit dem Code des Autors verknüpft bleibt.

  • Veröffentlichen

    Erfordert ein aktives Skript mit freigegebener Version, eine Beschreibung (10–512 Zeichen) und 1–8 Tags ([a-z0-9-], 1–24 Zeichen). Vor der ersten Veröffentlichung musst du die Store-Nutzungsbedingungen einmal akzeptieren.

  • Installieren

    Beim Installieren wird der Eintrag als eigenes Skript in deinen Server kopiert (Entwurf, vorab freigegebene Version, eigener Name mit Suffix, falls vergeben). KV wird nicht kopiert. Der Installationszähler des Eintrags steigt.

  • Zurückziehen

    Entfernt den Eintrag aus dem Store. Bestehende Kopien bleiben erhalten und laufen weiter, erhalten aber keine Updates mehr.

  • Kein Geld

    Verkäufe, Paywalls, Spenden-/Krypto-Links und ähnliche Monetarisierung in Beschreibung oder Code werden beim Veröffentlichen abgelehnt und können zur Sperrung des Skripts führen.

  • Meldungen

    Jeder mit Serverzugriff kann einen Eintrag mit Begründung melden. Das Team sieht eine Warteschlange offener Meldungen und schließt sie nach der Prüfung.

  • Limits

    Die Store-Liste zeigt bis zu 50 Einträge pro Abfrage (Suche, Tag, Sortierung nach Installationen oder Neueste). Für Installationen gilt das normale Skript-Kontingent des Servers.

Beispiele

Wähle ein Beispiel, um den Code zu sehen.