loading...

How to Send and Schedule Emails with Python

maxhumber profile image Max Humber ・3 min read

Sending an email with Python isn't all that difficult. But most tutorials leave out the most important bits! Namely:

  • How to get Gmail to work with Python
  • How to properly manage secrets (passwords)
  • How to schedule message delivery

In this tutorial you're going to learn how to do it all and how do it properly!

Configuring Gmail

First things first, you have to configure Gmail to work with Python...

Sign into your Google Account and go to: https://myaccount.google.com/

Find and click on Security in the sidebar on the left:

security

Scroll down and click on "Turn on access" in the "Less secure app access" panel:

less secure

Flip the toggle:

toggle

Google will probably send you a menacing email about a "Critical security alert"... don't worry, you're about to deal with this...

Managing Secrets

Google's freaking out right now because they know you're about to give Python your email and password. And if you copy-and-pasted your credentials as plaintext into a Python script they would have cause for concern! But you're not going to do that... you're going to manage your secrets the right way.

Create a new directory and cd into it:

mkdir email-tut
cd email-tut

Open text editor and save a .env file to the directory you just created with your email and password:

GMAIL_USER=replace_me@gmail.com
GMAIL_PASSWORD=Sup3r$ecretP@assW0rd!

Because you saved your credentials outside of Python you can .gitignore this .env file so that if/when you upload the repo to Github you won't expose your password!

Now you can create a Python script called emailbot.py inside the email-tut directory (right beside your .env) and fill it with:

import os
from dotenv import load_dotenv

load_dotenv(".env")

SENDER = os.environ.get("GMAIL_USER")
PASSWORD = os.environ.get("GMAIL_PASSWORD")

The load_dotenv function from the python-dotenv package will allow you to safely import your secrets.

Sending an email with the Standard Library requires the following imports and convenience function:

from email.message import EmailMessage
import smtplib

def send_email(recipient, subject, body):
    msg = EmailMessage()
    msg.set_content(body)
    msg["Subject"] = subject
    msg["From"] = SENDER
    msg["To"] = recipient
    server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
    server.login(SENDER, PASSWORD)
    server.send_message(msg)
    server.quit()

Calling the function should fire off an email:

send_email("replace_me@hey.com", subject="test", body="test")

Save the entire python script:

from email.message import EmailMessage
import smtplib
import os
from dotenv import load_dotenv

load_dotenv(".env")

SENDER = os.environ.get("GMAIL_USER")
PASSWORD = os.environ.get("GMAIL_PASSWORD")

def send_email(recipient, subject, body):
    msg = EmailMessage()
    msg.set_content(body)
    msg["Subject"] = subject
    msg["From"] = SENDER
    msg["To"] = recipient
    server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
    server.login(SENDER, PASSWORD)
    server.send_message(msg)
    server.quit()

send_email("replace_me@hey.com", subject="test", body="test")

And test it at the command line to make sure that it works:

python emailbot.py

Sending just "test" is a little pedestrian, spice up your script with some inspirational quotes:

from quote import quote
import random

quotes = quote("William Shakespeare", limit=50)
body = random.sample(quotes, k=1)[0]['quote']

send_email("replace_me@hey.com", subject="Quote of the Day", body=body)

Save the script and call it from the command line again to make sure you didn't screw anything up 😉

Scheduling Delivery

Hopefully you've got a cute little Shakespearean Email Bot working and now you're itching to schedule this bot to send you a quote every day at 9:00am. To accomplish this install hickory:

pip install hickory

Call hickory schedule at the command line with emailbot.py as the script argument:

hickory schedule emailbot.py --every=day@9:00am

And that's literally it!

If you want to check the status of the schedule you can with:

hickory status
# ID      FILE         STATE    RUNS
# ea6b74  emailbot.py  waiting  0  

And to kill the schedule:

hickory kill emailbot.py

Happy emailing!


TL;DR: The entire script:

from email.message import EmailMessage
import smtplib
import os
import random
from dotenv import load_dotenv
from quote import quote

load_dotenv(".env")

SENDER = os.environ.get("GMAIL_USER")
PASSWORD = os.environ.get("GMAIL_PASSWORD")

def send_email(recipient, subject, body):
    msg = EmailMessage()
    msg.set_content(body)
    msg["Subject"] = subject
    msg["From"] = SENDER
    msg["To"] = recipient
    server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
    server.login(SENDER, PASSWORD)
    server.send_message(msg)
    server.quit()

quotes = quote("William Shakespeare", limit=50)
body = random.sample(quotes, k=1)[0]['quote']

send_email("replace_me@hey.com", subject="Quote of the Day", body=body)

Posted on by:

maxhumber profile

Max Humber

@maxhumber

Max is the creator of gazpacho + gif, a Distinguished Faculty Member @ GA, and the author of Personal Finance w/ Python

Discussion

pic
Editor guide