Vue d'ensemble
Les scripts sont de petits programmes Lua qui étendent votre serveur. Ils s'exécutent dans un processus bac à sable Lua 5.5 isolé, complètement séparé du bot. Un script ne touche jamais à la base de données, au système de fichiers ni aux autres serveurs. Chaque capacité passe par une API hôte autorisée qui renvoie la main au bot via IPC.
Aucun script chargé, aucun processus.
Les appels bot:* et msg:* sont vérifiés par le bot selon les capacités, pas par Lua.
AutoMod, l'accueil et les tickets s'exécutent toujours. Les scripts complètent, ils ne remplacent pas.
Un script qui plante est déchargé ou mis en quarantaine, le bot continue de fonctionner.
Il n'y a pas d'éditeur dans le navigateur. Écrivez le .lua dans un vrai éditeur et importez-le depuis la page Scripts de votre serveur.
Pour commencer
- 1
Écrire un fichier .lua en local
VS Code, Neovim ou n'importe quel éditeur. - 2
Le vérifier en local (facultatif)
script_runner --check file.luaaffichecheck: ok. - 3
L'importer
Ouvrez la page Scripts de votre serveur, puis collez le code ou importez le fichier .lua (64 Kio max). - 4
Test à blanc
Valide la taille, l'encodage, le bytecode et les API interdites sans rien exécuter. - 5
Activer
Attendez l'approbation de la version par l'équipe, puis cliquez sur Activer. Le bot la charge en quelques secondes. - 6
Définir un salon de journalisation
Vous y recevez la sortie debot:loget les notifications d'erreur.
CLI locale
Le runner est le même binaire que celui utilisé par le bot.
- script_runner --check file.lua
Vérification statique de la syntaxe et des API interdites sans exécution. Affiche « check: ok ».
- script_runner --eval 'code'
Exécute un extrait dans le bac à sable en local. Les erreurs affichent le type + le message.
- script_runner --self-test
Exécute les autotests du bac à sable, du protocole et des sockets.
Cycle de vie et validation
Les scripts sont versionnés. Une version est figée au moment de sa soumission et identifiée par son SHA-256. Seules les versions approuvées peuvent être activées, et seules les versions actives sont chargées par le bot.
- 1
Écrire
Créez le fichier .lua en local dans VS Code, Neovim ou n'importe quel éditeur. Il n'y a volontairement pas d'éditeur dans le navigateur. - 2
Test à blanc / import
Collez le code ou importez le fichier .lua sur la page Scripts du serveur (≤ 64 Kio, pas de binaire). Le test à blanc valide la taille, l'encodage, le bytecode et les API interdites sans rien exécuter. - 3
Validation
Chaque nouveau script et chaque nouvelle version commence en attente. L'équipe approuve ou refuse la révision exacte du code avant qu'elle puisse s'exécuter. - 4
Activer
Vous activez une version approuvée. L'activation d'une version en attente ou refusée est refusée. Seules les versions actives sont chargées par le bot. - 5
Exécuter
Le bot charge la version (code + SHA-256, vérifié par le runner) dans un bac à sable Lua 5.5 isolé. Les événements ne sont distribués qu'aux gestionnaires enregistrés avec bot:on. - 6
Mise à jour / retour arrière
Importez une nouvelle version (de nouveau en attente) ou activez une ancienne version approuvée pour revenir en arrière. Le stockage KV est conservé. - 7
Désactiver / supprimer
Désactiver arrête immédiatement le chargement, « Tout désactiver » touche tous les scripts actifs du serveur. Supprimer retire le script, ses versions, son KV et ses erreurs.
État du script
Affiché sous forme de badge sur chaque script.
- brouillon
Créé, pas encore de version active.
- en attente
En attente de quelque chose (p. ex. la première approbation) — ne s'exécute jamais en tant que brouillon.
- actif
Une version approuvée est activée et chargée par le bot.
- refusé
Refusé par l'équipe ; le script ne sera pas chargé.
- désactivé
Désactivé manuellement (par vous, l'équipe, « Tout désactiver » ou suppression de quota) — déchargé.
- en quarantaine
Arrêt automatique après un abus de limites (surcharge de débit, surcharge de file, fautes répétées). Déchargé et nécessite l'attention de l'équipe.
Validation des versions
Chaque révision du code est examinée par l'équipe.
- en attente
L'équipe n'a pas encore décidé. Ne peut pas être activée.
- approuvée
Peut être activée. Les nouveaux imports de scripts de la boutique installés passent la file d'attente (la version de la fiche a déjà été approuvée).
- refusée
L'équipe a refusé cette révision du code. Importez plutôt une nouvelle version.
Quota : 2 scripts actifs par serveur en gratuit, 10 avec premium. Si le quota diminue, les plus anciens scripts actifs au-delà sont désactivés automatiquement et vous êtes prévenu via le salon de journalisation des scripts.
Gestionnaires et événements
Enregistrez des gestionnaires avec bot:on(event, fn). Le script déclare ses événements enregistrés au chargement, et seuls ces événements lui sont transmis. Un nom d'événement inconnu fait échouer le chargement, et un script peut enregistrer au maximum 8 gestionnaires par événement.
bot:on("message_create", function(msg)
if msg.content == "!ping" then
msg:reply("pong")
end
end)Les événements de type message (message_*, automod_check) transmettent un objet message avec des méthodes, voir Objet message.
Les appels hôte rendent la main : tant qu'un gestionnaire attend bot:kv_get ou des appels similaires, aucun second événement ne s'exécute sur le même script. Les nouveaux événements attendent dans une file de 32, un dépassement supprime l'événement le plus ancien, et un flot prolongé met le script en quarantaine.
guild_id est toujours injecté dans la charge utile. Les événements ne sont transmis qu'aux gestionnaires enregistrés avec bot:on. Les noms d'événements inconnus font échouer le chargement avec une erreur de politique. Il n'existe pas d'événement voice_state.
Données des événements
Chaque charge utile contient guild_id. Les champs sont des tables en snake_case minuscule.
- message_create
id, guild_id, channel_id, author_id, author, content, attachments, referenced_id
Se déclenche avant les tags/réponses automatiques. Pièces jointes : jusqu'à 8 entrées { id, filename, size }. referenced_id vaut 0 s'il n'y a pas de référence de réponse.
- message_update
same as message_create
- message_delete
channel_id, id
- message_delete_bulk
channel_id, ids
ids est un tableau des identifiants de messages supprimés.
- guild_member_add
user_id
Les comptes de bots sont ignorés.
- guild_member_remove
user_id
- guild_member_update
user_id
- reaction_add
channel_id, id, author_id, user_id, emoji, emoji_id
Les réactions de bots sont ignorées. emoji est le nom, emoji_id vaut 0 pour les emojis Unicode.
- 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
Se déclenche avant le message de bienvenue natif. Uniquement pour les arrivées hors bots.
- goodbye_before
user_id
Se déclenche avant le message de départ natif.
- ticket_created
ticket_id, user_id, channel_id
- automod_check
content, channel_id, author_id, id
Se déclenche avant l'AutoMod natif. msg:escalate() supprime le message en cours ; le refus natif l'emporte toujours ; msg:allow() n'a aucun effet.
- webhooks_update
channel_id
- emoji_update
count, ids
ids limité à 8.
- tick
unix
Toutes les 60 secondes, transmis uniquement aux scripts qui ont enregistré bot:on("tick"). unix est l'heure Unix.
API hôte
Ces appels rendent la main au bot via IPC. Limites de débit par script : 30 appels hôte / 10 s, plus 5 envois Discord / 10 s (reply, channel_send, react, edit, delete, pin, escalate, http_get). Les lectures de cache comme member_get et guild_get n'atteignent jamais Discord directement.
- bot:log(msg)
Écrit dans le salon de journalisation des scripts et dans le journal d'erreurs. Compte dans la limite de 30 appels / 10 s.
- bot:kv_get(key)
Renvoie la valeur JSON stockée ou nil. Clés : 1 à 64 caractères [A-Za-z0-9_-].
- bot:kv_set(key, value)
Stocke jusqu'à 8 Kio de JSON. 256 clés par script, 1 Mio par script (8 Mio en premium), 8 Mio par serveur.
- bot:kv_del(key)
Supprime une clé.
- bot:kv_keys()
Renvoie jusqu'à 256 clés, triées.
- bot:kv_incr(key, delta?)
Incrémente un nombre JSON stocké, delta par défaut 1, delta limité à ±1 000 000 000.
- bot:db_exec(sql, binds?)
Exécute une instruction d'écriture ou de schéma (CREATE TABLE/INDEX, INSERT, UPDATE, DELETE, DROP) sur la base de données de scripts du serveur. Binds facultatifs : un tableau de nil, boolean, number ou string, 8 maximum. Renvoie { changes, last_insert_rowid }. Les problèmes de quota, d'occupation ou de limite renvoient { error, kind } au lieu de lever une erreur ; les instructions interdites lèvent une erreur de politique.
- bot:db_query(sql, binds?)
Exécute un SELECT avec des binds facultatifs et renvoie jusqu'à 50 lignes, chacune étant une table indexée par nom de colonne. Utilisez toujours LIMIT. En cas d'échec, renvoie { error, kind }, donc vérifiez rows.error.
- bot:http_get(url)
GET HTTPS uniquement. L'hôte doit figurer dans la liste d'autorisation du serveur. Renvoie { status, body, truncated } ; le corps est limité à 16 Kio. L'attente ne compte pas dans les 100 ms d'horloge murale Lua.
- bot:channel_send(channel_id, text, embed?)
Envoie dans n'importe quel salon du serveur où le bot peut écrire. Mêmes règles de contenu que msg:reply.
- bot:channel_get(channel_id)
Lecture du cache : { id, name, type, parent_id }. Serveur actuel uniquement.
- bot:member_get(user_id)
Lecture du cache : { id, nick, bot, roles (20 maximum) }.
- bot:role_get(role_id)
Lecture du cache : { id, name, position, color }.
- bot:guild_get()
Cache du serveur actuel : { id, name, owner_id, member_count }.
- bot:self()
L'utilisateur bot : { id, username, bot }.
- bot:has_role(user_id, role_id)
Lecture du cache, renvoie { has: true|false }.
- bot:channels()
Liste en cache des salons du serveur (50 max).
- bot:roles()
Liste en cache des rôles du serveur (50 max).
- bot:now()
Heure Unix en secondes.
- bot:id()
L'UUID de ce script. Ne rend pas la main et ne compte pas dans les limites de débit.
- bot:message_get(channel_id, message_id)
Lecture du cache de messages du bot (sans récupération réseau).
- bot:can_send(channel_id)
Renvoie { ok: true|false } selon que le bot peut envoyer dans ce salon.
Ce qu'accepte bot:http_get
- HTTPS uniquement
Les URL http:// sont refusées. Pas d'identifiants dans l'URL (user:pass@host).
- Liste d'autorisation du serveur
L'hôte doit figurer dans la liste d'autorisation HTTP du serveur (page Scripts, jusqu'à 32 hôtes). Vous la gérez vous-même.
- Pas d'hôtes d'infrastructure
Les adresses IP, localhost, *.local et *.internal sont toujours refusés.
- Réponse
{ status, body, truncated }, corps limité à 16 Kio. Délai d'attente de 3 s. Partage le quota de 5 envois par 10 s de Discord.
Objet message
Les événements de message transmettent une table msg ainsi que les champs de la charge utile. Les méthodes agissent uniquement sur le message de l'événement en cours, vous ne pouvez pas récupérer et modifier des messages arbitraires.
Règles de contenu partout : texte de 1 à 2000 octets, au plus un embed { title, description, color }, les mentions ne sont résolues que pour l'auteur de l'événement, @everyone et @here sont supprimés. Pas de fichiers ni de stickers.
- msg:reply(text, embed?)
Répond dans le salon de l'événement. Les mentions ne sont résolues que pour l'auteur de l'événement ; @everyone / @here sont supprimés. Texte de 1 à 2000 octets, un seul embed maximum.
- msg:react(emoji)
Ajoute une réaction au message de l'événement.
- msg:unreact(emoji)
Retire la réaction du script lui-même.
- msg:edit(text, embed?)
Modifie le message en cours. Mêmes règles de contenu que pour reply.
- msg:delete()
Supprime le message en cours.
- msg:pin() / msg:unpin()
Épingle ou désépingle le message en cours.
- msg:escalate()
automod_check uniquement : supprime le message en cours (action supplémentaire, limitée en débit). Les décisions de l'AutoMod natif l'emportent toujours.
- msg:allow()
automod_check uniquement : marque le message comme autorisé. Cela ne peut pas annuler un refus natif — c'est en pratique un simple marqueur sans effet.
Limites et quotas
Toutes les limites se trouvent dans un fichier de configuration que le bot et le runner partagent. Elles sont appliquées à l'exécution, et pas seulement documentées ici. Les valeurs indiquées sont les valeurs par défaut actuelles.
- Langage
Lua 5.5, fragments de texte uniquement (le bytecode est refusé). Pas de io/os.file/require/load/debug/coroutine.
- Calcul
~100 ms d'horloge murale par gestionnaire, 10 000 000 d'instructions (hook de comptage).
- Mémoire
2 Mo par script en gratuit, 8 Mo en premium.
- Taille du code
.lua UTF-8 de 64 Kio max, sans octets NUL, sans bytecode.
- Appels hôte
30 par 10 s par script (bot:log inclus).
- Envois Discord
5 par 10 s par script (reply, channel_send, react/unreact, edit, delete, pin/unpin, escalate, http_get). 8 refus en 10 s → quarantaine.
- Gestionnaires
8 max par événement et par script. Les noms d'événements inconnus sont refusés au chargement.
- File d'événements
Profondeur de 32 par script, suppression du plus ancien en cas de dépassement. 8 pertes ou plus en 10 s → quarantaine.
- HTTP
HTTPS + liste d'autorisation du serveur, délai de 3 s, corps de 16 Kio, pas d'hôtes IP/localhost/internes, pas d'identifiants dans l'URL.
- KV
Clés de 1 à 64 [A-Za-z0-9_-], valeurs ≤ 8 Kio de JSON, 256 clés/script, 1 Mio/script (8 Mio en premium), 8 Mio/serveur.
- Base de données
Un fichier SQLite par serveur, partagé par tous ses scripts : 1 Mio (8 Mio en premium), une instruction par appel, SQL ≤ 2 Kio, ≤ 8 binds, ≤ 50 lignes, 20 ms par instruction, 64 tables et index. Pas d'ATTACH, PRAGMA, transactions, triggers, vues ni WITH. Chaque script du serveur peut lire et supprimer chaque table.
- Scripts actifs
2 par serveur en gratuit, 10 en premium. Les plus anciens scripts actifs en excès sont désactivés automatiquement.
- Versions
Nombre illimité par script ; chaque nouvelle version reste en attente jusqu'à son approbation par l'équipe.
- Mentions
Auteur de l'événement uniquement. @everyone / @here supprimés. Pas de fichiers ni de stickers.
- Isolation
3 fautes (timeout/memory/policy/runtime) → worker isolé dédié (4 max), sinon quarantaine + déchargement.
- Journal d'erreurs
Dernières 24 h visibles dans le tableau de bord, entrées purgées après 7 jours.
- Interdit
Pas de voice_state, pas d'API mod_* (ban/kick/timeout), pas d'accès inter-serveurs, pas de base de données/système de fichiers, pas de monétisation.
- AutoMod
L'AutoMod natif s'exécute en premier et l'emporte toujours sur les scripts.
Vérifications
Avant qu'une version soit enregistrée, le backend exécute des vérifications statiques. Le test à blanc fait la même chose sans enregistrer. Le bac à sable lui-même est la seconde ligne de défense : les globales interdites n'existent tout simplement pas à l'exécution.
- Taille / encodage
Non vide, ≤ 64 Kio, UTF-8 sans octets NUL.
- Bytecode
Tout ce qui commence par la signature du bytecode Lua est refusé.
- API interdites
L'analyse statique refuse : load(, loadfile, dofile, require(, os.execute, io.open, debug., string.dump, loadstring, getfenv, setfenv, package., coroutine., ffi., jit.
- Bac à sable d'exécution
Même si quelque chose passe à travers l'analyse statique, le bac à sable n'expose tout simplement pas ces globales — l'appel échoue avec une erreur de politique.
Fonctions intégrées disponibles : la bibliothèque Lua standard sans accès aux fichiers. string, table, math, tostring, tonumber, type, pairs, ipairs, os.time, os.clock, os.difftime et bot.
Erreurs et quarantaine
Chaque erreur de gestionnaire est enregistrée avec un type et affichée sur la page de détail du script (dernières 24 h). Les entrées sont purgées après 7 jours.
- runtime
Une erreur Lua dans votre gestionnaire (accès nil, arguments incorrects, …). Le message inclut le texte de l'erreur Lua.
- timeout
Le gestionnaire a dépassé les 100 ms d'horloge murale ou un appel IPC a expiré (1 s).
- memory
Le script a dépassé sa limite de mémoire.
- policy
Le script a tenté une action non autorisée : événement inconnu, trop de gestionnaires, API interdite, charge utile trop volumineuse.
Les abus répétés entraînent une escalade automatique : 3 fautes (timeout, memory, policy, runtime) déplacent le script vers son propre worker isolé. Si le comportement fautif continue, ou si aucun emplacement isolé n'est libre (4 max), le script est mis en quarantaine : déchargé et signalé dans le tableau de bord. Les surcharges de débit (8 envois Discord refusés ou 8 pertes de file en 10 s) entraînent une quarantaine immédiate. Vous êtes prévenu via le salon de journalisation des scripts ou en message privé.
Côté exploitation : le bot peut être mis en pause globalement via un interrupteur d'urgence sans redémarrage, et un signal scripts:updated recharge les versions modifiées en quelques secondes. Ces deux actions sont réservées à l'équipe.
Boutique
La boutique vous permet de partager des scripts publiquement et d'installer des scripts d'autres serveurs. La publication et l'installation créent toutes deux des copies. Installer une fiche crée un script indépendant dans votre serveur, il ne reste pas lié au code de l'auteur.
- Publier
Nécessite un script actif avec une version approuvée, une description (10 à 512 caractères) et 1 à 8 tags ([a-z0-9-], 1 à 24 caractères). Vous devez accepter une fois les conditions d'utilisation de la boutique avant la première publication.
- Installer
L'installation copie la fiche dans votre serveur comme un script à part entière (brouillon, version pré-approuvée, nom propre avec suffixe s'il est déjà pris). Le KV n'est pas copié. Le compteur d'installations de la fiche augmente.
- Dépublier
Retire la fiche de la boutique. Les copies existantes restent et continuent de fonctionner ; elles ne reçoivent simplement plus de mises à jour.
- Pas d'argent
Les ventes, accès payants, liens de dons/crypto et toute monétisation similaire dans la description ou le code sont refusés à la publication et peuvent entraîner le bannissement du script.
- Signalements
Toute personne ayant accès au serveur peut signaler une fiche en indiquant un motif. L'équipe voit une file de signalements ouverts et les clôture après examen.
- Limites
La liste de la boutique affiche jusqu'à 50 fiches par requête (recherche, tag, tri par installations ou nouveauté). Le quota normal de scripts du serveur s'applique aux installations.
Exemples
Sélectionnez un exemple pour voir son code.