Why do I get an authentication error with SMTP? - python

Every time I run this program, it runs the first time, but the second time it gives this error in terminal:
Traceback (most recent call last):
File "main.py", line 30, in <module>
timenow = datetime.now()
File "main.py", line 27, in temperature
moApparent = regexApparentTemp.search(str(json_object))
File "/usr/local/lib/python3.7/dist-packages/gspread/models.py", line 888, in append_row
return self.spreadsheet.values_append(self.title, params, body)
File "/usr/local/lib/python3.7/dist-packages/gspread/models.py", line 119, in values_append
r = self.client.request('post', url, params=params, json=body)
File "/usr/local/lib/python3.7/dist-packages/gspread/client.py", line 79, in request
raise APIError(response)
gspread.exceptions.APIError: {
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
The code is:
import requests, pprint, re, gspread, time
from oauth2client.service_account import ServiceAccountCredentials
from datetime import datetime
def temperature():
from oauth2client.service_account import ServiceAccountCredentials
from datetime import datetime
scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_name('Singapore-Weather-84def2be176a.json', scope)
gc = gspread.authorize(credentials)
wks = gc.open('Singapore Weather').sheet1
r = requests.get('https://api.darksky.net/forecast/b02b5107a2c9c27deaa3bc1876bcee81/1.312914,%20103.780257')
json_object = r.text
regexCurrentTemp = re.compile(r'"temperature":(\d\d.\d\d)')
moTemp = regexCurrentTemp.search(str(json_object))
temperature = moTemp.group(1)
regexApparentTemp = re.compile(r'"apparentTemperature":(\d\d.\d\d)')
moApparent = regexApparentTemp.search(str(json_object))
apparent = moApparent.group(1)
timenow = datetime.now()
wks.append_row([str(timenow), temperature, apparent])
while True:
temperature()
time.sleep(3597)
I put the autentication within the function in the loop so it would work, but it didn't. What is the problem?

As you can see on Github googleapis/oauth2client is deprecated
I would suggest you to try official Gmail API Python example:
def create_message(sender, to, subject, message_text):
"""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['to'] = to
message['from'] = sender
message['subject'] = subject
return {'raw': base64.urlsafe_b64encode(message.as_string(
))}
If you struggle with an example above try the Gmail API Quickstart Python tutorial

Related

Show first and last name in header when using Gmail API (python)

I have my mail my_name#company.com and uses gmails API (and python) to send some mails. The problem is that when the mail hit the inbox the "from" is shown as my_name#company.com '<my_name#company.com>' where I want it to be First Name <my_name#company.com>.
I have tried using different variations of "First Name '<my_name#company.com>'" but I get a RefreshError: ('invalid_request: Invalid impersonation "sub" field.', '{\n "error": "invalid_request",\n "error_description": "Invalid impersonation \\u0026quot;sub\\u0026quot; field."\n}').
from __future__ import print_function
from googleapiclient.discovery import build
from apiclient import errors
from httplib2 import Http
from email.mime.text import MIMEText
import base64
from google.oauth2 import service_account
# Email variables. Modify this!
EMAIL_FROM = "First Last '<my_name#company.com>'"
EMAIL_TO = 'some_mail#hotmail.com'
EMAIL_SUBJECT = 'Hello from Me!'
EMAIL_CONTENT = 'Some body'
# Call the Gmail API
def service_account_login():
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
SERVICE_ACCOUNT_FILE = 'my-credentials.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject(EMAIL_FROM)
service = build('gmail', 'v1', credentials=delegated_credentials)
return service
def create_message(sender, to, subject, message_text):
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
return {"raw": raw}
def send_message(service, user_id, message):
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
service = service_account_login()
message = create_message(EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, EMAIL_CONTENT)
sent = send_message(service,'me', message)
I just sent an email recently showing the name as you wanted, I got it with this line, similar to what you already had but without the single quotation marks:
message['from'] = "First Name <something#example.com>"
So the trick is fairly simple - the EMAIL_FROM is first used to create a service with that email, which is why you cannot write My Name <my_name#company.com>. You can do that when you need to specify the "FROM " mail e.g in the very buttom of create_message= f"First name<{EMAIL_FROM}>,...)

Python: Error sending more than one email in a row

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

Credentials are required to create a TwilioClient

I want to send whatsapp messages using python using the Twilio module. I got a code from youtube and when ran the code ,it came with an error as
Traceback (most recent call last):
File "whatsapp.py", line 4, in <module>
client = Client()
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\twilio\rest\__init__.py", line
54, in __init__
raise TwilioException("Credentials are required to create a TwilioClient")
twilio.base.exceptions.TwilioException: Credentials are required to create a TwilioClient
Here is my code:
from twilio.rest import Client
client = Client()
from_whatsapp_number = 'whatsapp: +60***86744'
to_whatsapp_number = 'whatsapp: +8134***727'
client.messages.create(body='Testing message using python',
from_ = from_whatsapp_number,
to = to_whatsapp_number)
From the docs, a Client should be declared as follows:
from twilio.rest import Client
# Your Account SID from twilio.com/console
account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Your Auth Token from twilio.com/console
auth_token = "your_auth_token"
client = Client(account_sid, auth_token)
You were missing your account_sid and auth_token when you declared your Client.
As #Alan pointed out, if account_sid and auth_token are not declared in the Client, Twilio looks for them as environment variables, TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN respectively.

GMAIL API:forward all the client gmail messages to a gmail address by using gmail api

Currently, I am writing a program to forward all the Gmail messages of users to a Gmail address
I took the help from:Link
I created all the storage, credentials file etc
but when I try to run the program it's giving an error:
Traceback (most recent call last):
File "hh.py", line 40, in <module>
thadari()
File "hh.py", line 30, in thadari
result = GMAIL.users().settings().forwardingAddresses().create(userId='me', body=address).execute()
File "/home/vishalthadari/Documents/Seperation 1/virutalenv/python 3/env/lib/python3.6/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
return wrapped(*args, **kwargs)
File "/home/vishalthadari/Documents/Seperation 1/virutalenv/python 3/env/lib/python3.6/site-packages/googleapiclient/http.py", line 842, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/gmail/v1/users/me/settings/forwardingAddresses?alt=json returned "Insufficient Permission">
i already gave the sending mail permission to it but I don't know why it's giving the error.there's no scope for forward message as I read here:link
Or do I need to buy google gsuite for this? which is defined here:Link
in gmail.settings.sharing
I know that whole wall of code is not reusable but as I am new to Google API's so I think their's small minor bugs in my code:
from apiclient import discovery
from apiclient import errors
from httplib2 import Http
from oauth2client import file, client, tools
import base64
from bs4 import BeautifulSoup
import re
import time
import dateutil.parser as parser
from datetime import datetime
import datetime
import csv
def thadari():
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
GMAIL = discovery.build('gmail', 'v1', http=creds.authorize(Http()))
user_id = 'me'
label_id_one = 'INBOX'
address = {'forwardingEmail': 'girlsdontloveme75#gmail.com'}
result = GMAIL.users().settings().forwardingAddresses().create(userId='me', body=address).execute()
if result.get('verificationStatus') == 'accepted':
body = {
'emailAddress': result.get('forwardingEmail'),
'enabled': True,
'disposition': 'trash'
}
if __name__ == '__main__':
thadari()
Is this the right way to do it? or there's a better way to do this please help me
Thanks in advance

HttpError 400 Bad Request - Google Admin Directory API (Python)

I'm having difficulty creating a Google Project as a Service Account. I am using the Admin SDK in Python, specifically the Directory API. I believe I am authenticating correctly but when it comes to calling users.list I get the following error:
Traceback (most recent call last):
File "googleproject.py", line 17, in <module>
userlist = service.users().list().execute(http = http_auth)
File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 135, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/googleapiclient/http.py", line 723, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/admin/directory/v1/users?alt=json returned "Bad Request">
My code is as follows:
from oauth2client.client import SignedJwtAssertionCredentials
from httplib2 import Http
from apiclient.discovery import build
#----AUTHORISATION----#
client_email = '*****#developer.gserviceaccount.com'
with open('*****.p12') as f:
private_key = f.read()
credentials = SignedJwtAssertionCredentials(client_email, private_key, 'https://www.googleapis.com/auth/admin.directory.user')
http_auth = credentials.authorize(Http())
#--------------------#
service = build('admin', 'directory_v1', http = http_auth)
userlist = service.users().list().execute(http = http_auth)
I have tried it with and without passing http = http_auth as an argument to execute(). I've basically followed the example given here: https://code.google.com/p/google-api-python-client/source/browse/samples/service_account/tasks.py
I have enabled Admin SDK in the Developers console, as well as added the client id and scope in the Google Apps control panel.
I managed to fix it! The problem was I wasn't setting the domain in the list argument. So the new code is as follows:
from oauth2client.client import SignedJwtAssertionCredentials
from httplib2 import Http
from apiclient.discovery import build
#----AUTHORISATION----#
client_email = '*****#developer.gserviceaccount.com'
with open('*****') as f:
private_key = f.read()
credentials = SignedJwtAssertionCredentials(client_email, private_key, 'https://www.googleapis.com/auth/admin.directory.user', sub = 'super-admin#domain.com')
http_auth = credentials.authorize(Http())
#--------------------#
service = build('admin', 'directory_v1', http = http_auth)
user = service.users().list(showDeleted = False, domain = 'domain.com').execute()

Categories

Resources