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を通じてメンバーのメールアドレスも取得しています。これには一長一短があります。