メインコンテンツまでスキップ
バージョン: 開発中

Discord Bot

@dicks/discord 0.1.0はGateway Botのフレームワークです。Botのハンドラー、コマンドの解析、プロトコルの状態遷移、RESTの補助機能はDicksモジュールです。汎用のCloudflareアダプターが、WebSocket、永続イベントキュー、上限付きのスケジュール処理、キャンセルを提供します。

examples/discord-bot から始めてください。commands/ping.dicks が !ping に応答し、events/ready.dicks は独立したモジュールです。検証済みのツールチェーンでビルドします。

dicks lock --project examples/discord-bot
dicks build --project examples/discord-bot

設定と機能グループ​

マニフェストは通常のモジュールのインポートを使います。ハンドラーのシグネチャは [Json state, Json context, Int now] -> [Json state, Json socketActions] です。HTTP呼び出しはRESTライブラリを通してハンドラー内で行い、多くのハンドラーは空のソケット操作配列を返します。機能グループは非公開シンボルを持つモジュールのディレクトリで、JavaScriptのコールバックではありません。

{
"target": "discord",
"entry": "app/router.dicks",
"effects": ["env", "resources", "random", "network", "clock"],
"dependencies": {
"@dicks/cloudflare": {"builtin": "cloudflare"},
"@dicks/discord": {"builtin": "discord"}
},
"bot": {
"intents": 33281,
"prefix": "!",
"guilds": ["YOUR_TEST_GUILD_ID"],
"commands": {"ping": {"from": "commands/ping", "name": "handle"}},
"events": {"READY": {"from": "events/ready", "name": "handle"}},
"slashCommands": {"ping": {"from": "commands/slash-ping", "name": "handle"}},
"tasks": {"cleanup": {"from": "tasks/cleanup", "name": "handle", "everyMs": 60000}}
}
}

サーバーIDの仮の値を、数値のDiscord snowflakeへ置き換えてください。例と同じく、参照する各ハンドラーとそのインポートも modules マップに宣言する必要があります。ハンドラーは最大32個です。guilds はコマンドとサーバーイベントを制限し、設定するとダイレクトメッセージのコマンドを無視します。READYと定期タスクは引き続き利用できます。招待されたすべてのサーバーで意図的に動作させる場合にだけ、guilds を省略してください。プレフィックスコマンドは一般のMESSAGE_CREATEハンドラーより優先します。スラッシュコマンドのハンドラーは、コンポーネントではなくアプリケーションコマンドのInteraction(type 2)を受け付けます。

intentsは明示的です。この例はGUILDS・GUILD_MESSAGES・MESSAGE_CONTENT(33281)を要求します。接続前に、Discord開発者ポータルでアプリケーションの特権Message Content Intentを有効にしてください。不正または許可されないintentsでは、Gatewayセッションを停止します。ビルドや起動時に権限を拡張したり、アプリケーションコマンドを登録したりすることはありません。

ライブラリのモジュール​

モジュールエクスポート
commandsparse, tokenize, integer, cooldownReady, hasPermissionBit
modelsid, content, channelId, guildId, name, permissions, author, isBot, mention
restrequest, sendMessage, editMessage, deleteMessage, addRole, fetchChannel, fetchGuild, replyInteraction, deferInteraction, editReply, registerCommands
gatewayreduce, initial(通常は生成したWorkerから呼び出します)

snowflakeの型はTextです。コマンドのトークン分解は、一重・二重引用符とバックスラッシュのエスケープに対応し、完結していない入力を拒否します。メッセージの送信・編集時のメンションは、初期状態で無効です。RESTの結果はHTTPのステータス・ヘッダー・本文を保持します。通信が成功しても、HTTP処理の成功を意味しません。操作を成功と扱う前にステータスを確認してください。再試行するのは明示的な429応答だけで、要求された待ち時間が3.5秒以下の場合に最大2回です。通信の失敗で成否が曖昧な場合や、サーバーの失敗の場合は、書き込みを再試行せずに呼び出し側へ返します。

