Elena' s AI Blog

Sending Emails with Python and receiving your messages

29 May 2024 / 20 minutes to read

Elena Daehnhardt


Midjourney 6.0, May 2024
I am still working on this post, which is mostly complete. Thanks for your visit!

If you click an affiliate link and subsequently make a purchase, I will earn a small commission at no additional cost (you pay nothing extra). This is important for promoting tools I like and supporting my blogging.

I thoroughly check the affiliated products' functionality and use them myself to ensure high-quality content for my readers. Thank you very much for motivating me to write.



Introduction

In this post, I will describe two main email methods using Google and Python. You won’t need to use third-party applications. I use some of these code blocks to send my subscription emails.

I will also share my setup for effortlessly getting your emails on this GitHub static website. This method is efficient, cost-effective, and easily adaptable to my needs.

Getting your messages

As you may know from my post AI-Free Website Design, this blog is hosted on GitHub, and it is a static website. Thus, I can only easily send forms with third-party solutions.

To facilitate form submissions on my static website, I use UseBasin.com for years, and I have just started my affiliation with them.

All you have to do is to generate your HTML form on UseBasin.com website and copy/paste your code into your website. It’s that easy. If you know a bit of HTML, you can customise your forms.

Indeed, you can use other solutions, but I am pleased with UseBasin.com because of its simplicity, quite-well developed spam filters, and, indeed, their integration and messages export features.

So, I got your subscription email list in CSV format and stored it in my mailer directory. Now, I can proceed with sending the emails!

Sending email messages

We will cover the necessary steps, including setting up your Gmail account, using Python’s smtplib module, and configuring all the essential security settings in Gmail.

Prerequisites

Before we start, ensure you have the following:

  1. Python installed on your system. Download it from the official website.
  2. Gmail Account to utilise Google’s SMTP server.

Security Settings

We can now use the Less Secure App Access and Enhanced Security (OAuth2.0).

The main difference between Enhanced Security (OAuth2.0) and Less Secure App Access for sending emails with Python via Gmail boils down to authentication method and security implications:

  • Enhanced Security (OAuth2.0) provides robust security features and granular control. Your application doesn’t directly expose your Gmail password, significantly reducing the risk of unauthorised access or hacking. To begin with, you create credentials (client ID and secret) through the Google Cloud Platform. Your application will request an access token from Google using these credentials. Google verifies your identity and grants a temporary access token specific to your application and intended actions (e.g., emails). Next, your application uses the access token to authenticate with Gmail’s API and send emails securely.
  • Less Secure App Access is a less secure option and should be avoided in production environments or when security is a priority. Opt for this only for testing purposes or if your application cannot use OAuth2.0. Your application directly enters your Gmail username and password to connect to Gmail’s SMTP server while having full access to your Gmail account based on the credentials you provided.

Please note that after September 30, 2024, you must use Enhanced Security when using Google Workspace. Read more in Google Workspace Updates.

Google is removing the IMAP enable/disable toggle from the personal Gmail settings in the coming weeks. IMAP access will always be enabled over OAuth, and your current connections won’t be affected. No action is needed from your end Google Workspace Updates

Suppose you are curious about the security of using Python’s e-mail libraries. In that case, I suggest reading the comprehensive article Nothing new, still broken, insecure by default since then: Python’s e-mail libraries and certificate verification.

Email parameters and messages

Firstly, we set up the email credentials, recipient, subject, and body.

MAIL_USERNAME = 'your_email@gmail.com' # your_email
password = 'your_password' #  is not required for the OAuth2 method
to_email = 'your_email@gmail.com'
subject = 'Test Email'
body = 'This is a test email sent from Python.'

The email.mime library creates email content in a structured format, allowing for more complex email structures, such as attachments and HTML content.

We use MIMEMultipart to create a message container and attach the email body using MIMEText.

from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

message = MIMEMultipart()
message['From'] = MAIL_USERNAME
message['To'] = to_email
message['Subject'] = subject

message.attach(MIMEText(body, 'plain'))

Here is the message. ' attach(MIMEText(body, 'plain')), which attaches the body of the email as plain text. However, it is quite desirable to send emails in HTML format.

You can further improve the HTML string with inline CSS that can enable a better design, dark/light mode, images and fonts you prefer,

html_string = f"<html><body style='background: black; color: white;'>{body}</body></html>"
body_html = MIMEText(html_string, "html")
message.attach(body_html)

The email client will try to render the last attached (HTML) part first; it will snow the plain text when it fails.

Less Secure App Access

Setting Up Your Gmail Account

To send emails through Gmail’s SMTP server, enable “Less secure app access” in your Gmail account settings. This allows third-party apps to access your account.

