修订的AWS SES转发器示例代码(Python)无法转发电子邮件正文

2024-10-02 00:35:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图让AWS SES Lambda forwarder Python sample code不经修改地将电子邮件转发到外部帐户。在下面的(A)中,除非电子邮件是多部分的,否则邮件正文将无法正确转发。大多数错误与以下因素有关:

body = MIMEText(str(mailobject.get_payload(decode=True), 'UTF-8'))

如果我正确理解python文档,当MIMEmultipart为false时,它应该将电子邮件正文作为字符串传递给body。根据该行代码的执行方式,它将发送如下所示的正文,或者编译器将返回如(B)所示的错误,或者“str”对象没有属性“policy”

当我收到一封电子邮件时,它是什么样子的。html格式显示为文本

<html>
        <body>
                <p>You have chosen to subscribe to the topic:
                <br /><b>arn:aws:sns:I INTENTIONALLY REMOVED THIS</b></p>
                <p>To confirm this subscription, click or visit the link below (If this was in error no action is necessary):
                <br /><a href="https://sns.us-east-1.amazonaws.com/confirmation.html?TopicArn=arn:aws:sns:us-east-1:I INTENTIONALLY REMOVED THIS</a></p>
                <p><small>Please do not reply directly to this email. If you wish to remove yourself from receiving all future SNS subscription confirmation requests please send an email to <a href="mailto:sns-opt-out@amazon.com">sns-opt-out</a></small></p>
        </body>
</html>

(A)我使用的代码:

import os
import boto3
import email
import re
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from datetime import datetime

region = os.environ['Region']

def get_time():
    
    # datetime object containing current date and time
    now = datetime.now()
    
    # dd/mm/YY H:M:S
    return now.strftime("%d/%m/%Y %H:%M:%S")

def get_message_from_s3(message_id):

    incoming_email_bucket = os.environ['MailS3Bucket']
    incoming_email_prefix = os.environ['MailS3Prefix']

    if incoming_email_prefix:
        object_path = (incoming_email_prefix + "/" + message_id)
    else:
        object_path = message_id

    object_http_path = (f"http://s3.console.aws.amazon.com/s3/object/{incoming_email_bucket}/{object_path}?region={region}")

    # Create a new S3 client.
    client_s3 = boto3.client("s3")

    # Get the email object from the S3 bucket.
    object_s3 = client_s3.get_object(Bucket=incoming_email_bucket,
        Key=object_path)
    # Read the content of the message.
    file = object_s3['Body'].read()

    file_dict = {
        "file": file,
        "path": object_http_path
    }

    return file_dict

def create_message(file_dict):

    stringMsg = file_dict['file'].decode('utf-8')

    # Create a MIME container.
    msg = MIMEMultipart('alternative')

    sender = os.environ['MailSender']
    recipient = os.environ['MailRecipient']

    # Parse the email body.
    mailobject = email.message_from_string(file_dict['file'].decode('utf-8'))
    #print(mailobject.as_string())

    # Get original sender for reply-to
    from_original = mailobject['Return-Path']
    from_original = from_original.replace('<', '');
    from_original = from_original.replace('>', '');
    print(from_original)

    # Create a new subject line.
    subject = mailobject['Subject']

    body = ""

    if mailobject.is_multipart():

        print('IS multipart')
        index = stringMsg.find('Content-Type: multipart/')
        stringBody = stringMsg[index:]
        #print(stringBody)
        stringData = 'Subject: ' + subject + '\nTo: ' + sender + '\nreply-to: ' + from_original + '\n' + stringBody

        message = {
            "Source": sender,
            "Destinations": recipient,
            "Data": stringData
        }
        return message

        for part in mailobject.walk():
            ctype = part.get_content_type()
            cdispo = str(part.get('Content-Disposition'))

            # case for each common content type
            if ctype == 'text/plain' and 'attachment' not in cdispo:
                bodyPart = MIMEText(part.get_payload(decode=True), 'plain', part.get_content_charset())
                msg.attach(bodyPart)

            if ctype == 'text/html' and 'attachment' not in cdispo:
                mt = MIMEText(part.get_payload(decode=True), 'html', part.get_content_charset())
                email.encoders.encode_quopri(mt)
                del mt['Content-Transfer-Encoding']
                mt.add_header('Content-Transfer-Encoding', 'quoted-printable')
                msg.attach(mt)

            if 'attachment' in cdispo and 'image' in ctype:
                mi = MIMEImage(part.get_payload(decode=True), ctype.replace('image/', ''))
                del mi['Content-Type']
                del mi['Content-Disposition']
                mi.add_header('Content-Type', ctype)
                mi.add_header('Content-Disposition', cdispo)
                msg.attach(mi)

            if 'attachment' in cdispo and 'application' in ctype:
                ma = MIMEApplication(part.get_payload(decode=True), ctype.replace('application/', ''))
                del ma['Content-Type']
                del ma['Content-Disposition']
                ma.add_header('Content-Type', ctype)
                ma.add_header('Content-Disposition', cdispo)
                msg.attach(ma)


    # not multipart - i.e. plain text, no attachments, keeping fingers crossed
    else:
        
        body = MIMEText(str(mailobject.get_payload(decode=True), 'UTF-8'))
        msg.attach(body)

    # The file name to use for the attached message. Uses regex to remove all
    # non-alphanumeric characters, and appends a file extension.
    filename = re.sub('[^0-9a-zA-Z]+', '_', subject)

    # Add subject, from and to lines.
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = recipient
    msg['reply-to'] = mailobject['Return-Path']
    
    # Create a new MIME object.
    att = MIMEApplication(file_dict["file"], filename)
    att.add_header("Content-Disposition", 'attachment', filename=filename)

    # Attach the file object to the message.
    msg.attach(att)
    message = {
        "Source": sender,
        "Destinations": recipient,
        "Data": msg.as_string()
    }
    return message
    
