SendGridのEvent Webhookでメールを識別するため、X-SMTPAPIヘッダのUnique Argumentsを使ってみた

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を試してみました。

 

目次

 

環境

 

Event Webhook受信アプリの作成

アプリの基盤

Event Webhookの受信は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アプリとします。

 
用意したWebアプリは以下のような感じです。

 

// 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++;
  }
  const firstRecord = pj[0]
  
  // 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 APIX-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スプレッドシートにバウンスしたメールアドレスの情報が書き込まれました。

f:id:thinkAmi:20210328181124p:plain

また、X-SMTPAPIヘッダに入れた項目も、 foo=ふー のように書き込まれていました。

そのため、 Unique Arguments を使うことで、バウンスしたときに日本語の識別情報を受け取れそうでした。

 

SendGridのコンソール

SendGridのコンソールにある SuppressionsBounceにも、通知のあったメールアドレスが書き込まれます。

 

受信メールのヘッダ

正常に受信したメールのヘッダを見てみましたが、X-SMTPAPI ヘッダはありませんでした。

SendGridから送信されるときに削除されるようです。

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi-sandbox/sendgrid_event_webhook-sample