I have a code which sends an email that uses HTML for templating.
All of the recipients get the message, but all of them are BCC.
def sendMail(to, cc, bcc, template, bodyParams, subjectParams):
global connection
if not testCurrentConnection(connection):
connect(currentConnectionName)
msg = MIMEMultipart('alternative')
subject, fromEmail, body = templateController.readTemplate(template)
header = 'To: ' + to + '\n' + 'Cc: ' + cc + 'From: ' + fromEmail + '\n' + 'Subject: ' + subject + '\n'
msg.attach(MIMEText(header, 'text'))
if subjectParams:
msg['Subject'] = templateController.replaceTextWithParams(subject, subjectParams)
else:
msg['Subject'] = subject
if bodyParams:
msg['Body'] = templateController.replaceTextWithParams(body, bodyParams)
else:
msg['Body'] = body
msg['From'] = fromEmail
msg['To'] = to
msg['Cc'] = cc
# no need to specify bcc here
msg.attach(MIMEText(msg['Body'], 'html'))
connection.sendmail(msg['From'], [msg['To'], msg['Cc'], bcc], msg.as_string())
del msg
I'm calling the function like this:
smtpController.sendMail("myMail#gmail.com", "ccMail#gmail.com", "", "email.html", None, None)
(The last two variables are actually a dict with key-value mapping used to populate the HTML, but the problem reproduces without them)
I read that I need to add header to my message to prevent it but for some reason, adding the header doesn't change anything (lines 7-8 in the above code).
What am I missing?
OK, I don't know how it makes a difference, but I fixed it by moving msg['To'] = to and msg['Cc'] = cc up, before Subject and Body. I completely removed the header.
def sendMail(to, cc, bcc, template, bodyParams, subjectParams):
global connection
if not testCurrentConnection(connection):
connect(currentConnectionName)
subject, fromEmail, body = templateController.readTemplate(template)
msg = MIMEMultipart('alternative')
msg['From'] = fromEmail
msg['To'] = to
msg['Cc'] = cc
if subjectParams:
msg['Subject'] = templateController.replaceTextWithParams(subject, subjectParams)
else:
msg['Subject'] = subject
if bodyParams:
msg['Body'] = templateController.replaceTextWithParams(body, bodyParams)
else:
msg['Body'] = body
msg.attach(MIMEText(msg['Body'], 'html'))
connection.sendmail(msg['From'], to.split(',') + cc.split(',') + bcc.split(','), msg.as_string())
del msg
Related
I'm wondering which approach is better for sending mail with different body using Python:
send each mail using separate functions
using one function and select body message with if-else statement
First case:
FROM = *from_email_address*
def send_mail_notify():
SUBJECT = *some_subject_for_notification_event*
TEXT = *any_text*
msg = EmailMessage()
msg['From'] = FROM
msg['To'] = *to_email_address*
msg['Subject'] = SUBJECT
msg.set_content(TEXT)
...(initialize connection to mail server, etc.)
FROM = *from_email_address*
def send_mail_error():
SUBJECT = *some_subject_for_error_event*
TEXT = *any_text*
msg = EmailMessage()
msg['From'] = FROM
msg['To'] = *email_address*
msg['Subject'] = SUBJECT
msg.set_content(TEXT)
...(initialize connection to mail server, etc.)
Second case:
FROM = *from_email_address*
def send_mail(param):
if param == "notify":
SUBJECT = *some_subject_for_notification_event*
TEXT = *any_text*
elif param == "error":
SUBJECT = *some_subject_for_error_event*
TEXT = *any_text*
msg = EmailMessage()
msg['From'] = FROM
msg['To'] = *email_address*
msg['Subject'] = SUBJECT
msg.set_content(TEXT)
...(initialize connection to mail server, etc.)
You can create an unlimited functions for different types of subject and text and you don't need to check the condition with if-else statement:
FROM = *from_email_address*
def notify():
return {"SUBJECT":"notification", "TEXT": "new message"}
def error():
return {"SUBJECT":"error: message dont send", "TEXT": "error message text"}
def send_mail(param):
msg = EmailMessage()
msg['From'] = FROM
msg['To'] = *email_address *
msg['Subject'] = globals().get(param)()["SUBJECT"]
msg.set_content(globals().get(param)()["TEXT"])
The param value must be the same as the name of the function.
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)
I am trying to send screenshots along with an email message..
The message gets through fine.
In Windows Live mail it has the attachment icon. but no attachments are there.
Online in outlook it has no attachment..
msg = MIMEMultipart('alternative')
msg['Subject'] = client_name + " eBay Template " + date
msg['From'] = sender_address
msg['To'] = recipients_address
msg.preamble = 'images'
...
# attach screenshot
iways_filename = dictstr['ItemID'] + "_i-ways" + '.png'
ebay_filename = dictstr['ItemID'] + "_ebay" + '.png'
# iways
img_data = open(iways_filename, 'rb').read()
image = MIMEImage(img_data, name=os.path.basename(iways_filename))
msg.attach(image)
#ebay
img_data2 = open(ebay_filename, 'rb').read()
image = MIMEImage(img_data2, name=os.path.basename(ebay_filename))
msg.attach(image)
I get no errors..
I found the solution..
msg = MIMEMultipart('alternative')
msg['Subject'] = client_name + " eBay Template " + date
msg['From'] = sender_address
msg['To'] = recipients_address
msg.preamble = 'images'
Take away the 'alternative' and Voila!
msg = MIMEMultipart()
msg['Subject'] = client_name + " eBay Template " + date
msg['From'] = sender_address
msg['To'] = recipients_address
msg.preamble = 'images'
I am sending email with my raspberrypi with python.
I successfully sent and received the message, but the content is missing.
Here is my code
import smtplib
smtpUser = 'myemail#gmail.com'
smtpPass = 'mypassword'
toAdd = 'target#gmail.com'
fromAdd = smtpUser
subject = 'Python Test'
header = 'To: ' + toAdd + '\n' + 'From: ' + fromAdd + '\n' + 'Subject: ' + subject
body = 'From within a Python script'
msg = header + '\n' + body
print header + '\n' + body
s = smtplib.SMTP('smtp.gmail.com',587)
s.ehlo()
s.starttls()
s.ehlo()
s.login(smtpUser,smtpPass)
s.sendmail(fromAdd, toAdd, msg)
s.quit()
Thank you for your attention!
I wrote a wrapper around sending emails in python; it makes sending emails super easy: https://github.com/krystofl/krystof-utils/blob/master/python/krystof_email_wrapper.py
Use it like so:
emailer = Krystof_email_wrapper()
email_body = 'Hello, <br /><br />' \
'This is the body of the email.'
emailer.create_email('sender#example.com', 'Sender Name',
['recipient#example.com'],
email_body,
'Email subject!',
attachments = ['filename.txt'])
cred = { 'email': 'sender#example.com', 'password': 'VERYSECURE' }
emailer.send_email(login_dict = cred, force_send = True)
You can also look at the source code to find how it works.
The relevant bits:
import email
import smtplib # sending of the emails
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
self.msg = MIMEMultipart()
self.msg.preamble = "This is a multi-part message in MIME format."
# set the sender
author = email.utils.formataddr((from_name, from_email))
self.msg['From'] = author
# set recipients (from list of email address strings)
self.msg['To' ] = ', '.join(to_emails)
self.msg['Cc' ] = ', '.join(cc_emails)
self.msg['Bcc'] = ', '.join(bcc_emails)
# set the subject
self.msg['Subject'] = subject
# set the body
msg_text = MIMEText(body.encode('utf-8'), 'html', _charset='utf-8')
self.msg.attach(msg_text)
# send the email
session = smtplib.SMTP('smtp.gmail.com', 587)
session.ehlo()
session.starttls()
session.login(login_dict['email'], login_dict['password'])
session.send_message(self.msg)
session.quit()
Why can't I send emails to multiple recipients with this script?
I get no errors nor bouncebacks, and the first recipient does receive the email. None of the others do.
The script:
#!/usr/bin/python
import smtplib
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
recipient = 'email#domain.com; email2#domain.com;'
sender = 'me#gmail.com'
subject = 'the subject'
body = 'the body'
password = "password"
username = "me#gmail.com"
body = "" + body + ""
headers = ["From: " + sender,
"Subject: " + subject,
"To: " + recipient,
"MIME-Version: 1.0",
"Content-Type: text/html"]
headers = "\r\n".join(headers)
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo
session.login(username, password)
session.sendmail(sender, recipient, headers + "\r\n\r\n" + body)
session.quit()
Semicolons are not the correct separator for addresses in recipient headers. You must use commas.
EDIT: I see now that you are using the library incorrectly. You're supplying a string which will always be interpreted as a single address. You must supply a list of addresses to send to multiple recipients.
Recipients in the standard header must be separated by commas, not semicolons. I blame Microsoft Outlook for leading people to believe otherwise.
You can, just put the emails in an array, and loop through the array for each email as in:
(my python is rusty... so forgive my syntax)
foreach recipient in recipients
headers = ["From: " + sender, "Subject: " + subject, "To: " + recipient, "MIME-Version: 1.0", "Content-Type: text/html"]
headers = "\r\n".join(headers)
session.sendmail(sender, recipient, headers + "\r\n\r\n" + body)
Change this in your code:
recipient = ['email#domain.com','email2#domain.com']
headers = ",".join(headers)
session.sendmail(sender, recipient.split(","), headers + "\r\n\r\n" + body)
Alternatively
recipient = ', '.join(recipient.split('; '))
if your recipients are a string of semicolon separated addresses.