def send_email(message):
    aws_region = os.environ['Region']

# Create a new SES client.
    client_ses = boto3.client('ses', region)

    # Send the email.
    try:
        #Provide the contents of the email.
        response = client_ses.send_raw_email(
            Source=message['Source'],
            Destinations=[
                message['Destinations']
            ],
            RawMessage={
                'Data':message['Data']
            }
        )

    # Display an error if something goes wrong.
    except ClientError as e:
        output = e.response['Error']['Message']
    else:
        output = "Email sent! Message ID: " + response['MessageId']

    return output

def lambda_handler(event, context):
    # Get the unique ID of the message. This corresponds to the name of the file
    # in S3.
    message_id = event['Records'][0]['ses']['mail']['messageId']
    print(f"Received message ID {message_id}")

    # Retrieve the file from the S3 bucket.
    file_dict = get_message_from_s3(message_id)

    # Create the message.
    message = create_message(file_dict)
    #this alone didn't fix it:   message = create_message(str(file_dict))
    # Send the email and print the result.
    result = send_email(message)
    print(result)

(B)this post中的代码示例在“body”赋值和我一直试图在(A)中得到的工作中失败了

body = MIMEText(mailobject.get_payload(decode=True), 'UTF-8')
    msg.attach(body)
Response:
{
  "errorMessage": "'bytes' object has no attribute 'encode'",
  "errorType": "AttributeError",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 190, in lambda_handler\n    message = create_message(file_dict)\n",
    "  File \"/var/task/lambda_function.py\", line 128, in create_message\n    body = MIMEText(mailobject.get_payload(decode=True), 'UTF-8')\n",
    "  File \"/var/lang/lib/python3.8/email/mime/text.py\", line 34, in __init__\n    _text.encode('us-ascii')\n"
  ]
}

若要复制此问题,请启动一个新的SES实例,按照original AWS SES Lambda forwarder Python sample code的教程进行操作并验证其是否有效。然后,将AWS示例代码替换为(a)中的my代码或其他链接示例之一

另外,I found this Lambda email test event code并且能够让它足够工作,以便从AWS Lambda仪表板测试此代码。为了使其工作,我在仪表板中创建了两个新的测试事件(多部分/非多部分),并粘贴在该代码中,更改(a)电子邮件地址(如适用),(b)Lambda函数ARN到我函数的ARN,以及(c)存储在我的活动s3实例上的适用电子邮件的消息id。这为我省去了手动创建和发送测试电子邮件以进行调试的麻烦,希望其他找到此帖子并有类似问题的人也能这样做。该测试代码将导致转发实际电子邮件


Tags: thetoinfrommessagegetobjects3

热门问题