count number of emails sent using smtplib python - python

I have a basic python code that sends out an email to addresses from a list in Google sheet.
I want to count the number of times an email is sent to a particular email address by the python script. I tried researching on it. I didn't find anything related to it. And being a complete beginner hasn't helped me make much progress.
If anyone can point me to a particular direction that would be super helpful. Thanks so much in advance. 
Below is the code
import smtplib
import ssl
from email.mime.text import MIMEText # New line
from email.utils import formataddr # New line
# User configuration
sender_email = 'email ID'
sender_name = 'name'
password = "password"
receiver_emails = [RECEIVER_EMAIL_1, RECEIVER_EMAIL_2, RECEIVER_EMAIL_3]
receiver_names = [RECEIVER_NAME_1, RECEIVER_NAME_2, RECEIVER_NAME_3]
# Email text
email_body = '''
This is a test email sent by Python. Isn't that cool?
'''
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
print("Sending the email...")
# Configurating user's info
msg = MIMEText(email_body, 'plain')
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, msg.as_string())
print('Email sent!')
except Exception as e:
print(f'Oh no! Something bad happened!n {e}')
finally:
print('Closing the server...')
server.quit()

I would suggest you to create a list of successful emails, which will be populated on each iteration and then, use Counter from collections module, which receives an iterable and returns an object with number of occurrences of each element in the iterable.
You can try the following code:
from collections import Counter
import json
counter_file_path = "counter.json"
try:
with open(counter_file_path, "r") as f:
email_stats = json.load(f)
except FileNotFoundError as ex:
email_stats = {}
successful_emails = []
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
print("Sending the email...")
# Configurating user's info
msg = MIMEText(email_body, 'plain')
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, msg.as_string())
print('Email sent!')
if receiver_email in email_stats:
email_stats[receiver_email] += 1
else:
email_stats[receiver_email] = 1
except Exception as e:
print(f'Oh no! Something bad happened!n {e}')
finally:
print('Closing the server...')
server.quit()
print(email_stats) # output - all occurrences for each email
with open(counter_file_path, "w") as f:
json.dump(email_stats, f)

You can use this code to store/print success mail count into a JSON format.
import smtplib
import SSL
import json
import os
from email.mime.text import MIMEText # New line
from email.utils import formataddr # New line
fileName = "sendMail_count.json"
# To store data into json file.
# It will create file in datetime format.
def store_data_to_file(jsonStr):
jsonFile = open(fileName, "w")
json.dump(jsonStr, jsonFile)
print("data stored successfully")
# User configuration
sender_email = 'email ID'
sender_name = 'name'
password = "password"
receiver_emails = [RECEIVER_EMAIL_1, RECEIVER_EMAIL_2, RECEIVER_EMAIL_3]
receiver_names = [RECEIVER_NAME_1, RECEIVER_NAME_2, RECEIVER_NAME_3]
# To store the count of successful mail received by receiver with their respective email.
if not os.path.exists(fileName) or os.stat(fileName).st_size == 0:
print("File is empty or not found")
print("Creating a JSON file to store the data")
jsonFile = open(fileName, "w+")
print("a JSON file has been created with name: " + str(fileName))
success_mail_count = {}
else:
with open(fileName) as jsonFile:
success_mail_count = json.load(jsonFile)
print(success_mail_count)
# Email text
email_body = '''
This is a test email sent by Python. Isn't that cool?
'''
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
count = 0
print("Sending the email to..." + receiver_email)
# Configurating user's info
msg = MIMEText(email_body, 'plain')
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, msg.as_string())
# Check if recevier is already present in the dict,
# then add 1 to its current count
if receiver_email in success_mail_count:
success_mail_count[receiver_email] = str(int(success_mail_count[receiver_email]) + 1)
# If reciever isn't present in map then create new entry for receiver and
# Update the count with one for successfull mail sent.
else:
success_mail_count[receiver_email] = str(count + 1)
print('Email sent!')
except Exception as e:
print(f'Oh no! Something bad happened!n {e}')
finally:
print('Closing the server...')
server.quit()
print(success_mail_count)
store_data_to_file(success_mail_count)
run this code and it will create data into file and then it will read data from file itself.

