NTTドコモR&Dの技術ブログです。

【Slack API初心者向け】スレッドで「いいね」数が多いユーザーを集計してみた

はじめに

NTTドコモ クロステック開発部の中村圭佑です。普段の業務では画像認識や生成AIに関する研究開発を行っています。今回はSlack APIを使ってスレッドで「いいね」数が多いユーザーを集計するツールをpythonとSlack APIで作ってみました。同じような記事が他の言語(Node.js&TypeScript等)だとありますが、python版のHow toとして参考になればと思います。

概要

Slackは多くのビジネスでチームコミュニケーションを支える重要なツールとして活用されています。当社も例外ではありません。その中で、ユーザーが「いいね」や他の絵文字リアクションを使ってメッセージに反応することはコミュニケーションの一環として頻繁に行われています。あるチャンネルの特定のスレッド内で「いいね」の多いユーザーが誰かを集計できれば、積極的に参加しているメンバーや影響力のあるメッセージを知ることができます。この手法により、チーム内で注目されている意見や、それを発信しているメンバーを特定でき、より円滑なコミュニケーションを促進することが期待できます。 というのは建前で、参加者が多いアイデアソンや社内イベントで割と使えるなぁということでサクッと作りました笑。

設定の手順

動作させるために、Slack APIの設定とPythonのスクリプトが必要です。

Slack APIの設定

Slack APIは、Slackのワークスペースでさまざまなデータを操作・取得するためのインターフェースです。以下に、APIトークンの取得手順とスコープ設定について説明します。

Slackアプリの作成とトークンの取得

  1. Slack APIページにアクセスしてログイン。
  2. 「Your Apps」から「Create New App」を選択し、新しいアプリを作成します。
  3. アプリ作成後、OAuth & Permissionsの設定ページに移動し、「Bot Token Scopes」を設定します。
  4. 必要なスコープとして、以下のスコープを追加します。
    • channels:read
    • reactions:read
    • users:read
  5. スコープ設定後、「Install App to Workspace」をクリックし、OAuth & PermissionsからBot User OAuth Tokenを取得します。xoxb-から始まるこのトークンは後ほどスクリプト内で利用します。

チャンネルIDとスレッドのタイムスタンプの取得
1. 特定のチャンネルにアクセスし、集計したいスレッドのメッセージを見つけます。 2. メッセージの右上のメニューから「リンクをコピー」を選択し、リンクの中のCXXXXXX部分が「チャンネルID」、リンクの後半が「スレッドのタイムスタンプ」に該当します。

スクリプト

設定が完了したら、以下のPythonプログラムを使用して、Slack APIからスレッドの「いいね」リアクションを集計します。 以下のコードは、特定のスレッド内でリアクション数を集計し、「いいね」数が多いユーザーTop3とメッセージを表示するものです。TARGET_REACTIONを好きな値に設定すると集計したいスタンプを変更できます。

import os
import requests

SLACK_TOKEN = 'xoxb-取得したBot User OAuth Token'
CHANNEL_ID = 'チャンネルIDを入力'
THREAD_TS = 'スレッドのタイムスタンプを入力'
TARGET_REACTION = '+1'  # 集計したいスタンプの種類

headers = {
    'Authorization': f'Bearer {SLACK_TOKEN}'
}

def fetch_thread_replies(channel_id, thread_ts):
    # タイムスタンプに小数点を追加
    formatted_ts = f"{thread_ts[:-6]}.{thread_ts[-6:]}"
    url = 'https://slack.com/api/conversations.replies'
    params = {
        'channel': channel_id,
        'ts': formatted_ts  # ドット付きに変換して利用
    }
    response = requests.get(url, headers=headers, params=params)
    if response.status_code == 200 and response.json().get("ok"):
        return response.json().get("messages", [])
    else:
        print("Failed to fetch replies:", response.json())
        return []

def fetch_user_info(user_id):
    """ユーザーIDからユーザー情報を取得"""
    url = 'https://slack.com/api/users.info'
    params = {
        'user': user_id
    }
    response = requests.get(url, headers=headers, params=params)
    if response.status_code == 200 and response.json().get("ok"):
        user_info = response.json().get("user", {})
        return user_info.get("name", ""), user_info.get("profile", {}).get("display_name", "")
    else:
        print(f"Failed to fetch user info for {user_id}")
        return user_id, ""  # デフォルトでIDを返す

def get_top_reacted_messages(replies, target_reaction, min_rank=3):
    messages_with_target_reaction = []
    user_top_reactions = {}

    for reply in replies:
        if 'reactions' in reply:
            for reaction in reply['reactions']:
                if reaction['name'] == target_reaction:
                    user = reply['user']
                    reaction_count = reaction['count']
                    if user not in user_top_reactions or user_top_reactions[user][1] < reaction_count:
                        user_top_reactions[user] = (reply['text'], reaction_count, reply['ts'])

    # リアクション数でソート
    sorted_reactions = sorted(user_top_reactions.items(), key=lambda x: x[1][1], reverse=True)

    # 必ず最低3位までの順位を表示するための処理
    top_reactions = []
    current_rank = 1
    last_reaction_count = None
    for i, (user, (text, count, ts)) in enumerate(sorted_reactions):
        # 同じリアクション数のメッセージを同順位とする
        if last_reaction_count is not None and count < last_reaction_count:
            current_rank += 1

        # 指定された最低順位までのメッセージを追加
        if current_rank > min_rank and len(top_reactions) >= min_rank:
            break

        top_reactions.append((current_rank, user, text, count, ts))
        last_reaction_count = count

    return top_reactions

# スレッドの全リプライを取得
replies = fetch_thread_replies(CHANNEL_ID, THREAD_TS)

# 最低3位までの指定スタンプが多いメッセージを取得
top_messages = get_top_reacted_messages(replies, TARGET_REACTION, min_rank=3)

# 出力
for rank, user, text, reaction_count, ts in top_messages:
    username, display_name = fetch_user_info(user)
    message_ts = ts.replace('.', '')
    link = f"https://your-workspace.slack.com/archives/{CHANNEL_ID}/p{message_ts}"
    print(f"Rank {rank}:")
    print(f"ユーザ名: @{display_name or username}")  # 表示名があれば表示、なければユーザー名
    print(f"Message: {text}")
    print(f"得票数: {reaction_count}")
    #print(f"Link: {link}")
    print("-" * 20)

結果

このプログラムを使うと、指定したスレッドのメッセージをすべて確認し、「いいね」数が最も多いユーザーの投稿を一覧で表示できました。以下が実行結果の例です。

Rank 1:
ユーザ名: @john_doe
Message: Great job on the recent project! Let's keep up the momentum!
得票数: 15
--------------------
Rank 2:
ユーザ名: @jane_smith
Message: I found some issues with the current implementation. We should discuss.
得票数: 10
--------------------
Rank 3:
ユーザ名: @michael_lee
Message: Here is the latest update on the team's progress.
得票数: 8
--------------------
Rank 3:
ユーザ名: @alex_wong
Message: Thanks everyone for the support! Excited for what's next!
得票数: 8
--------------------

まとめ

Slack APIを活用することで、特定のスレッド内でリアクション数が多いメッセージとユーザーを簡単に集計できるようになりました。注目されている意見・提案を確認するのに役立つ可能性があります。また、実装はPythonとSlack APIの基本的なリクエストで実現できるため、他の集計にも応用できると思います。ぜひ一度お試しください。