How to Build an InvestorGPT AI Chatbot on WhatsApp with Django, Twilio, and the ChatGPT API

April 19, 2023
Written by
Ezzeddin Abdullah
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by
Mia Adjei
Twilion

How to Build an InvestorGPT AI Chatbot on WhatsApp with Django, Twilio, and the ChatGPT API

Building an AI-powered chatbot can be a daunting task, but with the right tools and framework, it can be a fun and rewarding experience.

In this article, we will explore how to build an AI chatbot that can communicate with investors on WhatsApp using Django, Twilio, and the ChatGPT API. We will dive into the step-by-step process of integrating these technologies and provide a clear understanding of how they work together to create an intelligent chatbot.

You'll start by setting up the backend using Django and Django ORM to create a PostgreSQL database to store your customers' data. Then, you'll integrate Twilio's WhatsApp Messaging API, allowing customers to initiate conversations with your WhatsApp chatbot.

With Pyngrok, you'll put the Django localhost on the internet through Python, making it accessible for the Twilio API to communicate with.

Finally, the core of this AI chatbot will be built using OpenAI's API and one of the GPT-3.5 series models: the model that powers ChatGPT, the GPT 3.5 turbo model.

What you’re building

The AI chatbot you’re building in this tutorial is similar to the one we built in the previous post except that today you’re going to use Django for the backend. This will save you time from writing many lines of code as in the case of FastAPI. For example, in the code for the ORM — Django has its own ORM, so you’ll see that there is no need to configure SQLAlchemy or another external ORM library.

Both FastAPI and Django are popular web frameworks for building Python-based web applications. Here are some key differences between the two:

  1. Performance: FastAPI is faster than Django due to its async support and optimized performance for small- to large-scale applications.
  2. Simplicity: FastAPI has a simpler and more intuitive syntax, making it easier for developers to write and maintain code.
  3. API Development: FastAPI is particularly well-suited for building APIs due to its focus on async programming and support for OpenAPI (Swagger) documentation.
  4. Scalability: Django is a mature framework that has been around for over a decade, and it has proven to be highly scalable, handling large-scale applications with ease.
  5. Community: Django has a larger and more established community compared to FastAPI, which means there are more resources available and a wider variety of plugins and packages to choose from.

At the end of this tutorial, you will be able to build an investing chatbot (InvestorGPT) on WhatsApp. Here is a sample conversation this AI chatbot can create:

AI chatbot conversation

Let’s build this experienced investor.

Prerequisites

To follow this tutorial, you will need the following prerequisites:

  • Python 3.7+ installed on your machine.
  • PostgreSQL installed on your machine.
  • A Twilio account set up. If you don't have one, you can create a free account here.
  • An OpenAI API key to access ChatGPT.
  • A smartphone with WhatsApp installed to test your AI chatbot.
  • A basic understanding of Django, a fully-fledged web framework for building APIs and web apps with Python.
  • A basic understanding of what an ORM is. If you are not familiar with ORM, we recommend you to read this wiki page to get an idea of what it is and how it works.

Setting up your development environment

Before building the chatbot, you need to set up your development environment. Start with creating a new virtual environment:

mkdir django_chatgpt
cd django_chatgpt
python3 -m venv venv; . venv/bin/activate; pip install --upgrade pip

Here, you create the django_chatgpt directory and navigate into it. Then you create a new Python virtual environment using venv. Finally, you activate the environment and then upgrade pip, the Python package manager.

Next, create a requirements.txt file that includes the following:

django
twilio
openai
python-decouple
psycopg2-binary
pyngrok

Here is a breakdown of these dependencies:

  1. django: A package for Django, a web framework that provides a complete set of tools and features for building complex web applications, including an ORM system, a templating engine, built-in user authentication and authorization, and support for handling HTTP requests and responses.
  2. twilio: A Python helper library for Twilio, a cloud communications platform that allows software developers to programmatically make and receive phone calls, send and receive text messages, and perform other communication functions using its web service APIs.
  3. openai: A Python client for OpenAI, a research company that focuses on developing and advancing artificial intelligence in a way that is safe and beneficial for humanity. OpenAI offers various AI models, including the GPT 3.5 turbo model, which is used in this tutorial to power the chatbot.
  4. python-decouple: A library for separating the settings of your Python application from the source code. It allows you to store your settings in an environment file, instead of hardcoding them into your code.
  5. psycopg2-binary: A Python package that provides a PostgreSQL database adapter for the Python programming language.
  6. pyngrok: A Python wrapper for ngrok, a tool that allows you to expose a web server running on your local machine to the internet. You'll use it to test your Twilio webhook while you send WhatsApp messages.

