Azure FunctionsでサーバーレスDiscord Botを作る
2022-12-12
azblob://2022/12/09/eyecatch/2022-12-12-serverless-discord-bot-by-azure-functions-000.png

 

FIXER Rookies Advent Calendar 2022の12日目の記事です。

早速ですが皆さんDiscord使ってますか?私は友人と雑談するためによく使っています。特に目的があって会話する訳でもないですが、雑につないでいられる通話サービスというのはいいものですね。最近友人がスプラで温まってうるさかったりもするのですが(通話を分けろ)

さて、DiscordにはBotというものが存在します。自動でメッセージの送信やロールの付与など、色々勝手にやってくれる便利なやつです。ちょっと検索すればいくらでも便利なBotが出てきて、導入も容易なのでDiscordサーバーの使い勝手が良くなること間違いなしです。

ですが、目的に合ったものが見つかるとは限らないのがこの世の常。かくいう私も欲しい機能と微妙に合致しているものがなかったという経験があります。ではどうするのかといえば自作ですね。ほぼ常時Discordに接続している身からすると、Discordにコマンド打ち込んでちょっとした計算やランダム生成をしてくれると嬉しいわけです。という事で今回はDiscordBotの作り方を紹介します。

まずはWebサーバーを用意してめんどくせ~~~~~~~~

すみません、面倒くさくなりました。冗談はさておき私も初めてDiscordBotを作った時もサーバー用意の段階で大分手間取った記憶があります。まぁ今となっては便利なサービスもいくつかあるのでそれを使えばいいとは思うのですが、結構維持費必要だったり、無料にしても制限がきつかったりなどする印象です。一つぐらいならいいですがいくつもDiscordBotを作成しようとなると一個一個サーバーが必要になるのでなかなか面倒になるかと思います。

そこで今回はAzure Functionsを使ってサーバーレスでさくっとDiscordBotを作る方法を紹介していきます。

