Python Email-Bot with GCP and Gmail API

Scarab Systems Design
5 min readOct 9, 2022

Intro

Need to create an email dispatch server? Gotta spam your friends with fantasy league updates?

Look no further! Follow these steps and you’ll have your Python Email-Bot running in less than 20 minutes.

Checklist

Before you get started, make sure you have the following:

  • A Gmail Account
  • A GCP Account

Part 1: Google Cloud Configurations

  1. Create a new google cloud project
Creating your new project in GCP

2. Ensure the Gmail API is enabled

Make sure to click enable!

3. Create an OAuth Consent Screen

If the GCP Project you’re using is associated with an Organization you can select Internal, otherwise you’ll have to use the External option.

Populate the name and email fields as shown below.

Add your desired scopes. In this case, we’ll leave it blank.

Test users can be added if this is one of your requirements, but bear in mind, tokens generated in the test phase will only last one week before expiring completely.

4. Create credentials for our app

Click the Create Credentials button and select OAuth client ID

From there, we’ll select Desktop App

Make sure to download the generated keys after hitting confirm!

Download the generated key!

We’re now ready to build the bot!

Part 2: Python Script

Note: Most of these steps can be recreated using the Quickstart guide in the Gmail API documentation here

  1. Install the necessary libraries
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

2. Import necessary modules and define your scopes to compose emails.

You can read more about the scopes here.

from __future__ import print_function

import base64
import mimetypes
from email.message import EmailMessage

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

SCOPES = ['https://www.googleapis.com/auth/gmail.compose']

3. Generate our auth token.

Remember those OAuth credentials we just downloaded? Rename the file to credentials.json and paste them in the root directory of your Python project.

creds = None

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())

This bit of code will do the following:

  • Check for an existing OAuth token — if it exists and it’s not expired, use it
  • If there’s no valid token, use the downloaded credentials and create a new token.

4. Create the email!

try:
# create gmail api client
service = build('gmail', 'v1', credentials=creds)
mime_message = EmailMessage()

# headers
mime_message['To'] = "YOUR_EMAIL"
mime_message['From'] = 'TARGET_EMAIL'
mime_message['Subject'] = 'Sent from automated email server'

# text
mime_message.set_content(
"<p> Hi, this is automated mail with attachment on behalf of a Gmail bot</p><p>No need to reply, dw</p> "
, subtype="html"
)

# attachment
attachment_filename = 'YOURFILE.pdf'
# guessing the MIME type
type_subtype, _ = mimetypes.guess_type(attachment_filename)
maintype, subtype = type_subtype.split('/')

with open(attachment_filename, 'rb') as fp:
attachment_data = fp.read()
mime_message.add_attachment(attachment_data, maintype, subtype, filename="YOUR_FILENAME.pdf")

encoded_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode()

create_message = {
'raw': encoded_message
}
# pylint: disable=E1101
send_message = (service.users().messages().send
(userId="me", body=create_message).execute())
except HttpError as error:
print(F'An error occurred: {error}')
send_message = None
return send_message

5. This will leave with you an end file that looks something like this!

from __future__ import print_function

import base64
import mimetypes
import os
from email.message import EmailMessage

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

SCOPES = ['https://www.googleapis.com/auth/gmail.compose']

def gmail_send_attachment():

creds = None

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:
# create gmail api client
service = build('gmail', 'v1', credentials=creds)
mime_message = EmailMessage()

# headers
mime_message['To'] = "YOUR_EMAIL"
mime_message['From'] = 'TARGET_EMAIL'
mime_message['Subject'] = 'Sent from automated email server'

# text
mime_message.set_content(
"<p> Hi, this is automated mail with attachment on behalf of a Gmail bot</p><p>No need to reply, dw</p> "
, subtype="html"
)

# attachment
attachment_filename = 'YOURFILE.pdf'
# guessing the MIME type
type_subtype, _ = mimetypes.guess_type(attachment_filename)
maintype, subtype = type_subtype.split('/')

with open(attachment_filename, 'rb') as fp:
attachment_data = fp.read()
mime_message.add_attachment(attachment_data, maintype, subtype, filename="YOUR_FILENAME.pdf")

encoded_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode()

create_message = {
'raw': encoded_message
}
# pylint: disable=E1101
send_message = (service.users().messages().send
(userId="me", body=create_message).execute())
except HttpError as error:
print(F'An error occurred: {error}')
send_message = None
return send_message

if __name__ == '__main__':
gmail_send_attachment()

You’re good to go! Run and enjoy!

--

--