Python send emails with corresponding attachments - python

I have a desktop folder with 3 XLSX files: ID1.xlsx, ID2.xlsx, ID3.xlsx
I can use the following code to loop through a list of 3 email addresses and send individual emails to each address, but it attaches all XLSX files in the folder.
Expected Result: I want to attach ID1.xlsx to the first email and send, ID2.xlsx to the 2nd email and send, and finally ID3.xlsx to the 3rd email and send.
email_addresses = ['test1#test.com', 'test2#test.com', 'test3#test.com']
class EmailsSender:
def __init__(self):
self.outlook = win32.Dispatch('outlook.application')
def send_email(self, to_email_address, attachment_path):
mail = self.outlook.CreateItem(0)
mail.To = to_email_address
mail.Subject = 'Report'
mail.Body = """Report is attached."""
if attachment_path:
mail.Attachments.Add(Source=attachment_path, Type=olByValue)
mail.Send()
def send_emails(self, email_addresses, attachment_path=None):
for email in email_addresses:
self.send_email(email, attachment_path)
attachment_path = r'C:\Users\Desktop\Test\*.xlsx'
email_sender = EmailsSender()
email_sender.send_emails(email_addresses, attachment_path)

You will need to reference the file name with email so switch to Dictionaries.
Example
email_addresses = {'test1#test.com': 'ID1.xlsx',
'test2#test.com': 'ID2.xlsx',
'test3#test.com': 'ID3.xlsx'
}
attachment_path = r'C:\Users\Desktop\Test'
for email, file in email_addresses.items():
print(email, attachment_path + file)

Related

How to convert dataframe to csv on AWS Lambda and sendmail with csv attached using SES

I have to sendmail at the end of my code with csv attached containing a dataframe.
Im doing it at AWS Lambda using boto3 to call SES as it follows.
def sendMail1(value, df):
subject = "Comission"
client = boto3.client("ses")
body = f"""
Comission value is {value}.
"""
message = {"Subject": {"Data": subject}, "Body": {"Html": {"Data": body}}}
attachment = df.to_csv(f"Comission.csv", index=False)
response = client.send_email(Source = "myemail#gmail.com", Destination = {"ToAddresses": ["youremail#gmail.com"]}, Message = message, Attachment = attachment)
I had no ideia how to do it, I tried df.to_csv method and include it as attachment. Did not work.
The rest of the code works without the attachment parts, but I need to attach my df to the e-mail.
Do you guys have any idea how to do it?
df.to_csv(f"Comission.csv", index=False)
to_emails = [target_email1, target_email2]
ses = boto3.client('ses')
msg = MIMEMultipart()
msg['Subject'] = 'weekly report'
msg['From'] = from_email
msg['To'] = to_emails[0]
# what a recipient sees if they don't use an email reader
msg.preamble = 'Multipart message.\n'
# the message body
part = MIMEText('Howdy -- here is the data from last week.')
msg.attach(part)
# the attachment
part = MIMEApplication(open('Comission.csv', 'rb').read())
part.add_header('Content-Disposition', 'attachment', filename='Comission.csv')
msg.attach(part)
result = ses.send_raw_email(
Source=msg['From'],
Destinations=to_emails,
RawMessage={'Data': msg.as_string()})
# and send the message
print result

Gmail content downloader/parser

I'm trying to download all my email content from gmail using a python script but this is what I found. I tried it and it's only downloading the attachments into pdf files. Is there a way to modify it to download just the email content. I'm also trying to parse just the url links in my emails.
import email
import imaplib
import os
server = 'imap.gmail.com'
user = '#gmail.com'
password = 'pass'
outputdir = 'lolz'
subject = 'Order Completed' #subject line of the emails you want to download attachments from
def connect(server, user, password):
m = imaplib.IMAP4_SSL(server)
m.login(user, password)
m.select()
return m
def downloaAttachmentsInEmail(m, emailid, outputdir):
resp, data = m.fetch(emailid, "(BODY.PEEK[])")
email_body = data[0][1]
mail = email.message_from_bytes(email_body)
if mail.get_content_maintype() != 'multipart':
return
for part in mail.walk():
if part.get_content_maintype() != 'multipart' and part.get('Content-Disposition') is not None:
open(outputdir + '/' + part.get_filename(), 'wb').write(part.get_payload(decode=True))
#download attachments from all emails with a specified subject line
def downloadAttachments(subject):
m = connect(server, user, password)
m.select("Inbox")
typ, msgs = m.search(None, '(SUBJECT "' + subject + '")')
msgs = msgs[0].split()
for emailid in msgs:
downloaAttachmentsInEmail(m, emailid, outputdir)
downloadAttachments(subject)

How do I send emails to multiple people with different body?

