Google Apps ScriptでGmailとSlackを連携！自動通知の設定方法

## はじめに
アラートメールが大量に届く中で、特に重要なメールだけを選別し、それをSlackに転送したいと考えています。このプロセスのゴールは、膨大なアラートメールの中から、本当に必要なものだけを即座にSlackで確認できるようにすることです。これにより、作業の効率が向上し、緊急の対応が必要な場合に見落としを防ぐことができます。

## slackの設定
まずAppの設定がいるので済ませましょう。slack使ってない人はダウンロードして部屋作成も必要。

https://qiita.com/Yu-HaruWolf/items/0c28b8aa787d2c2880f8

https://qiita.com/ik-fib/items/b4a502d173a22b3947a0

## Google Apps ScriptでGmailとSlackを連携
### 機能概要

#### Slackへのメッセージ送信 (`sendToSlack` 関数):
- メッセージをSlackの指定されたWebHook URLに送信します。
- メッセージが未定義の場合、デフォルトメッセージを送信します。
- Slackに通知が成功したかどうかをログに出力します。

#### Gmailのメールをチェック (`checkEmailAndSendToSlackIfKeywordFound` 関数):
- 指定された検索クエリ (`searchQuery`) に基づいてGmailのメールを検索します。ここでは、5分以内に受信した未読メールが対象です。
- メールの本文に特定のキーワード (`keyword`) が含まれている場合、そのメールをSlackに通知します。
- メールが通知された後、メールを既読としてマークします。

#### `main` 関数:
- WebhookのURL、Gmail検索クエリ、キーワード、Slackに送るメッセージの内容を設定します。
- これらの設定をもとに `checkEmailAndSendToSlackIfKeywordFound` 関数を呼び出し、Gmailから該当するメールをチェックし、Slackに通知を送ります。

---

### 主な変数：
- **`webhookUrl`**: Slackに通知を送るためのWebHookのURL。
- **`searchQuery`**: Gmailで検索する条件。ここでは「5分以内に受信した未読メール」を対象としています。
- **`keyword`**: メール本文に含まれているべき特定のキーワード（ここでは「test」）。
- **`messageText`**: Slackに送信するメッセージの本文。該当するメールが見つかった場合に送信されます。

---

### 想定される利用シナリオ：
このスクリプトは、特定の重要なメール（たとえば「test」というキーワードが含まれるメール）をリアルタイムで確認し、Slackで通知を受け取りたい場合に有効です。また、5分ごとにメールをチェックする設定ができるため、リアルタイムで監視を続ける必要があるシナリオに適しています。

---

### 実行方法：
このスクリプトをGoogle Apps Scriptに配置し、トリガーを設定することで、5分ごとに自動的にGmailをチェックし、該当するメールがあればSlackに通知を送ることができます。

## コード作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/6f436b2c-c0fc-93ea-1cf7-db61c1faa5a1.png)

```js
function sendToSlack(messageText, webhookUrl) {
  Logger.log("sendToSlackで渡されたWebhook URL: " + webhookUrl);  // Webhook URLが正しく渡されているか確認

  if (!messageText) {
    messageText = "デフォルトメッセージ"; // デフォルトメッセージ
  }

  var payload = {
    "text": messageText.trim() // メッセージをトリム
  };

  var options = {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  try {
    var response = UrlFetchApp.fetch(webhookUrl, options);
    Logger.log("Response Code: " + response.getResponseCode());
    Logger.log("Response Body: " + response.getContentText());
  } catch (e) {
    Logger.log("Error: " + e.message);
  }
}

function checkEmailAndSendToSlackIfKeywordFound(webhookUrl, messageText, searchQuery, keyword) {
  Logger.log("checkEmailAndSendToSlackIfKeywordFoundで渡されたWebhook URL: " + webhookUrl);  // Webhook URLが正しく渡されているか確認
  Logger.log("検索クエリ: " + searchQuery);  // 検索クエリが正しく渡されているか確認
  var threads = GmailApp.search(searchQuery);
  Logger.log("スレッド数: " + threads.length);

  if (threads.length === 0) {
    Logger.log("指定された期間内に受信したメールがありません。");
    return;
  }

  var messages = GmailApp.getMessagesForThreads(threads);

  for (var i = 0; i < messages.length; i++) {
    var threadMessages = messages[i];

    for (var j = 0; j < threadMessages.length; j++) {
      var message = threadMessages[j];
      var messageDate = message.getDate(); // メールの受信時刻を取得
      Logger.log("メールの受信時刻: " + messageDate); // メールの受信時刻をログに記録

      var plainBody = message.getPlainBody() || message.getBody();
      if (plainBody.indexOf(keyword) !== -1) {  // mainで定義されたキーワードで条件チェック
        var bodyPreview = plainBody.substring(0, 500).replace(/[\r\n]+/g, ' ');
        var slackMessage = messageText + "\n" +  // メイン関数で渡されたmessageTextを使用
                           "件名: " + message.getSubject() + "\n" +
                           "受信時刻: " + messageDate + "\n" + // 受信時刻を追加
                           "本文（先頭500文字）: " + bodyPreview;

        sendToSlack(slackMessage, webhookUrl); // Slackに通知
        message.markRead(); // メールを既読に
      }
    }
  }
}

function main() {
  var webhookUrl = "https://hooks.slack.com/services/XXXX; // Webhook URLをmainで定義
  var messageText = "特定のキーワードを含むメールが見つかりました。"; // 送信するメッセージをmainで定義
  var searchQuery = 'is:unread newer_than:5m (from:@gmail.com OR from:otheremail@example.com)'; // 複数のメールアドレスを対象にするクエリ
  var keyword = 'test';  // 条件としてのキーワードをmainで定義
  Logger.log("Main内のWebhook URL: " + webhookUrl);  // Webhook URLの確認用ログ

  checkEmailAndSendToSlackIfKeywordFound(webhookUrl, messageText, searchQuery, keyword); // キーワードも渡す
}
```
        
