13
7

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のスタンプ数を集計して順位付けし、結果をslack内のチャンネルに通知する

Last updated at Posted at 2020-05-24

目的

どのslackのスタンプをどれだけ使っているか可視化するためにGASでスクリプト作成しました。

参考にした記事

概要

slackのworkspaceのAPIトークンを取得してGASに埋め込み、
APIメソッドを使ってslackから会話の情報からスタンプの利用分(月単位)を抽出して集計します。

抽出した結果をスプレッドシートに書き出し、書き出した内容をBotとしてslackの指定したチャンネルにpostします。

構築手順

slackAPIの取得

slack apiのアプリ作成画面から、「Create New App」を選択します。
image.png

適当な名前をつけてBasic infomation画面にてOAuth & Permissionsを選択し、OAuth Access TokenBot User OAuth Access Tokenを生成します。
image.png

Scopes設定項目からUser Token ScopesとしてAPIメソッドに対する許可を追加します。
conversations.listとconversations.historyに必要な以下のスコープを追加します。
image.png

  • conversations.list

    • channels:read
    • groups:read
    • im:read
    • mpim:read
  • conversations.history

    • channels:history
    • groups:history
    • im:history
    • mpim:history

また、Bot Token Scopesとしてchat:writeをスコープに追加します。

GASの作成

以下の通り記述します。

main.gs
function main() {
     StoreLogsDelta();
     Ranking();
     slack();
}
calculate.gs
var API_TOKEN = "OAuth Access Tokenを記載";
var BOT_TOKEN = "Bot User OAuth Access Tokenを記載";
// ページネーション
var MAX_HISTORY_PAGINATION = 10;
var HISTORY_COUNT_PER_PAGE = 1000;
var stamps = {};
var counts = [];
var names = [];
var sheet = SpreadsheetApp.getActiveSheet();
// 時間の判定
var timezone = sheet.getParent().getSpreadsheetTimeZone();
var now = new Date;

// ボタン表示
var meetingDate = Browser.inputBox("ランキングを出力したい期間(yyyy-MM)を入力してください。", Browser.Buttons.OK_CANCEL);

if(meetingDate == "cancel"){
  Browser.msgBox("スクリプトを終了します。");
} else {
  var targetmonth = Utilities.formatDate(new Date(meetingDate), timezone, 'yyyy-MM');
//入力制限が適当なので随時修正ください。
  Browser.msgBox( targetmonth + "の期間のランキングを出力します。");
}


function StoreLogsDelta() {
  var logger = new SlackChannelHistoryLogger();
  logger.run();
  //シートを削除
  sheet.clear();
  sheet.getRange(1,1).setValue(targetmonth);

  var names = Object.keys(stamps);
  var ary = [];
  for (var i=0; i<names.length; i++) {
    ary.push([names[i]]);
  }
  sheet.getRange(2,1,ary.length,1).setValues(ary);
  for (var i=0; i<names.length; i++) {
    counts.push([stamps[names[i]]]); 
  }
  sheet.getRange(2,2,counts.length,1).setValues(counts);
  //降順でソート
  sheet.getRange(2,2,counts.length,2).sort({column: 2, ascending: false});
};