I want to group recipients by reason and sent out tailored emails for each reason. The data looks like this.
Email reason
0 one#outlook.com Address
1 two#outlook.com Address
2 three#outlook.com Phone Number
3 one#outlook.com Postcode
import Pandas as pd
data = { 'Email': ['one#outlook.com','two#outlook.com','three#outlook.com','one#outlook.com'],
'reason': ['Address','Address', 'Phone Number','Postcode']}
df = pd.DataFrame(data)
df
This is how I started.
for e in df['reason'].unique():
print(f"Reason: {e}")
print(df[df['reason'] == e]['Email'].to_list())
print('\n')
Reason: Address
['one#outlook.com', 'two#outlook.com']
Reason: Phone Number
['three#outlook.com']
Reason: Postcode
['one#outlook.com']
Not sure how to use this list below.
message = "Update"
email_subject = "Test"
recipients = e
# sending email function
def send_mail(text, subject, recipients):
o = win32com.client.Dispatch("Outlook.Application")
Msg = o.CreateItem(0)
Msg.to = ''
Msg.BCC = "; ".join(recipients)
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
# sending email
send_mail(message,email_subject,recipients)
How to set 3(or more) different email body's and loop around the grouped list to sent separate emails for each reason?
You need to repeat the following code in the loop:
Msg = o.CreateItem(0)
Msg.to = recipient;
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
where recipient is an email address of a single recipient.
You may find the Using Visual Basic for Applications in Outlook article helpful.

Send Outlook Emails with attachments in Python (pywin32)

I am trying to use the library pywin32 to write Outlook e-mails in Python. I have a dataframe that has some recipients' emails and attachment paths. I'd like to know how to add all attachments under each unique recipient?
print(my_df)
Receiver_Email Attachment_Path HTMLBody
john.doe#gmail.com C:/Users/Desktop/Attachment1.pdf HTMLBODY_1
john.doe#gmail.com C:/Users/Desktop/Attachment2.pdf HTMLBODY_1
john.doe#gmail.com C:/Users/Desktop/Attachment3.pdf HTMLBODY_1
sean.smith#hotmail.com C:/Users/Desktop/Attachment11.pdf HTMLBODY_2
sean.smith#hotmail.com C:/Users/Desktop/Attachment22.pdf HTMLBODY_2
sean.smith#hotmail.com C:/Users/Desktop/Attachment33.pdf HTMLBODY_2
My coding is showing below:
for index, row in my_df.iterrows():
outlook = client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = row['Receiver_Email']
mail.Subject = 'Greetings from Here!'
mail.Body = 'Please find your attachment(s)'
mail.Attachments.Add(row['Attachment_Path'])
mail.HTMLBody = row['HTMLBODY']
mail.Send()
However, this would send 3 emails to john.doe#gmail.com and 3 emails to sean.smith#hotmail.com. My expected outputs would be sending 2 emails total (1 for john and 1 for sean) with 3 attachments each.
Is there a way to do that? Thank you for the help!
Do a groupby on the email addresses, which creates unique dataframes, then iterate on the mail attachments. Something like this should work:
GB = my_df.groupby("Receiver_Email")
for ID , DF in GB:
outlook = client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = row['Receiver_Email']
mail.Subject = 'Greetings from Here!'
mail.Body = 'Please find your attachment(s)'
for id , row in DF.iterrows():
mail.Attachments.Add(row['Attachment_Path'])
mail.Send()

CC email and multiple TO email addresses not working

I've created a function in Python which will build an email in html format and send it anonymously through an Exchange server which accepts anonymous email sends.
However, no one in the CC list gets a copy and only the FIRST person in the TO list gets a copy. I've tried 'comma' and 'semicolon' as the email separator but neither works. Curiously, in the email received ALL of the email addresses can be seen (and in the correct format) despite the fact that only one person (the person listed first in the TO list) gets a copy.
This is my code
def send_email(sender, subject, sendAsDraft = True): # sendAsDraft not implemented
global toEmail
global ccEmail
# create the email message
htmlFile = open(saveFolder + '\\' + htmlReportBaseName + '.html',encoding = 'utf-8')
message = htmlFile.read().replace('\n', '')
message = message.replace(chr(8217), "'") # converts a strange single quote into the standard one
message = message.replace("'",''') # makes a single quote html friendly
htmlFile.close()
# get the email body text
emailBody = open('emailBody.txt')
bodyText = emailBody.read()
emailBody.close()
now = dt.datetime.today().replace(second = 0, microsecond = 0)
timeStr = returnReadableDate(now) # returns the date as a string in the 'normal' reading format
# insert the current date/time into the marker in the email body
bodyText = bodyText.replace('xxxx', timeStr)
# insert the email body text into the HTML
message = message.replace('xxxx', bodyText)
msg = MIMEMultipart('Report Email')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = '; '.join(toEmail)
msg['Cc'] = '; '.join(ccEmail)
msg.add_header('Content-Type','text/html')
msg.set_payload(message)
# complete the email send
message = ''
try:
smtpObj = sl.SMTP('1.2.3.4:5') # obviously IP address anonymised
smtpObj.sendmail(sender, '; '.join(toEmail), msg.as_string())
message = 'Email successfully sent'
except:
message = 'Email could not be sent due to an error'
finally:
# write the outcome to the Instance log
global InstanceLog
# write the log
log(InstanceLog, message)
# close object and exit function
smtpObj.quit()
Is it possible that the issue is at the email server? That where an un-authenticated email can only be sent to one person? That would seem strange in one way, and make sense in another.
Thanks all.
Seamie
Beware. send_message can use the headers of a message to build its recipient list, but sendmail requires a list of addresses or handle a string as a single recipicient address.
And the headers should contain comma instead of semicolons.
So you should have:
...
msg['To'] = ' '.join(toEmail)
msg['Cc'] = ','.join(ccEmail)
...
and later either:
smtpObj.sendmail(sender, toEmail + ccEmail, msg.as_string())
or:
smtpObj.send_message(msg)
Looking at the docs, this should be a comma seperated string:
msg['To'] = ', '.join(toEmail)

Categories

Resources