- mainの変数は用途に合わせて修正が必要だよ
- mainが実行されるように注意しよう。たまにほかのメソッドになっている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/43b7d01b-ced6-185b-ff26-05de2bef72b4.png)
      
        
うまくいけばslackに通知くるよ。またメール既読になるようだった

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/f8361247-1203-6c9f-e819-c22b44489b79.png)

### 5分毎に実行する設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/f38af99b-f64d-0c50-2e5f-205f928dee0e.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/ca9f8479-599f-0bb0-5223-4e405ebae4b6.png)

実行履歴も見れた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131136/0eff284f-7f84-fa61-7a36-80a52a4a6b8b.png)


## ToDO
- 受信から５分以内のメールを対象にしたいが、未読のメールがすべて対象になっている。testかどうかは判断されている。ようは`newer_than:5m`が機能していない。一度受信してslack通知したメールも、未読に戻せば通知対象に戻ってしまう。通知すれば既読になるので、通知の無限ループに陥ることはなさそう


## 複数キーワードを設定する場合、未検証

```js
function checkEmailAndSendToSlackIfKeywordFound(webhookUrl, messageText, searchQuery, keywords) {
  Logger.log("checkEmailAndSendToSlackIfKeywordFoundで渡されたWebhook URL: " + webhookUrl);  // Webhook URLが正しく渡されているか確認
  Logger.log("検索クエリ: " + searchQuery);  // 検索クエリが正しく渡されているか確認
  var threads = GmailApp.search(searchQuery);
  Logger.log("スレッド数: " + threads.length);

  if (threads.length === 0) {
    Logger.log("指定された期間内に受信したメールがありません。");
    return;
  }

  var messages = GmailApp.getMessagesForThreads(threads);

  for (var i = 0; i < messages.length; i++) {
    var threadMessages = messages[i];

    for (var j = 0; j < threadMessages.length; j++) {
      var message = threadMessages[j];
      var messageDate = message.getDate(); // メールの受信時刻を取得
      Logger.log("メールの受信時刻: " + messageDate); // メールの受信時刻をログに記録

      var plainBody = message.getPlainBody() || message.getBody();
      var found = keywords.some(function(keyword) { 
        return plainBody.indexOf(keyword) !== -1;  // 複数キーワードで条件チェック
      });

      if (found) {
        var bodyPreview = plainBody.substring(0, 500).replace(/[\r\n]+/g, ' ');
        var slackMessage = messageText + "\n" +  // メイン関数で渡されたmessageTextを使用
                           "件名: " + message.getSubject() + "\n" +
                           "受信時刻: " + messageDate + "\n" + // 受信時刻を追加
                           "本文（先頭500文字）: " + bodyPreview;

        sendToSlack(slackMessage, webhookUrl); // Slackに通知
        message.markRead(); // メールを既読に
      }
    }
  }
}

function main() {
  var webhookUrl = "https://hooks.slack.com/services/XXXX"; // Webhook URLをmainで定義
  var messageText = "特定のキーワードを含むメールが見つかりました。"; // 送信するメッセージをmainで定義
  var searchQuery = 'is:unread newer_than:5m (from:@gmail.com OR from:otheremail@example.com)'; // 複数のメールアドレスを対象にするクエリ
  var keywords = ['test', 'urgent', 'action required'];  // 複数条件のキーワードを配列で定義
  Logger.log("Main内のWebhook URL: " + webhookUrl);  // Webhook URLの確認用ログ

  checkEmailAndSendToSlackIfKeywordFound(webhookUrl, messageText, searchQuery, keywords); // キーワードも渡す
}

```