HowTo Use Gmail API to send emails

From MediaWiki
Revision as of 19:27, 28 August 2024 by Rwh (talk | contribs) (→‎Enable Gmail API)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Using Gmail API to send emails

You can find very specific details on how to use the Gmail API for many different purposes.

This page skips over a lot of the details and provides a quick reference on how to send an email using the Gmail API.

Enable Gmail API

You will need to select or create a Google project and add the "Gmail API" to the list of APIs & Services panel as shown on the Google Cloud panel below. One can get to your Google Project here: Google Project. Then navigate within the Google Cloud console using the following: Go to Menu > APIs & Services > OAuth consent screen. Next, navigate to Menu > APIs & Services > Credentials and click Create Credentials > OAuth client ID. Enter a name for the credential.


APIs & Services panel shows that the Gmail API has been enabled for the project

Download Oauth JSON Credentials

You will need to enable/add the Oauth client option on the credentials panel as shown below and then press the download button to download the credentials file.

Credentials panel to download the JSON credentials file necessary for initial authentication of script

You can use the following command to format the downloaded JSON file:

cat ~/Downloads/client_secret_*.json | jq

The output of the above command will produce something similar to the following (where "..." will contain values specific to your authentication):

{
  "installed": {
    "client_id": "...",
    "project_id": "...",
    "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": "...",
    "redirect_uris": [
      "http://localhost"
    ]
  }
}

Note, for the example below save this to the default file location using these commands.

install -d -D -m 700 ~/.local/credentials
install -m 600 ~/Downloads/client_secret_*.json ~/.local/credentials/email-project-secret.json

Sample Python Script

The following python script (send-via-gmail-api.py) can be used to send emails.

#!/usr/bin/python3
#
# Virtual Environment:
#!/home/<user>/path/to/venv/bin/python3
#
# NOTE: In order to run this you need to add the following packages to the system
#
#    sudo dnf install python3-google-api-client python3-oauth2client

import base64
import httplib2
import oauth2client
import os
from email.mime.text import MIMEText
from googleapiclient.discovery import build
from oauth2client import client, tools, file

SCOPES = 'https://www.googleapis.com/auth/gmail.send'

# By default we will look for credentials files under ~/.local/credentials, you should set the permissions
# to this directory to 700
CREDENTIAL_DIR = os.getenv('CREDENTIALS_DIR', os.path.join(os.path.expanduser('~'), '.local', 'credentials'))

# You will need to download this JSON file from Google project that you have enabled the
# Gmail API on. NOTE: This is typically only needed to generate the limited scope credentials JSON
# file once at which point that is the only file that will typically be used when the script
# needs to present itself as legitimate when sending email messages out.

# You can export GOOGLE_PROJECT_SECRETS to the location of your credentials file to override default location
PROJECT_OAUTH_SECRET_FILE = os.getenv('GOOGLE_PROJECT_SECRETS', os.path.join(CREDENTIAL_DIR, 'email-project-secret.json'))
APPLICATION_NAME = 'Gmail API Python Send Email'


