OpenNH

日常のひとこま(自分用のメモとかあれこれ)

Slack:特定のリアクションでメンション通知されるようにするBotを作ってみた

更新

  • 2022.10.21 : Slack Even API で同じイベントが複数回処理されてしまう問題に対応しました

背景

Slackで承認boxのようなチャネルを作成し、承認者がチェックマークをリアクションすると、投稿者に自動でメンションがいくような仕組みを作りたい!
とチームメンバに言われたので作成しました。

通常仕様だと、リアクションされても通知いかないんですよね・・・。
Reacji Channelerとかもありますが、別チャネルに転送するだけだしあまりやりたいことができなかったので、作るしかなかったという感じ。どなたか、いいプラグイン等あれば教えてほしいです。

完成イメージ

目次

環境

参考: Bolt フレームワークを使って Slack Bot を作ろう | Slack

下準備とSlackへのアプリ追加

こちらのサイトを参考にしてSlackアプリの設定をしていきます。

www.pci-sol.com

上記サイトからの変更部分

  • アプリ名やボット名は適当に任意に設定
  • 「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回リトライしてしまうみたいです。今回は確実に処理が必要という代物でもないので、以下のサイトを参考に、全イベントを監視し、単純にリトライされたイベントであればスキップするという処理を追加しました。

対応コードは以下の部分です。

// 全ての event action command 前に実行
app.use(async ({ logger, context, next }) => {
  // リトライされたイベントであればスキップする
  if (context.retryNum) {
    return;
  }
  await next();
});

まとめ

同じチャネル内に、特定のemojiでリアクションされたら、投稿者にメンションがいく機能を作ることができました。