How to read emails from gmail? - python

I am trying to connect my gmail to python, but show me this error:
I already checked my password, any idea what can be?
b'[AUTHENTICATIONFAILED] Invalid credentials (Failure)'
Traceback (most recent call last):
File "/Users/myuser/Documents/migrations/untitled3.py", line 29, in read_email_from_gmail
mail.login(FROM_EMAIL,FROM_PWD)
File "/Users/myuser/opt/anaconda3/lib/python3.9/imaplib.py", line 612, in login
raise self.error(dat[-1])
imaplib.IMAP4.error: b'[AUTHENTICATIONFAILED] Invalid credentials (Failure)'
Here my code:
Also i want to know which port I can use?
import smtplib
import time
import imaplib
import email
import traceback
ORG_EMAIL = "#gmail.com"
FROM_EMAIL = "myemail" + ORG_EMAIL
FROM_PWD = "mypassword"
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = ??
def read_email_from_gmail():
try:
mail = imaplib.IMAP4_SSL(SMTP_SERVER)
mail.login(FROM_EMAIL,FROM_PWD)
mail.select('inbox')
data = mail.search(None, 'ALL')
mail_ids = data[1]
id_list = mail_ids[0].split()
first_email_id = int(id_list[0])
latest_email_id = int(id_list[-1])
for i in range(latest_email_id,first_email_id, -1):
data = mail.fetch(str(i), '(RFC822)' )
for response_part in data:
arr = response_part[0]
if isinstance(arr, tuple):
msg = email.message_from_string(str(arr[1],'utf-8'))
email_subject = msg['subject']
email_from = msg['from']
print('From : ' + email_from + '\n')
print('Subject : ' + email_subject + '\n')
except Exception as e:
traceback.print_exc()
print(str(e))
read_email_from_gmail()
My main goal is be able to get the CSV file from each email, but for now I just want to read messages.

