2021年10月1日

GmailをSlackのチャンネルに通知する超シンプルなGAS

読了時間の目安: 4分


友人と個人開発してまして、各種サービスは共有のGoogleアカウントを使っています。 どちらかしか通知を受けられない、操作できないのは面倒なので。

しかし、自分たちもGoogleアカウントを持っており、Gmailを見るためにアカウントを切り替えるのは面倒です。 また、コミュニケーションはSlackで行っているので、GmailをSlackのチャンネルに通知してSlackで会話できるといいなーと思っていました。

色々と調べると次の方法が紹介されていました。

  1. Slackの転送先メールアドレスをGmailの転送先に指定する
  2. Google Workspace Marketplaceの「 Slack for Gmail 」を使う
  3. Slackの「 Email 」アプリを使う
  4. GAS

しかしながら、どれも要件を満たすのが難しそう。

1はslackbotへのDMになってしまいチャンネルポストができなそうです。

2はブラウザでGmailを開いて、メールごとチャンネルごとに手作業が必要です。

3は「Email」アプリが有料ワークスペースでないと使えなそうです。

4は検索で引っかかるコードを見る限り、割と複雑そうです。

んー、どうしたものか。

ん。4、本当に複雑なのか。

あれ、お前、実はもっとシンプルになるんじゃないのか。

ということで、GmailをSlackに通知するGASはすごくシンプルだよ、の例を示します。

サンプルコード

いきなりサンプルコードです(メールをSlackに転送したいアカウントでやってください)。

コード.js
function main() {
const threads = GmailApp.search('in:Inbox is:Unread', 0, 100)
threads.forEach((thread) => {
thread.getMessages().forEach((message) => {
if (!message.isUnread()) { return }
const text = create_message(message)
send_to_slack(text)
message.markRead()
})
})
}
function create_message(message) {
return `[Date] ${message.getDate()}`
+ `\n[From] ${message.getFrom()}`
+ `\n[Subject] ${message.getSubject()}`
+ `\n[Body]\n${message.getPlainBody()}`
}
function send_to_slack(text) {
const webhook_url = <SlackIncoming webhook URL>
const headers = { "Content-type": "application/json" }
const data = { "text": text }
const options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(data),
"muteHttpExceptions": true
}
UrlFetchApp.fetch(webhook_url, options)
}

send_to_slack関数はSlackにテキストメッセージを送るところなのでノーカウント(この記事では説明しません)とすると、めちゃくちゃシンプルじゃありませんか。

JavaScriptのコードはおいておいて、GmailApp系の部分をちょっとだけ解説します。

GmailApp.search()

const threads = GmailApp.search('in:Inbox is:Unread', 0, 100)

GmailApp.search(query, start, max)queryの条件に合うスレッドの配列(GmailThread[])を取得する関数です。 今回は'in:Inbox is:Unread'を指定しているので、「受信ボックス」の中で「未読」のものを拾ってきます。 また、start0max100を指定しているので、最新のスレッドから100件までの中からqueryを検索してもらっています。

Class GmailApp  |  Apps Script  |  Google for Developers

thread.getMessages()

threads.forEach((thread) => {
thread.getMessages().forEach((message) => {
...
})
})

GmailThread.getMessages()でそのスレッド内のメッセージ(メール)の配列(GmailMessage[])を取得してます。 forEachを使えば1つ1つのメッセージを扱えます。こいつがメール本体。主役の登場です。

Class GmailThread  |  Apps Script  |  Google for Developers

message.isUnread()

if (!message.isUnread()) { return }

GmailMessage.inUnread()はそのメッセージが未読ならtrue、既読ならfalseを返却します。 このコードではそれの否定(!)でif文を書いているので、既読の場合return、つまり後続の処理をせずにforEachを継続させています。

Class GmailMessage  |  Apps Script  |  Google for Developers

message.getXXX()

const text = create_message(message)
...
function create_message(message) {
return `[Date] ${message.getDate()}`
+ `\n[From] ${message.getFrom()}`
+ `\n[Subject] ${message.getSubject()}`
+ `\n[Body]\n${message.getPlainBody()}`
}

Slackに投稿するテキストメッセージを生成しているのがこの辺です。 GmailMessage.getXXX()を使ってメッセージを作ってます。getXXX()はいろいろな種類があるので、投稿したいテキストメッセージに合わせてカスタマイズしてください。 今回は、「受信日時(getDate())」「送信者(getFrom)」「タイトル(getSubject())」「プレーンテキストの本文(getPlainBody())」を利用してみました。

Class GmailMessage  |  Apps Script  |  Google for Developers

message.markRead()

message.markRead()

最後にmessage.markRead()でメッセージを既読にしています。 これで、次回以降は!message.isUnread()に弾かれて処理されなくなります。

Class GmailMessage  |  Apps Script  |  Google for Developers

まとめ

どうでしょう。意外にシンプルですよね。 あとはGASのトリガーを好きな時間単位で指定すれば出来上がりです。 僕たちはGASのエラー通知をすぐに受信したいので1分単位でトリガーをセットしています。

GASでこういうことできるよ、という記事はいっぱいあるのですが、Googleで検索してもなかなか公式ドキュメントにたどり着けないんですよね。 実はめちゃくちゃわかりやすいので公式ドキュメントを眺めてやりたいことを実現していきましょう👏。

例えばGmailなら

Class GmailApp  |  Apps Script  |  Google for Developers

誰かのためになれば嬉しいです。一応丁寧に説明できたかなと。

気に入っていただけたら、サポートもお待ちしております!
  • 名前:asato
  • 仕事:スクラムマスター
  • 好き:家族、温泉、旅行、謎解き
  • 苦手:はじめまして、あんこ、うなぎ