2017/11/28 : 第1版
2017/12/11 : 第2版:DMの場合には送信相手にのみ送付するように変更。その他、詳細な箇所を修正
この記事の内容はslackを、私のようなメール中心の生活を送っている人間が使うときの内容です。
以前、outgoing webhookを用いたメール通知ボットを実現していました。slackのメール通知機能はデフォルトで何かとメールを送らない条件がある。細かく設定を変えても送らない条件が残るのです。メンバーもメールドリブンな場合、全員に設定を変えるように説明するのも面倒です。私としては常にすぐにメールで通知する機能が欲しかったのです。
新しいworkspace用に過去の資産を再利用しようとしたところ、outgoing webhookはlegacy扱いになっていて、新規に登録できませんでした。もしかしたら新しく登録する方法もあるのかもしれませんが、せっかくなので新しいevents apiを試してみることとしました。
ここでは単純に「誰が」「何と書いたか」をメンバー全員にメールで通知することとします。
- 「https://api.slack.com/apps/」から新規アプリを作成
- 「Events API(Event Subscriptions)」からenableとし、以下を追加
- file_comment_added
- file_created
- im_created
- message.channels
- message.groups
- message.im
- message.mpim
- 「Request URL」は後述のGoogle Apps ScriptのWeb URLを指定
- 「OAuth & Permissions」で以下の二つのScopeを追加
- users:read
- users:read.email
- 「OAuth Access Token」の内容をメモ
- Gdriveで適当なフォルダーにSpreadsheetを新規作成(履歴保管用)
- ツール→スクリプトエディター
- 以下の内容を作成
- 「xoxp-??????????」の部分は上の「OAuth Access Token」のメモ内容とする
- 公開→Webアプリケーションとして導入
- ここで得られたURLを3のURLにする
var SLACK_TOKEN = "xoxp-??????????";
var userList = {};
var nameList = {};
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetLog = ss.getSheets()[0];
var now = null;
function doGet(e) {
doPost(e);
}
function doPost(e) {
try {
now = new Date();
var values = JSON.parse(e.postData.contents);
// sheetLog.appendRow([ now, e ]);
// sheetLog.appendRow([ now, e.postData ]);
sheetLog.appendRow([ now, values ]);
if (values.type == "url_verification") {
sheetLog.appendRow([now, 'url_verification', values.type, values.token, values.challenge ]);
return ContentService.createTextOutput(JSON.stringify(values.challenge)).setMimeType(ContentService.MimeType.TEXT);
} else if (values.type == "event_callback") {
if (values.event.type == "message") {
getMails();
var text = values.event.text;
if (text != undefined) {
var username = userList[values.event.user];
var userRealname = nameList[values.event.user];
var channelName = getChannel(values.event.channel);
var wsName = getWS();
var toStr = "";
if (channelName != null) {
// channelへの投稿
for (var key in userList) {
if (toStr != "") {
toStr = toStr + ",";
}
toStr = toStr + userList[key];
}
} else {
// GRP名がない=DM?
// sheetLog.appendRow([ now, 'channel null', values.event.channel ]);
var uid = getDMTargetUID(values.event.channel);
if (uid != null) {
channelName = 'Direct Message';
toStr = userList[uid];
} else {
channelName = 'サポート外メッセージ';
text = 'サポート外のメッセージでした。';
}
}
sheetLog.appendRow([now, 'sendmail', toStr]);
MailApp.sendEmail(
null,
'notify slack(' + wsName + ') new text by ' + username,
userRealname + '(' + username + ') says\n\n ' + text + '\n\n'
+ 'at ' + channelName + ' channel, ' + wsName + '\n\n',
{bcc : toStr }
);
} else {
sheetLog.appendRow([now, 'same pre\ivious' ]);
}
}
}
} catch (ee) {
sheetLog.appendRow([now, ee ]);
}
}
function getMails() {
var response = UrlFetchApp.fetch("https://slack.com/api/users.list?token=" + SLACK_TOKEN);
var content = response.getContentText("UTF-8");
var values = JSON.parse(content);
// Logger.log(values);
// sheetLog.appendRow([ now, 'getMails', values ]);
userList = {};
nameList = {};
for (var i = 0; i < values['members'].length; i++) {
try {
var id = values['members'][i]['id'];
var ma = values['members'][i]['profile']['email'];
if (ma != undefined) {
userList[id] = ma;
nameList[id] = values['members'][i]['profile']['real_name'];
}
} catch (e1) {
;
}
}
}
function getChannel(cid) {
var channelName = null;
var response = UrlFetchApp.fetch("https://slack.com/api/channels.list?token=" + SLACK_TOKEN);
var content = response.getContentText("UTF-8");
var values = JSON.parse(content);
// Logger.log(values);
// sheetLog.appendRow([ now, 'getChannel', values['channels'] ]);
for (var i = 0; i < values['channels'].length; i++) {
try {
var id = values['channels'][i]['id'];
if (id == cid) {
var ma = values['channels'][i]['name_normalized'];
if (ma != undefined) {
channelName = ma;
break;
}
}
} catch (e1) {
;
}
}
// sheetLog.appendRow([ now, 'getChannel', cid, channelName ]);
return channelName;
}
function getDMTargetUID(did) {
var uid = null;
var response = UrlFetchApp.fetch("https://slack.com/api/im.list?token=" + SLACK_TOKEN);
var content = response.getContentText("UTF-8");
var values = JSON.parse(content);
// Logger.log(values);
// sheetLog.appendRow([ now, 'getDMTargetUID', did, values['ims'] ]);
for (var i = 0; i < values['ims'].length; i++) {
try {
// sheetLog.appendRow([ now, ''+values['ims'][i] ]);
var id = values['ims'][i]['id'];
if (id == did) {
uid = values['ims'][i]['user'];
// sheetLog.appendRow([ now, id, uid ]);
if (uid != undefined) {
break;
}
}
} catch (e1) {
;
}
}
// sheetLog.appendRow([ now, did, uid ]);
return uid;
}
function getWS() {
var response = UrlFetchApp.fetch("https://slack.com/api/team.info?token=" + SLACK_TOKEN);
var content = response.getContentText("UTF-8");
var values = JSON.parse(content);
return values['team']['name'];
}
過去のバージョンではSpreadsheetにメールアドレスをリスト化して用意していましたが、今回はslack apiを通じてメンバーのメールアドレスも取得しています。これには一長一短があります。

