AWS SNS 自定義郵件格式
AWS 的 SES (Simple Email Service) 可以提供郵件收發服務。對於郵件收發的反饋,如 Bounce message, 可以傳送到 SNS (Simple Notification Service) 作進一步處理。SNS 可以指定某個郵件地址作為訂閱者,將訊息傳送到該郵箱中。然而,SNS發出來的郵件是 JSON,可讀性不好,例如:
{"notificationType":"Delivery","mail":{"timestamp":"2019-02-18T06:03:02.669Z","source":"[email protected]","sourceArn":"arn:aws:ses:us-west-2:xxxxxx:identity/feichashao.com","sourceIp":"205.251.234.36","sendingAccountId":"xxxxxx","messageId":"01010168ff335c8d-f00ce1c1-e103-49cd-912f-9f397c7a463c-000000","destination":["[email protected]"],"headersTruncated":false,"headers":[{"name":"From","value":"[email protected]"},{"name":"To","value":"[email protected]"},{"name":"Subject","value":"free kfc"},{"name":"MIME-Version","value":"1.0"},{"name":"Content-Type","value":"text/plain; charset=UTF-8"},{"name":"Content-Transfer-Encoding","value":"7bit"}],"commonHeaders":{"from":["[email protected]"],"to":["[email protected]"],"subject":"free kfc"}},"delivery":{"timestamp":"2019-02-18T06:03:03.917Z","processingTimeMillis":1248,"recipients":["[email protected]"],"smtpResponse":"250 2.0.0 OK 1550469783 q2si13329671plh.79 - gsmtp","remoteMtaIp":"74.125.20.27","reportingMTA":"a27-30.smtp-out.us-west-2.amazonses.com"}}
怎麼能讓這個提醒郵件變得更加友好呢? SNS目前不支援自定義郵件格式。一個思路是,將 SNS 的訊息傳送到 Lambda 上,讓 Lambda 處理好格式後,再發送到指定郵箱。即 SES -> SNS -> Lambda -> SES.
建立 SNS Topic
建立一個SNS Topic, 比如名字叫"email-notify".
建立 Lambda Function
使用 Lambda 的 Blueprints ses-notification-python 作為模板,建立以下 Lambda Function.
這裡主要修改了 handle_delivery 這個 Handler, 期望每次 SES 傳送郵件後,都會觸發這個 Handler, 將相關資訊傳送到管理員郵箱。
from __future__ import print_function import json import boto3 from botocore.exceptions import ClientError def lambda_handler(event, context): message = json.loads(event['Records'][0]['Sns']['Message']) notification_type = message['notificationType'] handlers.get(notification_type, handle_unknown_type)(message) def handle_bounce(message): message_id = message['mail']['messageId'] bounced_recipients = message['bounce']['bouncedRecipients'] addresses = list( recipient['emailAddress'] for recipient in bounced_recipients ) bounce_type = message['bounce']['bounceType'] print("Message %s bounced when sending to %s. Bounce type: %s" % (message_id, ", ".join(addresses), bounce_type)) def handle_complaint(message): message_id = message['mail']['messageId'] complained_recipients = message['complaint']['complainedRecipients'] addresses = list( recipient['emailAddress'] for recipient in complained_recipients ) print("A complaint was reported by %s for message %s." % (", ".join(addresses), message_id)) def handle_delivery(message): message_id = message['mail']['messageId'] delivery_timestamp = message['delivery']['timestamp'] header_from = message['mail']['commonHeaders']['from'][0] header_to = message['mail']['commonHeaders']['to'][0] print("Message %s was delivered successfully at %s" % (message_id, delivery_timestamp)) SENDER = "feichashao-ses <[email protected]>" RECIPIENT = "[email protected]" AWS_REGION = "us-west-2" SUBJECT = "Amazon SES Test (SDK for Python)" BODY_TEXT = ("Amazon SES Test (Python)\r\n" "This email was sent with Amazon SES using the " "AWS SDK for Python (Boto)." ) BODY_HTML = """<html> <head></head> <body> <h1>Email Delivery</h1> <p>From: {}</p> <p>To: {}</p> </body> </html> """.format(header_from, header_to) CHARSET = "UTF-8" client = boto3.client('ses',region_name=AWS_REGION) try: #Provide the contents of the email. response = client.send_email( Destination={ 'ToAddresses': [ RECIPIENT, ], }, Message={ 'Body': { 'Html': { 'Charset': CHARSET, 'Data': BODY_HTML, }, 'Text': { 'Charset': CHARSET, 'Data': BODY_TEXT, }, }, 'Subject': { 'Charset': CHARSET, 'Data': SUBJECT, }, }, Source=SENDER, ) # Display an error if something goes wrong. except ClientError as e: print(e.response['Error']['Message']) else: print("Email sent! Message ID:"), print(response['MessageId']) def handle_unknown_type(message): print("Unknown message type:\n%s" % json.dumps(message)) raise Exception("Invalid message type received: %s" % message['notificationType']) handlers = {"Bounce": handle_bounce, "Complaint": handle_complaint, "Delivery": handle_delivery}
在 Designer 中,將剛剛建立的 SNS 作為 Trigger。
建立 SES 服務
上面兩個都是為 SES 服務的。現在,建立一個 SES 服務。認證一個 Domain 作為郵件傳送方,認證一個 Email 作為郵件接收方。
點進 SES 的 Domain(以 feichashao.com 為例),點開 Notification 一欄,點選 Edit Configuration, 將 Deliveries 選擇為上面的 SNS topic,如 "email-notify".
回到 Domain 的介面,點選 Send a Test Email 進行測試。傳送者是 [email protected],收件人是 [email protected]。在郵件傳送成功後,Lambda 應該會收到 SNS 的資訊,然後 Lambada 會把 JSON 整理後,傳送到管理員郵箱 [email protected].
Email Delivery From: [email protected] To: [email protected]