Now, you can install these dependencies:

pip install -r requirements.txt

Configuring your Django project

First, you need to set up a new Django project. You can do this by running the following command:

django-admin startproject chatbot .

This will create a new Django project called "chatbot" in the same directory. Next, you'll create a new Django app. You can call it "business" to indicate that this app is for the business chatbot:

python manage.py startapp business

Now, open the chatbot/settings.py file and append the business app in the INSTALLED_APPS list:

INSTALLED_APPS = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        "business",
]

Configuring your database

You can use your own PostgreSQL database or set up a new database with the createdb PostgreSQL utility command:

createdb mydb

Django has a built-in ORM set up for you, so you can use it to access your database. Navigate to the business app directory and fill in the following in the models.py file:

from django.db import models


class Conversation(models.Model):
    sender = models.CharField(max_length=15)
    message = models.CharField(max_length=2000)
    response = models.CharField(max_length=2000)

This code defines a Django model called Conversation that inherits from models.Model.

The Conversation model has three fields: sender, message, and response. These fields are defined as instances of the CharField class, which is a Django model field that stores strings of a specified maximum length.

max_length=15 is specified for the sender field, which means it can store a string up to 15 characters long. Similarly, max_length=2000 is specified for the message and response fields, which means they can store strings up to 2000 characters long.

To configure the database in Django, you need to modify the DATABASES dictionary in your settings.py file. This dictionary contains settings that specify the database engine, name, user, password, and other options. By default, Django is set up with a SQLite3 database engine. You can replace the existing DATABASES dictionary with the following configuration for a PostgreSQL database:

from decouple import config

# …
DATABASES = {
    "default": {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': config("DB_USER"),
        'PASSWORD': config("DB_PASSWORD"),
        'HOST': 'localhost',
        'PORT': '5432',
    },
}

Note: here, you've used decouple.config to access the environment variables for your database: DB_USER and DB_PASSWORD. You should now create a .env file that stores these credentials with their associated values. Something like the following, but replacing the placeholder text with your actual values:

DB_USER=<your-database-username>
DB_PASSWORD=<your-database-password>

To configure the database in your Django project, run the following commands:

python manage.py makemigrations
python manage.py migrate

The first command, python manage.py makemigrations, is a command in Django's command-line interface that creates new database migration files based on changes to your Django models. In this case, you’ve created a migrations directory for the Conversation models using the PostgreSQL database engine. Migration files are Python files that describe how to modify your application's database schema to match your models.

When you make changes to your models, such as adding or removing fields, you need to create new migration files using the makemigrations command. This command analyzes your models and generates the necessary migration files in a migrations directory within your app.

After creating the migration files, you can apply them to your database using the python manage.py migrate command. This command runs the migration files in the order they were created, modifying your application's database schema to match your models.

Creating your chatbot

Now that you have set up your environment and configured the database, it's time to build the chatbot. In this section, you will write the code for a basic chatbot using OpenAI and Twilio.

Configuring your Twilio Sandbox for WhatsApp

To use Twilio's Messaging API to enable the chatbot to communicate with WhatsApp users, you need to configure the Twilio Sandbox for WhatsApp. Here's how to do it:

  1. Assuming you've already set up a new Twilio account, go to the Twilio Console and choose the Messaging tab on the left panel.
  2. Under Try it out, click on Send a WhatsApp message. You'll land on the Sandbox tab by default and you'll see a phone number "+14155238886" with a code to join next to it on the left and a QR code on the right.
  3. To enable the Twilio testing environment, send a WhatsApp message with this code's text to the displayed phone number. You can click on the hyperlink to direct you to the WhatsApp chat if you are using the web version. Otherwise, you can scan the QR code on your phone.

Now, the Twilio sandbox is set up, and it's configured so that you can try out your application after setting up the backend. Before leaving the Twilio Console, you should take note of your Twilio credentials and edit the .env file as follows:

DB_USER=<your-database-username>
DB_PASSWORD=<your-database-password>
TWILIO_ACCOUNT_SID=<your-twilio-account-sid>
TWILIO_AUTH_TOKEN=<your-twilio-auth-token>
TWILIO_NUMBER=+14155238886

Setting up your Twilio WhatsApp API snippet

Before setting up the Django view method to send a POST request to WhatsApp, let's build a utility script first to set up sending a WhatsApp message through the Twilio Messaging API.

Create a new file called utils.py inside the business app and fill it with the following code:

# Standard library import
import logging