Best way to read email is using GMAIL API. Google has stopped accessing gmail account using username and password via any outside app from 30 the May 2022.
So this is how you read gmail from python.
What this app does:
Read gmail message (Unread)
Make the message as Read after reading the message
import os.path
import base64
import json
import re
import time
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
import logging
import requests
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/gmail.modify']
def readEmails():
"""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(
# your creds file here. Please create json file as here https://cloud.google.com/docs/authentication/getting-started
'my_cred_file.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)
results = service.users().messages().list(userId='me', labelIds=['INBOX'], q="is:unread").execute()
messages = results.get('messages',[]);
if not messages:
print('No new messages.')
else:
message_count = 0
for message in messages:
msg = service.users().messages().get(userId='me', id=message['id']).execute()
email_data = msg['payload']['headers']
for values in email_data:
name = values['name']
if name == 'From':
from_name= values['value']
for part in msg['payload']['parts']:
try:
data = part['body']["data"]
byte_code = base64.urlsafe_b64decode(data)
text = byte_code.decode("utf-8")
print ("This is the message: "+ str(text))
# mark the message as read (optional)
msg = service.users().messages().modify(userId='me', id=message['id'], body={'removeLabelIds': ['UNREAD']}).execute()
except BaseException as error:
pass
except Exception as error:
print(f'An error occurred: {error}')
You need to create credential file in Google account
How to create credential file here https://cloud.google.com/docs/authentication/getting-started

Here's the problem, Gmail has a new politic who doesn't allow to use insecure apps.
To help keep your account secure, from May 30, 2022, ​​Google no longer supports the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password.

You need to enable 'Less secure apps' in your Gmail account if you're going to check it this way. For that reason, it would be better to use the Gmail API.
SMTP port is not set - ensure you are using the correct port (993)

As Carlos pointed out, Google no longer allows insecure apps to access GMail. The 'Less secure apps' option is no longer available. I encountered the same Gmail login issue but I was glad to find the article below and have solved the issue.
https://towardsdatascience.com/how-to-easily-automate-emails-with-python-8b476045c151

smtp.gmail.com is for sending emails, to read the emails you want to connect to imap.gmail.com. It works for me with an App password. I wrote a short application to sync multiple email servers, which is tested with Google mail. Still as mentioned above, it is recommended to use the Google API. On my Github account I have an example application for using the Google API and sorting emails with machine learning, if that is interesting for you.

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

How to Capture The OAuth Redirect to POST A Response

So my colleague and I have an application whereby we need to capture the OAuth Redirect from Google's OAuth Server Response, the reason being is we need to send a payload to capture to renew our pickle token, and we need to do it without human intervention. The code looks like this:
#!/usr/bin/env python3
import pickle
import os.path
import pandas as pd
import requests
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import base64
from datetime import datetime, timedelta
from urllib.parse import unquote
from bs4 import BeautifulSoup
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def search_message(service, user_id, search_string):
"""
Search the inbox for emails using standard gmail search parameters
and return a list of email IDs for each result
PARAMS:
service: the google api service object already instantiated
user_id: user id for google api service ('me' works here if
already authenticated)
search_string: search operators you can use with Gmail
(see https://support.google.com/mail/answer/7190?hl=en for a list)
RETURNS:
List containing email IDs of search query
"""
try:
# initiate the list for returning
list_ids = []
# get the id of all messages that are in the search string
search_ids = service.users().messages().list(userId=user_id, q=search_string).execute()
# if there were no results, print warning and return empty string
try:
ids = search_ids['messages']
except KeyError:
print("WARNING: the search queried returned 0 results")
print("returning an empty string")
return ""
if len(ids) > 1:
for msg_id in ids:
list_ids.append(msg_id['id'])
return (list_ids)
else:
list_ids.append(ids['id'])
return list_ids
except:
print("An error occured: %s")
def get_message(service, user_id, msg_id):
"""
Search the inbox for specific message by ID and return it back as a
clean string. String may contain Python escape characters for newline
and return line.
PARAMS
service: the google api service object already instantiated
user_id: user id for google api service ('me' works here if
already authenticated)
msg_id: the unique id of the email you need
RETURNS
A string of encoded text containing the message body
"""
try:
final_list = []
message = service.users().messages().get(userId=user_id, id=msg_id).execute() # fetch the message using API
payld = message['payload'] # get payload of the message
report_link = ""
mssg_parts = payld['parts'] # fetching the message parts
part_one = mssg_parts[1] # fetching first element of the part
#part_onee = part_one['parts'][1]
#print(part_one)
part_body = part_one['body'] # fetching body of the message
part_data = part_body['data'] # fetching data from the body
clean_one = part_data.replace("-", "+") # decoding from Base64 to UTF-8
clean_one = clean_one.replace("_", "/") # decoding from Base64 to UTF-8
clean_one = clean_one.replace("amp;", "") # cleaned amp; in links
clean_two = base64.b64decode(clean_one) # decoding from Base64 to UTF-8
soup = BeautifulSoup(clean_two, features="html.parser")
for link in soup.findAll('a'):
if "talentReportRedirect?export" in link.get('href'):
report_link = link.get('href')
break
final_list.append(report_link) # This will create a dictonary item in the final list
except Exception:
print("An error occured: %s")
return final_list
def get_service():
"""
Authenticate the google api client and return the service object
to make further calls
PARAMS
None
RETURNS
service api object from gmail for making calls
"""
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)
auth_link = build('gmail', 'v1', credentials=creds)
parsed_url = unquote(auth_link).split('redirect')[-1]
return parsed_url
def get_report(link_array):
for link in link_array:
df = requests.get(link[0], allow_redirects=True)
# df.encoding
# dt = pd.DataFrame(data=df)
print(link)
# upload_to_database(df) -- Richard Barret please update this function
print(df.text)
## dt.to_csv(r'C:\Users\user\Desktop\api_gmail.csv', sep='\t',header=True)
if __name__ == "__main__":
link_list = []
monday = datetime(2022,12,5)#datetime.now() - timedelta(days=datetime.now().weekday())
thursday = datetime(2022,12,8)#datetime.now() - timedelta(days=datetime.now().weekday() - 3)
query = 'from:messages-noreply#linkedin.com ' + 'after:' + monday.strftime('%Y/%m/%d') + ' before:' + thursday.strftime('%Y/%m/%d')
service = get_service()
mssg_list = search_message(service, user_id='me', search_string=query)
for msg in mssg_list:
link_list.append(get_message(service, user_id='me', msg_id=msg))
get_report(link_list)
It is assumed that you have a directory structure like this:
├── credentials.json
├── gmail_api_linkedin.py
└── requirements.txt
Obviously, you won't have the credentials.json file, but in essence, the code works and redirects us to a login page to retrieve the new pickle:
The main thing is we can't interact with that in an autonomous fashion. As such, how can we capture the URL from the server that prints out the following information the is differenter every single time.
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=212663976989-96o952s9ujadjgfdp6fm0p462p37opml.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A58605%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly&state=ztJir0haFQlvTP79BRthhmEHlSsqIj&access_type=offline
More succinctly, how can we capture the URL in a pythonic manner to send POST and PUT requests to that redirect?
renew our pickle token
I still do not understand why you feel the need to renew your token pickle.
how it all works.
The following example will spawn the consent screen directly on the machine its running on. It then stores the token within the token.json file
token.json
This file contains all the information needed by the script to run. It can automatically request a new access token when ever it needs.
{
"token": "[REDACTED]",
"refresh_token": "[REDACTED]",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "[REDACTED]",
"client_secret": "[REDACTED],
"scopes": [
"https://mail.google.com/"
],
"expiry": "2023-01-03T19:06:13.959468Z"
}
gmail quickstart.
# 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 google.auth.exceptions
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 v1 API.
Prints a list of user messages.
"""
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'):
try:
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
creds.refresh(Request())
except google.auth.exceptions.RefreshError as error:
# if refresh token fails, reset creds to none.
creds = None
print(f'An error occurred: {error}')
# 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(
'C:\YouTube\dev\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:
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail v1 API
results = service.users().messages().list(
userId='me').execute()
messages = results.get('messages', [])
if not messages:
print('No messages found.')
return
print('Messages:')
for message in messages:
print(u'{0} ({1})'.format(message['id'], message['threadId']))
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
expired refresh token.
If your issue is in fact that your refresh tokens are expiring this is because your app is currently in the testing phase. If you set your app to production then your refresh tokens will stope expiring.

How to authonticate only one / offline with client.json key for Gmail API?

I am running an app that needs to access the gmail emails. I had store the credentials in the json file and writing pickle file to cache the token. Is it really necessary to authenticate every time you run the application ? I am running application on Pi and DO NOT HAVE an access to it once i deploy that. How can store the cache that i can use for longer time/ multiple time as right now it is going to expire in the middle of the run and turn off my application. I followed the Google API docs but hard to follow that. Here is my code for the authentication would look like(it is same as their documentation. You can comment out the Pi library and its function that would not stop you to run the code.)
Could anyone please suggest a better way to do this ?
# GMail Downloader using the GMail API
# Logs into GMail using the API, checks for Iridium SBD messages every minute,
# saves the message and attachment to file, and moves the message out of the inbox
# Follow these instructions to create your credentials:
# https://developers.google.com/gmail/api/quickstart/python
from __future__ import print_function
import httplib2
import os
import base64
import email
import datetime
import time
from apiclient import discovery
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from apiclient import errors
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
# Pi libraries
import RPi.GPIO as GPIO
Relay_Ch1 = 26
Relay_Ch2 = 20
Relay_Ch3 = 21
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(Relay_Ch1, GPIO.OUT)
GPIO.setup(Relay_Ch2, GPIO.OUT)
GPIO.setup(Relay_Ch3, GPIO.OUT)
# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/gmail-python-quickstart.json
#SCOPES = 'https://www.googleapis.com/auth/gmail.readonly' # Read only
# SCOPES = 'https://www.googleapis.com/auth/gmail.modify' # Everything except delete
SCOPES = 'https://mail.google.com/' # Full permissions
# CLIENT_SECRET_FILE = 'client_secret.json'
# APPLICATION_NAME = 'Gmail API Python Quickstart'
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
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())
service = build('gmail', 'v1', credentials=creds)
return service
def ListMessagesMatchingQuery(service, user_id, query=''):
"""List all Messages of the user's mailbox matching the query.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
query: String used to filter messages returned.
Eg.- 'from:user#some_domain.com' for Messages from a particular sender.
Returns:
List of Messages that match the criteria of the query. Note that the
returned list contains Message IDs, you must use get with the
appropriate ID to get the details of a Message.
"""
response = service.users().messages().list(userId=user_id,q=query).execute()
messages = []
if 'messages' in response:
messages.extend(response['messages'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().messages().list(userId=user_id, q=query,pageToken=page_token).execute()
messages.extend(response['messages'])
return messages
def SaveAttachments(service, user_id, msg_id):
"""Get and store attachment from Message with given id.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message containing attachment.
"""
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
local_date = datetime.datetime.fromtimestamp(float(message['internalDate'])/1000.)
date_str = local_date.strftime("%y-%m-%d_%H-%M-%S_")
for part in message['payload']['parts']:
if part['filename']:
if 'data' in part['body']:
data=part['body']['data']
else:
att_id=part['body']['attachmentId']
att=service.users().messages().attachments().get(userId=user_id, messageId=msg_id,id=att_id).execute()
data=att['data']
file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
path = date_str+part['filename']
with open(path, 'wb') as f:
f.write(file_data)
f.close()
def SaveMessageBody(service, user_id, msg_id):
"""Save the body from Message with given id.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message.
"""
message = service.users().messages().get(userId=user_id, id=msg_id, format='raw').execute()
msg_str = base64.urlsafe_b64decode(message['raw'].encode('ASCII'))
# mime_msg = email.message_from_string(msg_str)
mime_msg = email.message_from_bytes(msg_str)
messageMainType = mime_msg.get_content_maintype()
file_data = ''
#print(messageMainType)
if messageMainType == 'multipart':
for part in mime_msg.get_payload():
partType = part.get_content_maintype()
#print('...'+partType)
if partType == 'multipart':
for multipart in part.get_payload():
multipartType = multipart.get_content_maintype()
#print('......'+multipartType)
if multipartType == 'text':
file_data += multipart.get_payload()
break # Only get the first text payload
elif partType == 'text':
file_data += part.get_payload()
elif messageMainType == 'text':
file_data += mime_msg.get_payload()
local_date = datetime.datetime.fromtimestamp(float(message['internalDate'])/1000.)
date_str = local_date.strftime("%y-%m-%d_%H-%M-%S_")
subject = GetSubject(service, user_id, msg_id);
for c in r' []/\;,><&*:%=+#!#^()|?^': # substitute any invalid characters
subject = subject.replace(c,'_')
path = date_str+subject+".txt"
with open(path, 'w') as f:
f.write(file_data)
f.close()
def GetSubject(service, user_id, msg_id):
"""Returns the subject of the message with given id.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message.
"""
subject = ''
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
payload = message["payload"]
headers = payload["headers"]
for header in headers:
if header["name"] == "Subject":
subject = header["value"]
break
return subject
def MarkAsRead(service, user_id, msg_id):
"""Marks the message with given id as read.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message.
"""
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'removeLabelIds': ['UNREAD']}).execute()
def MoveToLabel(service, user_id, msg_id, dest):
"""Changes the labels of the message with given id to 'move' it.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message.
dest: destination label
"""
# Find Label_ID of destination label
results = service.users().labels().list(userId=user_id).execute()
labels = results.get('labels', [])
for label in labels:
if label['name'] == dest: dest_id = label['id']
# service.users().messages().get(userId='me', id=message['id']).execute()
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'addLabelIds': [dest_id]}).execute()
service.users().messages().modify(userId=user_id, id=msg_id, body={ 'removeLabelIds': ['INBOX']}).execute()
def main():
"""Creates a Gmail API service object.
Searches for unread messages with "SBD Msg From Unit" in the subject.
Saves the message body and attachment to disk.
Marks the message as read.
Moves it to the SBD folder.
"""
# credentials = get_credentials()
# http = credentials.authorize(httplib2.Http())
# service = discovery.build('gmail', 'v1', http=http)
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())
service = build('gmail', 'v1', credentials=creds)
results = service.users().messages().list(userId='me', labelIds = ['INBOX']).execute()
# results = service.users().messages().list(userId='me, q="subject:"SBD Msg From Unit" is:unread has:attachment").execute()
# messages = results.get('messages', [])
messages = ListMessagesMatchingQuery(service, 'me', 'subject:\"Emergency Alert Activated: GSATMicro\" is:unread') # has:attachment
if messages:
for message in messages:
print('Processing: '+GetSubject(service, 'me', message["id"]))
SaveMessageBody(service, 'me', message["id"])
# SaveAttachments(service, 'me', message["id"])
MarkAsRead(service, 'me', message["id"])
# MoveToLabel(service, 'me', message["id"], 'SBD')
#email_subject = message['subject']
#email_from = message['from']
#if email_from == "alerts#gsattrack.com" and email_subject == 'EMERGENCY: GSATMicro - Emergency On':
print("Emergency Alert received from GSATMicro..")
print("\n Help will be on its way .. Turning relay ON.. \n")
#Control the Channel 1
print("\n Turning Relay 1 ON.. \n")
GPIO.output(Relay_Ch1, GPIO.HIGH)
print("Channel 1:The Common Contact is access to the Normal Open Contact!")
time.sleep(0.5)
GPIO.output(Relay_Ch1, GPIO.LOW)
print("Channel 1:The Common Contact is access to the Normal Closed Contact!\n")
time.sleep(0.5)
GPIO.output(Relay_Ch1, GPIO.HIGH)
#else:
#print('No messages found!')
if __name__ == '__main__':
print('GMail API Downloader')
print('Press Ctrl-C to quit')
try:
while True:
#print('Checking for messages...')
main()
for i in range(60):
time.sleep(1) # Sleep
except KeyboardInterrupt:
print('Ctrl-C received!')