今回記事を書くにあたりこちらの記事(https://zenn.dev/drumath2237/articles/112fd0bfa7ea4f836195)を参考にさせていただきました。

 

DiscordBotの準備

とりあえずDiscordのBotを用意しましょう。まずDiscord Developer Portalにアクセスして、右上のNew Applicationからアプリケーションの作成をしましょう。今は設定変更の必要はないですが、APPLICATION IDとPUBLIC KEYをどこかに控えておくといいかもです。

その後Settings>Botページに移ってAdd Botを押すとBotが作成されます。Botを作成したら、ResetTokenを押してトークンのリセットを行い、表示されたトークンを必ず控えておきましょう。このトークンは後から確認することが出来ず、忘れたりしたら再生成することになるので気を付けましょう。

続いて作成したBotを自分のサーバーに招待します。招待するためにはSettings>OAuth2>URL Generatorのページに移ります。今回はDiscord Slash Commandsを使うのでSCOPESでapplications.commandsを選択します。

項目を選択すると下の方にURLが自動で作成されますのでこのURLにアクセスしてBotを自分のサーバーに招待していきます。ちなみにこのアクセス許可はサーバーの管理者しかできないので注意してください。URLにアクセスすると追加サーバーやBotへの権限許可などを選択していったら招待完了です。

Slash Commandsの登録

さてBotを作ったのはいいですが、今のところ何もできないBotなので出来ることを増やしてあげましょう。今回BotへのコマンドとしてSlashCommandsというのを使います。「/」を打てばコマンドがサジェストされる便利なやつです。コマンドの登録なのですが、HTTPリクエストを送る必要があります。

URL:https://discord.com/api/v8/applications/<APPLICATION_ID>/guilds/<GUILD_ID>/commands

<APPLICATION_ID>の部分には作成したアプリケーションのAPPLICATION IDを入力します。GUILD IDはDiscordサーバーにあるIDなのですが、これを見るためにはDiscordの詳細設定で開発者モードをオンにして、調べたいサーバーを選んで右クリックするとIDのコピーが出来ます。これがギルドIDになりますのでこれを使いましょう。

header:
{
	"Authorization": "Bot <BOT_TOKEN>",
	"Content-Type": "application/json",
}

<BOT_TOKEN>の部分にはBotのトークンが入ります。「Bot」との間にスペースが必要なので気を付けてください。

body:
{
	"name":"test",
	"description":"bot test",
	"options": [
    	{
      		"name": "option",
      		"description": "yes or no",
      		"type": 3,
			"required": true,
			"choices": [
			{
				"name":"Yes"
				"value":"yes"
			}
			{
				"name":"No"
				"value":"no"
			}
		]
	}
}

bodyでコマンド構造を指定してあげます。中身の詳細な説明については今回は省きます。詳しく知りたい方はDiscord Developer PortalのApplication Commandの項目を見て下さい。(https://discord.com/developers/docs/interactions/application-commands)

これらをHTTPリクエストで送ってあげます。送ってあげるとDiscordの方で「/」を打つとコマンドがサジェストされるようになります。作ったコマンドを入力してみてください。以下のように帰ってくるはずです。

コマンドを登録したのはいいですが、Bot側で何も処理を追加してないので応答してくれないのですね。という事で中身を作っていきます。

Azure Functionsの作成

という事でAzure Functionsを使ってBotの応答を作っていきましょう。まずはAzure Functionsを作ります。今回はランタイムスタックをNode.js、バージョンは16 LTSにしまして、プランはB1以上を選択しましょう。

コードはVSCodeで書いていきましょう。VSCodeでAzure周りの作業をするにあたって「Azure Tools」の拡張機能を入れてください。必要なのはAzure Functionsだけなのですが、全部まとめて入れといた方が間違いが無くて良いです。

Azureの拡張機能を入れるとタブにAzureって項目が出来るので、Azureにログインしたのち、そこのWorkSpaceから「Create Function」を選択してください。するとAzure Functions用のプロジェクトが作られます。今回は言語はTypeScript、トリガーはHTTP trigger、承認レベルはAnonymousにしましょう。

必要なパッケージをインストールしていきます。sがついていない名前のパッケージもあるそうなので間違えないように気を付けてください

$ npm install
$ npm install discord-interactions express @types/node

そしてindex.tsにコードを書いていきます。

Javascriptimport { AzureFunction, Context, HttpRequest } from "@azure/functions";
import {
  InteractionType,
  InteractionResponseType,
  verifyKey,
} from "discord-interactions";
const CLIENT_PUBLIC_KEY = process.env.CLIENT_PUBLIC_KEY;

const httpTrigger: AzureFunction = async function (
  context: Context,
  req: HttpRequest
): Promise<void> {
  const sig = req.headers["x-signature-ed25519"];
  const time = req.headers["x-signature-timestamp"];
  const isValid = await verifyKey(req.rawBody, sig, time, CLIENT_PUBLIC_KEY);

  if (!isValid) {
    context.res = {
      status: 401,
      Headers: {},
      body: JSON.stringify({
        data: {
          content: `${sig},${time}`,
        },
      }),
    };
    return;
  }

  const interaction = req.body;
  if (interaction && interaction.type === InteractionType.APPLICATION_COMMAND) {
    const option = req.body.data.options[0].value;
    const username = req.body.member.user.username;
    const not = option == "yes" ? "" : "'t";

    context.res = {
      status: 200,
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
        data: {
          content: `${username}${option} we can${not}」`,
        },
      }),
    };
  } else {
    context.res = {
      body: JSON.stringify({
        type: InteractionResponseType.PONG,
      }),
    };
  }
};

export default httpTrigger;

コードが書けたらAzure Functionsにデプロイしていきます。デプロイの方法はいろいろありますが今回はVSCodeから手動で行っていきます。Azureのタブから再びWorkSpaceを見ます。一応LocalProjectが入っているか確認してください。入ってなかったらVSCode再起動すれば映ると思います。Deployを選択し、先ほど作成したAzure Functionsを選択してデプロイしていきます。

デプロイが完了したら環境変数を登録します。Azure Functionsの設定>構成のアプリケーション設定で追加が出来ます。名前は「CLIENT_PUBLIC_KEY」で、値はDiscordのアプリケーションのパブリックキーを入れましょう。

Discord BotにAzure Functionsの登録

最後にDiscord BotがAzure FunctionsへHTTPリクエストを投げられるようにしておきます。Discord Depeloper Portalの今回作ったアプリケーションを開いてGeneral Informationの「INTERACTIONS ENDPOINT URL」にAzureFunctionsのトリガーを登録します。URLはAzure Functionsの関数>関数から今回作ったものを選んで、概要の関数のURL取得から行えます。URLを入力して変更のセーブをすると、署名の検証が行われるのでこれが問題なく通れば完了です。

確認としてDiscordでコマンドを入力してメッセージが正常に出てきたら完成です。

最後に

今回はサーバーレスなDiscord Botの作成という事でしたがいかがでしたでしょうか。トリガーを追加すればBotの追加作成も容易ですし、他Azureサービスとの連携もできそうで色々と出来ることがありそうですね。普段よく使うサービスというだけあって機能が増えると利便性が上がっていいですよね。

皆さんもよきDiscordライフをお送りください。