Related

I can't send a message from telegram to mail

When entering a message , the console outputs that the "ms" variable is empty , how can this be fixed ?
def send_mail(id_user, message_topic):
data = read_date_json()
ms = ''
login = data['MyDate'][0]['login']
password = data['MyDate'][0]['password']
mail = data['MyDate'][0]['login'].split('#')[1]
if ('yandex' in mail) or ('ya' in mail):
ms = 'yandex.ru'
elif 'gmail' in mail:
ms = 'gmail.com'
elif 'mail' in mail:
ms = 'mail.ru'
url = data['Mail'][0][ms]
number, topic, message = message_topic.split('$')
toaddr = data['MyFriend'][0]['mail'][int(number)]
msg = MIMEMultipart()
msg['Subject'] = topic
msg['From'] = login
body = message
msg.attach(MIMEText(body, 'plain'))
try:
server = root.SMTP_SSL(url, 465)
except:
print('no connect')
server.login(login, password)
server.sendmail(login, toaddr, msg.as_string())
server.quit()
bot.send_message(id_user, 'Ваше сообщение отправлено')
I tried to overwrite variables and tried to use other mail, but to no avail

Send email through AWS Workmail from Python script

Is it possible to send automated email messages with Amazon Workmail through a Python script?
My code keeps hanging and when I can the run I get a SMTP server connection error. How can I fix this? I presume, its the SMTP configuration, but does anyone know what that is?
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os.path
import csv
from random import randint
from time import sleep
def send_email(email_recipient,
email_subject,
email_message,
attachment_location=''):
email_mime_sender = 'xxxxx <xxxxx#yyyyyy.co.uk>'
email_sender = 'xxxxx'
email_password = 'xxxxx'
msg = MIMEMultipart()
msg['From'] = email_mime_sender
msg['To'] = email_recipient
msg['Subject'] = email_subject
msg.attach(MIMEText(email_message, 'plain'))
if attachment_location != '':
filename = os.path.basename(attachment_location)
attachment = open(attachment_location, "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)
try:
server = smtplib.SMTP_SSL('smtp.mail.eu-west-1.awsapps.com', 465)
server.ehlo()
server.starttls()
server.login(email_sender, email_password)
text = msg.as_string()
server.sendmail(email_sender, email_recipient, text)
print('Email sent to %s' % email_recipient)
server.quit()
except:
print("SMTP server connection error")
return True
def main():
file = 'test.csv'
with open(file, "rb") as f:
next(f)
for line in f:
line = line.replace("\n", "")
line = line.replace("\r", "")
split_line = line.split(",")
email_subject = 'Can I ask you a few questions?'
raw_email_message = [
'Test',
'Test'
]
email_message = '\n\n'.join(raw_email_message)
send_email(split_line[1], email_subject, email_message, '')
sleep(randint(1, 3))
main()
Removing server.starttls() from the this block fixed it:
try:
server = smtplib.SMTP_SSL('smtp.mail.eu-west-1.awsapps.com', 465)
server.ehlo()
server.login(email_user, email_password)
text = msg.as_string()
server.sendmail(email_sender, email_recipient, text)
print('Email sent to %s' % email_recipient)
server.quit()
except Exception as e:
print(e)
print("SMTP server connection error")
return True

ValueError: There may be at most 1 To headers in a message

I am trying to write a very basic email sending script. Here is my code ..
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg.set_content("Test message.")
msg['Subject'] = "Test Subject!!!"
msg['From'] = "myemail#gmail.com"
email_list = ["xyz#gmail.com", "abc#gmail.com"]
for email in email_list:
msg['To'] = email
server = smtplib.SMTP(host='smtp.gmail.com', port=587)
server.starttls()
server.login("myemail#gmail.com", "mypassword")
server.send_message(msg)
server.quit()
the script should send mail to multiple recipients so, I need to change the msg['To'] field when iterating through loop But I get the following error in traceback bellow.
Traceback (most recent call last):
File "exp.py", line 66, in <module>
msg['To'] = email
File "/usr/lib/python3.8/email/message.py", line 407, in __setitem__
raise ValueError("There may be at most {} {} headers "
ValueError: There may be at most 1 To headers in a message
How do I solve ? Please help. Thank you..
Clean the 'To' property of the message.
for email in email_list:
msg['To'] = email
server = smtplib.SMTP(host='smtp.gmail.com', port=587)
server.starttls()
server.login("myemail#gmail.com", "mypassword")
server.send_message(msg)
server.quit()
del msg['To]
Below is the code that throws the exception: (\Python385\Lib\email\message.py)
def __setitem__(self, name, val):
"""Set the value of a header.
Note: this does not overwrite an existing header with the same field
name. Use __delitem__() first to delete any existing headers.
"""
max_count = self.policy.header_max_count(name)
if max_count:
lname = name.lower()
found = 0
for k, v in self._headers:
if k.lower() == lname:
found += 1
if found >= max_count:
raise ValueError("There may be at most {} {} headers "
"in a message".format(max_count, name))
self._headers.append(self.policy.header_store_parse(name, val))
Not knowing the inner working of the EmailMessage class, what I can assume is that every call to __setitem__ writes to the head of the email message, so by calling it in a loop, the header is being written multiple times, what I'd recommend is that you make an email message for every email you'll send, but create only one server:
server = smtplib.SMTP(host='smtp.gmail.com', port=587)
server.starttls()
server.login("myemail#gmail.com", "mypassword")
email_list = ["xyz#gmail.com", "abc#gmail.com"]
for email in email_list:
msg = EmailMessage()
msg.set_content("Test message.")
msg['Subject'] = "Test Subject!!!"
msg['From'] = "myemail#gmail.com"
msg['To'] = email
server.send_message(msg)
server.quit()
Only if you need for the messages to be sent separately. If you want to send the same message to everyone at the same time you could do something like
msg['To'] = ', '.join(email_list)
If you have a list of addresses, and some of them include a name/title, then I think this is the correct way to do it. Please note that parseaddr + formataddr pair may not be needed, but parseaddr can correct some malformed recipients.
from email.header import Charset
from email.message import EmailMessage, MIMEPart
from email.utils import formataddr, parseaddr
test_recipients = [
"Mr. John Doe <johndoe#example.com>",
"Mr. Jane Doe <janedoe#example.com>",
"somebody#example.com"
]
to_header= []
for raw_address in (test_recipients):
# Parse and recreate
title, email = parseaddr(raw_address)
if title and email:
to_header.append(f"{title} <{email}>")
elif email:
to_header.append(email)
# Encode after join
message.add_header("To", Charset("utf-8").header_encode(", ".join(to_header)))
if you just delete
server.quit() from loop
and add
del msg['to']
then there is no error

How to fixed "554, Transaction failed: Duplicate header 'Subject' "?

When sending large count email response this error 554, Transaction failed: Duplicate header subject. I'm use smtplib + aws SES. For all messages, the header must be the same. How can I fix this error? If send message without subject, all working.
import smtplib
import json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
args = []
msg = MIMEMultipart('alternative')
msg['From'] = 'noreply#example.com'
html = open('mail.html').read()
EMAIL_HOST = 'email-smtp...'
EMAIL_HOST_USER = 'sss'
EMAIL_HOST_PASSWORD = 'ssssss'
EMAIL_PORT = 587
def lambda_handler(event, context):
body = event['Records'][0]['Sns']['Message']
global args
args = json.loads(body)['args']
set_worker(json.loads(body)['method'])()
return 'success'
def set_worker(method):
return {
'email' : email
}.get(method, 'Not found')
def email():
global msg, html
name = args[0]
title = args[1]
msg_body = args[2]
email = args[3]
url = args[4]
subject = "Test"
msg['Subject'] = subject
msg['To'] = email
html = html.format(title, community_name, title, msg_body, community_name)
mime_text = MIMEText(html, 'html')
msg.attach(mime_text)
send_message()
def send_message():
mail = smtplib.SMTP(EMAIL_HOST, EMAIL_PORT)
mail.ehlo()
mail.starttls()
mail.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
mail.sendmail(msg['From'], msg['To'], msg.as_string())
When working with aws-lambda cannot be used global variables . The error was in the fact that duplicate messages were written to the variable msg.

Email multiple recipients Python

I'm trying to email multiple recipients using the pyton script below. I've searched the forum for answers, but have not been able to implement any of them correctly. If anyone has a moment to review my script and spot/resolve the problem it would be greatly appreciated.
Here's my script, I gather my issue is in the 'sendmail' portion, but can't figure out how to fix it:
gmail_user = "sender#email.com"
gmail_pwd = "sender_password"
recipients = ['recipient1#email.com','recipient2#email.com']
def mail(to, subject, text, attach):
msg = MIMEMultipart()
msg['From'] = gmail_user
msg['To'] = ", ".join(recipients)
msg['Subject'] = subject
msg.attach(MIMEText(text))
part = MIMEBase('application', 'octet-stream')
part.set_payload(open(attach, 'rb').read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition',
'attachment; filename="%s"' % os.path.basename(attach))
msg.attach(part)
mailServer = smtplib.SMTP("smtp.gmail.com", 587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login(gmail_user, gmail_pwd)
mailServer.sendmail(gmail_user, to, msg.as_string())
mailServer.close()
mail("recipient1#email.com, recipient2#email.com",
"Subject",
"Message",
"attchachment")
Any insight would be greatly appreciated.
Best,
Matt
It should be more like
mail(["recipient1#email.com", "recipient2#email.com"],
"Subject",
"Message",
"attchachment")
You already have a array of recipients declared,that too globally,You can use that without passing it as an argument to mail.
I wrote this bit of code to do exactly what you want. If you find a bug let me know (I've tested it and it works):
import email as em
import smtplib as smtp
import os
ENDPOINTS = {KEY: 'value#domain.com'}
class BoxWriter(object):
def __init__(self):
pass
def dispatch(self, files, box_target, additional_targets=None, email_subject=None, body='New figures'):
"""
Send an email to multiple recipients
:param files: list of files to send--requires full path
:param box_target: Relevant entry ENDPOINTS dict
:param additional_targets: other addresses to send the same email
:param email_subject: optional title for email
"""
destination = ENDPOINTS.get(box_target, None)
if destination is None:
raise Exception('Target folder on Box does not exist')
recipients = [destination]
if additional_targets is not None:
recipients.extend(additional_targets)
subject = 'Updating files'
if email_subject is not None:
subject = email_subject
message = em.MIMEMultipart.MIMEMultipart()
message['From'] = 'user#domain.com'
message['To'] = ', '.join(recipients)
message['Date'] = em.Utils.formatdate(localtime=True)
message['Subject'] = subject
message.attach(em.MIMEText.MIMEText(body + '\n' +'Contents: \n{0}'.format('\n'.join(files))))
for f in files:
base = em.MIMEBase.MIMEBase('application', "octet-stream")
base.set_payload(open(f, 'rb').read())
em.Encoders.encode_base64(base)
base.add_header('Content-Disposition', 'attachment; filename={0}'.format(os.path.basename(f)))
message.attach(base)
conn = smtp.SMTP('smtp.gmail.com', 587)
un = 'user#gmail.com'
pw = 'test1234'
conn.starttls()
conn.login(un, pw)
conn.sendmail('user#domain.com', recipients, message.as_string())
conn.close()
I was facing the same issue, I fixed this issue now. Here is my code -
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
import datetime
def sendMail():
message = MIMEMultipart()
message["To"] = "xxxxx#xxxx.com,yyyy#yyyy.com"
message["Cc"] = "zzzzzz#gmail.com,*********#gmail.com"
message["From"] = "xxxxxxxx#gmail.com"
message["Password"] = "***************"
server = 'smtp.gmail.com:587'
try:
now = datetime.datetime.now()
message['Subject'] = "cxxdRL Table status (Super Important Message) - "+str(now)
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(message["From"], message["Password"])
server.sendmail(message["From"], message["To"].split(",") + message["Cc"].split(","), message.as_string())
server.quit()
print('Mail sent')
except:
print('Something went wrong...')
sendMail()

Categories

Resources