DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Python Send Email With Html And Plain Versions And Attachments

02.23.2010
| 7513 views |
  • submit to reddit
        
import os.path
import types

import smtplib
import email.Message
from email import Encoders
from email.MIMEAudio import MIMEAudio
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from email.MIMEText import MIMEText
import mimetypes

class SendMail(object):
    '''The swiss army knife of email programs. Here are some of the features:

        1) you can build parts of the message in pieces. This is useful for when
        you need to send out lots of emails with similar content. For example,
        I needed to added a unique unsubcribe token to each email. The email had
        a large attachment. This made it so I did not have to reprocess the
        attachment for each email.

        2) allows you to include both html and plain text versions of the message
        in the email.

        3) handles the encoding of a wide range of attachment file types.

        4) sends the email with the TLS protocol if the server uses TLS
           gmail uses TLS.

    This code was pieced together from snippets of code found using google.

    How to use? Just look down the list of methods. Use the ones you need.
    It should be pretty obvious. You will need to set the attributes:
    "from_email_addr" and "subject" directly before calling send.

    This code has NOT been throughly tested because there are too many combinations.
    I just test the combinations I need.
    '''
    def __init__(self,smtpserver,smtpuser = '',smtppass = '',port=None, debug=False):
        self.smtpserver=smtpserver
        self.smtpuser=smtpuser
        self.smtppass=smtppass
        self.port=port
        self.debug=debug
        self.encoded_attachments=[]
        self.cc_list=[]

    def add_attachments(self,attachment_list):
        '''attachment_list: a list of full file paths'''
        self.encoded_attachments=[]
        for path in attachment_list:
            dirname,filename=os.path.split(path)
            # Guess the content type based on the file's extension.  Encoding
            # will be ignored, although we should check for simple things like
            # gzip'd or compressed files.
            ctype, encoding = mimetypes.guess_type(path)
            if ctype is None or encoding is not None:
                # No guess could be made, or the file is encoded (compressed), so
                # use a generic bag-of-bits type.
                ctype = 'application/octet-stream'
            maintype, subtype = ctype.split('/', 1)
            if maintype == 'text':
                fp = open(path)
                # Note: we should handle calculating the charset
                msg = MIMEText(fp.read(), _subtype=subtype)
                fp.close()
            elif maintype == 'image':
                fp = open(path, 'rb')
                msg = MIMEImage(fp.read(), _subtype=subtype)
                fp.close()
            elif maintype == 'audio':
                fp = open(path, 'rb')
                msg = MIMEAudio(fp.read(), _subtype=subtype)
                fp.close()
            else:  # includes MS Word docs
                fp = open(path, 'rb')
                msg = MIMEBase(maintype, subtype)
                msg.set_payload(fp.read())
                fp.close()
                # Encode the payload using Base64
                Encoders.encode_base64(msg)
            # Set the filename parameter
            msg.add_header('Content-Disposition', 'attachment', filename=filename)
            self.encoded_attachments.append(msg)

    def add_to(self,to_list):
        self.to_list=to_list
        self.to_str=','.join(to_list)

    def add_cc(self,cc_list):
        self.cc_list=cc_list
        self.cc_str=','.join(cc_list)

    def add_body(self,body):
        if type(body) is types.StringType:
            self.body=MIMEText(body,'plain')
        else:
            self.body=MIMEMultipart('alternative')
            for k in body:
                self.body.attach(MIMEText(body[k],k))

    def send(self):
        '''No error checking. It should just crash if some stuff is not setup. '''
        message=MIMEMultipart()
        message.preamble = 'You will not see this in a MIME-aware mail reader.\n'
        message.epilogue = ''  # To guarantee the message ends with a newline
        message.attach(self.body)

        message['To']=self.to_str
        if self.cc_list:
            message['CC']=self.cc_str

        # Set these attributes up directly before calling this method
        message['Subject'] = self.subject
        message['From']=self.from_email_addr

        print self.encoded_attachments
        for a in self.encoded_attachments:
            message.attach(a)

        # Send it
        server = smtplib.SMTP(self.smtpserver,self.port)
        try:
            server.set_debuglevel(self.debug)
            server.ehlo()
            # If we can encrypt this session, do it
            if server.has_extn('STARTTLS'):
                server.starttls()
                server.ehlo() # re-identify ourselves over TLS connection
            server.login(self.smtpuser, self.smtppass)
            smtpresult =server.sendmail(self.from_email_addr, self.to_list+self.cc_list, message.as_string())
        finally:
            server.quit()
        
        if smtpresult:
            for recip in smtpresult.keys():
                print  """Could not delivery mail to: %s Server said: %s %s""" \
                      % (recip, smtpresult[recip][0], smtpresult[recip][1])
                return False
        else:
            return True

    def send_simple_email(self,to_list,from_email_addr,subject,body,attachment_list=[],
                          cc_list=[]):
        '''A wrapper for if you do not need to do things in pieces. You can also use this
        as a guide for how to build up an email in pieces.
        '''
        self.add_to(to_list)
        self.add_cc(cc_list)
        self.from_email_addr=from_email_addr
        self.subject=subject
        self.add_body(body)
        self.add_attachments(attachment_list)
        return self.send()
        

if __name__=="__main__":
    smtpuser = 'my_email@gmail.com'
    smtppass=raw_input('Enter gmail password->')
    
    body={}
    body['html']='<h1>hi</h1>'
    body['plain']='hello'
    
    sm=SendMail('smtp.gmail.com', smtpuser = smtpuser, smtppass = smtppass, port=587)
    r=sm.send_simple_email(['to_someone@yahoo.com'],'my_email@gmail.com','Test - from Fred',
        body,attachment_list=[r'C:\Documents and Settings\my_media\uploads\Case-of-the-Week invite 02-24-10.doc'])
    print r