def plaintext_to_message(old_message, plaintext): ''' Create a new Message with only the plain text. >>> # Test extreme cases >>> plaintext_to_message(None, None) == None True ''' new_message = None try: if old_message and plaintext: parser = Parser() plain_message = parser.parsestr(plaintext) payloads = plain_message.get_payload() if isinstance(payloads, list): new_message = MIMEMultipart(old_message.get_content_subtype(), old_message.get_boundary()) else: new_message = MIMEText(plain_message.get_payload()) # save all the headers for key, value in old_message.items(): new_message.add_header(key, value) if type(payloads) == list: for payload in payloads: new_message.attach(payload) # add the content type and encoding from the plain text for key, value in plain_message.items(): if key.lower() == mime_constants.CONTENT_TYPE_KEYWORD.lower(): new_message.__delitem__(key) new_message.add_header(key, value) elif key.lower( ) == mime_constants.CONTENT_XFER_ENCODING_KEYWORD.lower(): new_message.__delitem__(key) new_message.add_header(key, value) if type(new_message) == Message and DEBUGGING: log_message('new message:\n{}'.format(new_message.as_string())) except IOError as io_exception: record_exception(message=io_exception) except MessageException as message_exception: record_exception(message=message_exception) return new_message
def plaintext_to_message(old_message, plaintext): ''' Create a new Message with only the plain text. >>> # Test extreme cases >>> plaintext_to_message(None, None) == None True ''' new_message = None try: if old_message and plaintext: parser = Parser() plain_message = parser.parsestr(plaintext) payloads = plain_message.get_payload() if isinstance(payloads, list): new_message = MIMEMultipart(old_message.get_content_subtype(), old_message.get_boundary()) else: new_message = MIMEText(plain_message.get_payload()) # save all the headers for key, value in old_message.items(): new_message.add_header(key, value) if type(payloads) == list: for payload in payloads: new_message.attach(payload) # add the content type and encoding from the plain text for key, value in plain_message.items(): if key.lower() == mime_constants.CONTENT_TYPE_KEYWORD.lower(): new_message.__delitem__(key) new_message.add_header(key, value) elif key.lower() == mime_constants.CONTENT_XFER_ENCODING_KEYWORD.lower(): new_message.__delitem__(key) new_message.add_header(key, value) if type(new_message) == Message and DEBUGGING: log_message('new message:\n{}'.format(new_message.as_string())) except IOError as io_exception: record_exception(message=io_exception) except MessageException as message_exception: record_exception(message=message_exception) return new_message
def main(sender,gmail_password,recipients,subject,body,is_reminder): email_sent = dbcreate() msgID= email.utils.make_msgid() outer = MIMEMultipart() outer.add_header('Subject',subject) outer.add_header('From',sender) outer.add_header('Message-ID',msgID) # Send the email try: with smtplib.SMTP('smtp.gmail.com', 587) as s: s.ehlo() s.starttls() s.ehlo() s.login(sender, gmail_password) for recipient in recipients: outer.add_header('To',recipient) outer.attach(MIMEText(body, "html")) composed = outer.as_string() s.sendmail(sender, recipient, composed) outer.__delitem__('To') s.close() print("Reminder mail sent!") email_sent.insert_one( { 'to':recipients, 'from':sender, 'subject':outer['Subject'], 'MessageID':str(outer['Message-ID']), 'DateTime':datetime.datetime.now(), 'time':time.time(), 'message':body, 'is_reminder':is_reminder } ) except: print("Unable to send the email. Error: ", sys.exc_info()[0]) raise
class Message(object): """ Wrapper around email.Message class simplifying creation of simple email message objects. Allows most basic email messages types (including text, html & attachments) to be created simply from constructor. More complex messages should be created using the email.mime classes directly Class wraps the email.Message class and delegates item/attr lookups to the wrapped class (allows the object to be treated as a MIMEBase instance even though it doesnt inherit from this) Basic usage: >>> msg = Message('Test Message',to='*****@*****.**',text="Hello",html="<b>Hello</b>",attachments=['img.jpg']) """ def __init__(self, subject, to, cc=None, bcc=None, text=None, html=None, attachments=None, sender=None, reply_to=None): """ Create message object subject : Subject field to : To recipients (as string - eg. "A <*****@*****.**>, B <*****@*****.**>") cc : Cc recipients (same format as to) bcc : Bcc recipients (same format as to) text : Plain text body html : HTML body ('text' will be included as alternative) attachments : List of attachments - if the item is a subclass of MIMEBase this is inserted directly, otherwise it is assumed to be a filename and a MIME attachment craeted guessing the content-type (for detailed control of the attachment parameters create these separately) sender : Value for the 'From' header (e.g. Foo Barr <*****@*****.**>). If specified, 'Reply-To' header is also set to this address. reply_to : Value for the 'Reply-To' header. """ if not html and not attachments: # Simple plain text email self.root = MIMEText(text, 'plain', self._charset(text)) else: # Multipart message self.root = MIMEMultipart() if html: # Add html & plain text alernative parts alt = MIMEMultipart('alternative') alt.attach(MIMEText(text, 'plain', self._charset(text))) alt.attach(MIMEText(html, 'html', self._charset(html))) self.root.attach(alt) else: # Just add plain text part txt = MIMEText(text, 'plain', self._charset(text)) self.root.attach(txt) # Add attachments for a in attachments or []: self.root.attach(self._attachment(a)) # Set headers self.root['To'] = to if cc: self.root['Cc'] = cc if bcc: self.root['Bcc'] = bcc if sender: self.root['From'] = sender if not reply_to: # If 'Reply-To' is not provided, set it to the 'From' value self.root['Reply-To'] = sender if reply_to: self.root['Reply-To'] = reply_to self.root['Subject'] = subject def _charset(self, s): """ Guess charset - assume ascii for text and force utf-8 for unicode (email.mime classes take care of encoding) """ return 'utf-8' if isinstance(s, unicode_type) else 'us-ascii' def _attachment(self, a): """ Create MIME attachment """ if isinstance(a, MIMEBase): # Already MIME object - return return a else: # Assume filename - guess mime-type from extension and return MIME object main, sub = (guess_type(a)[0] or 'application/octet-stream').split('/', 1) attachment = MIMEBase(main, sub) with open(a, 'rb') as f: attachment.set_payload(f.read()) if sys.version_info[0] == 2: # Try to handle non-ascii filenames attachment.add_header('Content-Disposition', 'attachment', filename=unicode( os.path.basename(a), sys.getfilesystemencoding())) else: attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(a)) encode_base64(attachment) return attachment # Delegate to root MIME object (allows object to be treated as MIMEBase) def __getitem__(self, key): return self.root.__getitem__(key) def __setitem__(self, key, value): self.root.__setitem__(key, value) def __delitem__(self, key): return self.root.__delitem__(key) def __getattr__(self, attr): if attr != 'root': return getattr(self.root, attr)
class Message(object): """ Wrapper around email.Message class simplifying creation of simple email message objects. Allows most basic email messages types (including text, html & attachments) to be created simply from constructor. More complex messages should be created using the email.mime classes directly Class wraps the email.Message class and delegates item/attr lookups to the wrapped class (allows the object to be treated as a MIMEBase instance even though it doesnt inherit from this) Basic usage: >>> msg = Message('Test Message',to='*****@*****.**',text="Hello",html="<b>Hello</b>",attachments=['img.jpg']) """ def __init__(self,subject,to,cc=None,bcc=None,text=None,html=None, attachments=None, sender=None, reply_to=None): """ Create message object subject : Subject field to : To recipients (as string - eg. "A <*****@*****.**>, B <*****@*****.**>") cc : Cc recipients (same format as to) bcc : Bcc recipients (same format as to) text : Plain text body html : HTML body ('text' will be included as alternative) attachments : List of attachments - if the item is a subclass of MIMEBase this is inserted directly, otherwise it is assumed to be a filename and a MIME attachment craeted guessing the content-type (for detailed control of the attachment parameters create these separately) sender : Value for the 'From' header (e.g. Foo Barr <*****@*****.**>). If specified, 'Reply-To' header is also set to this address. reply_to : Value for the 'Reply-To' header. """ if not html and not attachments: # Simple plain text email self.root = MIMEText(text,'plain',self._charset(text)) else: # Multipart message self.root = MIMEMultipart() if html: # Add html & plain text alernative parts alt = MIMEMultipart('alternative') alt.attach(MIMEText(text,'plain',self._charset(text))) alt.attach(MIMEText(html,'html',self._charset(html))) self.root.attach(alt) else: # Just add plain text part txt = MIMEText(text,'plain',self._charset(text)) self.root.attach(txt) # Add attachments for a in attachments or []: self.root.attach(self._attachment(a)) # Set headers self.root['To'] = to if cc: self.root['Cc'] = cc if bcc: self.root['Bcc'] = bcc if sender: self.root['From'] = sender if not reply_to: # If 'Reply-To' is not provided, set it to the 'From' value self.root['Reply-To'] = sender if reply_to: self.root['Reply-To'] = reply_to self.root['Subject'] = subject def _charset(self,s): """ Guess charset - assume ascii for text and force utf-8 for unicode (email.mime classes take care of encoding) """ return 'utf-8' if isinstance(s,unicode_type) else 'us-ascii' def _attachment(self,a): """ Create MIME attachment """ if isinstance(a,MIMEBase): # Already MIME object - return return a else: # Assume filename - guess mime-type from extension and return MIME object main,sub = (guess_type(a)[0] or 'application/octet-stream').split('/',1) attachment = MIMEBase(main,sub) with open(a,'rb') as f: attachment.set_payload(f.read()) attachment.add_header('Content-Disposition','attachment',filename=os.path.basename(a)) encode_base64(attachment) return attachment # Delegate to root MIME object (allows object to be treated as MIMEBase) def __getitem__(self,key): return self.root.__getitem__(key) def __setitem__(self,key,value): self.root.__setitem__(key,value) def __delitem__(self,key): return self.root.__delitem__(key) def __getattr__(self,attr): if attr != 'root': return getattr(self.root,attr)
def main(sender, gmail_password, recipients, subject, body, is_reminder, resub, reminder_mails, reminder_numbers, remsg, remsms, remdatetime, timestamp, attachments): data = [] #To build reminder dictionary for each email recipient for i in recipients: d = { 'id': i, 'resub': resub, 'remsg': remsg, 'remmails': reminder_mails, 'remnumbers': reminder_numbers, 'remsms': remsms, 'remdatetime': remdatetime, 'timestamp': timestamp } data.append(d) #Checking whether DB present or not. If not create it. email_sent = dbcreate() #Generating a MesaageID locally so that we can store it and use it in future to track replies #Most of the current day email systems add 'In-Reply' parameter to header of reply email #'In-Reply' consists of MessageID of original mail for which reply mail is sent msgid = email.utils.make_msgid() #Our email can contain attachments too so creating MIMEMultipart() object outer = MIMEMultipart() #User add_header() method to add header values outer.add_header('Subject', subject) #Adding subject to header outer.add_header('From', sender) #Adding from to header outer.add_header('Message-ID', msgid) #Adding msgid to header #If you want to send customised emails rather than plain text then HTML programmed mails must be used #The recieving email system will parse the email as HTML lines. So you could use HTML tags like <b></b> to make text bold,etc. #Here we are attaching the Text part to our Multipart object outer.attach(MIMEText(body, "html")) #Attaching attachments to Multipart object for file in attachments: try: with open(file, 'rb') as fp: #Since attachment is not restricted to any file type #We are using Main Content type: application and Sub Content type: octet-stream for the part msg = MIMEBase('application', "octet-stream") #Ataaching the attachment file to the part msg.set_payload(fp.read()) #Files must be transferred in ASCII coding so the attachment is encoded using base scheme encoders.encode_base64(msg) # 'Content-Disposition' can have two values viz. # 1. 'inline' : This tells the email reader to open the file within the email as part of web page # 2. 'attachment': This tells the email reader to download the file locally and that to when user clicks on it msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(file)) #Here we are attaching the attachment part to our Multipart object outer.attach(msg) except: print("Unable to open one of the attachments. Error: ", sys.exc_info()[0]) raise try: with smtplib.SMTP('smtp.gmail.com', 587) as s: #ehlo() method identifies our system with Email Server s.ehlo() #starttls() shifts our connection to TLS mode for secure connection s.starttls() #As per smtplib documentation we need to again identify our system after using starttls s.ehlo() #login() method to login to the account s.login(sender, gmail_password) #Adding 'BCC' header doesn't work #So we loop over receipents and sendmail to each individual for recipient in recipients: #Adding 'To' header outer.add_header('To', recipient) #Converting our whole mail to a single string to send mail composed = outer.as_string() #Using sendmail() method to send mail #Arguments paased are sender(i.e. From) , recipient(i.e. To) , composed(i.e. The mail contents) s.sendmail(sender, recipient, composed) #We need to just delete current 'To' header so that we could reuse the smae Mulitpart mail object for other recipients outer.__delitem__('To') #Close smtp connection s.close() print("Email sent!") #Insering the data into Database email_sent.insert_one({ 'to': recipients, 'from': sender, 'subject': outer['Subject'], 'MessageID': msgid, 'DateTime': datetime.datetime.now(), 'time': time.time(), 'attachments': attachments, 'message': body, 'reminder': data, 'reminder_mails': reminder_mails, 'reminder_numbers': reminder_numbers, 'is_reminder': is_reminder }) except: print("Unable to send the email. Error: ", sys.exc_info()[0]) raise #raise keyword without exception mentioned reraises the last exception so you know what went wrong