Boto SES - send_raw_email() to multiple recipients - python

I'm having big time problems with this issue-- another question on SO that didn't solve it is here: Send Raw Email (with attachment) to Multiple Recipients
My code (that works) is simple:
def send_amazon_email_with_attachment(html, subject, now, pre):
dummy = 'test#example.com'
recipients = ['test1#exampl.ecom', 'test2#example.com', 'test3#example.com']
connS3 = S3Connection('IDENTIFICATION','PASSWORD')
b = connS3.get_bucket('BUCKET_NAME')
key = b.get_key('FILE_NAME.pdf')
temp = key.get_contents_as_string()
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = 'My Name <test#example.com>'
msg.preamble = 'Multipart message.\n'
part1 = MIMEText(u"Attached is the report", 'plain')
part2 = MIMEText(html, 'html')
msg.attach(part1)
msg.attach(part2)
part = MIMEApplication(temp) #read binary
part.add_header('Content-Disposition', 'attachment', filename='FILE_NAME.pdf')
msg.attach(part)
conn = boto.ses.connect_to_region('us-east-1', aws_access_key_id='AUTH_ID', aws_secret_access_key='AUTH_PW')
for recipient in recipients:
print recipient
msg['To'] = recipient
result = conn.send_raw_email(msg.as_string(), source=msg['From'], destinations=recipient)
but, there's a caveat... this is looping for each recipient. Any variation of this does not work. Passing a list to msg['Bcc'] or msg['BCC'] will return an error that the list can't be stripped (same exact error as the posted question). Passing a string separated by commas gives an Amazon SES issue saying 'Illegal Email' in the returned XML. Because I only get an error from Amazon in specific situations, I'm led to believe this is an error with the program before it hits their API call.
Any MIMEMultipart experts have some ideas?

Basically you need to specify the email recipients in 2 different places using 2 different formats.
def send_amazon_email_with_attachment(html, subject, now, pre):
dummy = 'test#example.com'
recipients = ['test1#exampl.ecom', 'test2#example.com', 'test3#example.com']
connS3 = S3Connection('IDENTIFICATION','PASSWORD')
b = connS3.get_bucket('BUCKET_NAME')
key = b.get_key('FILE_NAME.pdf')
temp = key.get_contents_as_string()
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = 'My Name <test#example.com>'
msg['To'] = ', '.join(recipients)
msg.preamble = 'Multipart message.\n'
part1 = MIMEText(u"Attached is the report", 'plain')
part2 = MIMEText(html, 'html')
msg.attach(part1)
msg.attach(part2)
part = MIMEApplication(temp) #read binary
part.add_header('Content-Disposition', 'attachment', filename='FILE_NAME.pdf')
msg.attach(part)
conn = boto.ses.connect_to_region('us-east-1', aws_access_key_id='AUTH_ID', aws_secret_access_key='AUTH_PW')
result = conn.send_raw_email(msg.as_string(), source=msg['From'], destinations=recipients)

msg['To'] = ', '.join(recipients) isn't working for me (throws encoding error). Simply comment that line and your 'destinations' in "send_raw_email" should contain a list. That will work like a charm.

Related

Python: send email to multiple addresses

