Google Apps Scriptを用いたSlack Bot開発【メール投稿Bot編】part.2

投稿日:2020/06/05 更新日:2020/06/05

はじめに

エンジニアリングソリューション事業部のS.Iです。
part.1に引き続いてGAS側の構築を行っていきます。
こちらのページはpart.1の続きとなっておりますので、前回記事をご覧になっていない方は先に part.1 をご参照ください。
[LINK]Google Apps Scriptを用いたSlack Bot開発【メール投稿Bot編】part.1

目次

  • 【part.1&2】 Bot概要
  • 【part.1】Slack側の準備
  • 【part.2】GAS側の準備 -Slackへ投稿
  • 【part.2】GAS側の準備 -Gmailから抽出
  • 【part.2】GAS側の準備 -プロパティストアの設定
  • 【part.2】G Suite Developer Hub の設定
  • 【part.2】まとめ

Bot概要

Botの概要は下記の図の通りです。
Google Apps Script (以降GASと記載)側の構築を行っていきます。
reportMail_bot 概要
reportMail_bot 概要

GAS側の準備 -Slackへ投稿

Slackへスニペットを投稿するメソッドを作成します。
スニペットで投稿された内容は縮小できるので、
メール本文が長くなった場合でもSlackが見やすくなるメリットがあります。
スニペット投稿例 非スニペット
スニペットで投稿 しなかった場合の例
スニペット投稿例 スニペット
スニペットで投稿 した場合の例
前項でSlack側の準備が終わっているので、
JSON 形式で内容を整形してSlackに渡すようにGASを作成します。
ファイルのPostは以下のサイトを参考にさせていただきました。

はじめてのGAS – Slackにファイルを送るhttps://qiita.com/RyutaKojima/items/28a6746c81756a000116 

function postFile(slackChannelID, msgBody, msgFrom, msgSubject){
  //Jsonのペイロードに格納  
  var payload = {
    "token" : PropertiesService.getScriptProperties().getProperty('slackToken'),
    "channels" : slackChannelID,
    'content' : msgBody,//メッセージの中身
    'filename':"sample.txt",//テキスト形式のファイルを指定
    //'initial_comment': msgFrom,//ファイルのコメント"
    'title': msgSubject //"Slack上でのファイルのタイトル"
  };
  var options = {
    "method" : "post",
    'contentType': 'application/x-www-form-urlencoded',
    "payload" : payload
  };
  
  var response = UrlFetchApp.fetch("https://slack.com/api/files.upload", options);
  Logger.log(response);
}

■コラム JSON (JavaScript Object Notation) 形式
JSON (JavaScript Object Notation)は、
軽量のデータ交換フォーマットです。
JSONは人、マシンの双方にとって理解しやすい形式で、
JavaScriptの一部をベースに作られています。  

GAS側の準備 -Gmailから抽出

Gmail に条件付で問い合わせを行い、
該当するメールがある場合は整形して後続処理に投げるメソッドを作成します。
GASを利用したGmailの抽出は多くのサイトで解説されている為、詳しい解説は行いません。
簡単に説明すると検索の条件を指定してスレッドを確保、メールを取得します。

なお、検索対象のメールボックスはGASを書いているアカウントのメールボックスになります。
今回は該当のラベルかつ未読メールを取得、取得したメールは既読にするようにコードを構築しました。 下記のサイトは大変分かりやすくスレッドについて記載されており、参考にさせていただきました。

【GAS】Gmailの特定条件で検索したスレッドの全メールを取得してスプレッドシートに書き出す
https://tonari-it.com/gas-gmail-get-thread/

function gmailSearch() {
  
  //Gmailから特定条件のスレッドを検索しメールを取り出す
  var searchTerms = PropertiesService.getScriptProperties().getProperty('gmailSearchWord');//検索条件
  var myThreads = GmailApp.search(searchTerms , 0, 50); //条件スレッドを取得
  var myMsgs = GmailApp.getMessagesForThreads(myThreads); //スレッドからメールを取得
  Logger.log("検索該当件数:" + myMsgs.length);
  Logger.log(searchTerms);
  //各スレッド×メール
  for (var i = myMsgs.length - 1; i >= 0; i--) {
    var msgsInThread = myMsgs[i];
    for (var j = 0; j < msgsInThread.length; j++) {
      
      //メッセージを展開して検証
      var msg = msgsInThread[j];
      //未読のみ
      if (msg.isUnread()) {
        //メールを既読にする
        msg.markRead();
          Logger.log("TEXT");
          var msgBody = 
              "件名:" + msg.getSubject() + "\n" +
                "送信元:" + msg.getFrom() + "\n" +
                  msg.getPlainBody() + "\n" +
                    msg.getDate() + "\n" 
                    ;
        var msgFrom = "送信元:" + msg.getFrom();
        var msgSubject = msg.getSubject();
        //スニペット形式でPost
        postSlackChannel(msgBody, msgFrom, msgSubject);
        
      }
    }
  }
}
チャンネルIDを指定するfunction も作成します。
Postするfunction かgmail抽出のfunction のどちらかに含めても良いですが、
テストする場合や複数のfunction に送る場合などのケースでは、
function が分かれているほうが扱いやすいため、今回は別出ししました。
/**
* Post先のチャンネルを指定
* チャンネルを複数管理しやすいようにfunctionを分けている
*/
function postSlackChannel(msgBody, msgFrom, msgSubject) {
  const slackChannelID = PropertiesService.getScriptProperties().getProperty('slackChannelID'); //チャンネル指定
  postFileToSlackChannel(slackChannelID, msgBody, msgFrom, msgSubject);
}

