28行でSlackのグループメンションBOTを実装した
Slackのユーザーグループがゲストには使えなかったのでSlackBOTを作成して再現をしました
Glitchで作ったのでRemixしてご活用いただけるかと思います
ゲストにもグループメンションさせたい人の助けになれば幸いです
とりあえず問題を解決したい方へ
ゲストでも使えるグループメンションBOTの作り方をまずは説明します
GlitchをRemix
Glitchでは他人が作ったソースコードをコピーしてカスタマイズし、ある程度は無料でプログラムを動かせるサービスです
Glitchのアカウントを作成し、私が作ったこのプロジェクトをRemixしてください
※自分のプロジェクトとしてコピーすることをRemixと呼ぶそうです
Remixリンク: https://glitch.com/edit/#!/remix/grey-elfin-ghost
SlackBOTを作成
公式のこの記事の「アプリの設定」セクションと「イベントの設定」セクションを実施してください
なお途中で権限は chat:write
に加えて app_mentions:read
も設定してください
この時作るボットの名前でグループメンションができるようになります
環境変数を修正
.env
ファイルを開き SLACK_SIGNING_SECRET
と SLACK_BOT_TOKEN
を設定してください
SLACK_SIGNING_SECRET
はここで取得できます
SLACK_BOT_TOKEN
はここで取得できます
メッセージを設定
.env
ファイルの MESSAGE
にこのBOTが返してほしいメッセージを入力してください
ただメンションをするだけなら <@メンバーID> <@メンバーID> <@メンバーID>
という形式の値を設定すれば @Aさん @Bさん @Cさん
とメンションを飛ばしてくれます
このメンバーIDはここで取得できます
仕組みの解説
Bolt
SlackのSDK「Bolt」を利用したBOTのGlitchをベースに開発しました
Boltのアプリインスタンスに対して app.use()
や app.event()
などに関数を渡すことで処理を定義できるのはシンプルで使いやすかったです
今回のソースコードは大きく分けて4つの要素で成り立っています
- アプリインスタンスの作成
- 再送信されたリクエストに対する処理
- メンションされたときの処理
- アプリの起動
アプリインスタンスの作成
const { App, ExpressReceiver } = require("@slack/bolt");
const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
token: process.env.SLACK_BOT_TOKEN,
receiver: new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
customPropertiesExtractor: ({ headers }) => ({ headers }),
}),
});
SlackのEvent APIはサーバーからのレスポンスが遅いとリトライをしてくれます
一方でGlitchはリクエストが来たタイミングで寝ていたサーバーを起動してからリクエストを処理するため、久々のリクエストに対するレスポンスが遅くなってしまいます
ただしGlitchはサーバー起動後にきちんとリクエストを送ってくれるためSlackからは複数回リクエストが届いてしまいます
これに対応すべくHTTPリクエストのヘッダーを後続の処理で取り扱うためアプリインスタンスの作成時に customPropertiesExtractor
でヘッダー情報を後続の処理に渡しています
再送信されたリクエストに対する処理
app.use(async ({ context, next }) => {
if (!context.headers["x-slack-retry-num"]) await next();
});
HTTPリクエストのヘッダーに x-slack-retry-num
が含まれるときはSlackが再送信したリクエストであることを示しています
これがないときだけ後続の処理を実行するようにします
メンションされたときの処理
app.event("app_mention", async ({ event, client }) => {
if (event.edited) return;
await client.chat.postMessage({
channel: event.channel,
text: process.env.MESSAGE,
thread_ts: event.ts,
});
});
メンションを受けたスレッドに環境変数で設定したメッセージを送っています
なお、編集したときにもイベントが発火するので編集した情報がある場合は処理をスキップしています
Boltを使うと短く簡潔にかけていいですね
アプリの起動
(async () => {
await app.start(process.env.PORT || 3000);
console.log("⚡️ Bolt app is running!");
})();
app.start()
を呼び出すことでサーバーが起動します
Discussion