Slackの履歴を定期的にメール(Google Apps Script)
Slackの無償版を使っていると、10,000件以上の履歴はどんどん消えていく。
まあしゃあないのだが、やっぱり一応過去ログは取っておきたいので、手っ取り早い方法をググったら下記のような記事が。
GAS(Google Apps Script)でSlack APIから情報取得して、Gmailでメール送信している。
ありがたくそのまま使わせてもらおうと思ったけど、自分で2点修正したので公開しておこうっと。
メールの最大長を修正
コードの冒頭部の
var MAX_MAIL_LENGTH = 10000000;
を100000に修正。
Gmailの仕様でエラーになるっぽかったので、色々試行錯誤したところ100分の1のサイズに変更。
メール最大長に達した場合にメールを分割して送信
オリジナルはMAX_MAIL_LENGTHに達したら残りを切り捨てている。
しかも新しいものを切り捨てていて、取得したタイミングをスクリプトのプロパティに格納記憶しているから、切り捨てた情報は永遠に見れなくなるので残念。
なので、メール本文が MAX_MAIL_LENGTH に達したら、その本文を分割して送付するように変更した。
あくまでbody.lengthで取得しているので、Slackコメントの途中でメール跨ぎが発生するけど、まあ無くなるよりは良いだろうと。
修正したスクリプト全部
// Configuration: Obtain Slack web API token at https://api.slack.com/web
var API_TOKEN = 'xoxp-・・・・・・';
var MAIL_TO = 'you@mail.domain';
// Slack offers 10,000 history logs for free plan teams
var MAX_HISTORY_PAGINATION = 10;
var HISTORY_COUNT_PER_PAGE = 1000;
var MAX_MAIL_LENGTH = 100000;
var prop = {};
function StoreLogsDelta() {
var logger = new SlackChannelHistoryLogger();
logger.run();
};
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 = [("token=" + encodeURIComponent(API_TOKEN))];
for (var k in params) {
qparams.push(encodeURIComponent(k) + "=" + encodeURIComponent(params[k]));
}
url += qparams.join('&');
Logger.log("==> GET " + url);
var resp = UrlFetchApp.fetch(url);
var data = JSON.parse(resp.getContentText());
if (data.error) {
throw "GET " + path + ": " + data.error;
}
return data;
};
SlackChannelHistoryLogger.prototype.run = function () {
var _this = this;
var p = PropertiesService.getScriptProperties().getProperties();
if (p != null) {
_this.prop = p;
} else {
_this.prop = {};
}
var usersResp = this.requestSlackAPI('users.list');
usersResp.members.forEach(function (member) {
_this.memberNames[member.id] = member.name;
});
var teamInfoResp = this.requestSlackAPI('team.info');
this.teamName = teamInfoResp.team.name;
var channelsResp = this.requestSlackAPI('channels.list');
var title = "";
var body = "";
for (var _i = 0, _a = channelsResp.channels; _i < _a.length; _i++) {
var ch = _a[_i];
if (ch.is_member) {
body = this.importChannelHistoryDelta(ch);
if (body != "") {
var mailStart = 0;
var mailText = "";
var mailNumber = Math.ceil(body.length / MAX_MAIL_LENGTH);
for(var _i = 0; _i < mailNumber; _i++){
mailText = body.substr(mailStart, MAX_MAIL_LENGTH);
// send mail
title = "[SlackLogMail][ch:" + ch.name + "] " + (_i + 1) + "/" + mailNumber + " exec: " + _this.formatDate(new Date());
MailApp.sendEmail({
to: MAIL_TO,
subject: title,
body: mailText
});
mailStart = mailStart + MAX_MAIL_LENGTH;
}
}
}
}
PropertiesService.getScriptProperties().setProperties(_this.prop);
};
SlackChannelHistoryLogger.prototype.importChannelHistoryDelta = function (ch) {
var _this = this;
Logger.log("importChannelHistoryDelta " + ch.name + " (" + ch.id + ")");
var oldestKey = ch.name + '-oldest';
var oldest = 1;
if (_this.prop[oldestKey] != null) {
oldest = _this.prop[oldestKey];
}
var options = {};
if (oldest != null) {
options['oldest'] = oldest;
}
var todayString = _this.formatDate(new Date());
var messages = this.loadMessagesBulk(ch, options);
var dateStringToMessages = {};
messages.forEach(function (msg) {
var date = new Date(+msg.ts * 1000);
var dateString = _this.formatDate(date);
if (!dateStringToMessages[dateString]) {
dateStringToMessages[dateString] = [];
}
dateStringToMessages[dateString].push(msg);
_this.prop[oldestKey] = msg.ts;
});
var body = "";
if (dateStringToMessages != null && dateStringToMessages.length != 0) {
for (var dateString in dateStringToMessages) {
body += "=================== date:[" + dateString + "] ===================\n\n";
dateStringToMessages[dateString].forEach(function (msg) {
var date = new Date(+msg.ts * 1000);
var user = msg.user;
if (_this.memberNames[msg.user] != null) {
user = _this.memberNames[msg.user];
}
body += '[' + Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm:ss') + '] ' +
'from:' + user + "\n" +
_this.unescapeMessageText(msg.text) + "\n";
});
}
}
return body;
};
SlackChannelHistoryLogger.prototype.formatDate = function (dt) {
return Utilities.formatDate(dt, Session.getScriptTimeZone(), 'yyyy-MM-dd');
};
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;
}
// order: recent-to-older
var resp = _this.requestSlackAPI('channels.history', options);
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++;
}
// oldest-to-recent
return messages.reverse();
};
SlackChannelHistoryLogger.prototype.unescapeMessageText = function (text) {
var _this = this;
return (text || '')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/&/g, '&')
.replace(/<@(.+?)>/g, function ($0, userID) {
var name = _this.memberNames[userID];
return name ? "@" + name : $0;
});
};
return SlackChannelHistoryLogger;
})();もちろん、↓冒頭のAPIトークンとメールアドレスは変えて実行してくださいね。
var API_TOKEN = 'xoxp-・・・・・・' var MAIL_TO = 'you@mail.domain';
先日、とあるベトナムの開発ベンダーさんと新規でお付き合いする時に、プロジェクトでのコミュニケーションをどうするか?について話ししたら、
「もうメールはやめましょう、効率が全然違います。SlackかSkypeでやりましょう」
と社長に言われてしまった(笑)
もうそういう時代なんだね、おじさんが取り残されていくんだろうね。

