虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Google Apps Script を使ってSlackにメール転送と通知を出す方法

こんにちは、とらのあなラボのnsdです。

今回はGoogle Apps Script(GAS) を使ってSlackにメール転送と通知を出す方法を紹介したいと思います。

やりたいこと

  • Gmailで
  • 過去24時間以内に
  • 特定の送信元(From)から来たメールが
  • 未読の場合に
  • Slackへ転送と通知を出す

実現方法

  • Gmailの操作:GAS関数のGmailAppを使用
  • Slackの操作:Webhook URLを使用

目的

* 重要な送信元からのメールを見逃さないようにする
* メンバーにも確認を促す

事前の設定

まずは定数を定義しておきます。

function send_notification_to_slack_when_mail_arrived() {

// SlackのWebhook URL
const slack_webhook_url = 'https://hooks.slack.com/services/xxx/xxx/WebhookURL';

// 過去何時間のメールを検索するか指定(24時間)
const hours = 24;

// 通知したいメールのFrom(部分一致)を指定(配列で複数可)
const fromMail = ['fromAddr1', 'fromAddr2']

// メールを転送するSlackチャンネルのメールアドレス
const transferAddr = 'xxx@xxx.slack.com'

// 通知メッセージを出力するSlackチャンネル
const slackCh = 'チャンネル名'

条件を指定してメールを取得

続いてメールを検索するクエリを作成してメールの取得を行います。

■クエリ
after:{24時間前} from:fromAddr1 OR fromAddr2 is:unread
* after : 指定した日時以降のメールを対象とする(今回は24時間前の日時を指定)
* from : 指定した送信元のメールを対象とする
* is : unread(未読)のメールを対象とする

■メールの取得
GmailApp.search(query);で指定のクエリに該当するメールの一覧が取得されます。

// 検索期間を指定するための日付を取得する
let date = new Date(); // 現在の日時を取得

// メール検索条件に使うため、UNIX時に変換する
let unixtime = Math.floor(date.getTime() / 1000);

// 事前設定で指定された時間分、マイナスして検索の開始時間にする
unixtime = unixtime - (hours * 3600);

// メールを検索するための条件文を作る
const query = 'after:' + unixtime + ' ' + 'from:' + fromMail.join(' OR ') + ' is:unread';

// Gmailからメールを取得する
const threads = GmailApp.search(query);

if (threads.length == 0) {
  // スレッド(メール)が1つも見つからない場合は処理を終了する
  return;
}

GmailApp.searchによりメールが検索されたときのイメージ

Gmail画面の検索フォームからも同様のクエリで検索が可能。
このときの検索結果一覧がスレッド(threads = GmailApp.search(query);)となる。

threads

メッセージごとの情報を保持しつつ転送

続いて取得結果から1メールずつ情報の保持と転送をします。

■検索結果(スレッド)からメールを取得
threads[i].getMessages(); メールの情報を取得

■メールを転送(送信)
GmailApp.sendEmail(recipient, subject, body, options);
* recipient : 送信先のアドレス
* subject : 件名
* body : 本文
* options : 拡張(今回はname : メールの送信者の名前 を使用)

■メールを既読化
messages[j].markRead();

// メールから必要な部分を抜き出して格納するための配列を宣言する
let result = [];

// スレッドを1つずつ処理する
for (let i = 0; i < threads.length; i++) {
  // 1スレッドに複数のメッセージ(=メール)が入っているので取得
  let messages = threads[i].getMessages();

  // メールを1通ずつ処理していく
  for (let j = 0; j < messages.length; j++) {
    // メールの差出人を取得する
    let mail_from = 'From : ' + messages[j].getFrom();

    // スレッドには返信も含まれるため差出人が同じもののみ抽出する
    if (mail_from.includes(fromMail)) {
      // メールの件名を取得する
      let mail_subject = '件名 : ' + messages[j].getSubject();
      // 結果用の配列に、差出人と件名を連結して追加する
      result.push(mail_from + ', ' + mail_subject);
      // Slackへメールを転送する
      GmailApp.sendEmail(transferAddr, messages[j].getSubject(), messages[j].getBody(), { name: messages[j].getFrom() })
      // 転送が完了したら既読にする。
      messages[j].markRead();
    }
  }
}

Slackに通知メッセージを送信

最後にSlackに出力するメッセージを組み立てて送信します。

// 結果の配列を改行で連結し、Slack投稿用テキストとする
const text = result.join('\n');

// SlackのWebhook URLに投稿するデータをまとめる
const json =
{
  'channel': slackCh,
  'username': 'xxxからの未読メールがあります!',
  'text': '<!channel>' + '\n' 
          + 'メールを確認してください' + '\n' 
          + 'この通知は以下のGASから送信しています' + '\n' 
          + 'https://script.google.com/d/xxx/edit?usp=sharing'
};

// SlackのWebhook URLに送信するデータをJSONに変換する
const payload = JSON.stringify(json);

// UrlFetchAppで使用するメソッドやコンテントタイプを指定
const options =
{
  'method': 'post',
  'contentType': 'application/json',
  'payload': payload
};

// Slackに送信
UrlFetchApp.fetch(slack_webhook_url, options);

最後に

この転送と通知により大事なメールを見逃すことがなくなりました。

単純な転送であればGmailの転送機能を使うだけでもよいですが、
Slackに@chanel付きで通知を出すことで、自身だけでなくチームメンバーに確認を促すことができるのが便利なところです。

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp