こちらの記事とやっていることはほぼ同じで、Lambda関数をS3のほうではなくてSESのアクションに設定しているだけです。
SESのeventに入っているmessageIdがobject keyになるように変更したり、環境変数を使ってみたりしてますが基本コピペです。
import os
import boto3
import email
import re
import logging
s3_client = boto3.client('s3')
ses_client = boto3.client('ses')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ORIGINAL_ADDRESS = os.environ.get('originalAddress', '')
FORWARD_ADDRESS = os.environ.get('forwardAdresses', '')
S3_BUCKET_NAME = os.environ.get('s3BucketName', '')
SES_REGION = 'us-east-1'
def parse_mail(raw_message):
from_name = 'No Name'
pattern = re.compile(r'^From:\s*(.+?)$', re.MULTILINE)
m = re.search(pattern, raw_message)
if m:
from_name = m.group(1)[:-1]
replaced_message = raw_message.replace(ORIGINAL_ADDRESS, FORWARD_ADDRESS)
replaced_message = re.sub(r'^From:\s*.+?$', r'From: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)
replaced_message = re.sub(r'^Return-Path:\s*.+?$', r'Return-Path: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)
replaced_message = re.sub(r'^Sender:\s*.+?$', r'Sender: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)
return replaced_message
def send_mail(message):
ses = boto3.client('ses', region_name=SES_REGION)
ses.send_raw_email(
Source = FORWARD_ADDRESS,
Destinations=[
FORWARD_ADDRESS
],
RawMessage={
'Data': message
}
)
def lambda_handler(event, context):
try:
s3_object = s3_client.get_object(
Bucket = S3_BUCKET_NAME,
Key = event['Records'][0]['ses']['mail']['messageId'])
raw_message = s3_object["Body"].read().decode('utf-8')
message = parse_mail(raw_message)
send_mail(message)
logger.info('SUCCESS')
except Exception:
logger.critical(
'Execution failed. Exception encountered',
exc_info=True)
logger.info('FAILED')
SES > Rule Sets > View Active Rule set > [rule name] でactionを追加したら完成です。
2019年6月28日追記
Amazonアソシエイトのユーザー追加確認メールなどDKIM-Signatureが2つ入っていたりするものが転送できない問題があった。
githubにもっとシンプルなものがあったのでこちらを使ったほうがよいと思う。
message_from_fileがStreamingBodyをうまく扱えなかったので、以下のようにmessage_from_stringに変更した。
from email import message_from_string
import json
import logging
import os
import re
import boto3
from botocore.exceptions import ClientError
FORWARD_MAPPING = {
'xxxxxxx@example.com': ['xxxxxxxx@gmail.com'],
}
VERIFIED_FROM_EMAIL = os.environ['VERIFIED_FROM_EMAIL'] # An email that is verified by SES to use as From address.
SES_INCOMING_BUCKET = os.environ['SES_INCOMING_BUCKET'] # S3 bucket where SES stores incoming emails.
s3 = boto3.client('s3')
ses = boto3.client('ses')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
record = event['Records'][0]
assert record['eventSource'] == 'aws:ses'
o = s3.get_object(Bucket=SES_INCOMING_BUCKET, Key=record['ses']['mail']['messageId'])
raw_mail = o['Body'].read().decode('utf-8')
msg = message_from_string(raw_mail)
del msg['DKIM-Signature']
del msg['Sender']
del msg['Return-Path']
original_from = msg['From']
del msg['From']
msg['From'] = re.sub(r'\<.+?\>', '', original_from).strip() + ' <{}>'.format(VERIFIED_FROM_EMAIL)
if not msg['Reply-To']: msg['Reply-To'] = original_from
msg['Return-Path'] = VERIFIED_FROM_EMAIL
msg_string = msg.as_string()
for recipient in record['ses']['receipt']['recipients']:
forwards = FORWARD_MAPPING.get(recipient, [])
if not forwards:
logger.warning('Recipent <{}> is not found in forwarding map. Skipping recipient.'.format(recipient))
continue
#end if
for address in forwards:
try:
o = ses.send_raw_email(Destinations=[address], RawMessage=dict(Data=msg_string))
logger.info('Forwarded email for <{}> to <{}>. SendRawEmail response={}'.format(recipient, address, json.dumps(o)))
except ClientError as e: logger.error('Client error while forwarding email for <{}> to <{}>: {}'.format(recipient, address, e))