友人と個人開発してまして、各種サービスは共有のGoogleアカウントを使っています。 どちらかしか通知を受けられない、操作できないのは面倒なので。
しかし、自分たちもGoogleアカウントを持っており、Gmailを見るためにアカウントを切り替えるのは面倒です。 また、コミュニケーションはSlackで行っているので、GmailをSlackのチャンネルに通知してSlackで会話できるといいなーと思っていました。
色々と調べると次の方法が紹介されていました。
- Slackの転送先メールアドレスをGmailの転送先に指定する
- Google Workspace Marketplaceの「 Slack for Gmail 」を使う
- Slackの「 Email 」アプリを使う
- GAS
しかしながら、どれも要件を満たすのが難しそう。
1
はslackbotへのDMになってしまいチャンネルポストができなそうです。
2
はブラウザでGmailを開いて、メールごとチャンネルごとに手作業が必要です。
3
は「Email」アプリが有料ワークスペースでないと使えなそうです。
4
は検索で引っかかるコードを見る限り、割と複雑そうです。
んー、どうしたものか。
ん。4
、本当に複雑なのか。
あれ、お前、実はもっとシンプルになるんじゃないのか。
ということで、GmailをSlackに通知するGASはすごくシンプルだよ、の例を示します。
サンプルコード
いきなりサンプルコードです(メールをSlackに転送したいアカウントでやってください)。
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 = <SlackのIncoming 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'
を指定しているので、「受信ボックス」の中で「未読」のものを拾ってきます。 また、start
に0
、max
に100
を指定しているので、最新のスレッドから100件までの中からquery
を検索してもらっています。

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

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

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()
)」を利用してみました。

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

まとめ
どうでしょう。意外にシンプルですよね。 あとはGASのトリガーを好きな時間単位で指定すれば出来上がりです。 僕たちはGASのエラー通知をすぐに受信したいので1分単位でトリガーをセットしています。
GASでこういうことできるよ、という記事はいっぱいあるのですが、Googleで検索してもなかなか公式ドキュメントにたどり着けないんですよね。 実はめちゃくちゃわかりやすいので公式ドキュメントを眺めてやりたいことを実現していきましょう👏。
例えばGmailなら

誰かのためになれば嬉しいです。一応丁寧に説明できたかなと。
サポートもお待ちしております!