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

Versitile And Sturdy Python Email Class/script .

04.24.2009
| 3790 views |
  • submit to reddit
        // Detailed script to send email through python. Can be called through sendEmail class or you can run it via command line. You can even pipe the body of the message from stdin!

#!/bin/env python2.3
#******************************************************************************* sendEmail.py
"""
Send email with given data or attachment files.
"""

#******************************************************************************* Imports
import sys,os
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.Utils import COMMASPACE, formatdate
from email.MIMEText import MIMEText
from email import Encoders
from optparse import OptionParser
from tempfile import NamedTemporaryFile

#=============================================================================== sendEmail
class sendEmail:
    #=============================================================================== __init__
    def __init__(self, msgTo='', msgFrom='', msgSubject='', msgBody='', attachmentDict={}, attachmentPaths=[]):
        '''
        Build and sends an email through local smtp server with the following options:
        *msgTo = List of email addresses to send to or comma separated string of emails.
        *msgFrom = String with email address to show as sent from.
        *msgSubject = String with subject line 
        msgBody = String with data for message body.
        attachmentDict = Dict of filename = content. Example: 'test.csv'='some,csv,file\nto,send,out'
        attachmentPaths = List of paths to files that need to be sent.
        * Required
        
        Note that You must use the send function to send the email.
        '''
        
        self.msg = MIMEMultipart()
        self.msgTo = msgTo
        self.msgFrom = msgFrom
        self.msgSubject = msgSubject
        self.msgBody = msgBody
        
        # Create and attach "files" from dict.
        if attachmentDict:
            for fileName in attachmentDict.keys():
                tmpFile = NamedTemporaryFile()
                tmpFile.write(attachmentDict[fileName])
                tmpFile.flush()
                self.msg.attach(self._encodeFile(fileName, tmpFile.name))
                tmpFile.close()
                
        # Attach passed files using paths if they exist.
        if attachmentPaths:
            for filePath in attachmentPaths:
                if os.path.exists(filePath):
                    self.msg.attach(self._encodeFile(os.path.basename(filePath), filePath))
                    
    #=============================================================================== encodeFile
    def _encodeFile(self, fileName, filePath):
        '''
        Internal method used to return an encoded file to be attached.
        Allows for more readablility in code.
        '''
        part = MIMEBase('application', "octet-stream")
        part.set_payload( open(filePath,"rb").read() )
        Encoders.encode_base64(part)
        part.add_header('Content-Disposition', 'attachment; filename="%s"' % fileName)
        print 'Sucessfully Attached: ', os.path.basename(fileName)
        return part
        
    #=============================================================================== attachFile
    def attachFile(self, filePath, fileName=''):
        '''
        Checks for valid filePath and then attaches the given fileName and filePath to the the message.
        If no fileName is give then default name is assumed.
        '''
        if os.path.exists(filePath):
            if not fileName:
                fileName = os.path.basename(filePath)
            self.msg.attach(self._encodeFile(fileName, filePath))
            
    #=============================================================================== attachFile
    def attachText(self, fileName,fileText):
        '''
        Options:
        fileName = desired name of attachment.
        fileText = list or string to attach as contents of file.
        '''
        if isinstance(fileText, list):
            fileText = '\n'.join(fileText)
        
        tmpFile = NamedTemporaryFile()
        tmpFile.write(fileText)
        tmpFile.flush()
        self.msg.attach(self._encodeFile(fileName, tmpFile.name))
        tmpFile.close()
        
    #=============================================================================== send
    def send(self, smtpAddress='localhost'):
        '''
        Sends the message using the passed smtpAddress or localhost if no address is specified.
        '''
        errText = '\n'
        if not self.msgTo:
            errText += 'No TO address\n'
        elif not self.msgFrom:
            errText += 'No FROM address\n'
        elif not self.msgSubject:
            errText += 'No SUBJECT line\n'
            
        if errText.strip():
            print errText + 'Message not sent.'
        else:
            self.msg.attach(MIMEText(self.msgBody))
        
            if isinstance(self.msgTo, list):
                self.msg['To'] = COMMASPACE.join(self.msgTo)
            else:
                self.msg['To'] = self.msgTo
                
            self.msg['From']    = self.msgFrom.strip()
            self.msg['Date']    = formatdate(localtime=True)
            self.msg['Subject'] = self.msgSubject
            
            smtp = smtplib.SMTP(smtpAddress)
            smtp.sendmail(self.msgFrom, self.msgTo, self.msg.as_string())
            smtp.close()
            print 'Email Sent To: %s, From: %s, Subject: %s' %(self.msgTo, self.msgFrom, self.msgSubject)
        
