Create an Email Lyrics bot in Python with SendGrid and OpenAI's ChatGPT

May 15, 2024
Written by
Sam Agnew
Twilion
Reviewed by

Create an Email Lyrics bot in Python with SendGrid and OpenAI's ChatGPT

People are doing so many creative things with OpenAI's latest developer tools. I personally love trying to get it to emulate things that my favorite musicians would write. Let’s walk through how to build an app using OpenAI's ChatGPT with Twilio SendGrid, Python, and Flask to generate lyrics in the style of your favorite artists over emails.

Prerequisites and dependencies

Make sure you have the following before moving on:

  • Python 3 installed on your machine
  • An OpenAI API Key
  • A free SendGrid account
  • A domain on which you will receive emails. For the purposes of this article, I’m going to use yourdomainhere.com. You will need to replace it with your own domain name.

Here is a guide you can follow for setting up your development environment if you are going to be doing more web development with Python and are unfamiliar with things like virtual environments.

Before writing code, you'll need to install some dependencies:

Make sure you create and activate a virtual environment, and then install these with the following command:

pip install sendgrid==6.9.6 Flask==1.1.2 openai==1.14.0

Sign up for SendGrid and create an API key

When creating a SendGrid account, you can choose the free tier for the purpose of this tutorial. Once you have an account, you need to create an API key as seen in this screenshot. You can name it whatever you want, but once it is created make sure you save it before moving on!

Creating a SendGrid API Key
Creating a SendGrid API Key

A good way to save this API key is to set it as an environment variable that you can access from your Python code in order to avoid writing it directly in your code. Set the value of the SENDGRID_API_KEY environment variable to be the API key from your SendGrid account. It still doesn't hurt to make note of it elsewhere though, because you cannot view it again. Here's a useful tutorial if you need help setting environment variables. We will use this API key later on.

Generate Song Lyrics with OpenAI

Now let's write a function using OpenAI's GPT to generate lyrics for us, which we will then call when sending an email. With your API key saved in an environment variable called OPENAI_API_KEY, the following code should work for generating lyrics in the style of whatever artist is passed in:

from openai import OpenAI

openai_client = OpenAI()


def generate_lyrics(artist):
    completion = openai_client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
            {"role": "system", "content": f"You are a songwriter for the artist {artist}, skilled at writing new songs in their classic style."},
            {"role": "user", "content": f"Give me lyrics for a new {artist} song."}
        ])

    return completion.choices[0].message.content

Opening a Python shell and calling this function with your favorite band should output something like the following, in which I used the classic punk band Dead Kennedys:

 

(Verse 1)
In the land of the free, the masses are asleep
Blinded by screens, consuming the deceit
Corruption runs deep, in the corridors of power
But the truth will rise, in our darkest hour

(Chorus)
Rise up, wake up, break out of the lies
Don't be a puppet, see through the disguise
Stand up, speak out, let your voice be heard
In this age of deception, be the rebel with a cause

(Verse 2)
They feed us fear, to keep us in line
Manufactured wars, waged on borrowed time
Divide and conquer, the oldest trick in the book
But unity is our strength, let's overturn the crook

(Chorus)
Rise up, wake up, break out of the lies
Don't be a puppet, see through the disguise
Stand up, speak out, let your voice be heard
In this age of deception, be the rebel with a cause

(Bridge)
The system's broken, but we're not defeated
Together we'll rise, our power can't be deleted
So raise your fist, and shout it from the streets

Seems about right to me!

Send an Email with Python

Now that you have a SendGrid account and API key, you're ready to dive into some code and send emails!

The following code is all you need to send emails in Python:

import os

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

sg_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))


def send_email(to_email, from_email, subject, body):
    message = Mail(from_email,
                   to_email,
                   subject,
                   body)
    response = sg_client.send(message)
    print(response.headers)

To test this code, open your Python shell again and try importing this function, using your own email address as the to_email, so you can verify that it actually works. For the from_email argument, you can use any email address for testing purposes, but I would recommend using one from the domain you own because we're going to use that later anyway.

The next step is to create a Flask app that can receive requests from a SendGrid Inbound Parse webhook, for the purpose of receiving incoming emails that we can then respond to with computer generated lyrics.

Domain authentication

The first step in setting up an email receiving pipeline is to authenticate your domain with Twilio SendGrid, so that they can send and receive emails on it on your behalf. Here is a detailed tutorial you can follow to set up domain authentication. The rest of this blog post will assume that you have a domain authenticated.

Write an incoming email endpoint with Flask

We are now ready to create a small Flask application that will receive incoming emails. Create a file called app.py and add the following, which is the code for the complete application:

import os

from flask import Flask, request
from openai import OpenAI
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail


app = Flask(__name__)

sg_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
openai_client = OpenAI()


