GeekFactory

int128.hatenablog.com

AWSでメールを受信してLambdaで処理する

AWSでメールを受信してLambdaで処理するには以下の方法があります。

  1. SES→S3, SES→Lambda
  2. SES→SNS→Lambda

大きなメールを受信したい場合は1を選びます。1はS3にメールを格納してから読みに行くため、サイズの大きなメールも受信できます。2では160kB以下のメールしか受信できません。

SESとLambdaを異なるリージョンに配置したい場合は2を選びます。SESから直接Lambdaを実行する場合は同じリージョンでなければならない制約がありますが、SNSを経由させると異なるリージョンに配置できます。LambdaをVPCに配置して内部APIを叩きたい場合などに便利です。

なお、大きなメールを受信する要件と異なるリージョンに配置する要件の両方が必要な場合はLambdaからLambdaを呼び出すといった工夫が必要になります。よい方法があったら教えてください!

SES→Lambda

Lambdaでは下記のようなイベントを受け取ります。

{
    "Records": [
        {
            "eventSource": "aws:ses",
            "eventVersion": "1.0",
            "ses": {
                "mail": {
                    "timestamp": "2017-11-20T05:17:16.168Z",
                    "source": "送り元メールアドレス",
                    "messageId": "****",
                    "destination": [
                        "宛先メールアドレス"
                    ],
                    "headersTruncated": false,
                    "headers": [
                        {
                            "name": "Return-Path",
                            "value": "<送り元メールアドレス>"
                        },
                        ...snip...
                    ],
                    "commonHeaders": {
                        "returnPath": "送り元メールアドレス",
                        "from": [
                            "送り元メールアドレス"
                        ],
                        "date": "Mon, 20 Nov 2017 05:17:02 +0000",
                        "to": [
                            "宛先メールアドレス"
                        ],
                        "messageId": "<****@mail.gmail.com>",
                        "subject": "Re: foo"
                    }
                },
                "receipt": {
                    "timestamp": "2017-11-20T05:17:16.168Z",
                    "processingTimeMillis": 876,
                    "recipients": [
                        "宛先メールアドレス"
                    ],
                    "spamVerdict": {
                        "status": "PASS"
                    },
                    "virusVerdict": {
                        "status": "PASS"
                    },
                    "spfVerdict": {
                        "status": "PASS"
                    },
                    "dkimVerdict": {
                        "status": "PASS"
                    },
                    "dmarcVerdict": {
                        "status": "GRAY"
                    },
                    "action": {
                        "type": "Lambda",
                        "functionArn": "arn:aws:lambda:us-west-2:****:function:****",
                        "invocationType": "Event"
                    }
                }
            }
        }
    ]
}

本文が必要な場合はS3からメールを取得してパースします。

SES→SNS→Lambda

Lambdaでは下記のようなイベントを受け取ります。

{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:aws:sns:us-west-2:****:****:38d16b3b-58f7-431b-9dbe-0362b0bcd7f2",
            "Sns": {
                "Type": "Notification",
                "MessageId": "8472fe29-c0fc-5e8d-8292-acb870a91f3e",
                "TopicArn": "arn:aws:sns:us-west-2:****:****",
                "Subject": "Amazon SES Email Receipt Notification",
                "Message": "...メールのJSON文字列...",
                "Timestamp": "2017-11-24T05:28:53.510Z",
                "SignatureVersion": "1",
                "Signature": "...snip...",
                "SigningCertUrl": "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-****.pem",
                "UnsubscribeUrl": "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:****:****:38d16b3b-58f7-431b-9dbe-0362b0bcd7f2",
                "MessageAttributes": {}
            }
        }
    ]
}

Record.Sns.Message には下記のようなメールオブジェクトが入っています。

{
  "notificationType": "Received",
  "mail": {
    "timestamp": "2017-11-24T05:28:52.922Z",
    "source": "送り元メールアドレス",
    "messageId": "****",
    "destination": [
      "宛先メールアドレス"
    ],
    "headersTruncated": false,
    "headers": [
      {
        "name": "Return-Path",
        "value": "<送り元メールアドレス>"
      },
      ...snip...
    ],
    "commonHeaders": {
      "returnPath": "送り元メールアドレス",
      "from": [
        "送り元メールアドレス"
      ],
      "date": "Fri, 24 Nov 2017 05:28:38 +0000",
      "to": [
        "宛先メールアドレス"
      ],
      "messageId": "<****@mail.gmail.com>",
      "subject": "Re: test"
    }
  },
  "receipt": {
    "timestamp": "2017-11-24T05:28:52.922Z",
    "processingTimeMillis": 569,
    "recipients": [
      "宛先メールアドレス"
    ],
    "spamVerdict": {
      "status": "PASS"
    },
    "virusVerdict": {
      "status": "PASS"
    },
    "spfVerdict": {
      "status": "PASS"
    },
    "dkimVerdict": {
      "status": "PASS"
    },
    "dmarcVerdict": {
      "status": "GRAY"
    },
    "action": {
      "type": "SNS",
      "topicArn": "arn:aws:sns:us-west-2:****:****",
      "encoding": "UTF8"
    }
  },
  "content": "...メールのヘッダと本文..."
}

本文を取得するにはJSONcontent をパースします。