更新
- 2022.10.21 : Slack Even API で同じイベントが複数回処理されてしまう問題に対応しました
背景
Slackで承認boxのようなチャネルを作成し、承認者がチェックマークをリアクションすると、投稿者に自動でメンションがいくような仕組みを作りたい!
とチームメンバに言われたので作成しました。
通常仕様だと、リアクションされても通知いかないんですよね・・・。
Reacji Channelerとかもありますが、別チャネルに転送するだけだしあまりやりたいことができなかったので、作るしかなかったという感じ。どなたか、いいプラグイン等あれば教えてほしいです。
完成イメージ
目次
環境
参考: Bolt フレームワークを使って Slack Bot を作ろう | Slack
下準備とSlackへのアプリ追加
こちらのサイトを参考にしてSlackアプリの設定をしていきます。
上記サイトからの変更部分
- アプリ名やボット名は適当に任意に設定
- 「Subscribe to bot events」タブを以下のように設定
実装
以下のドキュメントを参考に、特定のリアクションがあった場合に、メッセージ投稿者に対してメンションをするプログラムを書いていきます。
完成版:
Const {App} = requires ("@slack/bolt"); Concept store = require("./store"); const app = new App({ signingSecret: process.env.SLACK_SIGNING_SECRET, token: process.env.SLACK_BOT_TOKEN, }); // 全ての event action command 前に実行 app.use(async ({ logger, context, next }) => { // リトライされたイベントであればスキップする if (context.retryNum) { return; } await next(); }); // 特定のemojiがリアクションされた場合に呼ばれるメソッド app.event('reaction_added', async ({ event, message, say }) => { if (event.reaction === 'white_check_mark') { // await say(`<@${event.item_user}> :【承認されました】\n`+message.text); // チャネルにそのままま投稿したい場合 await say({text:`<@${event.item_user}> \n【承認されました】`,thread_ts:event.item.ts}); // スレッドに返信したい場合 } }); //アプリが起動時に呼ばれるメソッド (async () => { await app.start(process.env.PORT || 3000); console.log("⚡️ Bolt app is running!"); })();
今回のために追加したコードはハイライト部分のみです
ポイント1:スレッドに返信することでどの投稿への通知かわかるようにした
普通にsay
で投稿してしまうと、どの投稿に対しての通知だったのかがわからなくなってしまいます。なので、承認リアクションをもらえた投稿に対して、返信する形にすることでその問題に対応しました。(対象投稿に対してリンクを貼るなども考えたけど、返信が一番実装も簡単だし、使い勝手もいいかなと思い、これを採用)
コード言うと、thread_ts:event.item.ts
この部分。
event.item.ts
で、投稿されたメッセージのタイムスタンプを取得し、say
関数に対してthread_ts
を引数を指定することで、そのメッセージへの返信としてメッセージ投稿ができます。
// 特定のemojiがリアクションされた場合に呼ばれるメソッド app.event('reaction_added', async ({ event, message, say }) => { if (event.reaction === 'white_check_mark') { // await say(`<@${event.item_user}> :【承認されました】\n`+message.text); // チャネルにそのままま投稿したい場合 await say({text:`<@${event.item_user}> \n【承認されました】`,thread_ts:event.item.ts}); // スレッドに返信したい場合 } });
ポイント2:同じイベントで複数回通知をしてしまうことへの対処
これは30分くらい対応に悩みました・・・。 Slack Event APIの仕様として3秒レスポンスがないと追加で3回リトライしてしまうみたいです。今回は確実に処理が必要という代物でもないので、以下のサイトを参考に、全イベントを監視し、単純にリトライされたイベントであればスキップするという処理を追加しました。
- 対応方法を参考にしたページ:[Slack] Bolt.js のリトライ対策をちゃんとやる - Qiita -コードを参考にしたページ: bolt-js/express.js at main · slackapi/bolt-js · GitHub
- その他参考ページ:Slack Event APIで同じEventが何回も発行される問題の原因と対処 - Qiita
対応コードは以下の部分です。
// 全ての event action command 前に実行 app.use(async ({ logger, context, next }) => { // リトライされたイベントであればスキップする if (context.retryNum) { return; } await next(); });
まとめ
同じチャネル内に、特定のemojiでリアクションされたら、投稿者にメンションがいく機能を作ることができました。