この記事は移行しました!最新の内容はこちらをご覧ください😀
やってきました!12月!といえばアドベントカレンダーですね!
今年も最後まで楽しみましょう!
これは 株式会社マイホム Advent Calendar 2021 1日目の記事です。
はじめに
1年を振り返るにはSlackリアクションしかないでしょう!
ということでもう何番煎じかわからないSlackのリアクション集計!
ごにょごにょ実装したので投稿します。
※こちらの記事を大いに参考にさせていただきました。ありがとうございます!
https://qiita.com/ryosuk/items/1a18c663884d2748d4ca
なぜやるか
よく使われたリアクションを知ることで、会社の特色やどんな一年だったかがわかるのでは?😃
また、弊社はMVVのVALUE
- User First
- Profesional
- Love
- Playful
という4つのカスタム絵文字があり、
それを使った人、受け取った人を知ることで
MVVを体現している人がわかっちゃうのでは!?😃😃😃😃
できること
下記をスプレッドシートに出力します。
※プライベートチャット、スレッド内のリアクションは集計対象外。
- リアクションランキング
- MVVリアクションを受け取った回数(ユーザーごと)
- MVVリアクションを使った回数(ユーザーごと)
実行方法
Slack App を作成し User OAuth Token を取得する
Slack APIを叩くために必要。
メモ:scopesを設定する必要があり、ググると昔はGUI上で設定できたっぽいが今はManifestを直接書け、みたいに言われるので直接書いた。
oauth_config:
scopes:
user:
- channels:read
- groups:read
- im:read
- mpim:read
- channels:history
- groups:history
- im:history
- mpim:history
- users:read
スプレッドシートを用意する
下記3つのシートを作成しておきます。
- ranking
- mvv_get
- mvv_send
GASを実行する
下記コードをコピペして、チャチャっと書き換えて、main()を実行!
// 参考: https://qiita.com/ryosuk/items/1a18c663884d2748d4ca
var API_TOKEN = "ここに取得したトークンをコピペ";
var target_year = '2021'; // 対象年
var mvv_reactions = ['らぶ','ぷろ','ゆーざー','ぷれい']; // MVVの絵文字名
var sheet_name_ranking = 'ranking';
var sheet_name_mvv_get = 'mvv_get';
var sheet_name_mvv_send = 'mvv_send';
// ページネーション
var MAX_HISTORY_PAGINATION = 1; // デバッグ用の定数になった
var HISTORY_COUNT_PER_PAGE = 200; // 対象年分とってくる。
var stamps = {};
var counts = [];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var timezone = ss.getSpreadsheetTimeZone();
var slack_members = {};
var mvv_get_user = {};
var mvv_send_user = {};
function main() {
var logger = new SlackChannelHistoryLogger();
logger.run();
updateRankingSheet();
updateMvvGetSheet();
updateMvvSendSheet();
Logger.log(mvv_get_user);
Logger.log(mvv_send_user);
Logger.log(slack_members);
function updateRankingSheet() {
var sheet = ss.getSheetByName(sheet_name_ranking);
if (!sheet) {
sheet = ss.insertSheet().setName(sheet_name_ranking);
}
sheet.clear();
var names = Object.keys(stamps);
var ary = [];
for (var i=0; i<names.length; i++) {
ary.push([names[i]]);
}
sheet.getRange(1,1,ary.length,1).setValues(ary);
for (var i=0; i<names.length; i++) {
counts.push([stamps[names[i]]]);
}
sheet.getRange(1,2,counts.length,1).setValues(counts);
//降順でソート
sheet.getRange(1,1,counts.length,2).sort({column: 2, ascending: false});
}
function updateMvvGetSheet() {
var sheet = ss.getSheetByName(sheet_name_mvv_get);
if (!sheet) {
sheet = ss.insertSheet().setName(sheet_name_mvv_get);
}
sheet.clear();
var data = [];
data.push(['なまえ', ...mvv_reactions, '合計']);
Object.entries(mvv_get_user).forEach(function([key, value]){
var name = slack_members[key];
if (!name) {
return;
}
var tmp = Object.values(value);
var total = tmp.reduce(function(sum, element){
return sum + element;
}, 0);
data.push([name, ...tmp, total]);
});
sheet.getRange(1,1,data.length,6).setValues(data);
//降順でソート
sheet.getRange(1,1,data.length,6).sort({column: 6, ascending: false});
}
function updateMvvSendSheet() {
var sheet = ss.getSheetByName(sheet_name_mvv_send);
if (!sheet) {
sheet = ss.insertSheet().setName(sheet_name_mvv_send);
}
sheet.clear();
var data = [];
data.push(['なまえ', '送信回数']);
Object.entries(mvv_send_user).forEach(function([key, value]){
data.push([slack_members[key], value]);
});
sheet.getRange(1,1,data.length,2).setValues(data);
//降順でソート
sheet.getRange(1,1,data.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 headers = {
'Authorization': 'Bearer '+ API_TOKEN
};
var options = {
'headers': headers
};
var resp = UrlFetchApp.fetch(url, options);
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 usersResp = this.requestSlackAPI('users.list');
for (const member of usersResp.members) {
// //削除済、botユーザー、Slackbotを除く
// if (!member.deleted && !member.is_bot && member.id !== "USLACKBOT") {
slack_members[member.id] = member.profile.display_name;
// }
}
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();
// チャンネル絞って開発用
// if (ch.name != 'hogehoge') {
// return;
// }
var messages = this.loadMessagesBulk(ch, {});
var dateStringToMessages = {};
if(messages != "err"){
Logger.log(ch.name + ' messages:' + messages.length);
messages.forEach(function (msg) {
var date = new Date(+msg.ts * 1000);
var reactions = msg.reactions ? msg.reactions : "";
var m_year = Utilities.formatDate(date, timezone, 'yyyy');
// チャンネル絞って開発用
// if (ch.name == 'hogehoge') {
// // Logger.log(Utilities.formatDate(date, timezone, 'yyyy-MM-dd'));
// // Logger.log(msg);
// // Logger.log(reactions);
// }
if(reactions !== "" && m_year == target_year){
reactions.forEach(function (reaction) {
var name = reaction.name;
if (stamps[name]) {
stamps[name] = stamps[name] + reaction.count;
} else {
stamps[name] = reaction.count;
}
// MVVリアクションならさらにごにょごにょ
if (mvv_reactions.includes(name)) {
if (!mvv_get_user[msg.user]) {
mvv_get_user[msg.user] = {};
mvv_reactions.forEach(function (mvv_reaction) {
mvv_get_user[msg.user][mvv_reaction] = 0;
});
}
mvv_get_user[msg.user][name] = mvv_get_user[msg.user][name] + reaction.count;
reaction.users.forEach(function (send_user) {
if (!mvv_send_user[send_user]) {
mvv_send_user[send_user] = 0;
}
mvv_send_user[send_user]++;
});
}
});
}
});
}
};
SlackChannelHistoryLogger.prototype.loadMessagesBulk = function (ch, options) {
var _this = this;
if (options === void 0) { options = {}; }
var messages = [];
options['limit'] = HISTORY_COUNT_PER_PAGE;
options['channel'] = ch.id;
var loadSince = function (cursor) {
if (cursor) {
options['cursor'] = cursor;
}
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) {
// ページがあって、メッセージが対象年なら次ページを取得
// HACK: 最初のメッセージで判定してる関係で無駄に1回多く叩いてるけど
while (resp.has_more && target_year == Utilities.formatDate(new Date(+resp.messages[resp.messages.length-1].ts * 1000), timezone, 'yyyy')) {
resp = loadSince(resp.response_metadata.next_cursor);
page++;
}
return messages;
};
return SlackChannelHistoryLogger;
})();
できあがり
ranking(利用頻度の多いリアクションランキング)
mvv_get(MVVリアクションを受け取った人)
mvv_send(MVVリアクションを使った人)
さいごに
集計って楽しくてワクワクしますよね♪
それではみなさん、MVVを意識して仕事していきましょー!