■コラム Gmailの検索条件について
Gmailの検索欄横の▼を押すと詳細検索欄が出現します。
この詳細検索欄で検索を行うと画面遷移後、
検索欄に検索で使用した検索条件が1行で表示されます。
この検索条件をコピペして使うととても楽です!
GASから検索条件を調整するよりも手軽に行うことができます。

GAS側の準備 -プロパティストアの設定

GASにはプロパティストアという保存領域があります。
ここに大切な情報(たとえばAPIのTokenなど)を保存することで、
メンテナンス性の向上、ソースコード公開時のうっかりミス低減が期待できるので、積極的に使っていきます。 プロパティストアには次の情報を格納します。
GAS画面左上のファイル>プロジェクトのプロパティを選択して、
「スクリプトのプロパティ」タブから設定できます。
  • gmailSearchWord:Gmailの検索条件
  • slackToken:SlackのToken (BotのToken)
  • slackChannelID:SlsckのチャンネルID
GAS側の準備 プロパティストア
これで一通りの仕組みが出来上がりました。
ここまででメールを検索してSlackに投稿する仕組みは完成です。
動作するか確認しましょう。 検索条件に合致するメールを自分宛に送り、
未読のままメールボックスに存在するようにします。
次に、GASエディタ上で関数を選択→実行ボタンを押下、
Slackに投稿されればOKです。無事動作しています。

■コラム 正常に動作しなかった場合

問題を切り分けて原因を探しましょう。
下記に切り分けの一例を記載しましたのでご活用ください。

1.GASは起動されているか
 →下記段落のG Suite Developer Hubにある「実行数」画面から
  GASの実行状況が確認できます。

2.メールは既読になっているか
 →条件に一致したメールはGASで取り込んだ際にメールを
  既読にするように処理を加えています。
  メールが既読にならない場合はメールの検索条件か
  ソースの実装が間違っていないか確認してください。

3.メールは既読だがSlackに飛んでこない
 → G Suite Developer Hubにある「実行数」画面 で
  エラーになっていない場合は正常にPostされています。
  チャンネルID等が間違っていないか確認してください。
  エラーが出力されている場合は、
  トークンか実装が間違っている可能性があります。

G Suite Developer Hub の設定

全体の仕組みが出来上がりましたが、
まだ手動でしか動かないのでは意味がありませんので自動化します。
時間起動で定期的にメールを確認するアクティブ動作 を行わせるため、
「 G Suite Developer Hub 」(旧 Apps Script dashboard :2018/11/2より改名)
を利用してGASの時間起動を設定します。
(メールを受けたらGASを起動するパッシブ動作は行いません) GAS画面左上のマークを選択してG Suite Developer Hubを開きます。
G Suite Developer Hub 展開
画面右下部の「トリガーを追加」からトリガーを追加する。
G Suite Developer Hub トリガー追加
「 G Suite Developer Hub 」 で設定できる最小値は「1分」 です。
1分毎にGASを起動するように( メールを 確認するように)設定します。
G Suite Developer Hub トリガー設定
これで自動化の設定も完了です。お疲れ様でした。
Botの実行状況は左側のメニュー「実行数」から確認できます。

まとめ

GASを使うことで比較的簡素にメールをSlackにスニペット投稿することができました。
社内では週報がこのBotを通して投稿されており、今までよりもレスポンス良く反応が返ってくるようになりコミュニケーションが活性化されています。
Botはバージョンアップを繰り返しており、
現在ではスプレットシートと連携して提出者を記録したり、
全員の提出が確認できたら「お疲れ様!」と画像を投稿する機能が追加されたりしています。(そのうち記事にするかも知れません。) 手軽に開発ができるので皆さんもぜひアイデアを形にしてみてください!
この記事が何かの役に立てれば幸いです。

投稿者: エムシバ君