38
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-10-05

あのやりとりしたのGmailだっけ、Slackだっけ、こっちで検索し、あっちで検索しというのも面倒!
GmailをSlackに転送する方法は普通に紹介されていますけど、逆はなかなか見つからない・・・
無料枠で過去ログの制限もあるし、SlackログをGmailにアーカイブさせて使うのも悪くないのでは?
Google Apps Scriptを使うとサーバいらずで定期実行できるし、という事でさくっと書いてみました。

SlackにてWeb API用トークン生成

SLACK API Authenticationにアクセスし「Generate test tokens」を実行します。

スクリーンショット 2016-10-05 12.57.00.png

「Create token」するとパスワードなど聞かれるのでconfirmするとxoxp-から始まるtokenが生成されるのでメモります。
もちろんこのtokenが漏洩すると大変な事になるので注意してくださいね。
スクリーンショット 2016-10-05 12.59.02.png

スクリプトの作成

動作仕様

  • チャンネル毎にログをメール送信
  • 自分の参加しているチャンネルのみ対象
  • 初回は取得できる全てのログを送信し、次回以降は最終取得日時以降のログのみ送信
  • メールで送信する最大文字数を超えた場合はカット

スクリプトの保存場所はGoogla Drive
Apps Scriptを初めて使う場合は「新規」>「その他」>「アプリを追加」から「Google Apps Script」を追加してください。

スクリーンショット 2016-10-05 12.30.15.png
※ 社用のAppsで作業していますがお気になさらず

スクリーンショット 2016-10-05 12.31.38.png

新規Google Apps Scriptを作成します。
スクリーンショット 2016-10-05 12.32.19.png

プロジェクト名はー、「Slack2Gmail」にしてみます。
スクリーンショット 2016-10-05 13.14.01.png

コードに下記スクリプトを貼り付けます。下記書き換えてください。
[Your Slack token] => 先ほど取得したSlackのAPIトークンに
[Your Mail Address] => 送信先メールアドレスに

// Configuration: Obtain Slack web API token at https://api.slack.com/web
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 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 != "") {
                  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
					});
				}
			}
		}
		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(/&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;
})();

参考サイトのスプレットシートにログ保存するスクリプトをメール送信する仕組みに書き換えました。
スプレットシートの最終行から日時を取得し以降のログだけ取得する機能がありましたが、この最終日時をスクリプトプロパティに持たせるように変更しています。

ここで一度実行ボタンを押して権限の許可をしておきます。

スクリーンショット 2016-10-05 13.47.57.png

正常に実行できればこの時点でメールも届くはずです。「表示」>「ログ」にログで出力内容も確認できます。
またスクリプトを実行すると最終取得日時の情報が「ファイル」>「プロジェクトのプロパティ」>「スクリプトのプロパティ」にチャンネル毎の最終取得日時が保存されているのでまた最初から取得したい場合はこのプロパティを削除してください。
メール送信文字数の最大値はGmailがMAXで25MBまで送れるらしいということで2バイトコードで10000000で20MB+バッファ5MBに設定してみました。

スクリーンショット 2016-10-05 17.59.26.png

定期実行

ツールバーの時計の様なアイコンから実行スケジュールを設定します。
お好みのタイミングに指定してみると良いでしょう。

スクリーンショット 2016-10-05 17.16.27.png

スクリーンショット 2016-10-05 18.28.43.png

以上で設定完了です!
Gmail側でいい感じにアーカイブのフィルタをかけてあげれば受信トレイを汚さずにSlackログが検索にかかるGmailの完成です。
スクリーンショット 2016-10-06 12.35.25.png
スクリーンショット 2016-10-06 12.35.56.png

参考サイト
Slack のログを自動で Google Spreadsheet に保存する
Google Apps Scriptを使って定期実行するcronを作る

38
41
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?