var SlackChannelHistoryLogger = (function () {
    function SlackChannelHistoryLogger() {
        this.memberNames = {};
    }
    SlackChannelHistoryLogger.prototype.requestSlackAPI = function (path, params) {
        if (params === void 0) { params = {}; }
        var url = "https://slack.com/api/" + path + "?";
        var qparams = [];
        for (var k in params) {
            qparams.push(encodeURIComponent(k) + "=" + encodeURIComponent(params[k]));
        }
        url += qparams.join('&');
        try{
          var resp = UrlFetchApp.fetch(url, {
              headers: { "Authorization": `Bearer ${API_TOKEN}` }, 
          });
          var data = JSON.parse(resp.getContentText());
          if (data.error) {
            throw "GET " + path + ": " + data.error;
          }
          return data;
        }catch(e){
          return "err";
          }
    };
    SlackChannelHistoryLogger.prototype.run = function () {
        var _this = this;
        var channelsResp = this.requestSlackAPI('conversations.list');
            for (var _i = 0, _a = channelsResp.channels; _i < _a.length; _i++) {
              var ch = _a[_i];
              this.importChannelHistoryDelta(ch);
            }
      
    };  
    SlackChannelHistoryLogger.prototype.importChannelHistoryDelta = function (ch) {
        var _this = this;
        var now = new Date();
        var oldest = '1'; // oldest=0 does not work
        var messages = this.loadMessagesBulk(ch, { oldest: oldest });
        var dateStringToMessages = {};


      if(messages != "err"){
        messages.forEach(function (msg) {
          var date = new Date(+msg.ts * 1000);
          var rec = msg.reactions ? msg.reactions : "";
          var m_date = Utilities.formatDate(date, timezone, 'yyyy-MM');
              if(rec !== "" && m_date == targetmonth){
                var name = rec[0].name;
          Logger.log(m_date);
                var tmp = 0;
                if (stamps[name]) {
                  tmp = stamps[name];
                  stamps[name] = tmp + rec[0].count;
                } else {
                  stamps[name] = rec[0].count;
                }      
              }
        });
      }

    };
    SlackChannelHistoryLogger.prototype.loadMessagesBulk = function (ch, options) {
        var _this = this;
        if (options === void 0) { options = {}; }
        var messages = [];
        options['count'] = HISTORY_COUNT_PER_PAGE;
        options['channel'] = ch.id;
        var loadSince = function (oldest) {
            if (oldest) {
                options['oldest'] = oldest;
            }
            var resp = _this.requestSlackAPI('conversations.history', options);
          if(resp != "err"){
            messages = resp.messages.concat(messages);
          }
            return resp;
        };
        var resp = loadSince();
        var page = 1;
        while (resp.has_more && page <= MAX_HISTORY_PAGINATION) {
            resp = loadSince(resp.messages[0].ts);
            page++;
        }
        return messages.reverse();
    };
    return SlackChannelHistoryLogger;
})();
notify.gs
function Ranking() {
  var range = sheet.getRange(1, 1, sheet.getLastRow(), 2);
  var s_cnt = range.getValues();

  var tmp = 0;
  var tmp_n = "";

  for (var j=1; j<s_cnt.length; j++) {
    for (var k=j+1; k<s_cnt.length; k++) {
      if(s_cnt[j][1] < s_cnt[k][1]){
         tmp =  s_cnt[j][1];
        s_cnt[j][1] = s_cnt[k][1];
        s_cnt[k][1] = tmp;
         tmp_n =  s_cnt[j][0];
        s_cnt[j][0] = s_cnt[k][0];
        s_cnt[k][0] = tmp_n;
      }
    }
  }
  sheet.getRange(1,1,s_cnt.length,2).setValues(s_cnt);
  var snt = "年月:" + targetmonth
  for (var j=1; j<s_cnt.length; j++) {
    var snt = snt + "\n"+j+"位: :"+s_cnt[j][0] + ": " +s_cnt[j][1] + "\n"
  }
  Logger.log(snt);
  slack(snt,s_cnt[1][0]);
};

function slack(message,name) {
    var url        = 'https://slack.com/api/chat.postMessage';
    var token      = BOT_TOKEN;
    var channel    = '結果を出力するチャンネル名';
    var text       = message;
    var username   = 'GetStamp';
    var icon_emoji = ':'+name+':';

    var parse      = 'full';
    var method     = 'post';

    var payload = {
        'token'      : token,
        'channel'    : channel,
        'text'       : text,
        'username'   : username,
        'parse'      : parse,
        'icon_emoji' : icon_emoji,
        'link_names' : true
    };

    var params = {
        'method' : method,
        'payload' : payload
    };
    var response = UrlFetchApp.fetch(url, params);
};

実行/結果

GAS実行

main.gsを実行すると、集計スプレッドシート上に出力期間の入力を求めるメッセージボックスが表示されます。

スプレッドシート

このような形で記述されます。
image.png

slackチャンネルに通知

このような形で通知されます。

13
7
2

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
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?