To enable “Less Secure Apps” for your Gmail account, you’ll need to disable “Two-Step Verification” first if it is disabled.

To turn on the “Less secure app access”, go to Google Account Security Settings.

Import Necessary Libraries

We import smtplib for sending emails using the Simple Mail Transfer Protocol (SMTP), ssl for creating a secure SSL context, MIMEText and MIMEMultipart from email.mime.text and email.mime.multipart respectively, for creating the email content.

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import ssl

SSL is a security protocol that encrypts communication between a web server and a browser or client, protecting sensitive information such as login credentials and credit card details from unauthorised interception.

Send the Email

Finally, we establish a secure connection with Gmail’s SMTP server using SMTP_SSL, log in, and send the email.

# Create a secure SSL context
context = ssl.create_default_context()

# Send the email using SMTP over SSL
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
    # Log in to the SMTP server
    server.login(MAIL_USERNAME, password)
    # Send the email
    server.sendmail(MAIL_USERNAME, to_email, message.as_string())

We create a secure SSL context to secure the connection to the SMTP server with ssl.create_default_context(). The SSL context ensures that the connection to the SMTP server is secure, protecting your login credentials and email content from being intercepted.

This function uses SMTP over SSL with smtplib.SMTP_SSL(“smtp.gmail.com”, 465, context=context) is used as the server, maintaining a connection to the Gmail SMTP server on port 465. The function server.login(MAIL_USERNAME, password) logs us into the SMTP server using your Gmail address and password.

Finally, server.sendmail(MAIL_USERNAME, to_email, message.as_string()) sends the email from your Gmail address to the recipient’s email address using the created email message.

Please notice that if you have two-factor authentication enabled on your Google account, you’ll need to generate an app-specific password instead of using your regular Gmail password.

The test e-mail went well for me. Let’s go further with OAuth2 usage.

Using OAuth2.0 for secure authentication

OAuth2.0 enhances security by avoiding the need to store and manage passwords.

Prerequisites

When we explored the Less Secure App Access, we already mentioned the required G-mail account, Python installation, and the smtplib library (included with Python).

For this more secure OAuth2.0-based setup, we have to install the oauth2client and google-auth libraries:

pip install google-auth google-auth-oauthlib google-auth-httplib2

Setting Up Your Gmail Account

To send emails through Gmail’s SMTP server using OAuth2.0, follow these steps:

  1. Create a Project in Google Cloud Console:
  2. Create OAuth2.0 Credentials:
    • Go to the “Credentials” page.
    • Create OAuth 2.0 Client IDs and download the .json credentials file.

Please note that you might need to fill out the OAuth consent screen when you do not have an organisation set up. In that form, you can fill out your app name, user support email, developer contact information, and other relevant fields.

You can choose any permission level that fits your app requirements in the scope screen. For sending e-mails, I have enabled the following:

  1. Gmail API …/auth/gmail.send Send email on your behalf

You can add more scope elements as needed in your application.

Next, we will have to create an OAuth client ID. I have selected a Desktop app with the name “My blog mailer”. As a result, you will get the message window “OAuth client created

The client ID and secret can always be accessed from Credentials in APIs & Services, where we download our JSON credentials for further use. I have renamed it “client_secret_for_my_blog_mailer.json.”

Import Necessary Libraries

We import necessary libraries, os for checking the existence of the token file, Request from google.auth.transport.requests to refresh credentials, Credentials from google.oauth2.credentials to handle the credentials,InstalledAppFlow from google_auth_oauthlib.flow to manage the OAuth2 flow.

import os
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

For sending emails, we will use base64 to encode the message in a format suitable for the Gmail API, build from googleapiclient.discovery to build the Gmail API service, and HTTPError from requests to handle potential HTTP errors during the API request.

import base64
from googleapiclient.discovery import build
from requests import HTTPError

Define OAuth2.0 Scope and Token File

Set the Gmail API scope and specify paths for the token file TOKEN_FILE and credentials files CREDENTIALS_FILE (usually downloaded from the Google Cloud Console)

Scopes required for accessing the Gmail API. In this case, the scope is set to allow sending emails.

SCOPES = ['https://www.googleapis.com/auth/gmail.send']
TOKEN_FILE = 'token.json'
CREDENTIALS_FILE = 'client_secret_for_my_blog_mailer.json'

Please note that we still need the ‘token.json’ file; we will get it soon.

Function to Get OAuth2.0 Credentials

The function get_oauth2_credentials() handles the OAuth2.0 flow, storing and refreshing tokens as needed.