I have four python functions that I am using to send mail. If the program does one thing, it mails a set of results to a multiple recipients, if it does another thing, a separate function mails the results to one recipient.
def smail(to,sub,body):
addr_from = 'alert#example.com'
msg = MIMEMultipart()
msg['From'] = addr_from
msg['To'] = to
msg['Subject'] = sub
msg.attach(body)
s = smtplib.SMTP('webmail.example.com')
s.sendmail(addr_from, [to], msg.as_string())
s.quit()
def email_format2(results):
text = ""
text += 'The following applications in <environment> are non-compliant\n'
text += '<br>'
text += 'Here are there names and locations. Please inform the developer.'
text += '<br>'
text += '<br>'
table = pd.DataFrame.from_records(results)
table_str = table.to_html()
text += "%s" % table_str
return text
def mail_results_aq(results):
body = email_format2(results)
msg = MIMEText(body, 'html')
sub = "Placeholder subject"
to = 'email1#example.com, email2#example.com, email3#example.com'
smail(to, sub, msg)
def mail_results_prd(results):
body = email_format2(results)
msg = MIMEText(body, 'html')
sub = "Placeholder subject"
to = 'email4#example.com'
smail(to, sub, msg)
The mail_results_aq function will only email results to the first recipient (email1#example.com).
In other questions similar to this one, I've seen a the recipients being entered into a list i.e
import smtplib
from email.mime.text import MIMEText
s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me#example.com'
recipients = ['john.doe#example.com', 'john.smith#example.co.uk']
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
s.sendmail(sender, recipients, msg.as_string())
However, this use case only seems to work when used in the same function. How can I implement this feature across the four functions I have above?
Thanks in advance!

BCC not hidden on gmail using python smtplib

I am working on a python script to send email to my customer with a survey. I will send only one email with all my customers's emails in the BCC field so that I do not need to loop through all the emails. Everything works fine when I tested sending emails to my company's coleagues and also when I sent to my personal email, but whenever I send to a gmail account, the BCC field appears to not be hidden and show all the emails. I found this post Email Bcc recipients not hidden using Python smtplib and tried that solution as well, but as I am using a html body email, the emails were shown inside the body. Can anyone help me on this one?
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
def send_survey_mail():
template_path = 'template.html'
background_path = 'image.png'
button_path = 'image2.png'
try:
body = open(template_path, 'r')
msg = MIMEMultipart()
msg['Subject'] = 'Customer Survey'
msg['To'] = ', '.join(['myemail#domain.com.br', 'myemail2#domain.com'])
msg['From'] = 'mycompany#mycompany.com.br'
msg['Bcc'] = 'customer#domain.com'
text = MIMEText(body.read(), 'html')
msg.attach(text)
fp = open(background_path, 'rb')
img = MIMEImage(fp.read())
fp.close()
fp2 = open(button_path, 'rb')
img2 = MIMEImage(fp2.read())
fp2.close()
img.add_header('Content-ID', '<image1>')
msg.attach(img)
img2.add_header('Content-ID', '<image2>')
msg.attach(img2)
s = smtplib.SMTP('smtpserver')
s.sendmail('mycompany#mycompany.com.br',
['myemail#domain.com.br', 'myemail2#domain.com', 'customer#domain.com'],
msg.as_string())
s.quit()
except Exception as ex:
raise ex
send_survey_mail()
I removed the following line from the code and tried again. Now the email is not sent to my customer's Gmail email.
msg['Bcc'] = 'customer#gmail.com'
Just do not mention bcc mails in msg['To'] or msg['Cc']. Do it only in server.sendmail()
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from_addr = "your#mail.com"
to_addr = ["to#mail.com", "to2#mail.com"]
msg = MIMEMultipart()
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = "SUBJECT"
body = "BODY"
msg.attach(MIMEText(body, 'plain'))
filename = "FILE.pdf"
attachment = open('/home/FILE.pdf', "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
server = smtplib.SMTP('.....', 587)
server.starttls()
server.login(from_addr, 'yourpass')
text = msg.as_string()
server.sendmail(from_addr, to_addr + [bcc#mail.com], text)
server.quit()
Did you try not to define the msg['BCC'] field? Setting this field forces it to be included. It is sufficient that the BCC email address is in the sendmail command's destination address list. Take a look at this question.
MAIL_FROM = default#server.com
MAIL_DL = default#server.com
def send(to, cc, bcc, subject, text, html):
message = MIMEMultipart("alternative")
message["Subject"] = subject
message["From"] = MAIL_FROM
message["To"] = to
message["Cc"] = cc + "," + MAIL_DL
if html is not None:
body = MIMEText(html, "html")
else:
body = MIMEText(text)
message.attach(body)
server = smtplib.SMTP(MAIL_SERVER)
server.set_debuglevel(1)
server.sendmail(MAIL_DL, to.split(",") + bcc.split(","), message.as_string())
server.quit()
return {
"to": to,
"cc": cc,
"bcc": bcc,
"subject": subject,
"text": text,
"html": html,
}

Mail is going out 3 times instead of once to each address?

I have come up with something which seems weird to me and I am not sure what exactly to search for.
import smtplib, openpyxl, sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
wb = openpyxl.load_workbook('Book1.xlsx')
sheet = wb.get_sheet_by_name('Sheet1')
lastCol = sheet.max_column
latestMonth = sheet.cell(row=1, column=lastCol).value
recipients = []
unpaidMembers = {}
for r in range(2, sheet.max_row + 1):
payment = sheet.cell(row=r, column=lastCol).value
if payment != 'Y':
name = sheet.cell(row=r, column=1).value
email = sheet.cell(row=r, column=2).value
unpaidMembers[name] = email
recipients.append(email)
fromaddr = "xxxx#xxxx.com"
for n in recipients:
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = n
msg['Subject'] = "Hi"
body = "This is a test mail"
msg.attach(MIMEText(body, 'plain'))
filename = "xxx.pdf"
attachment = open("\\Users\xxx\xxx\xxx.pdf","rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
smtp0bj = smtplib.SMTP('smtp-mail.outlook.com', 587)
smtp0bj.ehlo()
smtp0bj.starttls()
smtp0bj.login(fromaddr, 'xxxxx')
text = msg.as_string()
smtp0bj.sendmail(fromaddr, recipients, text)
smtp0bj.quit()
I am guessing that the for-loop is being executed 3 times(there are 3 items in the list. I am at a loss about how to make it execute only once.
You basically do that :
for n in recipients: # for each recipient
smtp0bj.sendmail(fromaddr, recipients, text) # send mail to all recipients
So at each pass it sends the mail to all the recipients.
So if you have 3 recipients, they'll receive 3 mails each.
Replace with :
smtp0bj.sendmail(fromaddr, n, text)
Also I'm not sure, and can't test right now, but I believe 'to' has to be a list.
So if the above solution doesn't work, give this a shot :
smtp0bj.sendmail(fromaddr, [n], text)

smtplib: Why do the recipients in "To" field receive the mail twice?

I searched about this quite a lot, but could not fix the issue in my script. So finally, I decided to post it here.
Here's the code snippet:
fromaddr = "someValidAddress#xyz.com"
cc = ['SomeEmailAlias#xyz.com']
toaddr = ""
msg = MIMEMultipart()
toaddrlist = list(toaddr.split(',')) #As sendmail() accepts the list of recipients only in list form.
toaddrlist += (cc,)
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Cc'] = ', '.join(cc)
msg['Date'] = formatdate(localtime=True)
msgHtml = MIMEText(html, 'html')
msg.attach(msgHtml)
msg['Subject'] = "Test mail"
server = "someMailServer.xyz.com"
smtp = smtplib.SMTP(server, 25)
smtp.sendmail(fromaddr, toaddrlist, msg.as_string())
smtp.close() #Close the SMTP server connection.
I'm aware and I've ensured that msg['To'] accepts a string value (toaddr), whereas toaddrlist in sendmail() should be a list.
Catch: If I remove the line toaddrlist += (cc,), then the mail does not get delivered twice to the recipients in "To" field, but the mail does not get delivered to the Cc alias.
Please help.
When the line toaddrlist += (cc,) is evaluated, the value of toaddrlist in your case is :
["", ["SomeEmailAlias#xyz.com"]]
and it's wrong because toaddrlist must be a list of strings not a list containing some lists.
So the solution is to change :
toaddrlist += (cc,)
to
toaddrlist += cc
or the recommended form (the pythonic way) :
toaddrlist.extend(cc)

Send Raw Email (with attachment) to Multiple Recipients

I am currently using Python 2.7 and trying to send a raw email with an attachment (CSV to be exact) to multiple addresses with Boto SES. I can send a normal email with send_email(), but I keep getting an error when trying to send to multiple people via send_raw_email().
This is the error that I get with a comma-separated string of recipients:
Error sending email: SESIllegalAddressError: 400 Illegal address
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<Error>
<Type>Sender</Type>
<Code>InvalidParameterValue</Code>
<Message>Illegal address</Message>
</Error>
<RequestId>[the request ID]</RequestId>
</ErrorResponse>
That's from using this code:
to_emails = "me#example.com, them#example.com"
# create raw email
msg = MIMEMultipart()
msg['Subject'] = 'Email subject'
msg['From'] = 'me#example.com'
msg['To'] = to_emails
part = MIMEText('Attached is an important CSV')
msg.attach(part)
part = MIMEApplication(open(fname, 'rb').read())
part.add_header('Content-Disposition', 'attachment', filename=fname)
msg.attach(part)
# end create raw email
conn = boto.ses.connect_to_region(
'[my region]',
aws_access_key_id=s3_access_key,
aws_secret_access_key=s3_secret_key
)
conn.send_raw_email(msg.as_string(),
source=msg['From'],
destinations=msg['To']
)
Also, here is the error I get from using an array of strings for recipients:
Error sending email: 'list' object has no attribute 'lstrip'
It works fine if I have just one recipient, so it just throws the error when I have an array of recipients and a comma-separated string of recipients. Anyone have any experience with this?
I ended it getting it after looking at some docs and some more trial & error. It turns out that I just had to join the array of email strings for the msg['To'], and then I was able to pass in the email array for destinations parameter.
Here's what I did:
to_emails = "me#example.com, them#example.com"
COMMASPACE = ', '
# create raw email
msg = MIMEMultipart()
msg['Subject'] = 'Email subject'
msg['From'] = 'me#example.com'
msg['To'] = COMMASPACE.join(to_emails) ## joined the array of email strings
# edit: didn't end up using this ^
part = MIMEText('Attached is an important CSV')
msg.attach(part)
part = MIMEApplication(open(fname, 'rb').read())
part.add_header('Content-Disposition', 'attachment', filename=fname)
msg.attach(part)
# end create raw email
conn = boto.ses.connect_to_region(
'[my region]',
aws_access_key_id=s3_access_key,
aws_secret_access_key=s3_secret_key
)
conn.send_raw_email(msg.as_string(),
source=msg['From'],
destinations=to_emails ## passed in an array
)
I believe instead of a comma separated string with your recipients you have have to use a a list of strings.
Recipients = ['1#email.com', '2#email.com']
conn.send_raw_email(msg.as_string(),
source=msg['From'],
destinations= Recipients)
So something along those lines.
Source: http://boto.readthedocs.org/en/latest/ref/ses.html?highlight=send_raw_email#boto.ses.connection.SESConnection.send_raw_email
The official documentation says a list of strings or simply a string. This is why it works with only one recipient.
Second attempt::
to_emails = ['me#example.com', 'them#example.com']
# create raw email
msg = MIMEMultipart()
msg['Subject'] = 'Email subject'
msg['From'] = 'me#example.com'
msg['To'] = to_emails
conn.send_raw_email(msg.as_string(),
source=msg['From'],
destinations=msg['To'])
Am I right in assuming your code now looks like this? If not, try this.
The solution is set a string separated by comma to the header and a list to destinations field.
Something like:
to_emails = ['me#example.com', 'them#example.com']
msg['To'] = ', '.join( to_emails )
and
...
conn.send_raw_email(msg.as_string(),
source=msg['From'],
destinations=to_emails ## passed in an array
)
When sending without attachment just assigning list works. But in other case the below code helped..Thanks #Ezequiel Salas
to_emails = ['me#example.com', 'them#example.com']
or
to_emails = some_list
msg['To'] = ', '.join( to_emails )

Categories