Interactionのハンドラーは、Discordの初回応答期限である3秒以内に応答または遅延応答し、時間のかかる処理には editReply を使う必要があります。キューが飽和すると期限に間に合わない場合があります。このリリースは、待機中のInteractionを自動で遅延応答しません。GatewayのInteractionトークンはメモリーにだけ保持し、永続イベントキューから取り除きます。アクター再起動後、一時的な認証情報がない未完了のInteractionは、内容を秘匿したハンドラーエラーとともに破棄します。HTTP Interactionのエンドポイントと署名検証は、このGatewayターゲットとは別です。その構築には、プラットフォームのEd25519プリミティブを使えます。

実行時の動作​

制御ループとアプリケーションのキューは独立しています。既定で8つのハンドラーを同時実行でき、サーバーのスコープごとには最大1つです。キューは128イベント、アクターの状態は1MiB、スコープごとの状態は256KiBが上限です。あふれた場合は保存済みのGatewayシーケンスを進めないため、Resume可能な再接続でイベントを回復できます。タスクも同じ上限付きキューで実行し、同じスコープ内では重複実行しません。

Gateway処理は、heartbeatの送信時刻のずらしとACK検査、Identify、Resume、安全なResume URL、切断コードの処理、上限付きの指数的な再接続待機に対応します。新しくIdentifyする前にGet Gateway Botを読み、セッション開始予算を使い切っていれば回復を待ちます。Resumeでは、その予算を消費しません。定期的な起動が重なっても、正常なソケットを維持します。

アクターが中断した場合、永続化した未処理イベントが再実行されることがあります。外部サービスへ書き込むハンドラーには、イベントID・メッセージIDに基づく冪等性の方針が必要です。リモート書き込みとローカルの完了記録の間でクラッシュした場合まで、Discordメッセージを厳密に1回だけ送ることは保証できません。ハンドラーの失敗はイベントIDとエラーコードで記録し、ペイロードや認証情報は含めません。

このリリースには、音声通信、自動シャーディング、サーバー・メンバーの完全なキャッシュ、バケットごとのレート制限の調整器、Interactionの自動遅延応答はありません。discord.pyのAPI全体との互換ではありません。RESTの開始は少なくとも25ms離し、各呼び出しでサーバーの明示的なレート制限を守ります。

Cloudflareへの配備​

マニフェストに cloudflare.accountId、cloudflare.worker、cloudflare.compatibilityDate を追加します。ビルドは、SQLite Durable Object、BOT バインディング、DISCORD_TOKEN secretバインディング、5分ごとの定期トリガーを持つ cloudflare.config.ts を生成します。cf build でCloudflareのバンドルを検証します。デプロイとsecret登録は運用者が明示的に行い、Dicks CLIはデプロイしません。Workerのヘルスエンドポイントへ最初にGETすると、Cronの反映を待たず、未使用のアクターを起動できます。その後のヘルス読み取りは再接続を強制しません。ヘルス情報はソケットの状態、未処理数、最後の切断コード、次回の起動時刻を示し、プロトコル内部の状態や認証情報は公開しません。

外向きのWebSocketはハイバネーションできず、Durable Objectを稼働状態に保ちます。常時接続を有効にする前に実行時間の料金を確認し、まず専用のテストBotとサーバーを使ってください。

macOSでは次の明示的なコマンドで、クリップボードに用意したトークンを、引数やファイルへ入れず、標準入力でCloudflareへ渡せます。意図したWorkerを配備し、対象のプロジェクト・アカウントを確認してから実行してください。

pbpaste | node tools/discord/token.mjs YOUR_WORKER YOUR_PROJECT_DIRECTORY

この補助ツールは秘密情報を含み得るCLI出力を表示せず、確認できた成功か、0以外の終了コードだけを報告します。トークンを読み戻すことはありません。dicks.json、ソース、lockfile、チャットへトークンを含めないでください。

参考資料:Gateway、intents、Interactionへの応答、レート制限、外向きWebSocket。