LoginSignup
1
0

SkeetサーバーレスフレームワークでDiscord Webhooksをマスター 🔧🌐

Posted at

Skeet フレームワークを使用して Firebase Functions と Discord Webhooks の統合を探究します。このチャプターでは、Skeet のエコシステム内で Firebase Functions を活用して Discord イベントを処理する方法の詳細に迫ります。リアルタイムで反応するボット機能を作成し、ユーザーインタラクションを高めるための洞察を得ます。

Skeet に Discord Interaction Webhook のエンドポイントを追加する

Discord Interaction Webhook のエンドポイントを追加することで、
Discord ボットの Interaction を受け取ることができます。

skeet add webhook コマンドを使って、
Discord Interaction Webhook のエンドポイントを追加しましょう。

skeet add webhook
? Select Webhook Type (Use arrow keys)
❯ discord
  stripe
✅ discordRouter added 🎉
✅ helloAction added 🎉
✅ helloCommand added 🎉
✅ helloIndex added 🎉
✅ deployCommands added 🎉
✅ helloMessage added 🎉
🔗 ./functions/skeet/src/routings/http/discordRouter.ts
🔗 ./functions/skeet/src/lib/discord/actions/helloAction.ts
🔗 ./functions/skeet/src/lib/discord/commands/hello.ts
🔗 ./functions/skeet/src/lib/discord/commands/index.ts
🔗 ./functions/skeet/src/lib/discord/deploy-commands.ts
🔗 ./functions/skeet/src/lib/discord/messages/helloMessage.ts
✔ Successfully exported to ./functions/skeet/src/index.ts 🎉

Discord Interaction Webhook のエンドポイント及び、
以下のツリーのように discord ディレクトリが作成され、
必要なファイルが追加されました。

tree -L 2 functions/skeet/src/lib
functions/skeet/src/lib
├── config.ts
├── discord
│   ├── actions
│   ├── commands
│   ├── deploy-commands.ts
│   └── messages
...
  • actions - Discord インタラクションアクションの関数を定義するディレクトリ
  • commands  - Discord スラッシュコマンドの関数を定義するディレクトリ
  • messages  - Discord ボタンメッセージの関数を定義するディレクトリ
  • deploy-commands.ts - Discord スラッシュコマンドをデプロイする関数ファイル

アプリのデプロイ

変更したエンドポイントのみをデプロイするには、
--function オプションを使います。

skeet deploy --function skeet:discordRouter

カンマ区切りで複数のエンドポイントを指定することもできます。

skeet deploy --function skeet:discordRouter,skeet:root

Discord インタラクション Webhook の登録

ここで、Discord Developer Portal に移動し、
Discord インタラクション Webhook を登録します。

discordRouter のエンドポイントを表示させるには、
以下のコマンドを実行します。

skeet get https

表示されたエンドポイントをコピーし、
Discord Developer Portal の Webhook URL に設定し保存します。

エンドポイントが無事に登録されると、
Discord Developer Portal の Webhook URL に設定したエンドポイントに
Discord ボットからの Interaction が送信されます。

エンドポイントに問題がある場合は、ここでエラーが起きます。

問題が起きた場合には以下のコマンドでエラーを確認し、
エンドポイントを修正してください。

skeet logs

Discord ボタンメッセージの登録・実行

Discord ボタンメッセージのテンプレートは、
以下のディレクトリにあります。

functions/skeet/src/lib/discord/messages/

デフォルトでは helloMessage.ts が登録されています。

import {
  ButtonStyleTypes,
  MessageComponentTypes,
} from '@skeet-framework/discord-utils'

export const helloMessage = () => {
  const body = {
    content: 'hello button',
    components: [
      {
        type: MessageComponentTypes.ACTION_ROW,
        components: [
          {
            type: MessageComponentTypes.BUTTON,
            style: ButtonStyleTypes.PRIMARY,
            label: 'Hello Button',
            custom_id: 'hello-button',
          },
        ],
      },
    ],
  }
  return body
}

この関数を root エンドポイントから呼び出して、
Discord ボタンメッセージを送信します。

functions/skeet/src/routings/http/root.ts

import { onRequest } from 'firebase-functions/v2/https'
import { publicHttpOption } from '@/routings/options'
import { TypedRequestBody } from '@/types/http'
import { RootParams } from '@/types/http/rootParams'
import { helloMessage } from '@/lib/discord/messages/helloMessage'
import { messageChannel } from '@skeet-framework/discord-utils'
import { defineSecret } from 'firebase-functions/params'

