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

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)

Related

Send gmail with multiple recipients

I am trying to create a bulk email sender. I am struggling to add multiple recipients. I want the script to open contacts.csv that contains multiple email addresses, and send the email to them.
Simply, I need help creating a script that opens the csv file, reads the emails and sends the email to those addresses.
I have tried this but it did not work:
with smtplib.SMTP("smtp.gmail.com:587") as server:
with open("contacts.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name, email in reader:
server.sendmail(
sender_email,
email,text
#message.format(name=name),
)
my contacts.csv file:
name, email
john, test1#gmail.com
jake, test2#gmail.com
my emailsender.py file:
import smtplib
from email import encoders
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
fromaddr = "myemail#gmail.com"
toaddr = "emails#gmail.com"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "test"
body = "testing"
msg.attach(MIMEText(body, 'plain'))
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login(fromaddr, "password")
text = msg.as_string()
s.sendmail(fromaddr, toaddr, text)
print("Email sent!")
s.quit()
send()
I have tested this code and it works fine:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import csv
emails = []
with open('test.csv', 'r') as file: #open csv file
reader = csv.reader(file) #init the csv reader
for row in reader: #iterate through all rows in the csv file
emails.append(row[1]) #add the second element of every row (the email column) to the list "emails"
emails.pop(0) #remove the first email which is just the text "email" in the csv file that we don't want
print(emails) #remove if you wish
def sender(recipients, from_email, password, body, subj): #create a sender function. partially adapted from https://stackoverflow.com/a/51931160/8402369
msg = MIMEMultipart()
msg['Subject'] = subj
msg['From'] = from_email
msg['To'] = (', ').join(recipients)
msg.attach(MIMEText(body,'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(from_email, password)
server.send_message(msg)
server.quit()
sender(emails, 'youremail#gmail.com', 'your_password', 'Message body', 'Subject') #send all emails
Run and edit the code online
EDIT:
Here's a version with the ability to change the email content based on the csv data:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import csv
csvdata = []
with open('test.csv', 'r') as file: #open csv file
reader = csv.reader(file) #init the csv reader
for row in reader: #iterate through all rows in the csv file
csvdata.append(
row #instead of adding the email to the array, add the whole row to the array
) #add the second element of every row (the email column) to the list "emails"
csvdata.pop(0)
def sender(
recipient, name_to_display, from_email, password, body, subj
): #create a sender function. partially adapted from https://stackoverflow.com/a/51931160/8402369
msg = MIMEMultipart()
msg['Subject'] = subj
msg['From'] = f'{name_to_display} <{from_email}>'
msg['To'] = recipient
msg.attach(MIMEText(body, 'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(from_email, password)
server.send_message(msg)
server.quit()
your_email = "email#gmail.com"
your_password = "password"
name_to_display = "The name of the sender that displays in the inbox (your name or whatever you want)"
for item in csvdata:
name = item[0] #the first column of the csv file is the name
email = item[1] #the second is the email. if you have more columns get them here like so: item[2] (change the index accordingly)
print(f'Sending email to {email}')
body = "Hi " + name + ", The rest of the content goes here"
subject = "Subject"
sender(email, name_to_display, your_email, your_password, body, subject)
Run and edit this code online
Notes:
The sender function is partially adapted from here

python email multiple receiver, multiple attachment for error

def mail():
import os
import pandas as pd
import smtplib
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email.mime.multipart import MIMEMultipart
from email import encoders
from PyQt5.QtCore import QDate, Qt
path = 'C:/Users/user/Desktop/pdf/'
contact = 'con1.xlsx'
df = pd.read_excel(str(path)+contact, endcoding ='utf8')
df.set_index('unyong', inplace = True)
now = QDate.currentDate()
filenm = [f for f in os.listdir(path) if f.endswith('.pdf')]
unyong_nm = []
for w in filenm:
a = w.find('_')
b = w.rfind('_')
unyong_nm.append(w[a+1:b])
unyong_nm = list(set(unyong_nm))
for i in range(0,len(unyong_nm)):
send_from = 'ss#ddd'
recipients = df.loc[unyong_nm[i],'email']
send_to = ",".join(recipients)
attach = [s for s in filenm if s.find(unyong_nm[i]) >-1 ]
username = 'sss#ssss'
password = 'sss'
subject = ('111'+now.toString('yyyy.MM')+'_'+unyong_nm[i]+str(i+1))
text = ('hi')
msg = MIMEMultipart()
msg['From'] = send_from
msg['To']= send_to
msg['Subject'] = subject
msg['Date']=formatdate(localtime=True)
filename_match = [s for s in filenm if s.find(unyong_nm[i]) >-1 ]
for file in filename_match:
part =MIMEBase('application','octet-stream')
part.set_payload(open(str(path)+file, 'rb').read())
encoders.encode_base64(part)
part.add_header('Content-Disposition','attachment', filename =file)
msg.attach(part)
msg.attach(MIMEText(text))
mailServer = smtplib.SMTP("smtp.sssss.com", 587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login(username,password)
mailServer.sendmail(send_from, send_to, msg.as_string())
mailServer.close()
hi i have a problem with the email with attachment for statement.
the results of below def mail(),
multiple emails was sent to one person. (<- this is a error)
I want to send a each specific reciever with specific multiple attachments only once
why multiple email sented with diffrent number of attachements to one person.
the reciever have the 2 emails with a 1 attachment and, simultaneouly 2 attachment.
I want to send a email containg 2 attachments
please help me.
*CF) path containg thoes files:
['2221_sss_love.pdf', '2221_sss_happy.pdf', '2221_ddd_sad.pdf', '2221_ddd_lucky.pdf', 'con1.xlsx']
*result
unyong_nm = ['sss','ddd']
filenm = ['2221_sss_love.pdf', '2221_sss_happy.pdf', '2221_ddd_sad.pdf', '2221_ddd_lucky.pdf']
*CF) con1.xlsx file contenxt:
unyong email
sss 111#aaa
sss 777#bbb
ddd 666#sss
ddd 444#ccc
The code is sending an email for each attachment. By dedenting the mail-sending code, an email will be sent for each set of attachments grouped by 'ddd' or 'sss'.
for file in filename_match:
part = MIMEBase("application", "octet-stream")
part.set_payload(open(file, "rb").read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", "attachment", filename=file)
msg.attach(part)
msg.attach(MIMEText(text))
# Send mail outside the file grouping loop
mailServer = smtplib.SMTP("localhost", 1025)
mailServer.ehlo()
mailServer.sendmail(send_from, send_to, msg.as_string())
mailServer.close()
The recipient selection code
recipients = df.loc[unyong_nm[i],'email']
send_to = ",".join(recipients)
might need to be changed, I can't tell because the contents of the dataframe aren't provided in the question.

Automating email delivery

I took an example of automation from a book (Automate tasks with Python) which consists of opening and reading a spreadsheet and checking if the fee has been paid, if not, send an email to the client informing him. But when I run the code it doesn't show any error, but also, nothing happens. I would appreciate it if you could help me, and still recommend a library to carry out the process, if necessary.
Follow the code below:
import openpyxl, smtplib, sys
wb = openpyxl.load_workbook('C:/temp/cobranca.xlsx')
sheet = wb['Sheet1']
lastCol = sheet.max_column
latestMonth = sheet.cell(row=1, column=lastCol).value
unpaidMembers = {}
for r in range(2, sheet.max_row + 1):
payment = sheet.cell(row=r, column=lastCol).value
if payment != 'ok':
name = sheet.cell(row=r, column=1).value
email = sheet.cell(row=r, column=2).value
unpaidMembers[name] = email
smtpObj = smtplib.SMTP('mail.omnia.net.br', 465)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.login('dp.contabil#omnia.net.br', sys.argv[1])
for name, email in unpaidMembers.items():
body = "Subject: %s dues unpaid. \n Dear %s, \n Records show that you have not paid dues for %s. Please make this payment as soon as possible. Thank you!'" % (latestMonth, name, latestMonth)
print('Sending email to %s...' % email)
sendmailStatus = smtpObj.sendmail('dp.contabil#omnia.net.br', email, body)
if sendmailStatus != {}:
print('There was a problem sendind email to %s: %s' % (email, sendmailStatus))
smtpObj.quit()
Here is an example which I used:
import smtplib
from string import Template
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
MY_ADDRESS = 'XYZ#gmail.com'
PASSWORD = 'YourPassword'
def get_contacts(filename):
names = []
emails = []
with open(filename, mode='r', encoding='utf-8') as contacts_file:
for a_contact in contacts_file:
names.append(a_contact.split()[0])
emails.append(a_contact.split()[1])
return names, emails
def read_template(filename):
with open(filename, 'r', encoding='utf-8') as template_file:
template_file_content = template_file.read()
return Template(template_file_content)
def main():
names, emails = get_contacts('C:/Users/xyz/Desktop/mycontacts.txt') # read contacts
message_template = read_template('C:/Users/xyz/Desktop/message.txt')
s = smtplib.SMTP(host='smtp.gmail.com', port=587)
s.starttls()
s.login(MY_ADDRESS, PASSWORD)
for name, email in zip(names, emails):
msg = MIMEMultipart() # create a message
message = message_template.substitute(PERSON_NAME=name.title())
print(message)
msg['From'] = MY_ADDRESS
msg['To'] = email
msg['Subject'] = "Sending mail to all"
msg.attach(MIMEText(message, 'plain'))
s.send_message(msg)
del msg
s.quit()
if __name__ == '__main__':
main()

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,
}

Python SMTP/MIME Message body

I've been working on this for 2 days now and managed to get this script with a pcapng file attached to send but I cannot seem to make the message body appear in the email.
import smtplib
import base64
import ConfigParser
#from email.MIMEapplication import MIMEApplication
#from email.MIMEmultipart import MIMEMultipart
#from email.MIMEtext import MIMEText
#from email.utils import COMMASPACE, formatdate
Config = ConfigParser.ConfigParser()
Config.read('mailsend.ini')
filename = "test.pcapng"
fo = open(filename, "rb")
filecontent = fo.read()
encoded_content = base64.b64encode(filecontent) # base 64
sender = 'notareal#email.com' # raw_input("Sender: ")
receiver = 'someother#fakeemail.com' # raw_input("Recipient: ")
marker = raw_input("Please input a unique set of numbers that will not be found elsewhere in the message, ie- roll face: ")
body ="""
This is a test email to send an attachment.
"""
# Define the main headers
header = """ From: From Person <notareal#email.com>
To: To Person <someother#fakeemail.com>
Subject: Sending Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)
# Define message action
message_action = """Content-Type: text/plain
Content-Transfer-Encoding:8bit
%s
--%s
""" % (body, marker)
# Define the attachment section
message_attachment = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s
%s
--%s--
""" % (filename, filename, encoded_content, marker)
message = header + message_action + message_attachment
try:
smtpObj = smtplib.SMTP('smtp.gmail.com')
smtpObj.sendmail(sender, receiver, message)
print "Successfully sent email!"
except Exception:
print "Error: unable to send email"
My goal is to ultimately have this script send an email after reading the parameters from a config file and attach a pcapng file along with some other text data describing the wireshark event. The email is not showing the body of the message when sent. The pcapng file is just a test file full of fake ips and subnets for now. Where have I gone wrong with the message body?
def mail_man():
if ms == 'Y' or ms == 'y' and ms_maxattach <= int(smtp.esmtp_features['size']):
fromaddr = [ms_from]
toaddr = [ms_sendto]
cc = [ms_cc]
bcc = [ms_bcc]
msg = MIMEMultipart()
body = "\nYou're captured event is attached. \nThis is an automated email generated by Dumpcap.py"
msg.attach("From: %s\r\n" % fromaddr
+ "To: %s\r\n" % toaddr
+ "CC: %s\r\n" % ",".join(cc)
+ "Subject: %s\r\n" % ms_subject
+ "X-Priority = %s\r\n" % ms_importance
+ "\r\n"
+ "%s\r\n" % body
+ "%s\r\n" % ms_pm)
toaddrs = [toaddr] + cc + bcc
msg.attach(MIMEText(body, 'plain'))
filename = "dcdflts.cong"
attachment = open(filename, "rb")
if ms_attach == 'y' or ms_attach == "Y":
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(ms_smtp_server[ms_smtp_port])
server.starttls()
server.login(fromaddr, "YOURPASSWORD")
text = msg.as_string()
server.sendmail(fromaddr, toaddrs, text)
server.quit()
This is my second attempt, all "ms_..." variables are global through a larger program.
You shouldn't be reinventing the wheel. Use the mime modules Python has included in the standard library instead of trying to create the headers on your own. I haven't been able to test it out but check if this works:
import smtplib
import base64
import ConfigParser
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
filename = "test.pcapng"
with open(filename, 'rb') as fo:
filecontent = fo.read()
encoded_content = base64.b64encode(filecontent)
sender = 'notareal#email.com' # raw_input("Sender: ")
receiver = 'someother#fakeemail.com' # raw_input("Recipient: ")
marker = raw_input("Please input a unique set of numbers that will not be found elsewhere in the message, ie- roll face: ")
body ="""
This is a test email to send an attachment.
"""
message = MIMEMultipart(
From=sender,
To=receiver,
Subject='Sending Attachment')
message.attach(MIMEText(body)) # body of the email
message.attach(MIMEApplication(
encoded_content,
Content_Disposition='attachment; filename="{0}"'.format(filename)) # b64 encoded file
)
try:
smtpObj = smtplib.SMTP('smtp.gmail.com')
smtpObj.sendmail(sender, receiver, message)
print "Successfully sent email!"
except Exception:
print "Error: unable to send email"
I've left off a few parts (like the ConfigParser variables) and demonstrated only the email related portions.
References:
How to send email attachments with Python
Figured it out, with added ConfigParser. This is fully functional with a .ini file
import smtplib
####import sys#### duplicate
from email.parser import Parser
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import ConfigParser
def mail_man(cfg_file, event_file):
# Parse email configs from cfg file
Config = ConfigParser.ConfigParser()
Config.read(str(cfg_file))
mail_man_start = Config.get('DC_MS', 'ms')
security = Config.get('DC_MS', 'ms_security')
add_attachment = Config.get('DC_MS', 'ms_attach')
try:
if mail_man_start == "y" or mail_man_start == "Y":
fromaddr = Config.get("DC_MS", "ms_from")
addresses = [Config.get("DC_MS", "ms_sendto")] + [Config.get("DC_MS", "ms_cc")] + [Config.get("DC_MS", "ms_bcc")]
msg = MIMEMultipart() # creates multipart email
msg['Subject'] = Config.get('DC_MS', 'ms_subject') # sets up the header
msg['From'] = Config.get('DC_MS', 'ms_from')
msg['To'] = Config.get('DC_MS', 'ms_sendto')
msg['reply-to'] = Config.get('DC_MS', 'ms_replyto')
msg['X-Priority'] = Config.get('DC_MS', 'ms_importance')
msg['CC'] = Config.get('DC_MS', 'ms_cc')
msg['BCC'] = Config.get('DC_MS', 'ms_bcc')
msg['Return-Receipt-To'] = Config.get('DC_MS', 'ms_rrr')
msg.preamble = 'Event Notification'
message = '... use this to add a body to the email detailing event. dumpcap.py location??'
msg.attach(MIMEText(message)) # attaches body to email
# Adds attachment if ms_attach = Y/y
if add_attachment == "y" or add_attachment == "Y":
attachment = open(event_file, "rb")
# Encodes the attachment and adds it to the email
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename = %s" % event_file)
msg.attach(part)
else:
print "No attachment sent."
server = smtplib.SMTP(Config.get('DC_MS', 'ms_smtp_server'), Config.get('DC_MS', 'ms_smtp_port'))
server.ehlo()
server.starttls()
if security == "y" or security == "Y":
server.login(Config.get('DC_MS', 'ms_user'), Config.get('DC_MS', 'ms_password'))
text = msg.as_string()
max_size = Config.get('DC_MS', 'ms_maxattach')
msg_size = sys.getsizeof(msg)
if msg_size <= max_size:
server.sendmail(fromaddr, addresses, text)
else:
print "Your message exceeds maximum attachment size.\n Please Try again"
server.quit()
attachment.close()
else:
print "Mail_man not activated"
except:
print "Error! Something went wrong with Mail Man. Please try again."

Categories