#=============================================================================== getOptsAndArgs
def getOptsAndArgs(args):
    #========================================================================== requiredInput
    def requiredInput(prompt, name):
        while True:
            i = raw_input(prompt)
            if i.strip():
                return i
            else:
                print 'Please enter a(n) %s.' % name
                
    #========================================================================== getMultiLineInput
    def getMultiLineInput(prompt):
        retLines = []
        prompt =  '-- %s (Empty to end) --\n' % prompt
        newLine = raw_input(prompt)
        while newLine:
            retLines.append(newLine)
            newLine = raw_input()
        return '\n'.join(retLines)
        
    #========================================================================== getAttachmentsInteractively
    def getAttachmentsInteractively(options):
        attachmentDict = {}
        while True:
            attachPrompt = raw_input('Attachment?\n([T]ext,[F]ile,[N]one)\t').upper()
            if attachPrompt == 'T':
                attachmentName = requiredInput('Attachment Name:\t', 'attachment name')
                attachmentText = getMultiLineInput('Attachment Text')
                attachmentDict[attachmentName] = attachmentText
            elif attachPrompt == 'F':
                attachmentPath = requiredInput('Attachment Path:\t', 'attachment path')
                if os.path.exists(attachmentPath):
                    options.attachmentPaths.append(attachmentPath)
                else:
                    print 'File does not exist: ', attachmentPath
            else:
                break
                
        if attachmentDict:
            options.attachmentDict = attachmentDict
        
    #========================================================================== getOptsInteractively
    def getOptsInteractively(options):
        if not options.msgTo:
            options.msgTo = requiredInput('To:  \t', 'destination address')
        if not options.msgFrom:
            options.msgFrom = requiredInput('From:  \t', 'from address')
        if not options.msgSubject:
            options.msgSubject = requiredInput('Subject: ', 'message subject')
        if not options.msgBody:
            options.msgBody = getMultiLineInput('Body')
            
        getAttachmentsInteractively(options)
        
    #========================================================================== checkRequired
    def checkRequired(options):    
        errText = '\n'
        if not options.msgTo:
            errText += 'You must give an email address to send to using -t or --to=\n'
        if not options.msgFrom:
            errText += 'You must give an email address to send from using -f or --from=\n'
        if not options.msgSubject:
            errText += 'You must give a subject line using -s or --subject=\n'        
            
        return errText.strip()

    #========================================================================== validateRequired
    def validateRequired(validCheck):
        if validCheck.strip():
            parser.error(validCheck)
    
    #========================================================================== parseOpts
    parser = OptionParser()

    parser.add_option('-i', '--interactive',
                      action='store_true', dest='runInteractive',
                      help='Input information in an interactive prompt.'
                      )
                      
    parser.add_option('-t', '--to',
                      action='store', dest='msgTo',
                      help='Email address to send to.'
                      )

    parser.add_option('-f', '--from',
                      action='store', dest='msgFrom',
                      help='Email address to show as sent from.'
                      )

    parser.add_option('-s', '--subject',
                      action='store', dest='msgSubject',
                      help='Output summary only. Exclude statistics section.'
                      )

    parser.add_option('-b', '--body',
                      action='store', dest='msgBody',default='',
                      help='Text to send as body'
                      )
                      
    parser.add_option('-A', '--attachments-interactive',
                      action='store_true', dest='attachInteractive',
                      help='Input attachment information in an interactive prompt.'
                      )
                      
    parser.add_option('-p', '--attachment-paths',
                      action='store', dest='attachmentPaths',
                      help='Comma separated list of attachment files, works with one file as well'
                      )
                      
    parser.add_option('-S', '--smtp-address=',
                      action='store', dest='smtpAddress', default='localhost',
                      help='Comma separated list of attachment files, works with one file as well'
                      )

    options, args = parser.parse_args()
    
    options.attachmentDict = {}
    
    if options.attachmentPaths:
        options.attachmentPaths = options.attachmentPaths.split(',')
    else:
        options.attachmentPaths = []
        
    checkText = checkRequired(options)
    if not sys.stdin.isatty():
        options.msgBody += sys.stdin.read()
        validateRequired(checkText)
        
    if options.runInteractive:
        getOptsInteractively(options)
    elif options.attachInteractive:
        validateRequired(checkText)
        getAttachmentsInteractively(options)
    else:
        validateRequired(checkText)

    if options.attachmentPaths:
        for filePath in options.attachmentPaths:
            if not os.path.exists(filePath):
                parser.error('File does not exist: ', filePath)
    
    return options, args
    
#=============================================================================== __main__
if __name__ == '__main__':
    options, args = getOptsAndArgs(sys.argv[1:])
   
    message = sendEmail(msgTo=options.msgTo, msgFrom=options.msgFrom, msgSubject=options.msgSubject, msgBody=options.msgBody, attachmentDict=options.attachmentDict, attachmentPaths=options.attachmentPaths)
    message.send(smtpAddress=options.smtpAddress)