I have made a development who let me to send emails. If I try to send an email to one receipt there is no problem. But, If I try to send the same email to more than one receipts then I've got an error. I don't want to send an email to several receipts, what I want is to send one email to each of the receipts.
The error that I've got is:
File "/home/josecarlos/Workspace/python/reports/reports/pregame.py", line 22, in __init__
self.send_mail(subject, message, fileName)
File "/home/josecarlos/Workspace/python/reports/reports/report.py", line 48, in send_mail
message = mail.create_message()
File "/home/josecarlos/Workspace/python/reports/com/mail/mail.py", line 100, in create_message
message.attach(MIMEText(self.params["message"], "plain"))
File "/usr/lib/python3.6/email/mime/text.py", line 34, in __init__
_text.encode('us-ascii')
AttributeError: 'dict' object has no attribute 'encode'
To send an email I have this class:
import base64
import logging
import os
import os.path
import pickle
from email import encoders
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient import errors
from googleapiclient.discovery import build
class Mail:
def __init__(self, params):
'''
:param params: It's a dictionary with these keys:
from: Email account from the email is sended
to: Email account who will receive the email
subject: Subject of the email
message: Message of the email.
game: Next games
'''
self.params = params
#staticmethod
def get_service():
"""Gets an authorized Gmail API service instance.
Returns:
An authorized Gmail API service instance..
"""
# If modifying these scopes, delete the file token.pickle.
SCOPES = [
#'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.send',
]
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'com/mail/credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
return service
#staticmethod
def send_message(service, sender, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
sender: User's email address. The special value "me"
can be used to indicate the authenticated user.
message: Message to be sent.
Returns:
Sent Message.
"""
try:
sent_message = (service.users().messages().send(userId=sender, body=message)
.execute())
logging.info('Message Id: %s', sent_message['id'])
return sent_message
except errors.HttpError as error:
logging.error('An HTTP error occurred: %s', error)
def create_message(self):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
#message = MIMEText(message_text)
message = MIMEMultipart()
message['from'] = self.params["from"]
message['to'] = self.params["to"]
message['subject'] = self.params["subject"]
message.attach(MIMEText(self.params["message"], "plain"))
routeFile = self.params["routeFile"] + self.params["fileName"]
fileName = self.params["fileName"]
# Open PDF file in binary mode
with open(routeFile, "rb") as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
f"attachment; filename= {fileName}",
)
# Add attachment to message and convert message to string
message.attach(part)
s = message.as_string()
b = base64.urlsafe_b64encode(s.encode('utf-8'))
return {'raw': b.decode('utf-8')}
To send an email, I'll do it with this method:
def send_mail(self, subject, message, fileName):
args = self.params["destiny"]
if self.params["competition"] == COMPETITIONS.LF1 or self.params["competition"] == COMPETITIONS.LF2:
data = SearchData(args, "subscriptors.emails=")
else:
data = SearchDataFIBA(args, "subscriptors.emails=")
emails = data.get_result().getData()
for item in emails:
print(f"Enviamos informe a la cuenta: {item['email']}")
params = {
"from" : "basketmetrics#gmail.com",
"to" : item["email"],
"subject": subject,
"message" : message,
"fileName" : fileName,
"routeFile" : f"output/reports/{self.params['destiny']}/"
}
mail = Mail(params)
message = mail.create_message()
service = mail.get_service()
mail.send_message(service, "basketmetrics#gmail.com", message)
My application has secure access to google account.
I don't know how I cannot send more than an email in a row when I have no
problem to send only one e-mail.
Am I doing something wrong?
Edit I:
To reproduce the error, you can test it with this test code:
import unittest
import os
from com.mail.mail import Mail
class TestSendMail(unittest.TestCase):
def setUp(self) -> None:
os.chdir(os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')))
def test_send_mail(self):
message = "¡¡¡Hola!!!\n\nOs enviamos el informe pre partido previo a vuestro próximo partido.\n\nSaludos,\n\nBasketmetrics.com"
subject = "Informe pre partido"
fileName = "name_of_the_file"
emails = [{"email" : "receipt1#gmail.com"}, {"email" : "receipt2#gmail.com"}]
for item in emails:
print(f"Enviamos informe a la cuenta: {item['email']}")
params = {
"from" : "sender#gmail.com",
"to" : item["email"],
"subject": subject,
"message" : message,
"fileName" : fileName,
"routeFile" : "route to the file"
}
mail = Mail(params)
message = mail.create_message()
service = mail.get_service()
mail.send_message(service, "sender#gmail.com", message)
Also, you have to change some values for your own values and the file credentials.json file of your own google account
Edit II:
I have found where it produces the error but not why. The problem comes up when I invoque for second time the class Mail. In that moment I pass some parameters to the constructor with params variable. In that variable I pass the text of the message. This message is create outside of the loop.
If I read the first 120 characters of what receives the Mail class in params["message"] in the constructor:
def __init__(self, params):
'''
:param params: It's a dictionary with these keys:
from: Email account from the email is sended
to: Email account who will receive the email
subject: Subject of the email
message: Message of the email.
game: Next games
'''
self.params = params
print(f"message received: {params['message'][:120]}")
For the first time, I've got the content of message variable:
message received: ¡¡¡Hola!!!
Os enviamos el informe pre partido previo a vuestro próximo partido.
Saludos,
Basketmetrics.com
But the second time, I should receive the same text!!! But I receive an error:
Error
Traceback (most recent call last):
File "/usr/lib/python3.6/unittest/case.py", line 59, in testPartExecutor
yield
File "/usr/lib/python3.6/unittest/case.py", line 605, in run
testMethod()
File "/home/josecarlos/Workspace/python/reports/test/test_send_mail.py", line 26, in test_send_mail
mail = Mail(params)
File "/home/josecarlos/Workspace/python/reports/com/mail/mail.py", line 27, in __init__
print(f"message received: {params['message'][:120]}")
TypeError: unhashable type: 'slice'
If I read all the message without any limit or characers. For the first time, I receive the content of the variable message. But in the second time, I receive a very big string os characters, here is a little example:
lGWFBVZzhQc3pDNWdiNmhnMW1odDZHcmlFZWsyClZvTTBRT1R2dXpWaWlyZkpleS9mZXhtR3V3V2hTV0JjWERtSUNnWENTQVJ1QjdrN1Nzd3BrZ0c1Rkl3MXVDMmNyZk95ZUhySVM1dHQKSUh2T1YvWW1Pd2YzL3B2WEpLaEMza
Why I receive this string of charanters instead of the value of message variable?
I have check it that If I put message variable inside the for loop, instead of outside of the loop ... It works!!! I receive the two mails!!!
But, this solution isn't usefull because I want to reuse my code and I need to pass some values through variables.
So, why in the second time I don't receive the value of message variable and I receive a long string of characters?
How can I fix this error? Why it happens this error?
Edit III:
Checking the type of the value that I receive in the constructor of Mail, for the first time is "string":
typeof: <class 'str'>
But in the second time is "dict":
typeof: <class 'dict'>
And checking the keys of self.params["message"] are:
keys: dict_keys(['raw'])
I dont't understand anything ... How is it possible that params["message"] has the value of message variable and the second time params["message"] has modified its type to raw?
Edit IV:
I have modified the content of message variable from ...
message= ""
To ...
mesage = "¡¡¡Hola!!!0x0a0x0aOs enviamos el informe pre partido previo a vuestro próximo partido.0x0a0x0aSaludos,0x0a0x0aBasketmetrics.com"
But it doesn't work. I've got the same error.
Edit V:
I have modified the contento of message variable. Now, instead of plain text, I'm going to send an html ...
message = """\
<html>
<head></head>
<body>
<p>
Hi, this is a test!!!
</p>
<p>
Best regards!!!
</p>
</body>
</html>
"""
To do send this message, you have to modify this instruction in method create_message of Mail class:
message.attach(MIMEText(self.params["message"], "plain"))
to this:
message.attach(MIMEText(self.params["message"], "html"))
And ... I've got the same error!!!
I don't know what to do anymore ...
Edit VI:
Last attempt ... I have modified the text of the message removing "strange" character like "" or "<" and I have send a simple text with the message "Hello".
So, my message variable now is:
message = "Hello"
And I have modified again the format of the email, from "html" to "plain"
And ... I've got the same error in my second email!!!
This is frustrating ... :((((((
You have:
def send_mail(self, subject, message, fileName):
Where argument message is the message text to be sent. But we have in your function `test_send_mail':
def test_send_mail(self):
message = "¡¡¡Hola!!!\n\nOs enviamos el informe pre partido previo a vuestro próximo partido.\n\nSaludos,\n\nBasketmetrics.com"
# code omitted
for item in emails:
# code omitted
message = mail.create_message() # overlaying message text with return value from create_message
service = mail.get_service()
mail.send_message(service, "sender#gmail.com", message)
In the for item in emails: loop, you have overlaid message with the return value from the call to mail.create_message(), so for the next iteration message is not a string anymore. This, I believe is your problem. You need to use a different variable name for either the return value from the call to mail.create_message() or for the message text.
So the best solution would be to have a small list of all the email ids, and run through the for loop and you can send the mails, Or you can actually use this package from python to help you assist you in your work,
A simple example would be like this
def send_mail(names, emails, senderMail, password, subject):
port = 587
password = password
total = len(names)
server = smtplib.SMTP('smtp.gmail.com', port)
server.starttls()
server.login(senderMail, password)
cc = 0
for i in range(len(emails)):
body = '' #body of the mail, you can actually add html also please check the documentation
msg = MIMEMultipart()
msg['From'] = senderMail
msg['To'] = emails[i]
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
text = msg.as_string()
server.sendmail(senderMail,emails[i], text)
cc += 1
print(cc , " / ", total)
server.quit()
If you want a easier approach attaching images you can check this package also,
Please check this github repo for detailed work flow
I have been able to send personalized emails using the script below before, but all of a sudden I am getting the following error.
I am trying to read names and email ID's from a file and send a personalized email to each person. It picks a random subject from the list of subjects and uses a random delay. It worked fine until now but it won't now.
*Traceback (most recent call last):
File "C:/myprojects/normal_email.py", line 64, in <module>
main()
File "C:/myprojects/normal_email.py", line 57, in main
smtp.send_message(msg)
File "C:\Python\lib\smtplib.py", line 964, in send_message
bytesmsg, policy=msg.policy.clone(utf8=True))
File "C:\Python\lib\email\_policybase.py", line 72, in clone
raise TypeError(
TypeError: 'utf8' is an invalid keyword argument for Compat32*
import csv
from string import Template
import smtplib
import random
import time
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
template_file = r'C:\Users\91880\Desktop\template.txt'
filename = r'C:\Users\91880\Downloads\2307.csv'
# reads the file and returns the names, emails in a list
def get_contacts(file):
names_list = []
emails_list = []
with open(file, 'r') as mail_data:
data = csv.reader(mail_data)
for details in data:
names_list.append(details[0])
emails_list.append(details[1])
return names_list, emails_list
# reads the template from a text file and returns
def get_template(file):
with open(file, 'r') as template_info:
template_data = template_info.read()
return Template(template_data)
def main():
email_user = 'myemail#mail.com'
password = 'mypassword'
subs = ['Hi', 'Hello']
names, emails = get_contacts(filename)
template = get_template(template_file)
with smtplib.SMTP('smtp.office365.com', '587') as smtp:
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login(email_user, password)
for name, email in zip(names, emails):
subject = random.choice(subs)
number = random.randint(10, 30)
msg = MIMEMultipart()
message = template.substitute(name=name.title())
msg['From'] = email_user
msg['To'] = email
msg['Subject'] = subject
msg.attach(MIMEText(message, 'html'))
smtp.send_message(msg)
print(f'sent email to {name} at {email}')
del msg
time.sleep(number)
if __name__ == '__main__':
main()
This error occurs because there is at least one non-ascii character in one of the "to" or "from" addresses. It should be possible to fix it by setting the policy on the message to email.policy.default:
from email import policy
...
msg = MIMEMultipart(policy=policy.default)
The newer EmailMessage/EmailPolicy API is preferred over the old email.mime tools since Python 3.6. Using the preferred tools, the code would look like this:
from email.message import EmailMessage
from email import policy
...
msg = EmailMessage(policy=policy.default)
message = template.substitute(name=name.title())
msg['From'] = email_user
msg['To'] = email
msg['Subject'] = subject
msg.set_content(message, subtype='html')
smtp.send_message(msg)
For absolute RFC compatibility, you might consider setting the policy to SMTP, to generate \r\n line endings.
I am trying to write a program that logins with a Gmail id and sends a mail to a provided id.
import smtplib
email = input('Enter your email\n')
password = input('Enter your password\n')
reciever = input("To whom you want to send?\n")
content = input("Enter content below:\n")
mail= smtplib.SMTP('smtp.gmail.com',587)
mail.ehlo()
mail.starttls()
mail.login(email,password)
mail.send_message(email,reciever,content)
But when I execute the program I get this error...
Enter your email
soham.nandy2006#gmail.com
Enter your password
x
To whom you want to send?
soham.nandy#outlook.com
Enter content below:
HELLOOOO
Traceback (most recent call last):
File "c:/Users/soham/Desktop/Python Crack/main.py", line 15, in <module>
mail.send_message(email,reciever,content)
File "C:\Users\soham\AppData\Local\Programs\Python\Python38-32\lib\smtplib.py", line 928, in send_message
resent = msg.get_all('Resent-Date')
AttributeError: 'str' object has no attribute 'get_all'
PS C:\Users\soham\Desktop\Python Crack>
P.S- I am writing x instead of my password for security issue (In the program the password is correct)
You need to pass MIMEMultipart object, not strings, when you use send_message:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# collect data from user
email = input('Enter your email\n')
password = input('Enter your password\n')
reciever = input("To whom you want to send?\n")
content = input("Enter content below:\n")
# set up a server
mail = smtplib.SMTP('smtp.gmail.com', 587)
mail.ehlo()
mail.starttls()
mail.login(email, password)
# create and specify parts of the email
msg = MIMEMultipart()
msg['From'] = email
msg['To'] = reciever
msg['Subject'] = 'sample subject' # maybe you want to collect it as well?
msg.attach(MIMEText(content, 'plain'))
mail.send_message(msg)
mail.quit()
You are using the wrong function to send your email. You should use mail.sendmail() instead of mail.send_message(). The difference is the order of the arguements and in the first function, the message is a string, in the second it is a Message object.
https://docs.python.org/3/library/smtplib.html#smtplib.SMTP.sendmail
I'm trying to send an email from anoutlook account, reading contact from file.
When test without proxy no problem, but with company´s proxy i get this -->
"SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed"
im using smtplib and socks for proxy settings.
here the code:
import socks
from string import Template
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
MY_ADDRESS = 'email#outlook.com'
PASSWORD = 'pass'
def get_contacts(filename):
"""
Return two lists names, emails containing names and email addresses
read from a file specified by 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):
"""
Returns a Template object comprising the contents of the
file specified by 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('mycontacts.txt') # read contacts
message_template = read_template('message.txt')
print ("enviando mensaje")
# set up the SMTP server
socks.setdefaultproxy(socks.HTTP, 'proxy', port, True,'username', 'password')
socks.wrapmodule(smtplib)
s = smtplib.SMTP(host='smtp.office365.com', port=587)
s.set_debuglevel(True)
s.starttls()
s.login(MY_ADDRESS, PASSWORD)
# For each contact, send the email:
for name, email in zip(names, emails):
msg = MIMEMultipart() # create a message
# add in the actual person name to the message template
message = message_template.substitute(PERSON_NAME=name.title())
# Prints out the message body for our sake
print(message)
# setup the parameters of the message
msg['From']=MY_ADDRESS
msg['To']=email
msg['Subject']="This is TEST"
# add in the message body
msg.attach(MIMEText(message, 'plain'))
# send the message via the server set up earlier.
s.send_message(msg)
del msg
# Terminate the SMTP session and close the connection
s.quit()
if __name__ == '__main__':
main()
any help?
solved:
i change the port of smtp connection from 587 to 25, and works!
In linux, provided that your http_proxy/https_proxy environment variable are set, you should be good to go. In windows, https://www.proxifier.com/ will allow you to send all traffic over the proxy properly.
When I write the script below I get the error IndexError: list index out of range:
import smtplib
from string import Template
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
MY_ADDRESS = 'joaosoundcloudpython#gmail.com'
PASSWORD = 'XXXX'
def get_contacts(test1):
"""
Return two lists names, emails containing names and email addresses
read from a file specified by filename.
"""
names = []
emails = []
with open('/Users/joaopimenta/Documents/joaoPython/soundcloud_email.txt', 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()[100])
return names, emails
def read_template():
"""
Returns a Template object comprising the contents of the
file specified by filename.
"""
with open(soundcloud_email_message.txt, 'r', encoding='utf-8') as template_file:
template_file_content = template_file.read()
return Template(template_file_content)
def main():
names, emails = get_contacts('/Users/joaopimenta/Documents/joaoPython/soundcloud_email.rtf') # read contacts
message_template = read_template('/Users/joaopimenta/Documents/joaoPython/soundcloud_email_message.txt')
# set up the SMTP server
s = smtplib.SMTP(host='smtp.gmail.com', port=587)
s.starttls()
s.login(joaosoundcloudpython#gmail.com, XXXX)
# For each contact, send the email:
for name, email in zip(names, emails):
msg = MIMEMultipart() # create a message
# add in the actual person name to the message template
message = message_template.substitute(PERSON_NAME=name.title())
# Prints out the message body for our sake
print(message)
# setup the parameters of the message
msg['From']=MY_ADDRESS
msg['To']=email
msg['Subject']="This is TEST"
# add in the message body
msg.attach(MIMEText(message, 'plain'))
# send the message via the server set up earlier.
s.send_message(msg)
del msg
# Terminate the SMTP session and close the connection
s.quit()
if __name__ == '__main__':
main()