概要
スクリプトは、サーバーの機能を拡張する小さなLuaプログラムです。Botから完全に分離された、隔離されたLua 5.5のサンドボックスプロセスで実行されます。スクリプトがデータベース、ファイルシステム、他のサーバーに触れることはありません。すべての機能は、ホワイトリスト化されたホストAPIを通じて、IPC経由でBotに処理が戻されます。
スクリプトが読み込まれていなければ、プロセスは存在しません。
bot:* と msg:* の呼び出しは、Luaではなく、Botが権限を確認します。
AutoMod、ウェルカム、チケットは常に動作します。スクリプトは機能を装飾するもので、置き換えはしません。
クラッシュしたスクリプトはアンロードまたは隔離され、Botは動き続けます。
ブラウザ内のエディタはありません。実際のエディタで .lua を書き、サーバーのスクリプトページからアップロードしてください。
はじめに
- 1
ローカルで .lua ファイルを書く
VS Code、Neovim、お好みのエディタで。 - 2
ローカルで確認する(任意)
script_runner --check file.luaを実行するとcheck: okと表示されます。 - 3
アップロードする
サーバーのスクリプトページを開き、コードを貼り付けるか .lua ファイルをアップロードします(最大64 KiB)。 - 4
ドライラン
何も実行せずに、サイズ、エンコーディング、バイトコード、禁止APIを検証します。 - 5
有効化する
バージョンがスタッフに承認されるのを待ち、有効化を押します。Botは数秒以内に読み込みます。 - 6
ログチャンネルを設定する
bot:logの出力とエラー通知をそこで受け取ります。
ローカルCLI
ランナーはBotが使うものと同じバイナリです。
- script_runner --check file.lua
実行せずに、構文と禁止APIの静的チェックを行います。'check: ok' と表示されます。
- script_runner --eval 'code'
スニペットをローカルのサンドボックスで実行します。エラーは種別 + メッセージが表示されます。
- script_runner --self-test
サンドボックス、プロトコル、ソケットのセルフテストを実行します。
ライフサイクルと審査
スクリプトはバージョン管理されます。バージョンは送信時に固定され、SHA-256で識別されます。有効化できるのは承認済みのバージョンのみで、Botに読み込まれるのは有効なバージョンのみです。
- 1
書く
VS Code、Neovim、お好みのエディタで .lua ファイルをローカルに作成します。ブラウザ内のエディタは、設計上ありません。 - 2
ドライラン / アップロード
サーバーのスクリプトページで、コードを貼り付けるか .lua ファイルをアップロードします(64 KiB以下、バイナリ不可)。ドライランは何も実行せずに、サイズ、エンコーディング、バイトコード、禁止APIを検証します。 - 3
審査
新しいスクリプトと新しいバージョンはすべて保留から始まります。実行される前に、スタッフがそのコードの改訂を承認または却下します。 - 4
有効化
承認済みのバージョンを有効化します。保留中または却下されたバージョンの有効化は拒否されます。Botに読み込まれるのは有効なバージョンのみです。 - 5
実行
Botはバージョン(コード + SHA-256、ランナーが検証)を、隔離されたLua 5.5のサンドボックスに読み込みます。イベントは、bot:on で登録されたハンドラーにのみ配信されます。 - 6
更新 / ロールバック
新しいバージョンをアップロードする(再び保留)か、古い承認済みバージョンを有効化してロールバックします。KVストレージは残ります。 - 7
無効化 / 削除
無効化すると、読み込みが即座に止まります。「すべて無効化」はサーバーのすべての有効なスクリプトに作用します。削除すると、スクリプト、そのバージョン、KV、エラーが削除されます。
スクリプトのステータス
各スクリプトにバッジとして表示されます。
- 下書き
作成済みで、有効なバージョンはまだありません。
- 保留中
何か(例:最初の承認)を待っている状態。下書きとして動作することはありません。
- 有効
承認済みのバージョンが有効化され、Botに読み込まれています。
- 却下
スタッフが却下しました。スクリプトは読み込まれません。
- 無効
手動で無効化されました(ご自身、スタッフ、「すべて無効化」、またはクォータによる除外)。アンロードされています。
- 隔離中
上限の悪用(レートの大量送信、キューの大量送信、繰り返しの障害)により自動停止。アンロードされ、スタッフの対応が必要です。
バージョンの審査
コードの改訂はすべてスタッフの審査を受けます。
- 保留中
スタッフがまだ判断していません。有効化できません。
- 承認済み
有効化できます。インストール済みのストアスクリプトの新しいアップロードはキューをスキップします(掲載バージョンは承認済みのため)。
- 却下
スタッフがこのコードの改訂を却下しました。代わりに新しいバージョンをアップロードしてください。
クォータ:サーバーあたり、無料で2つの有効なスクリプト、プレミアムで10。クォータが減った場合、超過分の古い有効なスクリプトは自動的に無効化され、スクリプトログチャンネルで通知されます。
ハンドラーとイベント
ハンドラーは bot:on(event, fn) で登録します。スクリプトは読み込み時に登録済みのイベントを報告し、それらのイベントのみが配信されます。未知のイベント名は読み込みに失敗し、1つのスクリプトがイベントごとに登録できるハンドラーは最大8つです。
bot:on("message_create", function(msg)
if msg.content == "!ping" then
msg:reply("pong")
end
end)メッセージ系のイベント(message_*、automod_check)は、メソッドを持つメッセージオブジェクトを渡します。メッセージオブジェクトを参照してください。
ホスト呼び出しは処理を一時中断します。ハンドラーが bot:kv_get などの呼び出しを待つ間、同じスクリプトで2つ目のイベントは実行されません。新しいイベントは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 は削除されたメッセージIDの配列です。
- guild_member_add
user_id
Botアカウントはスキップされます。
- guild_member_remove
user_id
- guild_member_update
user_id
- reaction_add
channel_id, id, author_id, user_id, emoji, emoji_id
Botのリアクションはスキップされます。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
ネイティブのウェルカムメッセージより先に発火します。Bot以外の参加のみ。
- goodbye_before
user_id
ネイティブのお別れメッセージより先に発火します。
- ticket_created
ticket_id, user_id, channel_id
- automod_check
content, channel_id, author_id, id
ネイティブのAutoModより先に発火します。msg:escalate() は処理中のメッセージを削除し、ネイティブの拒否が常に優先され、msg:allow() は何もしません。
- webhooks_update
channel_id
- emoji_update
count, ids
ids は最大8件。
- tick
unix
60秒ごとに、bot:on("tick") を登録したスクリプトにのみ配信されます。unix はUNIX時間です。
ホストAPI
これらの呼び出しは、IPC経由でBotに処理を戻します。スクリプトごとのレート制限:ホスト呼び出し30回 / 10秒、さらにDiscord送信5回 / 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 KiBのJSONを保存します。スクリプトあたり256キー、1 MiB(プレミアムは8 MiB)、サーバーあたり8 MiB。
- bot:kv_del(key)
キーを1つ削除します。
- bot:kv_keys()
最大256個のキーを並べ替えて返します。
- bot:kv_incr(key, delta?)
保存されているJSONの数値を加算します。デルタの既定値は1で、±1,000,000,000に制限されます。
- bot:db_exec(sql, binds?)
サーバーのスクリプトデータベースで、書き込みまたはスキーマの文を1つ(CREATE TABLE/INDEX、INSERT、UPDATE、DELETE、DROP)実行します。任意のバインド:nil、boolean、number、stringの配列で最大8個。{ changes, last_insert_rowid } を返します。クォータ、ビジー、上限の問題は例外を投げず { error, kind } を返し、禁止された文はポリシーエラーを投げます。
- bot:db_query(sql, binds?)
任意のバインド付きでSELECTを1つ実行し、最大50行を返します。各行は列名をキーにしたテーブルです。必ずLIMITを使ってください。失敗時は { error, kind } を返すので、rows.error を確認してください。
- bot:http_get(url)
HTTPSのGETのみ。ホストはサーバーの許可リストに含まれている必要があります。{ status, body, truncated } を返し、bodyは16 KiBまで。待機時間は100ミリ秒のLua実時間にカウントされません。
- bot:channel_send(channel_id, text, embed?)
Botが書き込める自分のサーバーの任意のチャンネルに送信します。コンテンツのルールは 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()
Botユーザー:{ 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のメッセージキャッシュからの読み取り(ネットワーク取得なし)。
- bot:can_send(channel_id)
Botがそのチャンネルで送信できるかどうかを { ok: true|false } で返します。
bot:http_get が受け付けるもの
- HTTPSのみ
http:// のURLは拒否されます。ユーザー情報(user:pass@host)は不可。
- サーバーの許可リスト
ホストは、サーバーのHTTP許可リスト(スクリプトページ、最大32ホスト)に含まれている必要があります。管理はご自身で行います。
- インフラのホストは不可
IPリテラル、localhost、*.local、*.internal は常に拒否されます。
- レスポンス
{ status, body, truncated }。bodyは最大16 KiB。タイムアウトは3秒。10秒あたり5回のDiscordバケットを共有します。
メッセージオブジェクト
メッセージイベントは、msg テーブルとペイロードのフィールドを渡します。メソッドが作用するのは処理中のイベントのメッセージのみで、任意のメッセージを取得して編集することはできません。
コンテンツの共通ルール:テキストは1〜2000バイト、埋め込みは最大1つ { title, description, color }、メンションが解決されるのはイベントの作成者のみ、@everyone と @here は除去されます。ファイルとステッカーは不可。
- msg:reply(text, embed?)
イベントのチャンネルに返信します。メンションが解決されるのはイベントの作成者のみで、@everyone / @here は除去されます。テキストは1〜2000バイト、埋め込みは最大1つ。
- msg:react(emoji)
イベントのメッセージにリアクションを追加します。
- msg:unreact(emoji)
スクリプト自身のリアクションを削除します。
- msg:edit(text, embed?)
処理中のメッセージを編集します。コンテンツのルールは返信と同じです。
- msg:delete()
処理中のメッセージを削除します。
- msg:pin() / msg:unpin()
処理中のメッセージをピン留め/解除します。
- msg:escalate()
automod_check のみ:処理中のメッセージを削除します(追加の操作、レート制限あり)。ネイティブのAutoModの判断が常に優先されます。
- msg:allow()
automod_check のみ:メッセージを許可済みとしてマークします。ネイティブの拒否は取り消せないため、実質的には何もしないマーカーです。
上限とクォータ
すべての上限は、Botとランナーが共有する1つの設定ファイルにあります。ここに記載しているだけでなく、実行時に強制されます。数値は現在のデフォルトです。
- 言語
Lua 5.5、テキストのチャンクのみ(バイトコードは拒否)。io/os.file/require/load/debug/coroutine は使えません。
- 計算
ハンドラーあたり実時間で約100ミリ秒、1,000万命令(カウントフック)。
- メモリ
スクリプトあたり、無料で2 MB、プレミアムで8 MB。
- コードサイズ
64 KiBのUTF-8 .lua。NULバイトなし、バイトコードなし。
- ホスト呼び出し
スクリプトあたり10秒に30回(bot:log を含む)。
- Discord送信
スクリプトあたり10秒に5回(reply、channel_send、react/unreact、edit、delete、pin/unpin、escalate、http_get)。10秒間に8回拒否されると隔離されます。
- ハンドラー
スクリプトごと、イベントごとに最大8つ。未知のイベント名は読み込み時に拒否されます。
- イベントキュー
スクリプトあたり深さ32、あふれると最も古いものを破棄。10秒間に8回以上破棄されると隔離されます。
- HTTP
HTTPS + サーバーの許可リスト、タイムアウト3秒、body 16 KiB、IP/localhost/内部ホストは不可、ユーザー情報なし。
- KV
キー 1〜64文字 [A-Za-z0-9_-]、値は8 KiB以下のJSON、スクリプトあたり256キー・1 MiB(プレミアムは8 MiB)、サーバーあたり8 MiB。
- データベース
サーバーごとに1つのSQLiteファイルを、そのすべてのスクリプトで共有:1 MiB(プレミアムは8 MiB)、呼び出しごとに1文、SQLは2 KiB以下、バインドは8個以下、行は50以下、1文あたり20ミリ秒、テーブルとインデックスは64個。ATTACH、PRAGMA、トランザクション、トリガー、ビュー、WITHは不可。サーバー上のどのスクリプトも、すべてのテーブルを読み取り・削除できます。
- 有効なスクリプト
サーバーあたり、無料で2、プレミアムで10。超過分の古い有効なスクリプトは自動的に無効化されます。
- バージョン
スクリプトあたりの数に制限はありませんが、新しいバージョンはすべて、スタッフが承認するまで保留になります。
- メンション
イベントの作成者のみ。@everyone / @here は除去。ファイルとステッカーは不可。
- 隔離
3回の障害(タイムアウト/メモリ/ポリシー/ランタイム)で専用の隔離ワーカーへ(最大4)、それ以外は隔離してアンロード。
- エラーログ
ダッシュボードで直近24時間を表示し、エントリは7日後に削除。
- 禁止事項
voice_state なし、mod_* API(ban/kick/timeout)なし、サーバー間アクセスなし、データベース/ファイルシステムなし、収益化なし。
- AutoMod
ネイティブのAutoModが先に動作し、常にスクリプトより優先されます。
検証
バージョンが保存される前に、バックエンドが静的チェックを実行します。ドライランは保存せずに同じチェックを行います。サンドボックス自体が第二の防衛線で、禁止されたグローバルは実行時に存在しません。
- サイズ / エンコーディング
空でなく、64 KiB以下、NULバイトなしのUTF-8。
- バイトコード
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)、スクリプトは隔離され、アンロードされてダッシュボードに表示されます。レートの大量送信(10秒間にDiscord送信の拒否8回、またはキュー破棄8回)は即座に隔離されます。スクリプトログチャンネルまたはDMで通知されます。
運用面:Botは再起動なしでキルスイッチによりグローバルに一時停止でき、scripts:updated シグナルで変更されたバージョンが数秒以内に再読み込みされます。どちらもスタッフの操作です。
ストア
ストアでは、スクリプトを公開して共有したり、他のサーバーのスクリプトをインストールしたりできます。公開もインストールもコピーを作成します。掲載をインストールすると、サーバー内に独立したスクリプトが作られ、作者のコードとのリンクは残りません。
- 公開
承認済みバージョンを持つ有効なスクリプト、説明(10〜512文字)、1〜8個のタグ([a-z0-9-]、1〜24文字)が必要です。最初の公開の前に、ストア利用規約に一度同意する必要があります。
- インストール
インストールすると、掲載がサーバー内に独立したスクリプトとしてコピーされます(下書き、事前承認済みのバージョン、名前が重複する場合は接尾辞付き)。KVはコピーされません。掲載のインストール数が増えます。
- 非公開
掲載をストアから削除します。既存のコピーは残って動作し続けますが、更新は届かなくなります。
- 金銭は不可
説明やコード内での販売、有料の壁、寄付/暗号資産のリンク、類似の収益化は、公開時に拒否され、スクリプトが禁止されることがあります。
- 報告
サーバーにアクセスできる人なら誰でも、理由を添えて掲載を報告できます。スタッフは未対応の報告のキューを確認し、審査後に報告を閉じます。
- 上限
ストアの一覧は、1回の検索で最大50件の掲載を表示します(検索、タグ、インストール数または新着で並べ替え)。インストールには、サーバーの通常のスクリプトクォータが適用されます。
サンプル
サンプルを選択すると、コードが表示されます。