I am trying to append the variable to (which has an email id) to msg["To"] and send the email to this list. There is no error or anything, but the email isn't being sent. As soon as I remove the to variable from msg["To"], the email is successfully sent. Where am I going wrong?
def email (body,subject,to):
msg = MIMEText("%s" % body)
msg["Content-Type"] = "text/html"
msg["From"] = "service#company.com"
msg["To"] = to + "username#company.com"
msg["Subject"] = '%s' % subject
p = Popen(["/usr/sbin/sendmail", "-t"], stdin=PIPE)
p.communicate(msg.as_string())
The problem is that if you have an e-mail address, appending a second will just run them together.
to = "address1#example.com"
msg["To"] = to + "address2#example.com"
print msg["To"]
>>> address1#example.comaddress2#example.com
Needless to say, address1#example.comaddress2#example.com is not a valid e-mail address and any MTA is going to barf on it.
Per RFC 822 and its successors, MTAs expect commas between addresses, so:
msg["To"] = to + ", address2#example.com"
should work.
Adding to=to.strip() fixed it..
Related
Basically I'm creating a program to help with my work. It will send emails to people in an excel list and move down to the next first name and email address in the list until it's done. Heres the code so far
`#AutoMail Version 2
#Goal of new version is to run on any computer. With minimal or no mouse and keyboard input
import pandas as pd
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
#Random Variables
sender_address = str(input("Please enter your email address!: "))
sender_pass = str(input("Please enter your email password (No data is stored anywhere!): "))
count = 0
#This prompts user to input the file path of their CSV file.
file_path = "C:/Users/Spring/Documents/test_book_py.csv" #Change to input later!!!!!!
df = pd.read_csv(file_path, usecols=['First Name', 'Email Address'])
amount = int(input("How many emails would you like to send? "))
#Important Variables
cell_value = 0 #Which cell the info is coming from
#Cell Varialbes
name_cell = df["First Name"].values[cell_value]
email_cell = df["Email Address"].values[cell_value]
#Gmail info Variables
receiver_address = email_cell
email_subj = "This is a test subject"
email_body = "Hello " + name_cell + ",\n\nThis is a test body"
message = MIMEMultipart()
#Create SMTP session for sending the mail
session = smtplib.SMTP('smtp.gmail.com', 587) #use gmail with port
session.starttls() #enable security
session.login(sender_address, sender_pass) #login with mail_id and password
#Emailing Process Start
message['From'] = sender_address
message['To'] = receiver_address
message['Subject'] = email_subj
message.attach(MIMEText(email_body, 'plain'))
text = message.as_string()
#Email sending
while count < amount:
session.sendmail(sender_address, receiver_address, text)
cell_value = cell_value + 1
count = count + 1
print(cell_value)`
I've tried every fix I could find online for variables not updating. When I print the "cell_value" varible it prints with the updated value however the other lines in the code specifically lines 21 and 22 use that variable and they aren't using the updated varible so it is always at a constant 0 value when it should be cell_value + 1 every time the loop repeats. Is there a different way I should loop the variable updating? I need it to change that value by +1 every time so that it continues to move down the list. Keep in mind that I am a huge beginner so my code probably looks very confusing.
The issue is updating cell_value doesn't automatically updates all the data that was calculated with cell_value's old value. Once "Hello " + name_cell + ",\n\nThis is a test body" evaluates, for example, the resulting string has no relation to name_cell, and wan't change when name_cell changes. If you want that string to change when name_cell changes, you need to rerun the code that created that string.
For your case here, it looks like you could just loop over the latter half of the code. The closest to what you already have would be:
# i instead of cell_value for clarity
for i in range(amount):
name_cell = df["First Name"].values[cell_value]
email_cell = df["Email Address"].values[cell_value]
receiver_address = email_cell
email_subj = "This is a test subject"
email_body = "Hello " + name_cell + ",\n\nThis is a test body"
message = MIMEMultipart()
session = smtplib.SMTP('smtp.gmail.com', 587) #use gmail with port
session.starttls() #enable security
session.login(sender_address, sender_pass) #login with mail_id and password
message['From'] = sender_address
message['To'] = receiver_address
message['Subject'] = email_subj
message.attach(MIMEText(email_body, 'plain'))
text = message.as_string()
session.sendmail(sender_address, receiver_address, text)
Arguably, it would be may be considered more idiomatic to zip the two .values objects that you're looping over, then islice amount-many elements from that, but I think this is cleaner.
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!
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)
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.
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 )