さまざまなメールを通知としてSlackに飛ばす(2)

前回の内容について、具体的に説明します。

まず、スクリプトを作成します。GSuite Developer Hubを開き、新しくプロジェクトを作成します。
https://script.google.com/home

左にある「新規スクリプト」をクリックすると空のスクリプトファイル「コード.gs」が1つある「無題のプロジェクト」が開きますので、とりあえずは下記のプログラムをコピーし、貼り付けてください。
(なお、このプログラムは、JavaScriptをまともに書いたことがない人間が作成したものですので、変なところがあるはずです。改善案や問題点があれば、ぜひコメントをお寄せください!)

// Slackで得た送信用URL
var postUrl = "https://hooks.slack.com/services/XXXXXXXX/XXXXXXXX/XXXXXXXXXXXXXXXX";

// メイン処理 (トリガーで定期的に呼ばれる)
function gmailToSlack() {
  
  // Gmailのメールボックスの検索条件 (未読のメール・1時間以内のメール)
  var threads = GmailApp.search("is:unread newer_than:1h");
  
  // 条件を満たすスレッドの数を確認 なければ終了
  var count = threads.length;
  if (count == 0)
    return;
  
  // 定義ファイルの取得
  var mailDataDefinitions = getDefinitionFileFromGoogleDrive();
  if (mailDataDefinitions == null)
    return;
  
  for (var i = 0; i < count; i++) {
    
    var thread = threads[i];
    
    // スレッド内の最後のメールが処理対象
    var numberOfMessageInThread = thread.getMessages().length;
    var targetMessage = thread.getMessages()[numberOfMessageInThread - 1];
    
    var mailSubject = targetMessage.getSubject();
    
    // メールの題名から、メール定義情報を探す
    var jsonMailData = findMailData(mailDataDefinitions, mailSubject);
    if (jsonMailData == null)
      continue;
    
    // Slackに送信する内容は、「メールタイトル + 条件に従って抽出した本文」 とする
    var mailBody = mailSubject + "\n" + extractMailBody(targetMessage.getPlainBody(), jsonMailData);

    postToSlack(jsonMailData, mailBody);
    
    // Slackに送信した後、既読にして捨てる
    thread.markRead();
    thread.moveToTrash();
  }
}

// GoogleDriveにあるメール定義のファイルを取得し、JSON形式のデータを返す
function getDefinitionFileFromGoogleDrive() {
  
  // 定義ファイルがあるかどうかを確認
  var files = DriveApp.getFilesByName("MailData.json");
  if (files == null)
    return null;

  // 定義ファイルの内容を読み込み、JSONとして解釈して返す
  var file = files.next();
  var data = file.getBlob().getDataAsString();
  
  return JSON.parse(data);
}

// メールのタイトルから、メール定義を探す
function findMailData(mailDataDefinitions, mailSubject)  {
  
  var result = null;
  
  mailDataDefinitions.forEach(function (value) {
    if (mailSubject.indexOf(value.title) != -1)
      result = value;
  });
  
  return result;
}

// 本文の指定された範囲を抽出する
function extractMailBody(mailBody, jsonMailData) {
  
  var foundStartLine = false;
  var foundEndLine = false;
  var splitMailBody = mailBody.split('\n');
  var exractedMailBody = "";
  
  // 開始文字列が空文字列の場合には、先頭からすべてを対象とする
  if (jsonMailData.fromText == "")
    foundStartLine = true;
  
  splitMailBody.forEach(function (line) {
    
    // 終了文字列以降はすべて無視する
    if (foundEndLine)
      return;
    
    // 開始文字列をまだ見つけていない場合には、開始文字列かどうかの判定
    if (foundStartLine == false) {
      if (line.indexOf(jsonMailData.fromText) != -1) {
        foundStartLine = true;
      }
      return;
    }
    
    // 終了文字列を含むかどうかの判定
    if (line.indexOf(jsonMailData.toText) != -1) {
      foundEndLine = true;
      return;
    }
    
    // 抽出対象の本文に追加
    exractedMailBody += line + "\n";
  });      
  
  return exractedMailBody;
}

// Slackに送信する
function postToSlack(jsonMailData, message) {
  
  // Slackで定義されているデータを作成
  var jsonData = {
    "channel": jsonMailData.channel,
    "username": jsonMailData.username,
    "text": message
  };
  
  var payload = JSON.stringify(jsonData);

  // 送信用データの作成  
  var options = {
    "method": "post",
    "contentType": "application/json",
    "payload": payload
  };
  
  // Slackに送信
  UrlFetchApp.fetch(postUrl, options);
  
  // 荷物お届けの場合のみ、LINEに送信
  if (jsonMailData.channel == "荷物お届け")
  {
    //後日、別記事で公開するかも...
  }
}

