みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

SendGridのSigned Event Webhookを使う

SendGridの送信履歴は数週間しか残らないので,履歴を保存するために,Webhookを利用して自分のサーバーに記録する仕組みを用意しなければなりません.

Google Cloud Functionsを利用して,かんたんにPythonで実装してみました.とりあえずverifyできるかを試すために,ログにverified変数の結果を出力するようにしました.認証できたら,目的のデータであるpayload変数がSendGridによって提供されたデータであると言えます.

ソースコード

hello_http関数を用意しました.functions_frameworkFlaskの派生なので,request変数はFlaskの流儀で処理しました.

main.py

import os

import functions_framework
from sendgrid.helpers.eventwebhook import EventWebhook

ew = EventWebhook(public_key=os.getenv("PUBLIC_KEY"))

@functions_framework.http
def hello_http(request):
    signature = request.headers.get("X-Twilio-Email-Event-Webhook-Signature")
    timestamp = request.headers.get("X-Twilio-Email-Event-Webhook-Timestamp")
    print(f"signature={signature}")
    print(f"timestamp={timestamp}")
    if not (signature and timestamp):
        return "No signature."
    payload = request.get_data(as_text=True)
    print(f"payload={payload}")
    verified = ew.verify_signature(
        payload=payload, signature=signature, timestamp=timestamp
    )
    print(f"verified={verified}")
    return "OK"

requirements.txt

functions-framework==3.0.0
sendgrid==6.9.5

デプロイ

これをCloud Functionsにデプロイします.SendGridでWebhookの設定をして,テストメールを送信したところ,以下のようにpayload変数に意図したデータが格納されていました.

payload = [
    {
        "email": "xxxxxxx@example.com",
        "event": "processed",
        "send_at": 0,
        "sg_event_id": "WxxdkLTIyNTU3MjUwLVhMNkxUSG9KUmdPN3JDODJoWExSdssv",
        "sg_message_id": "XL6RgO7rC82hXLRvg.filterdrecv-55446c3d49-64rbq-1-61A2C9C1-85.0",
        "smtp-id": "<XL6RgO7rC82hXLRvg@geopod-ismtpd-1-1>",
        "timestamp": 1643301326,
    },
    {
        "email": "xxxxxx@example.com",
        "event": "delivered",
        "ip": "xxx.xxx.xxx.xxx",
        "response": "250 2.0.0 OK 1643301329 a14si333819qtx.25 - gsmtp",
        "sg_event_id": "WxxdkXZlcmVkLTAtMjI1NTcyNTAtWEw2TFRIb0pSZ083ckM4MmhYWExSdssv",
        "sg_message_id": "XL6RgO7rC82hXLRvg.filterdrecv-55446c3d49-64rbq-1-61A2C9C1-85.0",
        "smtp-id": "<XL6RgO7rC82hXLRvg@geopod-ismtpd-1-1>",
        "timestamp": 1643301329,
        "tls": 1,
    },
]

対応

送信メールのリクエストと,Webhookのデータを対応させるために,custom_argsを使用すると便利です.メール送信時にcustom_argsに適当な識別子を設定し,Webhookを受信したら一致する識別子を持つ送信データを検索します.

値段

1ヶ月に1万件のメールをSendGridで送信した履歴をCloud Functionsで処理するために必要な費用を計算しました.CPUとメモリーの割当は最小(Memory: 128MB CPU: 200MHz)で,1回あたりの平均実行時間は100ms以下でしたのでこれで十分です.料金を計算したところ0円でした.

f:id:atsuhiro-me:20220207014153p:plain

データの保存先をログにすれば,ほぼ料金は発生しないので,お財布にやさしいです.なお,データベースに保存するならば別途費用が発生すると思われます.

参考にしたウェブサイト