Building a ChatGPT like SMS Bot

March 02 2023
image prompt: a render of an iphone 14 on a light backround with chat bubbles with text displayed on the screen
image prompt: a render of an iphone 14 on a light backround with chat bubbles with text displayed on the screen

Update: Jan 22 2024 - This tutorial is _slightly_ outdated and I'm working on an updated version that uses the Chat endpoints. The high level flow should still be the same.

In December, a friend shared a TikTok video that demonstrated how GPT-3 could be used via SMS and selling a no-code Zapier guide for it. As someone who had used OpenAI APIs before, I had a general understanding of how they were implementing it, but I needed to figure out the SMS portion of it. Twilio is the only service that I've heard of which is what I decided to go with. I'll be laying out all of the steps I took to build out the solution. At the end of this tutorial you'll be able to do something like this:

Click here if you want to skip to the tldr

Before we begin, there are a few prerequisites that need to be met. While there are free trials available for OpenAI and Twilio, you'll need to use a paid account to continue using their services.

Prerequisites

  1. A NodeJS environment set up on your machine

  2. The code editor of your choice (I use Visual Studios Code)

  3. An OpenAI Account

  4. A Twilio Account

  5. Your hosting provider of choice (I'm using DigitalOcean for this)

Getting your OpenAI API Key

After you've your OpenAI account setup, you'll need to get the API key. This is what we'll need so we can interact with the OpenAI API in our application. To do this you'll need to to the Api Keys settings: https://platform.openai.com/account/api-keys. Click create new secret key, and copy the value and save it somewhere.

post image

Setting up your Twilio Phone Number

Login to your Twilio account and on the left hand navigation there you'll see the phone number section. To get a new number it is under the Phone Number > Manage > Buy A Number. You do get a free trial phone number when you sign up but if you buy a number it comes out to $1.15 a month.

post image

What we want to configure here is in the Messaging section of the phone number. We'll be configuring a webhook that points to our application.

The App

post image

Server vs Serverless

For most of my projects, I prefer using a serverless stack, specifically Next.js and Contentful (sometimes Firebase), which I host on either Netlify or Vercel. This setup has worked well for many of my use cases.

I initially added an API route to an existing Next.js project to quickly test it out. However, after using it for a while, I noticed that the timeout limit of 10 seconds wasn't sufficient for some of the messages. As a result, I set up an Express project and hosted it on DigitalOcean instead using their App Platform. The timeouts here can be set up to 100s but it did come at a slightly higher cost ($5/mo). I plan to use this same project for future APIs I want to create for my AI projects.

Initialize your app

To make things a bit easier I have a repo that you can use that has the configuration taken care of an Express + Typescript project. This is based off of Log Rocket's Guide, they go into a lot more detail on how things are setup but this way we can get straight to the implementation.

https://github.com/romeldris/create-typescript-express-rd

post image

After you've created a new repository, clone it, and run yarn to install the packages

git clone https://github.com/romeldris/create-typescript-express-rd ai-sms cd ai-sms yarn

Start the application running:

yarn dev 12:44:48 a.m. - Starting compilation in watch mode... [0] [0] [0] 12:44:49 a.m. - Found 0 errors. Watching for file changes. [1] ⚡️[server]: Server is running at http://localhost:3000

You'll be able to navigate to http://localhost:3000

Dependencies

Besides express there are a couple more packages we'll need to install.

  1. openai - https://www.npmjs.com/package/openai

  2. twilio - https://www.npmjs.com/package/twilio

We can do this by running this command

yarn add twilio openai

We'll need to import them into our `index.ts` file so we can start using them within our application. We'll just need to add this to the top of the file where the other imports are:

import { Configuration, OpenAIApi } from 'openai' import { twiml } from 'twilio'

Create a .env file and it should contain the following:

PORT=
OPENAI_API_KEY=

You can change the port you want your application to run on by setting PORT= to a different value and OPENAI_API_KEY is where we'll stick the API key that we got when we setup our openai account.

We can initialize openai right after we import it:

const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, }) const openai = new OpenAIApi(configuration)

By using an environment variable here we won't expose our OPENAI_API_KEY to the world when we check this into our repo.

Endpoint

Once we've got everything installed, we'll need to create the endpoint that twilio will hit once it gets a text message. To do this we can add this inside the `index.ts` file.

app.post('/sms', (req: Request, res: Response) => { // we'll handle the message in here })

This defines a route within our express application and says when we make a POST request to `localhost:3000/sms` this function is what will handle that.

The logic

If you'd like a closer look at to what twilio sends the endpoint you can look into their documentation here: https://www.twilio.com/docs/messaging/guides/webhook-request and their guide to the webhooks here: https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js

All we really want out of their POST request is that body.

  1. Get the Body from the POST request

  2. Take the Body and send it to openai to generate a response

  3. Get the response from openai and send it back to twilio

  4. twilio sends that text back to the user

This is going to be the general flow for how we will handle the request to our endpoint.

I've left the comments inline to describe our steps in the code itself

app.post('/sms', async (req: Request, res: Response) => { // gets the Body from the request body // this contains the text message that the user has sent const { Body } = req.body // creates a messaging response that we'll send back to twilio const messagingResponse = new twiml.MessagingResponse() try { // makes a call to the openai api with the settings // the prompt itself is the text message const response = await openai.createCompletion({ model: 'text-davinci-003', prompt: Body, temperature: 0.7, max_tokens: 256, top_p: 1, frequency_penalty: 0, presence_penalty: 0, }) // gets the response from openai const message = response.data.choices[0].text //sets that as the message for twilio messagingResponse.message(`${message}`) } catch (e) { //if there is an error we will reply with this (you can omit this to fail silently) messagingResponse.message('Sorry there was an error please try again.') console.log(e) } res.setHeader('Content-Type', 'text/xml') return res.status(200).send(twiml.toString()) })

Testing

Now lets test this! The way I like to test this is just to create a simple tunnel so that I can set that URL in twilio itself but it'll still hit my local machine. There are a couple ways to do this, you can either use localtunnel.me or ngrok. I've started to use localtunnel a lot more but it does go down from time to time.

Once you have localtunnel installed, start the app normally and in another terminal run:

lt --port 3000

And you'll get a url that looks something like your url is: https://witty-beans-punch-75-154-232-231.loca.lt/sms this is what we'll use to put in our webhook for the phone number:

post image

Now all you have to do is send a text to that phone number with a question and you should get something back like:

post image

Hosting

You can for this repo if you just want to host it without having to worry about building it yourself. All you'll need is to set the environment variables and you should be good to go: https://github.com/romeldris/building-a-chatgpt-like-sms-bot

I'm not well versed when it comes to DevOps so I like to keep things as simple as possible. I either go with Netlify, Vercel, or DigitialOcean as they have a lot of the CICD in place and I have to do is give them a repo and they handle the rest. Like I mentioned earlier, we'll be using DigitalOcean for this as they have the longest timeout and these request can take a while. It does cost $5/mo but I'll be using this for all of my AI projects that need endpoints so I hope it'll be worth it. This referral should get you some credits to start off with: https://m.do.co/c/15ad5d6a925f

We'll first create a new app:

post image

Select the GitHub repository that you've created for the app:

post image

Go through the wizard edit the plan and choose Basic $5/mo

post image

Set your environment variables:

post image

Finish the wizard and you should see the app building:

post image

Once it's done building you'll see the URL:

post image

Use this URL in twilio and you're all done!

Don't forget to add the /sms to it or whatever you named your endpoint.

If you run into any issues or have any questions let me know! Shoot me an email or hit me up on discord.

Join the Discord forum thread

- Romel