# Third-party imports
from twilio.rest import Client
from decouple import config


# Find your Account SID and Auth Token at twilio.com/console
# and set the environment variables. See http://twil.io/secure
account_sid = config("TWILIO_ACCOUNT_SID")
auth_token = config("TWILIO_AUTH_TOKEN")
client = Client(account_sid, auth_token)
twilio_number = config('TWILIO_NUMBER')

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Sending message logic through Twilio Messaging API
def send_message(to_number, body_text):
    try:
        message = client.messages.create(
            from_=f"whatsapp:{twilio_number}",
            body=body_text,
            to=f"whatsapp:{to_number}"
            )
        logger.info(f"Message sent to {to_number}: {message.body}")
    except Exception as e:
        logger.error(f"Error sending message to {to_number}: {e}")

First, the necessary libraries are imported, which include the logging library, the Twilio REST client, and the decouple library used to store private credentials in a .env file.

Next, the Twilio Account SID, Auth Token, and phone number are retrieved from the .env file using the decouple library. The Account SID and Auth Token are required to authenticate your account with Twilio, while the phone number is the Twilio WhatsApp sandbox number.

Then, a logging configuration is set up for the function to log any info or errors related to sending messages. If you want more advanced logging to use as a boilerplate, check this out.

The meat of this utility script is the send_message function which takes two parameters, the to_number and body_text, which are the recipient's WhatsApp number and the message body text, respectively.

The function tries to send the message using the client.messages.create method, which takes the Twilio phone number as the sender (from_), the message body text (body), and the recipient's WhatsApp number (to). If the message is successfully sent, the function logs an info message with the recipient's number and the message body. If there is an error sending the message, the function logs an error message with the error message.

Setting up your Django backend

To set up the Django backend for the chatbot, navigate to the business app directory and open the views.py script. Inside that file, you will set up a basic Django view method that will handle a single incoming request:

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello")

To route the URL of this view method, you need to configure it in the urls.py script. Open the chatbot/urls.py file and fill in the following highlighted snippet:

from django.contrib import admin
from django.urls import path
from business import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", views.index),
]

To run the Django server, run the following command:

python manage.py runserver

Open your browser to http://localhost:8000. The result you should see is an HTTP response of Hello.

However, since Twilio needs to send messages to your backend, you need to host your app on a public server. An easy way to do that is to use Ngrok.

If you're new to Ngrok, you can consult this blog post and create a new account.

Leave the Django backend running on port 8000, and run this ngrok command from another terminal window:

ngrok http 8000

The above command sets up a connection between your local server running on port 8000 and a public domain created on the ngrok.io website. Once you have the Ngrok forwarding URL, any requests from a client to that URL will be automatically directed to your Django backend.

ngrok terminal

If you click on the forwarding URL, Ngrok will try to redirect you to your Django app's index endpoint, but it will fail and it will raise an error similar to the following:

django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'd8c1-197-36-101-223.ngrok.io'. You may need to add 'd8c1-197-36-101-223.ngrok.io' to ALLOWED_HOSTS.

This error occurs when Django receives a request with an HTTP_HOST header that does not match any of the hostnames specified in the ALLOWED_HOSTS setting in your Django project's settings.py file.

To fix this error, you need to add the hostname that is causing the error to the ALLOWED_HOSTS list in your settings.py file. You can do this by adding the following line to your settings.py file:

ALLOWED_HOSTS = ['d8c1-197-36-101-223.ngrok.io']

Replace d8c1-197-36-101-223.ngrok.io with the actual hostname that is causing the error. You can also specify a wildcard * to allow any hostname to access your application, but this is not recommended for production use.

After adding the hostname to the ALLOWED_HOSTS list, refresh your Django development ngrok URL in the browser window and try accessing your application again.

Configuring the Twilio webhook

You must set up a Twilio-approved webhook to be able to receive a response when you message the Twilio WhatsApp sandbox number.

To do that, head over to the Twilio Console and choose the Messaging tab on the left panel. Under the Try it out tab, choose Send a WhatsApp message. Next to the Sandbox tab, choose the Sandbox settings tab.

Copy your ngrok.io forwarding URL and append /message/. Paste it into the box next to WHEN A MESSAGE COMES IN. The sandbox settings should be something like the following:

Twilio sandbox settings

The complete URL should look like this: https://d8c1-197-36-101-223.ngrok.io/message/. Don’t forget the trailing slash.

The endpoint you will configure in the Django application is /message/, as noted. The chatbot logic will be on this endpoint.

When done, press the Save button.

