How to Send and Schedule Emails with Python
Tags: python, tutorial, hickory, scheduling
Published: 2020-09-09 15:37:31

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/](https://myaccount.google.com/) 

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

![security](https://dev-to-uploads.s3.amazonaws.com/i/tl1v8u65ffusb0nylj5k.png)

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

![less secure](https://dev-to-uploads.s3.amazonaws.com/i/llpwej9ayt6g0ex8rd8j.png)

Flip the toggle:

![toggle](https://dev-to-uploads.s3.amazonaws.com/i/clzi4fki6wdqf6jed4gq.png)

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:

```sh
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:

```python
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](https://github.com/theskumar/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:

```python
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:

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

Save the entire python script:

```python
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:

```sh
python emailbot.py
```

Sending just "test" is a little pedestrian, spice up your script with some inspirational [quotes](https://github.com/maxhumber/quote):

```python
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](https://github.com/maxhumber/hickory):

```sh
pip install hickory
```

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

```sh
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:

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

And to kill the schedule:

```sh
hickory kill emailbot.py
```

Happy emailing!

---

TL;DR: The entire script:

```python
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)
```