def get_credentials():
    if not os.path.exists(CREDENTIAL_DIR):
        os.makedirs(CREDENTIAL_DIR)
    # Actual authorization file for our script with necessary scope set so we can send out email
    credential_path = os.path.join(CREDENTIAL_DIR, "gmail-api-email-send.json")
    credentials = False
    store = oauth2client.file.Storage(credential_path)
    if os.path.exists(credential_path):
        credentials = store.get()
    if not credentials or credentials.invalid:
        # Authorization file needs to be (re)created, request a new one using project credentials
        # IMPORTANT: When this occurs, you will need to interact with a browser to confirm that
        # it is OK to allow the script to send emails on your behalf!
        print(f"Need to update/create {credential_path}")
        flow = client.flow_from_clientsecrets(PROJECT_OAUTH_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        credentials = tools.run_flow(flow, store)
        print(f"Stored credentials to {credential_path}")
    return credentials


def send_email(service, sender, to, subject, message_text):
    message = MIMEText(message_text, "html")
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode("utf-8")
    message = {'raw': raw_message}
    try:
        message = service.users().messages().send(userId='me', body=message).execute()
        print(f'Message sent, Id: {message["id"]}')
        return message
    except Exception as error:
        print(f'An error occurred: {error}')
        return None

def send_email_fixed_font(service, sender, to, subject, message_text):
    pre_message_text = "<pre>" + message_text + "</pre>"
    message = MIMEText(pre_message_text, "html")
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode("utf-8")
    message = {'raw': raw_message}
    try:
        message = service.users().messages().send(userId='me', body=message).execute()
        print(f'Message sent, Id: {message["id"]}')
        return message
    except Exception as error:
        print(f'An error occurred: {error}')
        return None

if __name__ == '__main__':
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = build('gmail', 'v1', http=http)
  #
  # HTML Content
    send_email(service, 'FROM_USER@DOMAIN', 'TO_USER@DOMAIN', 'My Subject', '<p>My Email <b>Body</b></p>')
  #
  # Read a plain text file - Send content using a Fixed Font (<pre> - Tag)
  # TEXT_PATH_FILE_NAME = "/opt/devel/logs/stats.log"
  # with open(TEXT_PATH_FILE_NAME, "r") as src:
  #   contents = src.read()
  # fromAddr = 'tom.smith@gmail.com'
  # toAddr = 'bill.frame@verizon.net'
  # subject = 'Stats Log'
  # send_email_fixed_font(service, toAddr, fromAddr, subject, content)

Be aware of the following:

  • The first time the script is run it will pull up a browser to confirm that you want to grant permission (this won't be required on subsequent runs).
  • You will need to change the FROM_USER@DOMAIN and TO_USER@DOMAIN values at the end of the script to the email accounts that you want to test with.
  • It expects to find the oauth credentials JSON file that you downloaded at $HOME/.local/credentials/email-project-secret.json (feel free to change).
  • The initial authentication will create a new $HOME/.local/credentials/gmail-api-email-send.json file that will then be used during subsequent invocations.
  • Easier to create the initial authentication file on a desktop for the user because a browser will be used for this purpose.

Install Required DNF Packages

The NST distribution does not have all of the Python packages required to run this script by default. You should be able to add the necessary packages via the following command:

sudo dnf install python3-google-api-client python3-oauth2client

Install Required Package Using pip

If you are using a different operating system, you should be able to use "pip" to install the necessary python packages. The example belows show the required python packages using: "pip" in a python virtual environment:

/home/pi/path/to/venv/bin/pip list
Package                  Version
------------------------ ---------
cachetools               5.5.0
certifi                  2024.7.4
charset-normalizer       3.3.2
google-api-core          2.19.1
google-api-python-client 2.142.0
google-auth              2.34.0
google-auth-httplib2     0.2.0
google-auth-oauthlib     1.2.1
googleapis-common-protos 1.65.0
httplib2                 0.22.0
idna                     3.8
oauth2client             4.1.3
oauthlib                 3.2.2
pip                      23.0.1
proto-plus               1.24.0
protobuf                 5.27.3
pyasn1                   0.6.0
pyasn1_modules           0.4.0
pyparsing                3.1.4
requests                 2.32.3
requests-oauthlib        2.0.0
rsa                      4.9
setuptools               66.1.1
six                      1.16.0
smbus                    1.1.post2
sugarpie                 1.4.0
uritemplate              4.1.1
urllib3                  2.2.2

A virtual python environment may be required to install the required packages using "pip". To create a python virtual environment use the following:

python3 -m venv path/to/ven

Install Oauth JSON Credentials

Before running the script, you will need to install the project secrets file in the location that the script will attempt to read it from.

install -d -D -m 700 ~/.local/credentials
install -m 600 ~/Downloads/client_secret_*.json ~/.local/credentials/email-project-secret.json

Initial Run Confirmation

Try running the script initially by hand. This is best done on a desktop since a browser will be required to grant permissions.

./send-via-gmail-api.py

A web browser should appear and you will need to confirm that you want to grant permission to the script before it will be permitted to send emails. Once successfully granted, you should see output similar to the following:

[nst@nst-test ~]$ ./send-via-gmail-api.py 
Need to update/create /home/nst/.local/credentials/gmail-api-email-send.json

Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?client_id=...

If your browser is on a different machine then exit and re-run this
application with the command-line parameter

  --noauth_local_webserver

Authentication successful.
Stored credentials to /home/nst/.local/credentials/gmail-api-email-send.json
Message sent, Id: 190ade399aa9f639

You should find that a new non-zero length credentials JSON file (gmail-api-email-send.json) has been created under your ~/.local/credentials directory:

[nst@nst-test ~]$ ls -al ~/.local/credentials/
total 8
drwx------ 1 nst nst  100 Jul 13 16:58 .
drwx------ 1 nst nst   42 Jul 13 10:46 ..
-rw------- 1 nst nst  402 Jul 13 11:05 email-project-secret.json
-rw------- 1 nst nst 1410 Jul 13 16:58 gmail-api-email-send.json
[nst@nst-test ~]$ 

Subsequent Runs No Confirmation

Run the script again. Now that the system has been confirmed, subsequent runs should proceed without user interaction.

[nst@nst-test ~]$ ./send-via-gmail-api.py 
Message sent, Id: 190ade73471976a6
[nst@nst-test ~]$ 

At this point you should see the emails showing up in your inbox.