【Google Apps Script】Gmailから「相手からの返信待ちメール」を抽出する
はじめに
夏。繁忙期。日はとうに暮れた。
またメールか。受信ボックスはとうに溢れている。
上司:「わたし宛てのメールに24時間以内に返信がなかったらリマインドしてね」
心の悪魔が囁く。
「あの、人をリマインド代わりに使うのやめてくれます?」
「ピシッ、ピシッ、ピシッ」。耳に伝うように来るのは、人間関係に亀裂が走る音...
筆者はGmailの受信トレイを都度アーカイブすることでToDo管理をしていますが、[1]
繁忙期になると受信トレイの一通一通まで追い切れなくなります。
ここにきて、
「わたし宛てのメールに24時間以内に返信がなかったらリマインドしてね」
と来るとさすがにキャパが足りません。そこでGoogle Apps Scriptで上司のリクエストに応えてみた、というお話です。
目標
「わたし宛てのメールに24時間以内に返信がなかったらリマインドしてね」
をGoogle Apps Scriptで実装する。
具体的には下記の条件をすべて満たすメールを抽出する。
抽出条件
- 送信日から24時間以上経過している。
- スレッドの最後のメールの宛先が特定のアドレスになっている。
- スレッドの最後のメールの差出人が自分
- 最終メールに☆マークがついていない
※「ご確認ありがとうございました。」のような要返信でないメールを除外するため。
使うもの
- Gmailアカウント
- Google Apps Script
- GmailAppクラスのメソッドを使います。
https://developers.google.com/apps-script/reference/gmail/gmail-app
ベースとなる記事
Find Unanswered Emails with Apps Script
2014年5月の記事。これをアップデート・カスタムして実装します。
実装
1. 「24時間以上経過している」「特定の宛先の」メールを抽出
条件に該当するメールはGmailApp.search(query)で抽出できます。
引数のquery
は、Gmail上で検索するのと同じ書式でOK。
まずは、「24時間以上経過している」「特定の宛先の」 の2条件で下記のquery
を作ります。(2022/07/23に実行の場合)
in:sent before:2022/07/22 to:sample@example.com
これだと検索対象が前日以前の全期間になり膨大なので、検索範囲を1週間に限定し、
in:sent after:2022/07/16 before:2022/07/22 to:sample@example.com
とします。
ただ、これだとやや問題があって、日時指定でquery
を渡すと、タイムゾーンがUTCになるようです。(参考)
Gmail APIのドキュメントに記載のとおり、UNIX時間に変換することでGmailApp.search
でも日本時間に対応できます。
以上をもとにquery
組み立てます。
//特定の宛先のメールに検索を限定したい場合は指定する。
const toEmailAddress = []
//引数の日数分前の日付を返す関数
const daysBefore = (days) => {
const d = new Date();
d.setDate(d.getDate() - days);
const unixTime = Date.parse(d)/1000
return unixTime.toFixed()
}
let query = `in:sent after:${daysBefore(7)} before:${daysBefore(1)}`
if (toEmailAddress.length > 0) {
toEmailAddress.forEach((email) => {
query += ` to:${email}`
}
)
}
console.log(`Search query:${query}`)
query
ができたら、GmailApp.search(query)
でスレッドを検索します。
const threads = GmailApp.search(query);
これでqueryに該当するGmailThreadクラスの配列が得られました。
2. 「最後の差出人が自分であるもの」「☆マークなし」のスレッドを抽出
続いて、thread.getMessages()[thread.getMessageCount() - 1]
で、スレッドの最後のメールを取得し、差出人が自分のアドレスであるものに絞り込みます。
Session.getEffectiveUser().getEmail()は、スクリプトを実行するユーザーのメールアドレスを返します。
//送信元メールアドレスの指定
const userEmailAddress = Session.getEffectiveUser().getEmail();
//有効なメールアドレスかどうか判定する正規表現
const emailRegex = /[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-z.A-Z]+/g;
const threadsToUpdate = [];
for (let i = 0; i < threads.length; i++) {
const thread = threads[i];
const lastMessage = thread.getMessages()[thread.getMessageCount() - 1];
const lastMessageSender = lastMessage.getFrom().match(emailRegex)[0];
if (lastMessageSender == userEmailAddress && !lastMessage.isStarred()) {
threadsToUpdate.push(thread);
}
}
3. ラベルの作成・追加
最後に抽出したメールスレッドにラベル付けを行います。
すでにラベルが存在する場合は、既存のラベルの削除し、ラベル付けを更新します。
ラベルの作成は、GmailApp.createLabelを用い、スレッドへのラベルの追加は、GmailLabel.addToThreads(threads)を用います。
//古いラベルの削除
const old_label = GmailApp.getUserLabelByName("返信待ち");
if (old_label) {
GmailApp.deleteLabel(old_label);
}
const label = GmailApp.createLabel("返信待ち");
label.addToThreads(threadsToUpdate);
これをスクリプトトリガで日毎に実行すると、過去1週間~24時間のメールにラベルが付き、目的のメールを抽出できました。
おわりに
以上のコードをまとめて提示します。
function findUnansweredMail() {
//特定の宛先のメールに検索を限定したい場合は指定する。
const toEmailAddress = []
//引数の日数分前の日付を返す関数
const daysBefore = (days) => {
const d = new Date();
d.setDate(d.getDate() - days);
const unixTime = Date.parse(d)/1000
return unixTime.toFixed()
}
let query = `in:sent after:${daysBefore(7)} before:${daysBefore(1)}`
if (toEmailAddress.length > 0) {
toEmailAddress.forEach((email) => {
query += ` to:${email}`
}
)
}
console.log(`Search query:${query}`)
const threads = GmailApp.search(query);
if (threads.length == 0) {
return
}
//送信元メールアドレスの指定
const userEmailAddress = Session.getEffectiveUser().getEmail();
//有効なメールアドレスかどうか判定する正規表現
const emailRegex = /[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-z.A-Z]+/g;
const threadsToUpdate = [];
for (let i = 0; i < threads.length; i++) {
const thread = threads[i];
const lastMessage = thread.getMessages()[thread.getMessageCount() - 1];
const lastMessageSender = lastMessage.getFrom().match(emailRegex)[0];
if (lastMessageSender == userEmailAddress && !lastMessage.isStarred()) {
threadsToUpdate.push(thread);
}
}
//古いラベルの削除
const old_label = GmailApp.getUserLabelByName("返信待ち");
if (old_label) {
GmailApp.deleteLabel(old_label);
}
const label = GmailApp.createLabel("返信待ち");
label.addToThreads(threadsToUpdate);
}
GmailAppの各種メソッドを覚えておくと、今回の例の他にも「最後のメッセージが自分宛てになっている」といった条件で未返信メールを抽出したりと、いろいろ応用が効きますね。Gmailで使える検索演算子が多少やっかいですが、フィルタ検索でも使えるので覚えておいて損はありません。
GmailをGoogle Apps Scriptで書く記事はネット上に多くありますが、業務上の実際の活用例としてご参考ください。
Discussion