なお、上記のスクリプトは、以下のページのスクリプトをベースに作成しました。この場をお借りしてお礼申し上げます。
https://github.com/comefigo/gmail-to-slack/blob/master/Main.gs

このスクリプトの先頭行の内容は、Slackのアプリの「Incoming Webhook」で取得しなければなりません。Slackの設定画面を開き、アプリの「Incoming Webhook」をインストールしてください。設定画面の「インテグレーションの設定」にある「Webhook URL」から取得できますので、そのまま貼り付けてください。
(このURLを、自分自身以外が知ることがないようにしてください。)

入力後に、保存して下さい。今回は、プロジェクト名を「GmailToSlack」としてあります。

 

 

次に、Slackに飛ばすメールの情報を定義した、JSONファイルを作成します。メモ帳などのテキストエディタを起動し、下記の内容を貼り付けてください。(中身については別途説明します。)

[
{
"username" : "テスト",
"channel" : "#general",
"title" : "テストサンプル",
"fromText" : "",
"toText" : "dummy"
}
]

 

このファイル名を「MailData.json」として保存後、Google Driveを開き、このファイルを配置してください。配置する場所はどこでもかまいません。以下のURLからGoogle Driveに入り、Webブラウザにファイルをドラッグ&ドロップして配置できます。
https://drive.google.com/drive/my-drive

 

これで最低限の準備が完了しました。動作確認のためのメールが必要となるので、Gmailで自分自身宛のメールを作成し、タイトルを「テストサンプル」とし、適当に本文を記入して送信してください。すぐに自分自身が受信しますが、開封(既読)にしないでください。

 

再度、Google Apps Scriptに戻り、上部のメニューから「実行」→「関数を実行」→「gmailToSlack」を実行してください。スクリプトからGmailアカウントおよびGoogle Driveのファイルを参照するために、権限を付与することが必要です。「アカウントの選択」画面が表示されるので利用しているGmailのアカウント選択します。「このアプリは確認されていません」の画面が表示されるはずですが今回は自分が作ったものなので、画面下の「詳細」をクリックし、「GmailToSlack(安全ではないページ)に移動」を選択してください。

すべてがうまくいっていれば、Slackに投稿され、メール自体は既読になりゴミ箱に移動します。Slack側でログを保存する前提ですが、そうでなければメールをアーカイブしてもよいと思います。

// Slackに送信した後、既読・アーカイブを行う
thread.markRead();
thread.moveToArchive();

なお、下記のページにあるように、メールの取得数制限(1日2万通以内)やスクリプトの動作時間制限(1日90分以内)があります。
https://developers.google.com/apps-script/guides/services/quotas

2万通というとそんなにメールは来ない、と思うかもしれませんが、これはあくまでも1日の処理数です。例えば、1分に1回スクリプトを動作するように設定し、処理対象外のメールが受信トレイに50通残っているとすると、1分ごとに50通のメールを取得して判定し、1日では50通x60分x24時間=72000通、ということで制限を超えてしまいます。

この制限に引っかからないようにすることと、処理時間を短くするために、Gmail側のフィルタで直近1時間以内のメールのみを取得するようにしました。

明日は、定義ファイルの具体的な内容と、トリガーとして定期的に実行する方法を投稿します。

2019/7/23追記

その後、運用する中でJavaScriptの内容を改善していたので、サンプルを差し替えました。
(今は、「荷物お届け」の場合のみ、妻のLINEにも通知する機能も増えています)

2コメント

  1. はじめまして。
    こちらを参考に挑戦しましたが、
    SyntaxError: Unexpected token: %
    となり処理出来ません。調べてみたものの全く解らずです。アドバイスお願いできませんか??

    1. コメントありがとうございました。

      が、すいません、いただいたエラーの内容からは、何が原因か推測できません。

      もし、JavaScriptのコード内に「%」の文字があるか、コメント化されていない日本語文字(行の先頭に//がない)があれば、それが原因と思います。

      また、JSONの定義ファイル側に問題があるか、文字コードが不適切などで発生する可能性もありそうな気がするので、MailData.jsonファイルをいったん削除して実行してみると、どうでしょうか?
      これでエラーが消えるようであれば、MailData.jsonファイルの中身に問題がある可能性があり、エラーが消えないならばJavaScript側に問題がありそうです。

      追伸:
      この記事のコードは、その後運用を経て改良しています。後ほど内容を更新します!

コメントを残す

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