Discord のコマンド (Application Commands) を AWS CDK + AWS Lambda で実装してみた
メッセージアプリの Discord のスラッシュコマンド (Application Commands) は、Webhook でも実装できるようになっています。
今回は AWS CDK + AWS Lambda (Function URL) で実装してみました。
参考:
注意点
Webhook で実装する際の注意として、Gateway Events は使用できません。
Gateway の機能を使う場合は Gateway にコネクションを貼り続ける必要があるので、Lambda では実装できません。
このような場合は従来の常駐型 Bot のほうが適しています。
Discord に Application を登録する
コマンドを作るためには、Discord に Application を登録する必要があります。
Discord Developers Portal のアプリケーション一覧にアクセスします。
New Application をクリックします。
用途がわかる適当な名前を入力して、Create を押します。
Bot の作成
ユーザーにコマンドの応答を返す Bot を実装します。
Bot ユーザーを作成するには、Bot メニューを開きます。
Add Bot をクリックします。
Bot ユーザーを追加していいかどうかの確認画面がでるので Yes, do it! をクリックします。
cdk init する
cdk init します。
今回は TypeScript で書くので
npx cdk init --language typescript
とします。
CDK スタックを記述する
以下のような形でスタックを用意します:
export class MyStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const applicationCommandHandler = new NodejsFunction( this, "application-command-handler", { entry: "./src/index.ts", handler: "handler", environment: { DISCORD_PUBLIC_KEY: "Developers PortalからコピーしたPublic Key" } } ); applicationCommandHandler.addFunctionUrl({ authType: FunctionUrlAuthType.NONE, }); } }
Developers Portal のアプリケーション設定から Public Key をコピーして、DISCORD_PUBLIC_KEY 環境変数の値を編集します。
コマンドの登録処理を書く
コマンドの登録には Bot のアクセストークンが必要です。
アクセストークンをコピーするために、Reset Token でトークンをリセットします。
アクセストークンをコピーします。今後二度と表示されないので安全な場所にメモしておきます。
Discord へコマンドを登録する API を呼び出す際に必要になります。
グローバルコマンドは /applications/{applicationId}/commands
に POST することで登録できます。
以下のようなスクリプトを用意しておくと簡単です:
const commands = [ { name: "hello", // コマンド type: 1, // CHAT_INPUT description: "Lambda で実装したコマンド", // コマンドの説明 }, ]; await fetch( `https://discord.com/api/v8/applications/${process.env["APPLICATION_ID"]}/commands`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bot ${process.env["BOT_ACCESS_TOKEN"]}`, }, body: JSON.stringify(commands), } );
APPLICATION_ID=アプリケーションID BOT_ACCESS_TOKEN=アクセストークン node scripts/register-commands.mjs
Application Command Object の詳細:
コマンド登録処理の詳細:
Webhook ハンドラとなる Lambda 関数を書く
依存関係をインストールします。
npm install aws-lambda @types/aws-lambda discord-interactions
Lambda ハンドラーを記述します。
import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda"; import { InteractionResponseType, InteractionType, verifyKey, } from "discord-interactions"; // Interaction リクエストの署名検証 (ないと失敗する) const verifyRequest = (event: APIGatewayProxyEventV2) => { const { headers, body } = event; const signature = headers["x-signature-ed25519"]; const timestamp = headers["x-signature-timestamp"]; const publicKey = process.env["DISCORD_PUBLIC_KEY"]; if (!body || !signature || !timestamp || !publicKey) { return false; } return verifyKey(body, signature, timestamp, publicKey); }; // interaction の処理 const handleInteraction = (interaction: Record<string, unknown>) => { if (interaction.type === InteractionType.APPLICATION_COMMAND) { const { data } = interaction as { data: Record<string, unknown> }; if (data.name === "hello") { return { type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: "hello, world", }, }; } } return { type: InteractionResponseType.PONG }; }; // エントリポイント export const handler = async ( event: APIGatewayProxyEventV2 ): Promise<APIGatewayProxyResultV2> => { if (!verifyRequest(event)) { return { statusCode: 400, }; } const { body } = event; const interaction = JSON.parse(body!); return { statusCode: 200, headers: { "Content-Type": "application/json", }, body: JSON.stringify(handleInteraction(interaction)), }; };
実行されたら hello, world
と返すだけのコマンドを実装しています。
デプロイ
デプロイします。
npx cdk bootstrap npx cdk deploy
Interaction URL を設定する
デプロイが完了したら、Interaction Endpoint URL にデプロイした Lambda 関数の URL をセットします。
サーバーに Bot を追加する
OAuth2 メニュー → URL Generator に移動して、bot と applications.commands にチェックを入れます。
一番下の Generated URL をコピーしてブラウザでアクセスします。
コマンドを実行してみる
まとめ
Discord の Interactions Endpoint 機能を活用すれば、サーバーに Bot を常駐させることなく実装することができます。
簡単なコマンドであればサーバーレスに実装できるので、ぜひお試しください。