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

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

## 修正スクリプト
```JavaScript
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](https://api.slack.com/methods/groups.history) APIの実行を追加してPrivateチャンネル、およびDirect Messageでのgroupチャットのログも取得するようにしています。

ついでにメッセージに[attachments](https://api.slack.com/docs/messages/builder?msg=%7B%22attachments%22%3A%5B%7B%22fallback%22%3A%22Required%20plain-text%20summary%20of%20the%20attachment.%22%2C%22color%22%3A%22%2336a64f%22%2C%22pretext%22%3A%22Optional%20text%20that%20appears%20above%20the%20attachment%20block%22%2C%22author_name%22%3A%22Bobby%20Tables%22%2C%22author_link%22%3A%22http%3A%2F%2Fflickr.com%2Fbobby%2F%22%2C%22author_icon%22%3A%22http%3A%2F%2Fflickr.com%2Ficons%2Fbobby.jpg%22%2C%22title%22%3A%22Slack%20API%20Documentation%22%2C%22title_link%22%3A%22https%3A%2F%2Fapi.slack.com%2F%22%2C%22text%22%3A%22Optional%20text%20that%20appears%20within%20the%20attachment%22%2C%22fields%22%3A%5B%7B%22title%22%3A%22Priority%22%2C%22value%22%3A%22High%22%2C%22short%22%3Afalse%7D%5D%2C%22image_url%22%3A%22http%3A%2F%2Fmy-website.com%2Fpath%2Fto%2Fimage.jpg%22%2C%22thumb_url%22%3A%22http%3A%2F%2Fexample.com%2Fpath%2Fto%2Fthumb.png%22%2C%22footer%22%3A%22Slack%20API%22%2C%22footer_icon%22%3A%22https%3A%2F%2Fplatform.slack-edge.com%2Fimg%2Fdefault_application_icon.png%22%2C%22ts%22%3A123456789%7D%5D%7D)が含まれていた場合はその内容もJSONとしてメールに出力するようにしてみました。

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

ちゃんとしたAPIキーを発行する場合は[Slack App](https://api.slack.com/apps)として登録して[OAuth認証](https://api.slack.com/docs/oauth)を行う必要があります。

## 参考記事
SpecialThanks‼
>[Slackの過去ログをGmailに定期的に飛ばしてアーカイブ（Google Apps Script）](http://qiita.com/supertaihei02/items/61fd9e60ce63692b649f)