def get_oauth2_credentials():
    """
    Retrieves OAuth2 credentials for accessing the Gmail API.

    This function checks if valid credentials are available locally. If not,
    it initiates the OAuth2 flow to obtain new credentials and saves them for
    future use.

    Returns:
        Credentials: OAuth2 credentials for the Gmail API.
    """
    creds = None

    # Check if the token file exists
    if os.path.exists(TOKEN_FILE):
        # Load the credentials from the token file
        creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
    
    # If no valid credentials are available, initiate the OAuth2 flow
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            # Refresh the credentials if they are expired
            creds.refresh(Request())
        else:
            # If no valid credentials are found, initiate the OAuth2 flow
            flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
            creds = flow.run_local_server(port=0)
        
        # Save the new credentials to the token file for future use
        with open(TOKEN_FILE, 'w') as token:
            token.write(creds.to_json())
    
    return creds

If the token file exists, we load the credentials from the file. Otherwise, we initiate the OAuth2 flow. We also refresh the credentials using the refresh token. The newly refreshed token is saved into the file ‘TOKEN_FILE’ for future usage.

When calling the function above, your default web browser window will open and request email access. When you select your Gmail account, you will get a message: “The authentication flow has completed. You may close this window.”

Send the Email

We can next use these credentials in the function send_email_with_oauth2(). The service = build('gmail', 'v1', credentials=creds) uses the retrieved credentials to build the Gmail API service for version 1 of the API.

def send_email_with_oauth2():
    """
    Sends an email using the Gmail API with OAuth2 authentication.

    This function retrieves OAuth2 credentials, builds the Gmail API service,
    creates a raw email message, and sends it using the Gmail API.
    """
    # Retrieve OAuth2 credentials
    creds = get_oauth2_credentials()
    
    # Build the Gmail API service
    service = build('gmail', 'v1', credentials=creds)
    
    # Create the raw message object
    # The 'message' variable should be defined with the email content
    raw_message = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
    
    try:
        # Send the email message
        sent_message = service.users().messages().send(userId="me", body=raw_message).execute()
        print(f'Message {sent_message} was sent')
    except HTTPError as error:
        # Handle HTTP errors
        print(f'Error: {error}')
        sent_message = None

send_email_with_oauth2()

We use base64 for creating the raw email message raw_message = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}, which encodes the email message in base64 format. The message variable is an instance of an email message object (e.g., email.mime.text.MIMEText defined before.

Finally, we send the email in the try block using the Gmail API. When catching the HTTPError exceptions, we print an error message and set’ sent_message to None`.

The e-mail was sent well again :)

This approach ensures that the function is self-contained, easy to understand, and handles potential errors gracefully.

Using OAuth2.0 to send emails with Python and Gmail provides a secure way to handle authentication, avoiding the need to manage passwords directly.

For more details, refer to the official Python documentation on smtplib and the Gmail API Overview.

Conclusion

In this post, I have shared the main ideas behind my email messaging setup, how I get your subscription and contact requests with UseBasin.com, and how I send my emails to you with Python and Gmail.

Sending emails using Python with Gmail is straightforward with the smtplib library. Now, you can automate email sending for various applications, from notifications to bulk email campaigns. We have covered Gmail’s SMTP server with Python’s smtplib, set up with a simple, less secure password-based method, and a method utilising OAuth2.0 for secure authentication.

Now, you can also start your email pet project and send emails using Python. All the best!

Try the following fantastic AI-powered applications.

I am affiliated with some of them (to support my blogging at no cost to you). I have also tried these apps myself, and I liked them.

B12.io Recently, I have found an AI-powered platform that enables you to create professional websites, pages, posts, and emails with ease. I will also give it a try and soon write a new post about B12.io (I am working on my coding post at the moment :).

UseBasin.com is a comprehensive backend automation platform for handling submissions, processing, filtering, and routing without coding.

Mixo.io generates websites instantly using AI. Builds stunning landing pages without any code or design. Includes a built-in email waiting list and all the tools you need to launch, grow, and test your ideas.

10web.io builds a website with AI. You can also host your wesbite on 10Web Hosting, and optimise it with PageSpeed Booster.

Did you like this post? Please let me know if you have any comments or suggestions.

Python posts that might be interesting for you



References

1. AI-Free Website Design

2. UseBasin.com

3. Active Python Releases

4. Google Workspace Updates

5. Google Account Security Settings

6. smtplib — SMTP protocol client

7. Gmail API Overview

8. Nothing new, still broken, insecure by default since then: Python’s e-mail libraries and certificate verification

desktop bg dark

About Elena

Elena, a PhD in Computer Science, simplifies AI concepts and helps you use machine learning.





Citation
Elena Daehnhardt. (2024) 'Sending Emails with Python and receiving your messages', daehnhardt.com, 29 May 2024. Available at: https://daehnhardt.com/blog/2024/05/29/sending-emails-with-python/
All Posts