const DISCORD_TOKEN = defineSecret('DISCORD_TOKEN')
export const root = onRequest(
  { ...publicHttpOption, secrets: [DISCORD_TOKEN] },
  async (req: TypedRequestBody<RootParams>, res) => {
    try {
      const message = helloMessage()
      const channelId = 'your-channel-id'
      await messageChannel(DISCORD_TOKEN.value(), channelId, message)
      res.json({ status: 'success' })
    } catch (error) {
      res.status(500).json({ status: 'error', message: String(error) })
    }
  }
)

DISCORD_TOKEN は、Secret Manager に登録した Discord のトークンが defineSecret で定義されています。

channelId は、お好きなチャンネルの ID を設定してください。

それでは skeet s コマンドでローカルサーバーを起動し、
ログに表示される root エンドポイントにアクセスしてみましょう。

skeet s
✔  functions: Loaded functions definitions from source: discordRouter, root.
✔  functions[asia-northeast1-discordRouter]: http function initialized (http://127.0.0.1:5001/your-project-id/asia-northeast1/discordRouter).
✔  functions[asia-northeast1-root]: http function initialized (http://127.0.0.1:5001/your-project-id/asia-northeast1/root).

http://127.0.0.1:5001/your-project-id/asia-northeast1/root にアクセスすると、
Discord ボタンメッセージが送信されます。

ボタンを押してみると

無事にレスポンスが返ってきました 🎉

Discord スラッシュコマンドの登録・実行

Discord スラッシュコマンドのテンプレートは、
以下のディレクトリにあります。

functions/skeet/src/lib/discord/commands/

デフォルトでは hello.ts が登録されています。

import { SlashCommandBuilder } from '@skeet-framework/discord-utils'

export const data = new SlashCommandBuilder()
  .setName('hello')
  .setDescription('Hello Slash Command')
  .addStringOption((option) =>
    option.setName('hey').setDescription('Say something').setRequired(true)
  )

この関数を deploy-commands エンドポイントから呼び出して、
Discord スラッシュコマンドをデプロイします。

skeet deploy --discord を実行すると、
デプロイ必要な DISCODE_TOKEN を Secret Manager から呼び出して実行することができます。

skeet deploy --discord
Started refreshing application (/) commands.
Successfully reloaded application (/) commands.
✨  Done in 4.01s.

無事にスラッシュコマンドがデプロイされました🎊

スラッシュコマンドを実行してみると、

無事にレスポンスが返ってきました 🎉

Discord インタラクションアクションの登録

Discord インタラクションアクションのテンプレートは、
以下のディレクトリにあります。

functions/skeet/src/lib/discord/actions/

デフォルトでは helloAction.ts が登録されています。

import {
  DiscordRouterParams,
  deferResponse,
  updateResponse,
} from '@skeet-framework/discord-utils'
import { Response } from 'firebase-functions/v1'
import { inspect } from 'util'

export const helloAction = async (
  res: Response,
  db: FirebaseFirestore.Firestore,
  discordToken: string,
  body: DiscordRouterParams
) => {
  console.log('helloAction')
  console.log(inspect(body))
  await deferResponse(discordToken, body.id, body.token)
  // define your logic here
  await updateResponse(discordToken, body.application_id, body.token, {
    content: 'hello response!',
    flags: 64,
  })
  return true
}

ボタンアクションやスラッシュコマンドが実行されると、
Discord から discordRouter エンドポイントにリクエストが送信されます。

discordRouter エンドポイントは、
functions/skeet/src/routings/http/discordRouter.ts に定義されています。

関数の中の if 文で、
リクエストの種類に応じて、
helloActionhelloCommand などの関数を呼び出しています。
デフォルトでは、helloActionhelloコマンド、 hello-button トリガー発動時に呼び出されます。

...
if (req.body.type === InteractionType.PING) {
  // To verify the request
  res.send({
    type: InteractionType.PING,
  })
} else if (req.body.type === InteractionType.APPLICATION_COMMAND) {
  // Slash command
  const options = req.body.data as CommandData
  if (options.name.match(/^hello$/)) {
    await helloAction(res, db, DISCORD_TOKEN.value(), req.body)
  }
} else {
  // Button action
  const { custom_id } = req.body.data as ActionData
  if (custom_id.match(/^hello-button$/)) {
    await helloAction(res, db, DISCORD_TOKEN.value(), req.body)
  }
}
...

それでは次の章で、これらの関数と Firestore を組み合わせて、
Discord アプリを作成していきましょう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0