@app.route('/email', methods=['POST'])
def email_response():
    to_email = request.form['to']
    from_email = request.form['from']
    artist = request.form['text']

    ai_lyrics = generate_lyrics(artist)

    send_email(from_email, to_email, f'Here are your lyrics in the style of {artist}', 
        ai_lyrics)
    return '', 200


def send_email(to_email, from_email, subject, body):
    message = Mail(from_email,
                   to_email,
                   subject,
                   body)
    response = sg_client.send(message)
    print(response.headers)


def generate_lyrics(artist):
    completion = openai_client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
            {"role": "system", "content": f"You are a songwriter for the artist {artist}, skilled at writing new songs in their classic style."},
            {"role": "user", "content": f"Give me lyrics for a new {artist} song."}
        ])

    return completion.choices[0].message.content


if __name__ == '__main__':
    app.run(debug=True)

The application defines a single web route attached to the /email URL. SendGrid will invoke this route each time an incoming email arrives, sending all the information about the email as a standard form post. You can review the complete list of email parameters in the SendGrid documentation. In this function, the code from our other module is called in order to respond to this incoming email with output from our generate_lyrics() function, based on the artist they included in the body.

You can run this code with the command python app.py in your terminal.

The application is now running and waiting for incoming requests, but it is only reachable from your own computer because we are running a development server. Leave it running for now, and we will make it accessible on the Internet.

Configure the SendGrid Inbound Parse webhook

With the Flask application running, we need to connect it with SendGrid to be able to receive emails. We are going to use a tool called ngrok to create a public URL that SendGrid can use to forward requests to our locally running application.

If you haven’t yet, install ngrok on your system. With the Python code running, open a second terminal window to start ngrok as follows:

ngrok http 5000

This is telling ngrok to create a “tunnel” from the public Internet into port 5000 in our local machine, where the Flask app is listening for requests. The output should look something like this:

A terminal window displaying an ngrok tunnel running
A terminal window displaying an ngrok tunnel running

Now that you have a publicly accessible URL, go back to the SendGrid dashboard and under Settings select Inbound Parse, then click on “Add Host & URL”.

The SendGrid Dashboard displaying the page for configuring an Inbound Parse webhook, pointing to the "Add Host & URL" button
The SendGrid Dashboard displaying the page for configuring an Inbound Parse webhook, pointing to the "Add Host & URL" button

In the next screen you can optionally enter a subdomain on which you will be receiving emails. This can be any subdomain that is available on your domain. If you want to receive emails on your top-level domain it can be left empty.

Next you have to select your domain name. This is a dropdown list that shows all the domains that you have verified with SendGrid. If you are doing this for the first time you will only see the domain that you verified above.

Next you have to enter the destination URL for your webhook. This is the URL generated by ngrok with /email attached at the end. In my case this was http://bbf1b72b.ngrok.io/email.

Click Add to configure your webhook.

The prompt for adding a SendGrid Inbound Parse webhook, pointing to the "Add" button
The prompt for adding a SendGrid Inbound Parse webhook, pointing to the "Add" button

Now you will see an entry for your webhook in the main Inbound Parse page:

A screenshot displaying what the Inbound Parse configuration page should look like afterwards.
A screenshot displaying what the Inbound Parse configuration page should look like afterwards.

Keep in mind that ngrok URLs change each time ngrok is stopped and restarted, so during development you may need to edit your webhook to update the URL. When you deploy your webhook for production use you will host it directly on a public URL, so ngrok will not be needed.

Set up an MX Record

The domain that you selected to receive emails in the previous section needs to be defined in the DNS configuration of your domain provider with a MX record. The value is the same for all SendGrid customers: mx.sendgrid.net. (note the trailing dot after “net”).

For example, if you are using a subdomain called "parse" to receive your emails, in Google DNS it should look something like this:

A screenshot of Google Domains with an MX record set
A screenshot of Google Domains with an MX record set

Depending on what domain provider you use, you may need to enter the complete hostname for this DNS record, so in that case it would be parse.yourdomainhere.com.

If you decided to not define a subdomain in the previous section, then your hostname is going to be yourdomainhere.com or just “ @”.

Once you add this final entry to your domain’s DNS configuration you will once again need to wait for it to propagate before you can use it.

Send a test email

While having your Python code and ngrok running, open your email client and send a test email. In the To field you can enter any name that you want, as SendGrid captures all usernames. For the body of the email, just send the name of your favorite band or artist. What goes after the @ needs to be your complete email-receiving domain.

Wait a minute or two and eventually SendGrid will invoke the ngrok webhook URL, which in turn will invoke your Flask endpoint. You should get a response that looks something like this:

An email containing computer-generated lyrics in the style of Black Sabbath
An email containing computer-generated lyrics in the style of Black Sabbath

Now that you know how to combine SendGrid with OpenAI, I can't wait to see what you build with Inbound Parse in the future.