To configure the message endpoint on Django, one way to do it is to go to the chatbot/urls.py file and enter the following highlighted line:

from django.contrib import admin
from django.urls import path
from business import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", views.index),
    path("message/", views.reply),
]

In the following section, you will create the reply view method that should route to the message/ endpoint.

Sending your message with OpenAI API

Now, it's time to create the logic for sending the WhatsApp message to the OpenAI API so that you'll get a response from the ChatGPT API.

Update the business/views.py script to the following:

# Third-party imports
import openai
from decouple import config
from django.http import HttpResponse
from django.db import transaction
from django.views.decorators.csrf import csrf_exempt

# Internal imports
from .models import Conversation
from .utils import send_message, logger

# Set up the OpenAI API client
openai.api_key = config("OPENAI_API_KEY")

def index():
    return HttpResponse("Hello")


@csrf_exempt
def reply(request):
    # Extract the phone number from the incoming webhook request
    whatsapp_number = request.POST.get('From').split("whatsapp:")[-1]
    print(f"Sending the ChatGPT response to this number: {whatsapp_number}")

    # Call the OpenAI API to generate text with ChatGPT
    body = request.POST.get('Body', '')
    messages = [{"role": "user", "content": body}]
    messages.append({"role": "system", "content": "You're an investor, a serial founder and you've sold many startups. You understand nothing but business."})
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=messages,
        max_tokens=200,
        n=1,
        stop=None,
        temperature=0.5
        )

    # The generated text
    chatgpt_response = response.choices[0].message.content

    
    # Store the conversation in the database
    try:

        with transaction.atomic():
                conversation = Conversation.objects.create(
                    sender=whatsapp_number,
                    message=body,
                    response=chatgpt_response
                )
                conversation.save()
                logger.info(f"Conversation #{conversation.id} stored in database")
    except Exception as e:
        logger.error(f"Error storing conversation in database: {e}")
        return HttpResponse(status=500)

    send_message(whatsapp_number, chatgpt_response)
    return HttpResponse('')

This code sets up a Django view function to handle incoming requests from the webhook you set up on WhatsApp. The reply view function is decorated with the @csrf_exempt decorator, which disables the CSRF protection that is usually enabled by default in Django.

The view function also imports several modules that are used to handle the incoming request and generate a response.

The openai.api_key variable is set to the value of the OPENAI_API_KEY environment variable using the config() function from the decouple library. To make this work, you will need to add your OpenAI API key to the .env file, so it looks like the following:

DB_USER=<your-database-username>
DB_PASSWORD=<your-database-password>
TWILIO_ACCOUNT_SID=<your-twilio-account-sid>
TWILIO_AUTH_TOKEN=<your-twilio-auth-token>
TWILIO_NUMBER=+14155238886
OPENAI_API_KEY=<your-openai-api-key>

The reply() function is the main view function that handles incoming requests from the WhatsApp webhook. It first extracts the phone number of the user sending the message from the incoming request using the request.POST.get() method, which retrieves data from the request's POST dictionary. It then prints a message indicating that the ChatGPT response will be sent to that phone number.

The function then calls the OpenAI API to generate a response using the openai.ChatCompletion.create() method. This method takes several arguments, including the text that was sent by the user, the name of the GPT-3.5 model to use, the maximum number of tokens to generate, and the temperature parameter, which controls the "creativity" of the generated text.

The function then attempts to store the conversation in the database using Django's transaction mechanism to ensure atomicity. If an exception occurs during the database operation, the function rolls back the change. It also logs an error and returns an HTTP 500 error status code.

Finally, the function uses the send_message() function from the utils module to send the generated response back to the user via WhatsApp. It then returns an empty HTTP response with status code 200 to indicate that the request was processed successfully.

Testing your AI chatbot

Now, you're ready to send a WhatsApp message and wait for a response from your AI chatbot. Try asking the AI chatbot anything you can ask ChatGPT.

The example below shows a business question about investing and the InvestorGPT response:

AI chatbot conversation

Note: If the response is trimmed, you can tune the max_tokens parameter.

Now, your AI chatbot is functioning well on WhatsApp. Perhaps your next step is to use your own WhatsApp Business Account instead of Twilio Sandbox and make this InvestorGPT live in production using a VPS instead of building it locally. I hope you enjoyed this tutorial and see you in the next one.

If you like this post, you may take a look at integrating ChatGPT API with FastAPI.

Ezz is an AWS Certified Machine Learning Specialist and a Data Platform Engineer. He helps tech companies take developer experience to the next level through his technical blog posts. Check out his website for more.