Downloading password-protected PDFs using GMAIL API in Python

This is my code to download the pdf attachments using GMAIL API in Python. It works fine in sense that I am able to download the pdf attachments but they don't open. I get the following error "There was an error opening this document. The file is damaged and could not be repaired".
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
import base64
from apiclient import errors
# 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.
# to manage a different gmail account, delete the existing token.json file from the folder
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())
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail API
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
# Call the Gmail API to fetch INBOX
results = service.users().messages().list(userId='me', labelIds=['Label_9213971794059785832']).execute()
messages = results.get('messages', [])
print(messages[1])
if not messages:
print("No messages found.")
else:
print("Message snippets:")
for message in messages:
GetAttachments(service,'me',message['id'])
# msg = service.users().messages().get(userId='me', id=message['id']).execute()
# print(msg)
# break
def GetAttachments(service, user_id, msg_id, store_dir="attachments/"):
"""Get and store attachment from Message with given id.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message containing attachment.
store_dir: The directory used to store attachments.
"""
try:
message = service.users().messages().get(userId=user_id, id=msg_id).execute()
parts = [message['payload']]
while parts:
part = parts.pop()
if part.get('parts'):
parts.extend(part['parts'])
if part.get('filename'):
if 'data' in part['body']:
file_data = base64.urlsafe_b64decode(part['body']['data'].encode('UTF-8'))
#self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], part['size']))
elif 'attachmentId' in part['body']:
attachment = service.users().messages().attachments().get(
userId=user_id, messageId=message['id'], id=part['body']['attachmentId']
).execute()
file_data = base64.urlsafe_b64decode(attachment['data'].encode('UTF-8'))
#self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], attachment['size']))
else:
file_data = None
if file_data:
#do some staff, e.g.
path = ''.join([store_dir, part['filename']])
with open(path, 'w') as f:
f.write(str(file_data))
except errors.HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
please help!
Found the mistake! I needed to write the bytes, not string.
modified two lines of the code as following:
with open(path, 'wb') as f:
f.write(file_data)

