ドキュメント

Lua 5.5 · API v0.1

概要

スクリプトは、サーバーの機能を拡張する小さなLuaプログラムです。Botから完全に分離された、隔離されたLua 5.5のサンドボックスプロセスで実行されます。スクリプトがデータベース、ファイルシステム、他のサーバーに触れることはありません。すべての機能は、ホワイトリスト化されたホストAPIを通じて、IPC経由でBotに処理が戻されます。

スクリプトごとに1つのサンドボックス

スクリプトが読み込まれていなければ、プロセスは存在しません。

レート制限付きのホスト呼び出し

bot:* と msg:* の呼び出しは、Luaではなく、Botが権限を確認します。

ネイティブ機能が優先

AutoMod、ウェルカム、チケットは常に動作します。スクリプトは機能を装飾するもので、置き換えはしません。

エラーは封じ込められる

クラッシュしたスクリプトはアンロードまたは隔離され、Botは動き続けます。

ブラウザ内のエディタはありません。実際のエディタで .lua を書き、サーバーのスクリプトページからアップロードしてください。

はじめに

  1. 1

    ローカルで .lua ファイルを書く

    VS Code、Neovim、お好みのエディタで。
  2. 2

    ローカルで確認する(任意)

    script_runner --check file.lua を実行すると check: ok と表示されます。
  3. 3

    アップロードする

    サーバーのスクリプトページを開き、コードを貼り付けるか .lua ファイルをアップロードします(最大64 KiB)。
  4. 4

    ドライラン

    何も実行せずに、サイズ、エンコーディング、バイトコード、禁止APIを検証します。
  5. 5

    有効化する

    バージョンがスタッフに承認されるのを待ち、有効化を押します。Botは数秒以内に読み込みます。
  6. 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. 1

    書く

    VS Code、Neovim、お好みのエディタで .lua ファイルをローカルに作成します。ブラウザ内のエディタは、設計上ありません。
  2. 2

    ドライラン / アップロード

    サーバーのスクリプトページで、コードを貼り付けるか .lua ファイルをアップロードします(64 KiB以下、バイナリ不可)。ドライランは何も実行せずに、サイズ、エンコーディング、バイトコード、禁止APIを検証します。
  3. 3

    審査

    新しいスクリプトと新しいバージョンはすべて保留から始まります。実行される前に、スタッフがそのコードの改訂を承認または却下します。
  4. 4

    有効化

    承認済みのバージョンを有効化します。保留中または却下されたバージョンの有効化は拒否されます。Botに読み込まれるのは有効なバージョンのみです。
  5. 5

    実行

    Botはバージョン(コード + SHA-256、ランナーが検証)を、隔離されたLua 5.5のサンドボックスに読み込みます。イベントは、bot:on で登録されたハンドラーにのみ配信されます。
  6. 6

    更新 / ロールバック

    新しいバージョンをアップロードする(再び保留)か、古い承認済みバージョンを有効化してロールバックします。KVストレージは残ります。
  7. 7

    無効化 / 削除

    無効化すると、読み込みが即座に止まります。「すべて無効化」はサーバーのすべての有効なスクリプトに作用します。削除すると、スクリプト、そのバージョン、KV、エラーが削除されます。

スクリプトのステータス

各スクリプトにバッジとして表示されます。

  • 下書き

    作成済みで、有効なバージョンはまだありません。

  • 保留中

    何か(例:最初の承認)を待っている状態。下書きとして動作することはありません。

  • 有効

    承認済みのバージョンが有効化され、Botに読み込まれています。

  • 却下

    スタッフが却下しました。スクリプトは読み込まれません。

  • 無効

    手動で無効化されました(ご自身、スタッフ、「すべて無効化」、またはクォータによる除外)。アンロードされています。

  • 隔離中

    上限の悪用(レートの大量送信、キューの大量送信、繰り返しの障害)により自動停止。アンロードされ、スタッフの対応が必要です。

バージョンの審査

コードの改訂はすべてスタッフの審査を受けます。

  • 保留中

    スタッフがまだ判断していません。有効化できません。

  • 承認済み

    有効化できます。インストール済みのストアスクリプトの新しいアップロードはキューをスキップします(掲載バージョンは承認済みのため)。

  • 却下

    スタッフがこのコードの改訂を却下しました。代わりに新しいバージョンをアップロードしてください。

クォータ:サーバーあたり、無料で2つの有効なスクリプト、プレミアムで10。クォータが減った場合、超過分の古い有効なスクリプトは自動的に無効化され、スクリプトログチャンネルで通知されます。

ハンドラーとイベント

ハンドラーは bot:on(event, fn) で登録します。スクリプトは読み込み時に登録済みのイベントを報告し、それらのイベントのみが配信されます。未知のイベント名は読み込みに失敗し、1つのスクリプトがイベントごとに登録できるハンドラーは最大8つです。

luaLua
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件の掲載を表示します(検索、タグ、インストール数または新着で並べ替え)。インストールには、サーバーの通常のスクリプトクォータが適用されます。

サンプル

サンプルを選択すると、コードが表示されます。