Missing API key even though I am using OAuth - python

I am a beginner programmer. I am trying to work with Google API. I am using the OAuth to send requests to Google Calendar API. However, I get error 403:
{
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"errors": [
{
"message": "The request is missing a valid API key.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
}
}
The following question Google Calendar "The request is missing a valid API key" when using OAuth suggests using:
google.calendar({ version: "v3", auth: client })
However my code provided by Google uses:
service = build('calendar', 'v3', credentials=creds)
so the suggested solution is not applicable.
My full code is the same as Google API's provided 'Quickstart.py' except for the name of the client_secre_file:
from __future__ import print_function
import datetime
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = None
# The file token.json 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.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'client_secret_desktop.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
return
# Prints the start and name of the next 10 events
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
print(start, event['summary'])
except HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
Maybe there is an issue in my Credentials configuration?

Related

How to read emails with Python and Gmail API

I want to get unread emails from my inbox with python code.
I set up google developer account, I made an app (I set it to DESKTOP) and I downloaded credentials.
{"installed":{"client_id":"xxx",
"project_id":"xxx",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"client_secret":"xxx",
"redirect_uris":["http://localhost"]
}
}
This is the code that I have:
import os
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
creds = Credentials.from_authorized_user_file(os.path.expanduser('gmail_credencials.json'), ['https://www.googleapis.com/auth/gmail.readonly'])
service = build('gmail', 'v1', credentials=creds)
print(service)
messages = service.users().messages()
print(messages)
But I am getting this error:
ValueError: Authorized user info was not in the expected format, missing fields refresh_token, client_secret, client_id.
I have client_secret and client_id, but I do not have a clue where should I get refresh_token.
Does anyone else has the experience with this error?
This is a sample code based on the Google Documentation here. It has an example about the refresh token on it. I'm also listing the files emails that have the label UNREAD. After that, I get the emails with the IDs of the list, and decode the messages payload using base64. Take in consideration that this is just a sample.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
import email
import base64 #add Base64
import time
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json 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.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
#Filter and get the IDs of the message I need.
#I'm just filtering messages that have the label "UNREAD"
try:
service = build('gmail', 'v1', credentials=creds)
search_id = service.users().messages().list(userId='me', labelIds="UNREAD").execute()
number_result = search_id['resultSizeEstimate']
final_list = [] # empty array, all the messages ID will be listed here
# review if the search is empty or not
# if it has messages on it, It will enter the for
if number_result>0:
message_ids = search_id['messages']
for ids in message_ids:
final_list.append(ids['id'])
# call the function that will call the body of the message
get_message(service, ids['id'] )
return final_list
# If there are not messages with those criterias
#The message 'There were 0 results for that search string' will be printed.
else:
print('There were 0 results for that search string')
return ""
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
#new function to get the body of the message, and decode the message
def get_message(service, msg_id):
try:
message_list=service.users().messages().get(userId='me', id=msg_id, format='raw').execute()
msg_raw = base64.urlsafe_b64decode(message_list['raw'].encode('ASCII'))
msg_str = email.message_from_bytes(msg_raw)
content_types = msg_str.get_content_maintype()
#how it will work when is a multipart or plain text
if content_types == 'multipart':
part1, part2 = msg_str.get_payload()
print("This is the message body, html:")
print(part1.get_payload())
return part1.get_payload()
else:
print("This is the message body plain text:")
print(msg_str.get_payload())
return msg_str.get_payload()
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
Reference:
Method: users.messages.list
Method: users.messages.get
Format
Google Documentation "Python quickstart"
base64 Python

Gmail api create draft results in cant find GOOGLE_APPLICATION_CREDENTIALS

I keep getting this error and have looked everywhere and I cannot fix it. Can someone please hold my hand. I have downloaded the json filed and named it so it matches. I have watched every youtube video to make sure I created credentials correctly and also added my email as a test user. Not sure what to do at this point. I have json file and the token in my IDE, also I was able to log in and choose my username and got the flow message so everything seems to be working. Up until I copy and paste the create_email_draft from gmail api documents. Basically I just want to send some automated emails. Here is my code...let me know if you need anything else.
ERROR:
Traceback (most recent call last):
File "C:\Users\carlo\PycharmProjects\pythonProject3\quickstart.py", line 61, in
gmail_create_draft()
File "C:\Users\carlo\PycharmProjects\pythonProject3\quickstart.py", line 25, in gmail_create_draft
creds, _ = google.auth.default()
File "C:\Users\carlo\PycharmProjects\pythonProject3\venv\lib\site-packages\google\auth_default.py", line 616, in default
raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application. For more information, please see https://cloud.google.com/docs/authentication/getting-started
Process finished with exit code 1
from __future__ import print_function
import os.path
import base64
from email.message import EmailMessage
import google.auth
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def gmail_create_draft():
"""Create and insert a draft email.
Print the returned draft's message and id.
Returns: Draft object, including draft id and message meta data.
Load pre-authorized user credentials from the environment.
TODO(developer) - See https://developers.google.com/identity
for guides on implementing OAuth2 for the application.
"""
creds, _ = google.auth.default()
try:
# create gmail api client
service = build('gmail', 'v1', credentials=creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'carlosmedina239#gmail.com'
message['From'] = 'selenytesting123#gmail.com'
message['Subject'] = 'Automated draft'
# encoded message
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
# pylint: disable=E1101
draft = service.users().drafts().create(userId="me",
body=create_message).execute()
print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}')
except HttpError as error:
print(F'An error occurred: {error}')
draft = None
return draft
if __name__ == '__main__':
gmail_create_draft()
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json 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.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
# You don't have to care about anything on top of this comment
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
return
print('Labels:')
for label in labels:
print(label['name'])
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
I think you have tried to copy the code from the quickstart without trying understanding what its doing.
Your gmail_create_draft method is not building the credentials properly. So its looking for it in the default env var which you dont have set which is the cause of your error message.
I have tested this code and it works.
# To install the Google client library for Python, run the following command:
# pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
from __future__ import print_function
import os.path
import base64
from email.message import EmailMessage
import google.auth
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json 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('tokenSend.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('tokenSend.json', 'w') as token:
token.write(creds.to_json())
# You don't have to care about anything on top of this comment
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'xxxx#gmail.com'
message['From'] = 'xxxx#gmail.com'
message['Subject'] = 'Automated draft'
# encoded message
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
# pylint: disable=E1101
draft = service.users().drafts().create(userId="me",
body=create_message).execute()
print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}')
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
Results in
Draft id: r4575751609218968324
Draft message: {'id': '182fcd72a7a06a96', 'threadId': '182fcd72a7a06a96', 'labelIds': ['DRAFT']}

Download Attachment from gmail api using Python

I am trying to download the attachment from gmail using the python and I am not able to fetch the attachment id from my mail. Please find my code below
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def get_gmail_service():
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
# The file token.json 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.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
return service
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
def get_email_list():
service = get_gmail_service()
results = service.users().messages().list(userId='me',q='from:abc#gmail.com is:read').execute()
# print(results.get('messages',[])[0].get('id',[]))
return results.get('messages', [])[0].get('id', [])
# return results.get('messages',[])
def get_email_content(message_id):
service = get_gmail_service()
attach = service.users().messages().get(userId='me',id =message_id).execute()
attach_id = attach.get('payloads',[]).get('parts',[]).get('body',[])
data = service.users().messages().get(userId='me',id = message_id).execute()
return attach_id
if __name__ == '__main__':
# get_email_list()
print(get_email_content(get_email_list()))
Please correct my code so that I can download the attachment using the gmail api.
There are two main issues with this code.
results.get() method either returns a Message or MessagePart Object. So you only need to use the get() method once to get the complete object and then you can target the specific part of the object you want.
For Example. results.get('messages', [])[0]['id']
A payload for an email can be multipart (which means that "parts" will be an array of MessagePart objects). So we need to iterate over to get a "message part" that has a file. In this case, we can check if the MessagePart object has a filename.
parts = attach.get('payload',[])['parts']
for i in parts:
if( i['filename'] ):
return i['body']['attachmentId']
So After taking care of these two issues, this is the new code:
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def get_gmail_service():
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
# The file token.json 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.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
return service
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
def get_email_list():
service = get_gmail_service()
results = service.users().messages().list(userId='me',q='from:abc#gmail.com is:read').execute()
# print(results.get('messages',[])[0]['id'] )
return results.get('messages', [])[0]['id']
# return results.get('messages',[])
def get_email_content(message_id):
print(message_id)
service = get_gmail_service()
data = service.users().messages().get(userId='me',id = message_id).execute()
attach = service.users().messages().get(userId='me',id =message_id).execute()
parts = attach.get('payload',[])['parts']
for i in parts:
if( i['filename'] ):
return i['body']['attachmentId']
if __name__ == '__main__':
# get_email_list()
print(get_email_content(get_email_list()))
I hope this answers your question!

Google Calendar API -- "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup"

I'm trying to write some Python code that creates a calendar (within my own account) and adds events to it.
I keep on getting the following 403 error:
Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup
This does not happen when I create new calendars:
created_calendar = service.calendars().insert(body=calendar).execute()
But the error does come up when I try and create events within the calendar I just made:
event = service.events().insert(calendarId=created_calendar, body=event).execute()
As per other help threads, I've made all of the correct authentications (API key, OAuth, Service Account):
And my daily limit is way beyond the number of requests I've been sending:
I am specifying the OAuth credentials.json file when creating my api client. I am pointing the GOOGLE_APPLICATION_CREDENTIALS environment variable at the service account key (also json) before running.
I'm not sure how else I can authenticate myself...some help would really be appreciated!
EDIT
This is my script (and I believe a minimal example of the error) in case it's helpful:
import datetime
import pytz
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
# Add .readonly for read only
SCOPES = ['https://www.googleapis.com/auth/calendar']
def build_cal_service():
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(
'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('calendar', 'v3', credentials=creds)
return service
def main():
service = build_cal_service()
calendar = {
'summary': 'TEST CAL',
'timeZone': 'America/Los_Angeles'
}
created_calendar = service.calendars().insert(body=calendar).execute()
time = datetime.datetime(
year=2019,
month=11,
day=9,
hour=21,
tzinfo=pytz.timezone('US/Pacific'))
event = {
'summary': 'test summary',
'description': 'test description.',
'start': {
'dateTime': time.isoformat(),
},
'end': {
'dateTime': (time + datetime.timedelta(hours=1)).isoformat(),
},
'attendees': [
{'email': 'test1#example.com'},
{'email': 'test2#example.com'},
],
}
event = service.events().insert(calendarId=created_calendar, body=event).execute()
How about this modification?
Unfortunately, in my environment, when I tested your provided script, I couldn't confirm the error of Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup. But I think that in your script, event = service.events().insert(calendarId=created_calendar, body=event).execute() occurs an error. So how about the following modification?
From:
event = service.events().insert(calendarId=created_calendar, body=event).execute()
To:
event = service.events().insert(calendarId=created_calendar['id'], body=event).execute()
Note:
created_calendar of created_calendar = service.calendars().insert(body=calendar).execute() is an object. So when the calendar ID is retrieved, please use created_calendar['id'].
This answer supposes that credentials.json is the file which was downloaded by "Create credentials" at "OAuth client ID".
If the error related to the authorization occurs, please delete the file of token.pickle and run the script, and then authorize again.
References:
Calendars: insert
Events: insert

Google calendar error Insufficient Permission

I use this code to test google calendar api,when I use calendarId='primary', there is no error.
But when I use calendarId='uf0udovlhgel0u7a1ifs69o2u8#group.calendar.google.com'.
The error message in my pycharm console is:
gleapiclient.errors.HttpError: https://www.googleapis.com/calendar/v3/calendars/uf0udovlhgel0u7a1ifs69o2u8%40group.calendar.google.com/events?maxResults=10&singleEvents=true&orderBy=startTime&alt=json returned "Insufficient Permission">
In fact,I have this Calendar ID,you can see it in this picture:
Here is my code:
from __future__ import print_function
import datetime
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
# If modifying these scopes, delete the file token.json.
# SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
SCOPES = 'https://www.googleapis.com/auth/calendar'
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
store = file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('./credentials.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('calendar', 'v3', http=creds.authorize(Http()))
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
# events_result = service.events().list(calendarId='primary',
# maxResults=10, singleEvents=True,
# orderBy='startTime').execute()
events_result = service.events().list(calendarId='uf0udovlhgel0u7a1ifs69o2u8#group.calendar.google.com',
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
print(start, event['summary'])
if __name__ == '__main__':
main()
When I click the link in my pycharm console,the error is:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
How can I fix this error?
I know where is wrong,my python file is in folder A,folder A doesn't have file credentials.json.
So there is error.
This is a very stupid mistake.

Categories

Resources