目的
どのslackのスタンプをどれだけ使っているか可視化するためにGASでスクリプト作成しました。
参考にした記事
概要
slackのworkspaceのAPIトークンを取得してGASに埋め込み、
APIメソッドを使ってslackから会話の情報からスタンプの利用分(月単位)を抽出して集計します。
-
conversations.list
所属するworkspacesの全てのチャンネルの一覧を取得するメソッドです。 -
conversations.history
会話の履歴を取得するメソッドです。
抽出した結果をスプレッドシートに書き出し、書き出した内容をBotとしてslackの指定したチャンネルにpostします。
構築手順
slackAPIの取得
slack apiのアプリ作成画面から、「Create New App」を選択します。
適当な名前をつけてBasic infomation画面にてOAuth & Permissionsを選択し、OAuth Access Token
とBot User OAuth Access Token
を生成します。
Scopes設定項目からUser Token Scopes
としてAPIメソッドに対する許可を追加します。
conversations.listとconversations.historyに必要な以下のスコープを追加します。
-
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の作成
以下の通り記述します。
function main() {
StoreLogsDelta();
Ranking();
slack();
}
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;
})();
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を実行すると、集計スプレッドシート上に出力期間の入力を求めるメッセージボックスが表示されます。