How to send an email through gmail without enabling 'insecure access'?

Google are pushing us to improve the security of script access to their gmail smtp servers. I have no problem with that. In fact I'm happy to help.
But they're not making it easy. It's all well and good to suggest we Upgrade to a more secure app that uses the most up to date security measures, but that doesn't help me work out how to upgrade bits of code that look like this:
server = smtplib.SMTP("smtp.gmail.com", 587)
server.ehlo()
server.starttls()
server.login(GMAIL_USER, GMAIL_PASSWORD)
server.sendmail(FROM, TO, MESSAGE)
server.close()
Sure, I'll go and turn on "Access for less secure apps", but if anyone has worked out what to replace this code with, I'll be grateful.
This was painful, but I seem to have something going now...
Python3 is not supported (yet)
I don't think it will be too hard to attain, as I was stumbling through converting packages without hitting anything massive: just the usual 2to3 stuff. Yet after a couple of hours I got tired of swimming upstream. At time of writing, I couldn't find a published package for public consumption for Python 3. The python 2 experience was straight-forward (in comparison).
Navigating the Google website is half the battle
No doubt, over time, this will change. Ultimately you need to download a client_secret.json file. You can only (probably) do this setting up stuff via a web browser:
You need a google account - either google apps or gmail. So, if you haven't got one, go get one.
Get yourself to the developers console
Create a new project, and wait 4 or 400 seconds for that to complete.
Navigate to API's and Auth -> Credentials
Under OAuth select Create New Client ID
Choose Installed Application as the application type and Other
You should now have a button Download JSON. Do that. It's your client_secret.json—the passwords so to speak
But wait that's not all!
You have to give your application a "Product Name" to avoid some odd errors. (see how much I suffered to give you this ;-)
Navigate to API's & auth -> Consent Screen
Choose your email
Enter a PRODUCT NAME. It doesn't matter what it is. "Foobar" will do fine.
Save
Newsflash! Whoa. Now there's even more!
Navigate to API's & auth -> APIs -> Gmail API
Click the button Enable API
Yay. Now we can update the emailing script.
Python 2
You need to run the script interactively the first time. It will open a web browser on your machine and you'll grant permissions (hit a button). This exercise will save a file to your computer gmail.storage which contains a reusable token.
[I had no luck transferring the token to a machine which has no graphical browser functionality—returns an HTTPError. I tried to get through it via the lynx graphical browser. That also failed because google have set the final "accept" button to "disabled"!? I'll raise another question to jump this hurdle (more grumbling)]
First you need some libraries:
pip install --upgrade google-api-python-client
pip install --upgrade python-gflags
you need to change the to and from addresses
make sure you have the client_token.json file whereever the Storage instructions expect it
the directory needs to be writable so it can save the gmail.storage file
Finally some code:
import base64
import httplib2
from email.mime.text import MIMEText
from apiclient.discovery import build
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import run
# Path to the client_secret.json file downloaded from the Developer Console
CLIENT_SECRET_FILE = 'client_secret.json'
# Check https://developers.google.com/gmail/api/auth/scopes for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.compose'
# Location of the credentials storage file
STORAGE = Storage('gmail.storage')
# Start the OAuth flow to retrieve credentials
flow = flow_from_clientsecrets(CLIENT_SECRET_FILE, scope=OAUTH_SCOPE)
http = httplib2.Http()
# Try to retrieve credentials from storage or run the flow to generate them
credentials = STORAGE.get()
if credentials is None or credentials.invalid:
credentials = run(flow, STORAGE, http=http)
# Authorize the httplib2.Http object with our credentials
http = credentials.authorize(http)
# Build the Gmail service from discovery
gmail_service = build('gmail', 'v1', http=http)
# create a message to send
message = MIMEText("Message goes here.")
message['to'] = "yourvictim#goes.here"
message['from'] = "you#go.here"
message['subject'] = "your subject goes here"
body = {'raw': base64.b64encode(message.as_string())}
# send it
try:
message = (gmail_service.users().messages().send(userId="me", body=body).execute())
print('Message Id: %s' % message['id'])
print(message)
except Exception as error:
print('An error occurred: %s' % error)
Hopefully that gets us all started. Not as simple as the old way, but does look a lot less complicated now I can see it in the flesh.
It seems that John Mee's answer is out of date.
It does not work in July, 2016.
Maybe due to the update of Gmail's API.
I update his code (python 2) as below:
"""Send an email message from the user's account.
"""
import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
import os
#from __future__ import print_function
import httplib2
import os
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
from apiclient import errors
SCOPES = 'https://www.googleapis.com/auth/gmail.compose'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Quickstart'
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
def SendMessage(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: 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:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print 'Message Id: %s' % message['id']
return message
except errors.HttpError, error:
print 'An error occurred: %s' % error
def CreateMessage(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())}
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'sendEmail.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
if __name__ == "__main__":
try:
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
SendMessage(service, "me", CreateMessage("send#gmail.com", "receive#gmail.com", "Test gmail automation", "Hello world"))
except Exception, e:
print e
raise
Note that if you encounter the error Insufficient Permission, one possible reason is that the scope in program is not set correctly.
The other possible reason may be that you need to delete the storage json file ("sendEmail.json" in this program) and refresh your program. More details may be seen in this post.
Updated sample for Python 3, and GMail's current API, below.
Note that to get the credentials.json file below, you'll need to create an Oauth client ID credential here, after selecting the relevant GCP project. Once you've created it you'll be shown the client key and client secret. Close that prompt, and click the down arrow next to the account. This is the file you'll need.
import base64
import logging
import mimetypes
import os
import os.path
import pickle
from email.mime.text import MIMEText
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient import errors
from googleapiclient.discovery import build
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(
'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
def send_message(service, sender, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: 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(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
s = message.as_string()
b = base64.urlsafe_b64encode(s.encode('utf-8'))
return {'raw': b.decode('utf-8')}
if __name__ == '__main__':
logging.basicConfig(
format="[%(levelname)s] %(message)s",
level=logging.INFO
)
try:
service = get_service()
message = create_message("from#gmail.com", "to#gmail.com", "Test subject", "Test body")
send_message(service, "from#gmail.com", message)
except Exception as e:
logging.error(e)
raise
Have you considered using the Gmail API? The API has security features built in and is optimized specifically for Gmail. You can find the API documentation on http://developers.google.com - for example, here's the documentation for the Send API call:
https://developers.google.com/gmail/api/v1/reference/users/messages/send
I'm including some code that has been updated for python 3 usage - it seems to send emails once you get the necessary permissions and OAuth tokens working. It's mostly based off the Google API website samples.
from __future__ import print_function
import base64
import os
from email.mime.text import MIMEText
import httplib2
from apiclient import discovery
from googleapiclient import errors
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/gmail-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Quickstart'
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'gmail-python-quickstart.json')
store = Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
to = 'test#email.com'
sender = 'test#email.com'
subject = 'test emails'
message_text = 'hello this is a text test message'
user_id = 'me'
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_bytes()).decode())}
def send_message(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: 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:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: {}'.format(message['id']))
return message
except errors.HttpError as error:
print('An error occurred: {}'.format(error))
def main():
"""Shows basic usage of the Gmail API.
Creates a Gmail API service object and outputs a list of label names
of the user's Gmail account.
"""
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
msg = create_message(sender,to,subject,message_text)
message = (service.users().messages().send(userId=user_id, body=msg)
.execute())
print('Message Id: {}'.format(message['id']))
results = service.users().messages().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
else:
print('Labels:')
for label in labels:
print(label['name'])
if __name__ == '__main__':
main()
it is very simple.
Go to Gmail account settings.
Search for "App passwords".
Generate an app password.
Use that password instead of the Gmail password.
The advantage of app password is that you don't need to turn off two factor authentication and also you don't need to allow insecure apps.
This is the official link about Google app passwords https://support.google.com/mail/answer/185833?hl=en-GB
Hope this helps👍
protected string SendEmail(string toAddress, string subject, string body)
{
string result = "Message Sent Successfully..!!";
string senderID = "...........";// use sender's email id here..
const string senderPassword = "........."; // sender password here...
try
{
SmtpClient smtp = new SmtpClient
{
Host = "smtp.gmail.com", // smtp server address here...
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
Credentials = new System.Net.NetworkCredential(senderID, senderPassword),
Timeout = 30000,
};
MailMessage message = new MailMessage(senderID, toAddress, subject, body);
smtp.Send(message);
}
catch (Exception ex)
{
result = "Error sending email.!!!";
}
return result;
}

Categories

Resources