🔰
Discord botコマンド登録の個人的チートシート
友人と2人で創作をやっているDiscordサーバに入れる用のbotを開発しました。
Vide Codingで終われるはずがbotのコマンド登録でつまづき、知見が得られたので自分用にまとめておきます。
ちなみに作ったbotはこちら:
前提
対象
- Node.jsでDiscord botを開発する人
- JavaScript初心者だけどやっていく人
開発環境
プラットフォーム: Cloudflare Workers + Durable Objects
開発言語: TypeScript
Wrangler: v4.34.0
npm: v10.9.0
Node.js: v22.12.0
botのコマンド登録って何
- Discordのbotが受け付けるslash commandsを宣言すること
これ
コマンド登録の流れ
- 大まかには、「登録先情報の参照→URL作成→Discord API呼び出し→コマンド定義の送信」となります。
- 以下のようにpackage.jsonにエイリアスを指定し、mjsファイルが実行されるようにすると便利
package.json
...
"scripts": {
"deploy": "wrangler deploy",
...
"register:commands": "node ./scripts/register-commands.mjs"
},
...
以下では、コマンド登録をスクリプトにまとめておく前提で、入れておくと便利な機能や書き方をまとめます。
スクリプトに入れておくと便利なもの一覧
ログ出力
基本のログ出力
console.log('ここまでOK');
変数のログ出力
- 例1: 変数の中身を出力する
console.log('app id:', APP_ID, 'env global:', PURGE_GLOBAL);
- 例2: 配列の中身を出力する
const arr = [1, 2, 3, 4];
という配列があったとき、
console.log(arr);
- 例3: エラーが出たときエラー内容を出力する
try {
...
} catch (e) {
console.error('Fetch error', e);
}
重要情報をハードコード以外で追加する
環境変数を別ファイルから読み出す
-
dotenv
を使ってファイルに環境変数を書き、それを読み出すことが可能-
npm install dotenv
をターミナルで実行し、.env
ファイル、コマンド登録スクリプトをそれぞれ以下のように編集する
-
.env
DUMMY_ID=thisIsFrom.envFile
register-commands.mjs
import dotenv from 'dotenv';
dotenv.config();
console.log('DUMMY_ID:',process.env.DUMMY_ID);
// 出力例: DUMMY_ID: thisIsFrom.envFile
既存の環境変数を変数に代入する
- PowerShellのターミナル画面などで指定した環境変数を読み出して使用することも可能
$env:BOT_TOKEN="xxxxx"
...
const BOT_TOKEN = process.env.BOT_TOKEN ?? '';
-
ターミナルと
.env
ファイルで同名の環境変数が指定されているときは、デフォルトではターミナル優先となる -
dotenv.config({ override: true });
と指定しておくと.env
ファイルの内容が優先される
リトライロジックを入れる
-
40333 internal nerwork error
や500系エラー対策 - コマンド削除の直後に登録を行うと、レートリミットに引っかかりやすい
- 何msか待機を入れておくことで制限に引っかかりにくくした
- 429(Too Many Requests)ではレスポンスヘッダに
retry_after
(再試行まで何ms待てばよいか)が書かれているので、それに従うと無駄がない
- レートリミット時の応答サンプル
< HTTP/1.1 429 TOO MANY REQUESTS
< Content-Type: application/json
< Retry-After: 65
< X-RateLimit-Limit: 10
< X-RateLimit-Remaining: 0
< X-RateLimit-Reset: 1470173023.123
< X-RateLimit-Reset-After: 64.57
< X-RateLimit-Bucket: abcd1234
< X-RateLimit-Scope: user
{
"message": "You are being rate limited.",
"retry_after": 64.57,
"global": false
}
- リトライ処理入りリクエスト送信関数のサンプル
- 429エラー(レートリミット)と500系などのサーバエラー時にリトライする処理を入れています
async function reqWithRetry(method, url, body, tries = 3) {
for (let i = 0; i < tries; i++) {
const res = await apiFetch(method, url, body);
if (res.ok || i === tries - 1) return res;
const text = await res.text().catch(() => '');
const retryAfterMsDef = 1000;
if (res.status === 429) {
let retryAfterMs = retryAfterMsDef;
try {
const data = JSON.parse(text);
if (typeof data.retry_after === 'number') retryAfterMs = Math.ceil(data.retry_after * 1000);
} catch {}
await sleep(retryAfterMs + 100); //一応100ms延ばしておく
continue;
}
// 何秒待てばいいかわからないエラーの場合は固定値をもとにバックオフ待機して再送
if (res.status >= 500 || text.includes('40333')) {
await sleep(retryAfterMsDef * (i + 1) );
continue;
}
const out = new Response(text, { status: res.status });
out.debugText = text;
return out;
}
}
参考
公式ガイド・Q&A
Discord bot関連
Cloudflare関連
Discussion