SendGridにはEvent Webhookがあり、メール送信のイベントをWebhookとして拾うことができます。
ただ、Webhookで取得できるデフォルトの項目が送信先メールアドレスや発生日時などに限られていました。
項目を増やせないかを調べたところ、
などがありました。
このうち、Categoriesについては
カテゴリはUS-ASCII文字セットを使った7bitエンコードを使用する必要があります。 カテゴリはメッセージをグループ化するために使用されます。メッセージにユニークなデータや識別子を付加したい場合、代わりにUnique Argumentsを使用してください。
https://sendgrid.kke.co.jp/docs/API_Reference/SMTP_API/categories.html
とのことです。
できれば日本語の件名を識別に使いたかったことから、X-SMTPAPIヘッダのUnique Argumentsを試してみました。
目次
環境
- SendGrid
- メール送信アプリ
- Event Webhook受信アプリ
- Google Apps ScriptのWebアプリ
- Googleスプレッドシート
Event Webhook受信アプリの作成
アプリの基盤
Event Webhookの受信はWebアプリを用意すればよかったので、今回はお手軽に
- Google Apps ScriptによるWebアプリ
としました。
Basic認証について
SendGridのEvent WebhookではBasic認証をサポートしています。
Event Webhook は HTTP のBasic認証をサポートしています。この認証を使用する場合は、Settings画面で、認証情報を含めたURLを HTTP Post URL に指定してください。
http(s)://username:password@domain/foo.php
https://sendgrid.kke.co.jp/docs/API_Reference/Webhooks/event.html#-Setup
一方、Google Apps ScriptのWebアプリではヘッダ情報を取得できないことから、Basic認証のサポートは難しそうです。
https://issuetracker.google.com/issues/67764685
ただ今回はお手軽なアプリのため、Basic認証はかけずに使ってみます。
Google Apps ScriptによるWebアプリの作成
doPost()
関数を用意し、Webアプリとして公開します。
公開した際にリクエスト用のURLが発行されますので、これを使います。
なお、今回はGoogleスプレッドシートの所有者として動かし、誰でもアクセスできるWebアプリとします。
- 今から10分ではじめる Google Apps Script(GAS) で Web API公開 - Qiita
- Google Apps ScriptのdoPostでJSONなパラメータのPOSTリクエストを受ける - Qiita
用意したWebアプリは以下のような感じです。
- SendGridのEvent Webhookを受信したら、Googleスプレッドシートに書き込む
// main.gs function doPost(e) { const id = 'YOUR SHEET ID'; const ss = SpreadsheetApp.openById(id); const sheet = ss.getSheetByName('シート1'); // 一行目に最終更新日時をセット sheet.getRange('A1').setValue(Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss')) const rangeValues = sheet.getRange('A:A').getValues(); let targetRow = rangeValues.filter(String).length + 1; // A列の最終行の次の行へ入力 const posts = JSON.parse(e.postData.getDataAsString()); for (const post of posts) { sheet.getRange(`A${targetRow}`).setValue(toDateTime(post.timestamp)); sheet.getRange(`B${targetRow}`).setValue(post.email); sheet.getRange(`C${targetRow}`).setValue(post.event); sheet.getRange(`D${targetRow}`).setValue(post); targetRow++; } // 2023/07/30 13:04 追記 ここから // コメントいただいた通り、この行は不要 //const firstRecord = pj[0] // 2023/07/30 13:04 追記 ここまで // SendGridのEvent WebhookではHTTPステータスコードさえあれば良いけど念のため const result = { message: "hello" } const response = ContentService.createTextOutput(); response.setMimeType(ContentService.MimeType.JSON); response.setContent(JSON.stringify(result)); return response; } // Unixタイムスタンプを日時に直す function toDateTime(unixTimestamp) { const dtFormat = new Intl.DateTimeFormat('ja-JP', { dateStyle: 'medium', timeStyle: 'medium', timeZone: 'Asia/Tokyo' }); return dtFormat.format(new Date(unixTimestamp * 1e3)); }
SendGridの設定
Event Webhookの設定
ドキュメントに従い、Event Webhookの設定をします。
Event Webhook - ドキュメント | SendGrid
HTTP Post URL
には、上記のGoolge Apps Scriptで作成したWebアプリのURLを指定します。
その後、 Test Your Integration
をクリックし、Googleスプレッドシートにテストデータが記載されればOKです。
あとは忘れずに ENABLE
にした後 Save
します。
なお、Event Webhookの設定が反映されるまでは少々時間がかかります。
そのため、設定後即バウンスするメールを送信してもEvent Webhookが動作しないことがあります。
APIキーの設定
送信用のAPIキーを作成します。
APIキーの管理 - ドキュメント | SendGrid
X-SMTPAPIヘッダを持つメールをSMTP送信
準備ができたので、SMTP APIで X-SMTPAPI
ヘッダを持つメールを送信してみます。
Integrating with the SMTP API - ドキュメント | SendGrid
X-SMTPAPIヘッダを自分で作成するのは手間なため、SendGridの公式ライブラリを使います。
APIライブラリ - ドキュメント | SendGrid
今回はPythonからSMTP送信するため、 smtpapi-python
を使います。
https://github.com/sendgrid/smtpapi-python
今回は独自の文字列とメールの件名を X-SMTPAPI
へ入れて送信してみます。
# smtpy_sender.py import os import smtplib from email.mime.text import MIMEText from smtpapi import SMTPAPIHeader from dotenv import load_dotenv load_dotenv() def main(): server = create_smtp_server() from_email = os.environ['FROM_EMAIL'] to_email = [ os.environ['NOT_FOUND_EMAIL'], os.environ['EXISTS_EMAIL'], ] body = 'hello' message = MIMEText(body) message['From'] = from_email message['TO'] = ','.join(to_email) subject = 'メールバウンステスト' message['Subject'] = subject message['X-SMTPAPI'] = create_smtpapi_header(subject) server.sendmail(from_email, to_email, message.as_string()) server.quit() def create_smtp_server(): host = 'smtp.sendgrid.net' port = 587 user = 'apikey' password = os.environ['SENDGRID_API_KEY'] server = smtplib.SMTP(host, port) server.starttls() server.login(user, password) return server def create_smtpapi_header(subject): header = SMTPAPIHeader() header.set_unique_args({ 'foo': 'ふー', 'subject': subject }) return header.json_string() if __name__ == '__main__': main()
なお、メール送信アプリで必要な情報は .env
ファイルを用意して python-dotenv
にて読み込むようにしました。
https://github.com/theskumar/python-dotenv
SENDGRID_API_KEY= FROM_EMAIL= NOT_FOUND_EMAIL= EXISTS_EMAIL=
実行結果
上記のPythonスクリプトを python stmp_sender.py
として実行した時の結果を確認します。
Googleスプレッドシート
Googleスプレッドシートにバウンスしたメールアドレスの情報が書き込まれました。
また、X-SMTPAPIヘッダに入れた項目も、 foo=ふー
のように書き込まれていました。
そのため、 Unique Arguments
を使うことで、バウンスしたときに日本語の識別情報を受け取れそうでした。
SendGridのコンソール
SendGridのコンソールにある Suppressions
のBounceにも、通知のあったメールアドレスが書き込まれます。
受信メールのヘッダ
正常に受信したメールのヘッダを見てみましたが、X-SMTPAPI
ヘッダはありませんでした。
SendGridから送信されるときに削除されるようです。
ソースコード
Githubに上げました。
https://github.com/thinkAmi-sandbox/sendgrid_event_webhook-sample