Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.6k views
in Technique[技术] by (71.8m points)

django - How do I reply to an email using the Python imaplib and include the original message?

I'm currently using imaplib to fetch email messages from a server and process the contents and attachments.

I'd like to reply to the messages with a status/error message and links to the resulting generated content on my site if they can be processed. This should include the original message but should drop any attachments (which will be large) and preferably replace them with just their filenames/sizes.

Since I'm already walking the MIME message parts, I'm assuming what I need to do is build a new MIME message tree containing a copy of the original message and delete/replace the attachment nodes.

Before I start down that path, I was hoping someone can give me some tips. Is there any kind of library function to do this? Any kind of standard behavior I should stick to?

I currently know of/am using the imaplib, smtplib and email modules and but may have missed something obvious in there. This is running in Django too, so can use anything in django.core.email if that makes it easier.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The original MIME tree structure of the incoming message is as follows (using email.iterators._structure(msg)):

multipart/mixed
    text/html                (message)
    application/octet-stream (attachment 1)
    application/octet-stream (attachment 2)

Replying via GMail results in the following structure:

multipart/alternative
    text/plain
    text/html

I.e. they aren't being as smart as I thought, just discarding the attachments (good) and providing text and HTML versions that explicitly restructure the "quoted content."

I'm beginning to think that's all I should do too, just reply with a simple message as after discarding the attachments there's not much point in keeping the original message.

Still, might as well answer my original question since I've figured out how to now anyway.

First, replace all the attachments in the original message with text/plain placeholders:

import email

original = email.message_from_string( ... )

for part in original.walk():
    if (part.get('Content-Disposition')
        and part.get('Content-Disposition').startswith("attachment")):

        part.set_type("text/plain")
        part.set_payload("Attachment removed: %s (%s, %d bytes)"
                         %(part.get_filename(), 
                           part.get_content_type(), 
                           len(part.get_payload(decode=True))))
        del part["Content-Disposition"]
        del part["Content-Transfer-Encoding"]

Then create a reply message:

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.message import MIMEMessage

new = MIMEMultipart("mixed")
body = MIMEMultipart("alternative")
body.attach( MIMEText("reply body text", "plain") )
body.attach( MIMEText("<html>reply body text</html>", "html") )
new.attach(body)

new["Message-ID"] = email.utils.make_msgid()
new["In-Reply-To"] = original["Message-ID"]
new["References"] = original["Message-ID"]
new["Subject"] = "Re: "+original["Subject"]
new["To"] = original["Reply-To"] or original["From"]
new["From"] = "me@mysite.com"

Then attach the original MIME message object and send:

new.attach( MIMEMessage(original) )

s = smtplib.SMTP()
s.sendmail("me@mysite.com", [new["To"]], new.as_string())
s.quit()

The resulting structure is:

multipart/mixed
    multipart/alternative
        text/plain
        text/html
    message/rfc822
        multipart/mixed
            text/html
            text/plain
            text/plain

Or it's a bit simpler using Django:

from django.core.mail import EmailMultiAlternatives
from email.mime.message import MIMEMessage

new = EmailMultiAlternatives("Re: "+original["Subject"],
                             "reply body text", 
                             "me@mysite.com", # from
                             [original["Reply-To"] or original["From"]], # to
                             headers = {'Reply-To': "me@mysite.com",
                                        "In-Reply-To": original["Message-ID"],
                                        "References": original["Message-ID"]})
new.attach_alternative("<html>reply body text</html>", "text/html")
new.attach( MIMEMessage(original) ) # attach original message
new.send()

The result ends (in GMail at least) showing the original message as "---- Forwarded message ----" which isn't quite what I was after, but the general idea works and I hope this answer helps someone trying to figure out how to fiddle with MIME messages.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...