slackでのメール通知アプリの作成(events apiの利用とGoogle Apps Scriptでの実装)

2017/11/28 : 第1版
2017/12/11 : 第2版:DMの場合には送信相手にのみ送付するように変更。その他、詳細な箇所を修正

この記事の内容はslackを、私のようなメール中心の生活を送っている人間が使うときの内容です。

以前、outgoing webhookを用いたメール通知ボットを実現していました。slackのメール通知機能はデフォルトで何かとメールを送らない条件がある。細かく設定を変えても送らない条件が残るのです。メンバーもメールドリブンな場合、全員に設定を変えるように説明するのも面倒です。私としては常にすぐにメールで通知する機能が欲しかったのです。

新しいworkspace用に過去の資産を再利用しようとしたところ、outgoing webhookはlegacy扱いになっていて、新規に登録できませんでした。もしかしたら新しく登録する方法もあるのかもしれませんが、せっかくなので新しいevents apiを試してみることとしました。

ここでは単純に「誰が」「何と書いたか」をメンバー全員にメールで通知することとします。

  1. 「https://api.slack.com/apps/」から新規アプリを作成
  2. 「Events API(Event Subscriptions)」からenableとし、以下を追加
    • file_comment_added
    • file_created
    • im_created
    • message.channels
    • message.groups
    • message.im
    • message.mpim
  3. 「Request URL」は後述のGoogle Apps ScriptのWeb URLを指定
  4. 「OAuth & Permissions」で以下の二つのScopeを追加
    • users:read
    • users:read.email
  5. 「OAuth Access Token」の内容をメモ
  6. Gdriveで適当なフォルダーにSpreadsheetを新規作成(履歴保管用)
  7. ツール→スクリプトエディター
    • 以下の内容を作成
    • 「xoxp-??????????」の部分は上の「OAuth Access Token」のメモ内容とする
  8. 公開→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を通じてメンバーのメールアドレスも取得しています。これには一長一短があります。

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です