ROBOT PAYMENT TECH-BLOG

株式会社ROBOT PAYMENTのテックブログです

ロボペイSlackリアクションランキング 集計してみた

こんにちは、SRE課の塚本です。
今回はSlackAPIを使って、ずっとやろうと思ってやってなかった「Slackリアクションランキング」を集計していきます。

集計する対象についてはこんな感じで集計していきます

  • slackに投稿されたメッセージに対するリアクションの総数を集計
  • 集計期間は2023年12月の1ヶ月間
  • パブリックチャンネルのみ
  • 1~20位までを集計

取得する流れとしてはこうです

  1. channel_idの一覧を取得
  2. channel_idの一覧をもとにchannel_idに紐づくメッセージを取得
  3. メッセージからリアクションを取り出し、ランキングを集計

引っかかったところとしては、今回使用したメソッドに関して、 Paginationが導入されており、取得できるデータがページごとに分割されるところです。cursorが空になるまでAPIを叩く必要がありました。

Paginationについて api.slack.com

また、APIのレート制限に引っかかったら処理を一定期間空けるといった対応も必要です。(今回はできてないのでガバガバ集計となってしまいました。。。)
ちなみに、今回スレッドに対するリアクションが拾えていないので、そこも改良の余地がありそうです。

気になる集計結果がこちら!

おはようが圧倒的に多いですね!(笑)あとは最近体調崩されている方が多いので、おだいじにが上位にランクインしてしまいました。。。みなさんお大事に!

ありがとう系のスタンプがたくさんあるのは嬉しいですね!これからも周りの方に感謝しながら仕事していければなと思います!

使用したコードがこちら↓

SLACK_API_TOKEN = "BotのUserTokenを貼る"

def main():
    channel_list = get_channel_list(SLACK_API_TOKEN)
    messages = []
    for channel in channel_list:
        print(channel)
        try:
            channel_messages = get_one_day_slack_message(SLACK_API_TOKEN, channel)
            messages.extend(channel_messages)
        except Exception as e:
            print(f"Error occurred while fetching messages for channel {channel}: {str(e)}")
            channel_messages = get_one_day_slack_message(SLACK_API_TOKEN, channel)
            continue
        
    result = lank_reaction(messages)
    return result

def get_one_day_slack_message(bot_token, channel):
    url = "https://slack.com/api/conversations.history"
    headers = {
        "Authorization": "Bearer " + bot_token,
        "Content-Type": "application/json; charset=utf-8"
    }
    
    messages = []
    cursor = None
    while True:
        payload = {
            "channel": channel,
            "limit": 100,
            "oldest": time.time() - 60 * 60 * 24 * 30,  # 30 days ago
        }
        if cursor:
            payload["cursor"] = cursor
        
        response = requests.post(url, headers=headers, json=payload)
        json_data = response.json()
        
        if not json_data["ok"]:
            print("Error:", json_data["error"])
            break
        
        messages.extend(json_data["messages"])
        
        if not json_data.get("response_metadata") or not json_data["response_metadata"].get("next_cursor"):
            break
        
        cursor = json_data["response_metadata"]["next_cursor"]
    
    return messages

def lank_reaction(messages):
    reactions_list = []
    for message in messages:
        if 'reactions' in message:
            for reaction in message['reactions']:
                reaction_info = {
                    'name': reaction['name'],
                    'count': reaction['count']
                }
                # 既に同じ名前のリアクションがリストに存在するか確認し、存在する場合はカウントを追加する
                found = False
                for existing_reaction in reactions_list:
                    if existing_reaction['name'] == reaction_info['name']:
                        existing_reaction['count'] += reaction_info['count']
                        found = True
                        break
                
                # もしリストに存在しない場合は新たに追加する
                if not found:
                    reactions_list.append(reaction_info)
    # countの値が大きい順にreactions_listを並べ替える
    sorted_reactions_list = sorted(reactions_list, key=lambda x: x['count'], reverse=True)
    # 上位10個の要素を取得する
    top_10_reactions = sorted_reactions_list[:20]
    return top_10_reactions

    
    
def get_channel_list(bot_token):
    url = "https://slack.com/api/conversations.list"
    headers = {
        "Authorization": "Bearer " + bot_token
    }
    channel_list = []
    cursor = None
    while True:
        payload = {
            "limit": 100
        }
        if cursor:
            payload["cursor"] = cursor
        
        response = requests.post(url, headers=headers)
        json_data = response.json()
        
        if not json_data["ok"]:
            print("Error:", json_data["error"])
            break
        
        channel_list.extend(channel['id'] for channel in json_data['channels'])
        
        
        if not json_data.get("response_metadata") or not json_data["response_metadata"].get("next_cursor"):
            break
        
        cursor = json_data["response_metadata"]["next_cursor"]
    return channel_list

使用したAPIの定義はこちら
api.slack.com api.slack.com

こちらは今回使いませんでしたが、Slack Boltというフレームワークも登場しています。こちらの公式チュートリアルからスタートしていくのがおすすめです! slack.dev



We are hiring!!

ROBOT PAYMENTでは一緒に働く仲間を募集しています!!!

speakerdeck.com
www.robotpayment.co.jp