3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【GAS】Slack APIをつかってリアクションランキングを作ってみた

Posted at

こんにちは。
株式会社Acompanyでマーケティングをしている @take_m です。
今日は『アカンクリスマスアドベントカレンダー2023』の6日目の記事を書いていこうと思います。

Slackで最もリアクションがつけられたメッセージを取得したい

Acompanyの特徴的な文化のひとつに、「Slackでのリアクションが活発」というのがあります。
基本リモートなのでオンラインコミュニケーションが多い分、めちゃくちゃリアクションつけます。
毎週「誰が一番リアクションつけたか」をランキングにして発表するくらいには、リアクションをつけます。

image.png

そこでふと「最もリアクションを稼いだ投稿を知りたい」と思ったのですが、Slackのアナリティクス機能ではそういった集計はできません。
SlackのAPI使えば作れそうだったので、作りました。

誰でも手軽に使えるようにしたかったので、以下のような処理手順にしています。

  1. Slackワークフローで集計期間を指定
  2. スプレッドシートへ吐き出し
  3. 吐き出された値を取得して、リアクション数をメッセージごとに集計
  4. ワークフローが実行されたチャンネルで、リアクション数上位の投稿をポスト

作成手順

Slackワークフローを作る

集計対象期間を入力してもらい、その日付と、ワークフローが実行されたチャンネルをスプレッドシートに吐き出します。

image.png

ワークフローの作り方は以下をご参考に。

こんな具合に吐き出されます。
列名は予め記入しておきます。

image.png

Slack Appを作る

メッセージの投稿やら取得やらをするためのアプリを作ります。
以下のページのCreate New AppFrom scratchからアプリを作成します。

image.png

適当にタイトルや説明文を入力したら、メニューのOAuth & Permissionsから、必要な権限を付与します。

image.png

Botが参加していないチャンネルからもメッセージを取得するには、channels:historychannels:readUser Token Scopeに入れないといけないので、ご注意ください。

諸々設定を完了したら、Install AppからSlackのワークスペースにインストールしてください。
App Homeから、Botの名前を入力しないとインストールできませんのでご注意を。

image.png

スクリプト

reactionRanking.js
let conversations_history_url = 'https://slack.com/api/conversations.history';
let chat_postMessage_url = 'https://slack.com/api/chat.postMessage';
let conversations_list_url = 'https://slack.com/api/conversations.list';
let slack_app_token = 'xoxp-XXXXXXX'; // 先程作成したSlack Appのトークン
let channel_id = 'XXXXXX'; //メッセージを取得する対象のチャンネルID
let sheet_id = 'XXXXXXXX'; //ワークフローで入力された値を吐き出すスプレッドシートID
let sheet = SpreadsheetApp.openById(sheet_id).getSheetByName('シート1');

function reactionRanking(){
  // 集計日を取得
  let lastRow = sheet.getLastRow();
  let startDate = sheet.getRange(lastRow, 1).getValue();
  let endDate = sheet.getRange(lastRow, 2).getValue();
  let formatStartDate = Utilities.formatDate(startDate, 'GMT', 'dd MMM yyyy HH:mm:ss z')
  let formatEndDate = Utilities.formatDate(endDate, 'GMT', 'dd MMM yyyy HH:mm:ss z')
  let unixStartTime = (Date.parse(formatStartDate)/1000).toFixed();
  let unixEndTime = (Date.parse(formatEndDate)/1000).toFixed();
  
  // ランキングを投稿するチャンネル名を取得
  let post_channel_name = sheet.getRange(lastRow, 3).getValue();
  // #が邪魔なので除外
  let sub_post_channel_name = post_channel_name.substr(1, post_channel_name.length - 1);

  let reaction_count = 0;
  let ranking_array = [];
  let reactions = '';
  let message_url = '';

  let chu_options = {
    "method" : "get",
    "contentType": "application/x-www-form-urlencoded",
    "payload" : { 
      "token": slack_app_token,
      "channel": channel_id,
      "oldest": unixStartTime,
      "latest": unixEndTime      
    }
  };

  // 期間内のメッセージを取得
  let response = UrlFetchApp.fetch(conversations_history_url, chu_options)
  let jsonResponse = JSON.parse(response);
  let messages = jsonResponse.messages;
  
  // それぞれのメッセージごとにリアクション数をカウント
  messages.forEach(function(message){
    message_url = 'https://a-company-talk.slack.com/archives/' + channel_id + '/p' + message.ts.replace('.', '');
    
    // リアクションがついていない場合は処理を逃がす
    try{
      reactions = message.reactions;
      reactions.forEach(function(reaction){
        reaction_count += reaction.count;
      });
      ranking_array.push([message_url, reaction_count]);
    } catch{
    } finally{
      reaction_count = 0; 
    }
  });
  
  
  // リアクション数降順で並べ替え
  ranking_array.sort(function(a, b){
    return b[1] - a[1];
  });
 
  let clu_options = {
    "method" : "get",
    "contentType": "application/x-www-form-urlencoded",
    "payload" : { 
      "token": slack_app_token,
      "limit": 1000,
      "exclude_archived": true
    }
  };

  // チャンネル一覧を取得
  response = UrlFetchApp.fetch(conversations_list_url, clu_options)
  jsonResponse = JSON.parse(response);
  let channels = jsonResponse.channels;
  let post_channel_id = '';

  // ワークフローが実行されたチャンネルのIDを取得
  channels.forEach(function(channel){
    if(channel.name == sub_post_channel_name){
      post_channel_id = channel.id;
    };
  });

  let i = 0;
  let post_text = '';

  // 上位5個をポスト
  while(i < 5){
    post_text = (i + 1).toString() + '位 リアクション数:' + ranking_array[i][1] + ' ' + ranking_array[i][0];

    options = {
      "method" : "get",
      "contentType": "application/x-www-form-urlencoded",
      "payload" : { 
        "token": slack_bot_app_token,
        "channel": post_channel_id,
        "text": post_text
      }
    };
    
    UrlFetchApp.fetch(chat_postMessage_url, options);
    i ++;
  }  
  
}

今回は1つのチャンネルのメッセージしか取得していませんが、実際には主要ないくつかのチャンネルのメッセージを取得して、リアクション数をカウントしています。
ワークスペースによってよく投稿されるチャンネルがあると思うので、よしなに調整して使ってください。

全チャンネルを対象にしてやってみたら、あまりにもAPIリクエスト数が多くなってしまい、GAS実行がタイムアウトしてしまいました。

トリガーを設定

ワークフローからスプレッドシートが編集されたら、上記のスクリプトを実行するように、トリガーを設定します。

image.png

完成

image.png

ワークフローをSlackから実行すれば、その期間リアクションを最も多く獲得した投稿がリポストされます。
どうやらAcompanyでは結婚をすると多くのリアクションを獲得できるようです。
リアクションが欲しい人は、ぜひ結婚しましょう!

さいごに

私は普段マーケティングの仕事をしているのですが、たまにこんな感じでGASで遊んでいます。
いくつかの作業タスクを効率化していたら、ありがたいことにBe Hacker Awardなるものを受賞しました。

Be Hacker というのは、AcompanyのVALUEのひとつで、改善を繰り返す姿勢のことを指します。

image.png

社員全員が当たり前のように改善することを考え、行動しているなと感じます。
他にもBHAGに「2035年までに世界No.1の偉大なプライバシーテックカンパニーになる」ことを掲げていたり、
まだまだ成長途中のデータクリーンルーム市場で事業を展開していたりと、かなりエキサイティングな日々を送れます。
興味あるよという方は、ぜひカジュアル面談で気軽にお話しましょう!!!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?