🤖

Slackのリアクションした人一覧を取得するBot

2023/12/02に公開

はじめに

株式会社エイプリルナイツのアドベントカレンダー2日目の記事です。
https://adventar.org/calendars/8956

Slack Reaction Indexerの概要

きっかけは社内イベントなどをよく企画する社員からの要望でした。

ゲーマーが集まる企業エイプリルナイツではオンラインでのゲーム交流がよく企画されます。そのへんはオウンドメディアにいくつも記事があるので興味があればこちらからご覧ください。
https://akspot.game/?cat=206

要件は

  • リアクションした人がメンションで一覧化され、コピペして再利用できること
  • 企画募集メッセージ自体のリアクションもしくはそのスレッド内で完結すること(チャンネルの投稿を一つの企画で流さないため)

そのため、投票系のBotを使うという案はボツに(スレッド内で使えないため)

主な機能

リアクションした人一覧を作成したいメッセージからショートカットを起動する

DMに集計結果が送信される。

参加表明した人のメンションをコピペして、スレッド内でメンションしたり、チャンネルを作成して一度で招待したりという使い方ができます。

環境構築

必要なツールとライブラリ

このプロジェクトを実行するためには、Pythonがインストールされている必要があります。また、Slackのワークスペースとボットトークン、アプリトークンが必要です。以下のライブラリも使用します。

  • python-dotenv: 環境変数を管理するためのライブラリ。
  • slack_bolt: Slackのイベントとインタラクションを処理するためのフレームワーク。
  • slack_sdk: SlackのWeb APIを呼び出すためのPythonライブラリ。

これらのライブラリは、以下のコマンドでインストールできます。

pip install python-dotenv slack_bolt slack_sdk

環境変数の設定

.envファイルをプロジェクトのルートディレクトリに作成し、以下の環境変数を設定します。

SLACK_BOT_TOKEN=xoxb-あなたのボットトークン
SLACK_SIGNING_SECRET=あなたの署名シークレット
SLACK_APP_TOKEN=xapp-あなたのアプリトークン

SlackAppsの設定

SocketModeをONにします。

Interactivity & ShortcutsのInteractivityをONにします。
ShortcutsにCallbackIDを get_reaction_users を入力してショートカットメニューを作成します。

メッセージの右上の︙をクリックするとメッセージショートカットとして出てくるようになります。

Bot Token Scopesは以下を設定しています。

  • channels:history
  • channels:join
  • channels:read
  • chat:write
  • chat:write.public
  • commands
  • groups:history
  • groups:read
  • groups:write
  • im:history
  • im:read
  • im:write
  • reactions:read

コードの解説

ボットの初期化

App クラスを使用してSlackボットを初期化し、環境変数からトークンと署名シークレットを読み込みます。WebClient インスタンスも作成してAPI呼び出しを行います。

app = App(token=os.getenv("SLACK_BOT_TOKEN"),
          signing_secret=os.getenv("SLACK_SIGNING_SECRET"),
          )
client = WebClient(os.getenv("SLACK_BOT_TOKEN"))

リアクション取得ショートカットのハンドラー

@app.shortcut("get_reaction_users") デコレーターを使用して、ショートカットイベントを処理するハンドラーを定義します。このハンドラーは、ユーザーがショートカットを使用したときに呼び出されます。
SlackAppsのCallbackIDに設定した文字列を指定しましょう。

@app.shortcut("get_reaction_users")
def handle_get_reactions(ack, body, client, logger):
    ack()
    # ここでリアクションの取得とユーザーへのメッセージ送信を行います。

リアクションのユーザー一覧化機能

メッセージに付けられたリアクションとそのユーザーを取得し、リアクションごとにユーザーの一覧を作成します。それぞれのリアクションに対して、ユーザーのメンションを含むメッセージを構築し、ショートカットを起動したユーザーにDMとして送信します。

        # メッセージに対するリアクションを取得
        response = client.reactions_get(channel=channel_id, timestamp=message_id)
        message = response['message']

        if 'reactions' in message:
            reactions = message['reactions']

            # リアクションをしたユーザーの一覧を作成
            blocks = []
            for reaction in reactions:
                users = reaction['users']
                user_mentions = [f"<@{user}>" for user in users]  # メンション形式に変換
                user_mentions_str = ', '.join(user_mentions)
                blocks.append({
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": f":{reaction['name']}: (人数: {len(users)}) : {user_mentions_str} "
                    }
                })
	# ...
        else:
            client.chat_postMessage(channel=user_id, text="このメッセージにはリアクションがありません。")

DMに送信するので、元のメッセージがわかるようにURLも一緒に送信しておくとプレビューされます。

 # 元のメッセージのURLを作成
            message_url = f"https://aprilknights.slack.com/archives/{channel_id}/p{message_id.replace('.', '')}"

            # ショートカットを起動したユーザーにDMを送る
            client.chat_postMessage(
                channel=user_id,
                text="メッセージにリアクションしたユーザーを一覧化しました!",
                blocks=[
                    {
                        "type": "header",
                        "text": {
                            "type": "plain_text",
                            "text": "リアクションしたユーザーの一覧",
                            "emoji": True
                        }
                    },
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": f"*元のメッセージ*: <{message_url}>"
                        }
                    },
                    *blocks
                ]
            )

テストの様子

リアクションを色んな人にたくさんしてほしかったのでお願いしてみました。

たくさんリアクションがあっても大丈夫なようです

コード自体はほぼChatGPTが書いてます

最初に要望があってから数カ月後に実装した理由は、CursorというVSCodeベースのエディタで何か試しに作ってみたかったからです。
既存のSlackBotでショートカットハンドラーを使って実装しているコードを開いた状態で、Cursorに指示をしたら90%完成した状態でコード書いてくれました。

参考リンク

ソースコード全体はこちらから
https://github.com/april-knights-dev/slack-reaction-indexer

Discussion