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 ... :((((((
from Python: Error sending more than one email in a row
No comments:
Post a Comment