ChatGPT APIを使って、Gmailの予約メールから Google Calendarに予定を自動で入れる
ChatGPTが登場するまで、
コロナ禍になる前、映画を見に行くのがとても好きで、よく映画館に足を運んでいました。
webでチケットを予約して、映画館に行って、発券マシンでチケットを発券していたのですが、
- チケットを取った後に、手動でGoogleCalendarに予定を追加するのが面倒
- 毎回メールボックスから予約番号などを探すのが面倒
でした。
Gmailは、楽天トラベルや、JRの新幹線予約、飛行機の予約などに関しては、
自動で連携してGoogle Calendarに予定を追加してくれる機能があるのですが、
映画館のチケットなどのメールは、自動で検知してくれません。(2024/05/03時点)
何か解決方法が無いかなと調べていたら、この記事を見つけました。
自分もこの記事を真似して、
- Google Apps Scriptで定期的にメールボックスを検索
- もしTOHOシネマズの映画のチケットの予約メールがあったら、それを正規表現でパース
- Google Calendarに予定を追加
という流れで、Gmailが正式に対応してない予約メールも、自動で予定を追加してくれるというのができました。
しかし、このアプローチだと、当たり前ですが、映画館の種類によって、メールのパターンが違うので、
映画館の種類の分だけ、条件を書いて正規表現を書く必要がありました。
自分は、新しい映画館に行く度に、この正規表現を書いて、
結局、109シネマズ、Tohoシネマズ、イオンシネマなど、合計5,6種類の正規表現を手動で書いてました。
また、滅多に起こらないのですが、予約メールのフォーマットが変わった時に、正規表現を書き直さなければ行けないというのも地味に面倒でした。
それ、ChatGPT APIでできるんじゃない?
コロナ禍を挟んで、映画館に行く頻度が下がっていたのですが、
「あれ、これって解析するのってChatGPTで簡単に実装できるんじゃないか」と、ふと思いました。
そこで、GPTに相談しながら、書いてみたところ、サクッと4時間くらいでほぼ動くものが実装できました。
できたもの
こんな感じで、勝手にカレンダーに登録してくれるものができました。
もちろんですが、映画のチケットの予約以外の美容院やローカルなお店の予約メールも処理できます。
仕組み
仕組みは、前の正規表現で書いてたものとほぼ同じで、Google Apps Scriptで10分ごとにメールボックスをチェックするという流れなのですが、GPTがメールの本文を読んでJSON形式で返してくれるので、それをもとに、GoogleCalendarに予定を追加します。
上のシン・エヴァンゲリオンの例だと、こんなJSONが返ってきます。
{
"title": "[映画]シン・エヴァンゲリオン劇場版",
"startDate":"2021-03-08T16:45:00",
"endDate":"2024-03-08T17:45:00",
"location":"T・ジョイPRINCE 品川",
"reservationNumber": "XXXXXX3214",
"seatNumber":"G-25"
}
送るメールのフィルター
ChatGPTのAPIにメールを解析してもらう時に、APIの課金枠が消費されるので、
何も考えず雑に大量のメールを読み込ませてしまうと、一気に予算を使い果たしてしまいます。
デバッグ中に、余り何も考えずに、APIを叩いていたら、数時間で簡単に$10消費してしまいました。
なので、ChatGPTにリクエストを送る前に、メールをある程度フィルターした状態で送る工夫と、
メール全文ではなく、必要な部分だけ送るということをしてAPI課金の消費を節約しています。
GASのソースコードの中に、メールの条件を直接書くこともできると思うのですが、
それをやってしまうと、メールの解析対象を変更しようとする度に、ソースコードの変更が必要になってしまうので避けようと思いました。
今回は
- Gmailのフィルター機能を使って、対象のメールにラベル(
GPT/willCheck
)を自動付与する - Google Apps Scriptで
GPT/willCheck
のラベルが付いているものを検索して、処理する
という流れにしました。
なので、対象を変更したい時は、Gmailでフィルターの設定を変更するだけで、良くなりました。
[Gmailでの操作]
手動で、「この予定Googleカレンダーに追加しとこう」と思ったときも、手動でラベルを付けて処理させることができます。
function getLabeledEmailThreads(labelName: string, count: number): GoogleAppsScript.Gmail.GmailThread[] {
let label = GmailApp.getUserLabelByName(labelName);
let threads = label.getThreads(0, count);
return threads;
}
処理後は、ラベル(GPT/willCheck
) を外すことで、次のチェックの際に、重複して処理されることを避けています。一応、どれが処理されたかを確認するために、ラベル(GPT/didCheck
)を付与しています。
あとは、メール本文を1000文字程度で切って、ChatGPTのAPIに送っている感じです。
ChatGPTのAPIを使う
ChatGPTのAPIを叩くのは簡単でsystemに前提条件、userに内容を入れて送るだけでした。
const messages: { role: string; content: string }[] = [
{ role: 'system', content: systemContent },
{ role: 'user', content: userContent }
];
例えば、
system = "端的に答えてください。答えは単語でお願いします。"
user = "日本の総理大臣は?"
と入れると、岸田文雄
とレスポンスが帰ってきます。
ちなみに今回のメールの解析に使ったプロンプトはこんな感じです。
あなたは予約メールを解析してJSON形式で返すアシスタントです。
JSONのフォーマットは下記を参考にしてください。
例1
{
"title": "[映画]トイ・ストーリー",
"startDate":"2024-03-31T10:30:00",
"endDate":"2024-03-31T11:30:00",
"location":"TOHOシネマズ六本木ヒルズ",
"reservationNumber": "1234",
"seatNumber":"G-10"
}
例2
{
"title": "[歯医者]歯科診療",
"startDate":"2024-03-31T10:30:00",
"endDate":"2024-03-31T11:30:00",
"location":"佐藤クリニック"
}
下記の場合は {"title":null} だけを返してください
- メールの中に予約などの文言が含まれないメールの場合
- startDateが分からない場合
- 明らかに広告や宣伝などのメールの場合
locationが不明の場合は ""を設定してください。
例に無いkeyは追加しないでください。
startDateのみが分かる場合は、endDateは1時間後の時間を設定してください。
メール1件に対して、最大1つの予定を返してください。配列では返さないでください。
メールの送信日時をstartDateとして扱わないように気をつけてください。
ソースコード自体は全てGitHubに上げてあるので、もし真似したい方がいたら、ご自由にforkするなどして、使ってください。(自己責任でお願いします)
気をつけること
レスポンスをJSONモードにする
最初これを知らずに、出力結果がJSON以外のものが返ってきて困っていました。
response_format: {"type": "json_object"}
これをリクエストに含めることで、確実にJSONでレスポンスを返しくれるようになります。
modelを適切なものを使う
OpenAIのドキュメントなどを見ると、GPTのモデルごとの料金などが書いてあります。
GPT-4-turbo
などを使うと、GPT-3-turbo
よりも、かなり良い結果が返ってきますが、20倍の費用が掛かります。
自分はGPT-3-turbo
だと、精度が出なかったので、GPT-4-turbo
を使っていますが、
用途によって、適切なモデルに切り替えたり、デバッグ中はGPT-3-turbo
にするなど気をつけたほうが良さそうです。
ちなみに、自分の使い方だと、メール1件処理するのに、$0.01~0.02掛かっているので、日本円にすると、ざっくり1-3円程度掛かっています。
自分は、今の条件だと、想定が100件/年もなく、年間で100-300円程度の支出の計算になるので、このままにしていますが、プロンプトを短くしたり、APIに送るメールの文章を切り詰める、GPT-3-turbo
に切り替えるなどすれば、もう少し安くなると思います。
ChatGPT APIの課金はこまめに。
すごく当たり前の話ですが、まとめて、$100入れて、気づいたら大量にAPI叩いてて、全部使ってしまったとなると、怖いので、少額の$10など入れて、足りなくなったらrefilするの方が安全です。お気をつけて。
余談
そのうち、GoogleがGeminiを使って類似のことをやってくる気もしているのですが、自分でコードを書くことで、好きなフォーマットや好きなルールでカスタムしたり、細かく改善できるので面白いですね。
git clone
して、clasp push
して、少し登録などするだけで使えるようになっているので、もし導入したよという方がいたら、コメントやStar付けてみてください。
Discussion