SlackログをGASでGmailに転送(Private ch,groupチャット対応版)

  • 3
    いいね
  • 0
    コメント

Google Apps Scriptを使ってSlackのログを定期的にGmailに転送するこちらの記事のスクリプトをほんの少しだけ改変してみました。(修正部分のコードはかなりやっつけです;)

修正スクリプト

var API_TOKEN = '[Your Slack token]'
var MAIL_TO = '[Your Mail Address]';

// 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 = 10000000;
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 groupsResp = this.requestSlackAPI('groups.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.importHistoryDelta(ch);
                if (body != "") {
                  if (body.length > MAX_MAIL_LENGTH) {
                    body = body.substr(0, MAX_MAIL_LENGTH);
                    body += "\n--- string length over " + MAX_MAIL_LENGTH + " ---\n";
                  }
                    // send mail
                    title = "[Slack Log][channel:" + ch.name + "] " + _this.formatDate(new Date());
                    MailApp.sendEmail({
                        to: MAIL_TO,
                        subject: title,
                        body: body
                    });
                }
            }
        }
        for (var _i = 0, _g = groupsResp.groups; _i < _g.length; _i++) {
            var gr = _g[_i];
            if (!gr.is_archived) {
                body = this.importHistoryDelta(gr);
                if (body != "") {
                  if (body.length > MAX_MAIL_LENGTH) {
                    body = body.substr(0, MAX_MAIL_LENGTH);
                    body += "\n--- string length over " + MAX_MAIL_LENGTH + " ---\n";
                  }
                    // send mail
                    title = "[Slack Log][group:" + gr.name + "] " + _this.formatDate(new Date());
                    MailApp.sendEmail({
                        to: MAIL_TO,
                        subject: title,
                        body: body
                    });
                }
            }
        }
        PropertiesService.getScriptProperties().setProperties(_this.prop);
    };
    SlackChannelHistoryLogger.prototype.importHistoryDelta = function (ch) {
        var _this = this;
        Logger.log("importHistoryDelta " + 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.username ? msg.username : msg.user;
                    if (_this.memberNames[user]) {
                        user = _this.memberNames[user];
                    }
                    body += '[' + Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm:ss') + '] ' +
                        'from:' + user + "\n" +
                        _this.unescapeMessageText(msg.text) + "\n";
                    if (msg.attachments) {
                        var attachments = JSON.stringify(msg.attachments);
                        body += 'attachments:' + _this.unescapeMessageText(attachments) + "\n";
                    }
                    body += "\n";
                });
            }
        }
        return body;
    };
    SlackChannelHistoryLogger.prototype.formatDate = function (dt) {
        return Utilities.formatDate(dt, Session.getScriptTimeZone(), 'yyyy-MM-dd');
    };
    SlackChannelHistoryLogger.prototype.loadMessagesBulk = function (tr, options) {
        var _this = this;
        if (options === void 0) {
            options = {};
        }
        var messages = [];
        options['count'] = HISTORY_COUNT_PER_PAGE;
        options['channel'] = tr.id;

        var loadSince = function (oldest) {
            if (oldest) {
                options['oldest'] = oldest;
            }
            // order: recent-to-older
            var apiPath = 
                tr.is_channel ? 'channels.history'
              : tr.is_group   ? 'groups.history'
              :                 'im.history'
            var resp = _this.requestSlackAPI(apiPath, 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(/&lt;/g, '<')
            .replace(/&gt;/g, '>')
            .replace(/&quot;/g, '"')
            .replace(/&amp;/g, '&')
            .replace(/<@(.+?)>/g, function ($0, userID) {
                var name = _this.memberNames[userID];
                return name ? "@" + name : $0;
            });
    };
    return SlackChannelHistoryLogger;
})();

変更点

そのままではパブリックなchannelだけしか取得できないようでしたので、groups.history APIの実行を追加してPrivateチャンネル、およびDirect Messageでのgroupチャットのログも取得するようにしています。

ついでにメッセージにattachmentsが含まれていた場合はその内容もJSONとしてメールに出力するようにしてみました。

おまけ

2017/03/24現在、元記事でのSlack APIキーの取得方法は非推奨(Legacy扱い)になっているようです。
https://api.slack.com/custom-integrations/legacy-tokens

ちゃんとしたAPIキーを発行する場合はSlack Appとして登録してOAuth認証を行う必要があります。

参考記事

SpecialThanks‼

Slackの過去ログをGmailに定期的に飛ばしてアーカイブ(Google Apps Script)