Building a Fitness and diet Chatbot with FaunaDB, OpenAI, and Twilio in Python
Tags: twilio, whatsapp, python
Published: 2023-08-08 23:10:10

In this article, you will learn how to build a Fitness and diet chatbot using Python, FaunaDB, OpenAI, and Twilio. The chatbot will be able to communicate with you via WhatsApp and provide responses based on your conversation history. You'll be walked through the code and understand the key components and libraries involved in the process.

## Prerequisites
Before you begin, make sure you have the following:
- [Python 3.x](https://www.python.org/) installed on your machine
- [FaunaDB](https://dashboard.fauna.com/accounts/register) account and API key
- [OpenAI API key](https://www.openai.com/signup) to access the `gpt-3.5-turbo` model
- [Twilio account](https://www.twilio.com/try-twilio) SID, auth token, and a phone number
- [Ngrok](https://ngrok.com/): It allows us to expose our local Flask server to the internet.

## Setting Up the Fauna database
To begin using [Fauna](https://dashboard.fauna.com/accounts/register), your initial step is to establish an account on the official website. This can be accomplished by providing your email address or utilizing your GitHub or Netlify account through the following link: [Fauna](https://dashboard.fauna.com/accounts/register). For efficient communication with the chatbot, it is necessary to utilize the Fauna database for storing and retrieving user messages.

## Creating the database
![Faunadashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ck3os4m95j38scbxv1fu.png)
After creating your account with fauna, you will be creating a database to store the `Users` and `Messages`. Here you'd be asked for the database name and you are going to name it `MyChatBot` and the region is going to be set to `Classic` and like that you've created your database. Then, you should be presented with a screen like this one below:

![Faunahomepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kyip9gsquip0d4skytwg.png)

## **Creating your collection**
Next, you will be creating 2 collections, which is basically `Tables` in the SQL world but with a twist in our context.

![Faunatable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oszx2nkilcs7f84unqnd.png)
To create your Collection, click on the `Create Collection` button on the home page and give it a name, but since you'd be creating the two collections will be naming them `Users` and `Messages`. The "Users" collection is for storing the user's username from WhatsApp, while the `Messages` collection is for storing the user's chat history with the bot. You will be asked for History `Days` and `TTL`. The History Days is used to define the number of days Fauna should retain a historical record of any data in that particular collection while the TTL serves as an expiry date for data in the collection. For example, if the TTL is set to 7, any data stored in that collection will be automatically deleted 7 days after its last modified date, but for this tutorial you'll not be needing it so it will be left untouched. After creating the two collections, you should be seeing this:
![FaunaCollection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sqoenaqxuo11ugh8ilgo.png)

## Creating the Index
Wondering what an Index is?, Well an Index is simply a way to browse data in a collection more efficiently by organizing it based on specific fields or criteria, allowing for faster and targeted retrieval of information. To create your Index, you'll navigate to the Index tab and you should see something like this:

![FaunaIndex](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/utpp82tlf9064p8bwrwv.png)
To create the Index, you first of all need to `Select a Collection`, then specify the `Terms`, which is the specific data the Index is only allowed to browse. But for this you will be creating two Indexes, `users_by_username` which would be under the `Users` collection for registering users and `users_message_by_username` which would be filtering the user's messages by their username. The `Terms` for `users_by_username` and `users_messages_by_username` would be set to `data.username`  will be set to `data.username`, then click `SAVE` to continue.

## **Getting our Database key**
Before you proceed to building the ChatBot, you need to create an API key that would allow your application to easily communicate with your database. To create an API key, you need to navigate to the security tab on the Fauna sidebar (on the left side of the screen).

![Fauna Sidebar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p689f53q4l8h4d0nv9wm.png)
Next, you will click on the `NEW KEY` button that will navigate you to the page below:
![Fauna](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5c7mib8nxpm5dxpmlffb.png)
Here, your key role would be set to `Server` instead of `Admin` and set your Key name to our database name which is optional, then click on `SAVE` and you'd be navigated to a page where your database key would be displayed and meant to be copied immediately. You should see something like this:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7x67gwtwqgwshp2yuxa5.png)
After getting the `API KEY`, store it in the _.env_ file in your coding environment.

## Configuring Twilio
In order to demonstrate the procedure, you will set up your Twilio account to use WhatsApp by making use of the Twilio Sandbox for WhatsApp. To access the WhatsApp Sandbox in your Twilio Console, navigate to the Messaging section located on the left sidebar (if you can't see it, click on Explore Products to unveil the product list, where you can locate Messaging). Afterward, expand the "Try it out" dropdown and choose "Send a WhatsApp message" from the available choices. Subsequently, you will be presented with the following display:
![WhatsApp sandbox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fzhh2fuymd10bo9s5qy6.png)
After scanning the QR code, you will immediately receive a "join disease-see" message on your WhatsApp. Simply send the message, and just like that, you will be connected to your WhatsApp account.

## Setting up the Environment
First, create an _app.py_ file in your coding environment then you have to install the necessary libraries. You need to install `faunadb`, openai, `python-dotenv`, `twilio`, and `flask` libraries. You can use the following command to install them:
`pip install telebot faunadb openai python-dotenv twilio flask`

Once the libraries are installed, you can proceed to the code implementation.

## Importing Libraries
Let's start by importing the required libraries in your Python script:
```python
import telebot
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient
import openai
from dotenv import load_dotenv
from twilio.rest import Client
import os
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse

```
You need to import `faunadb` for integrating FaunaDB, `openai` for utilizing OpenAI's `gpt-3.5-turbo` model, `dotenv` for loading environment variables, `twilio` for sending and receiving WhatsApp messages, `os` for accessing environment variables, `flask` for creating a web server, and `twilio.twiml.messaging_response` for generating TwiML responses.

## Initializing Flask App and FaunaDB Client
Let's initialize the Flask app and create a FaunaDB client:
```python
app = Flask(__name__)

def prompt(username, question):
    load_dotenv()
    client = FaunaClient(secret=os.getenv('FAUNA_SECRET_KEY'))
    # ...

```

You create a Flask app using the `Flask(__name__)` constructor. Inside the `prompt` function, you load the environment variables using `load_dotenv()` and create a FaunaDB client using the provided secret key.

## Storing User Messages in FaunaDB
Within the `prompt` function, you store user messages in FaunaDB using the FaunaDB client:

```python
data = {
    "username": username,
    "message": {
        "role": "user",
        "content": question
    }
}
result = client.query(
    q.create(
        q.collection("Messages"),
        {
            "data": data
        }
    )
)

```
You create a dictionary `data` containing the `username` and the user's `question`. Then you use the `client.query` method to create a new document in the "Messages" collection, storing the `data` dictionary.

## Retrieving User Messages from FaunaDB
You retrieve the user messages from FaunaDB by querying the database based on the `username`:
```python
index_name = "users_message_by_username"
username = username
result = client.query(
    q.map_(
        lambda ref: q.get(ref),
        q.paginate(q.match(q.index(index_name), username))
    )
)

messages = []

for document in result['data']:
    message = document['data']['message']
    messages.append(message)

```

You specify the `index_name` for the index to query from FaunaDB. Then you use `q.paginate` and `q.match` to retrieve all the documents associated with the given `username`. You loop through the result and extract the `message` content, storing it in the `messages` list.

## Setting up OpenAI API
Let's set up the OpenAI API and define the assistant's persona:
```python
openai.api_key = os.getenv('OPENAI_API_KEY')

system_message = {"role":"system", "content" : "You are a dietitian, food nutritionist, and fitness consultant, you provide expert guidance and advice to individuals facing dietary challenges or seeking direction on their food choices and exercise routines. You offer personalized recommendations and solutions to those who are unsure about the right foods to eat or the appropriate exercises to engage in. If user says hello or any greeting introduce yourself."}

prompt_with_persona = [system_message] + [
    {"role": "user", "content": message["content"]} if message["role"] == "user"
    else {"role": "assistant", "content": message["content"]} for message in messages
]

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=prompt_with_persona
)

generated_reply = response["choices"][0]["message"]["content"]

```

You set the OpenAI API key using `os.getenv('OPENAI_API_KEY')` from the _.env_ file as created earlier. Then you define a `system_message` dict representing the assistant's persona. You construct the conversation prompt by combining the `system_message` with the user and assistant messages from the `messages` list.

Afterward, you make an API call to the OpenAI ChatCompletion API using `openai.ChatCompletion.create()`, passing the conversation prompt as the `messages` parameter. The API response contains the generated reply from the model, which you extract and store in the `generated_reply` variable.

## Storing Assistant's Reply in FaunaDB
You store the assistant's reply in FaunaDB using the FaunaDB client:
```python
newdata = {
    "username": username,
    "message": {
        "role": "assistant",
        "content": generated_reply
    }
}
result = client.query(
    q.create(
        q.collection("Messages"),
        {
            "data": newdata
        }
    )
)

```
You create a new dictionary `newdata` containing the `username` and the assistant's `generated_reply`. Then you use the `client.query` method to create a new document in the "Messages" collection, storing the `newdata` dictionary.

## Handling WhatsApp Messages
Now, let's implement the route for handling WhatsApp messages:
```python
@app.route('/whatsapp', methods=["POST", "GET"])
def chat():
    load_dotenv()
    twilio_phone_number = os.getenv('TWILIO_PHONE_NUMBER')
    sender_phone_number = request.values.get('From', '')
    username = request.values.get('ProfileName')
    question = request.values.get('Body')
    account_sid = os.getenv('TWILIO_ACCOUNT_SID')
    auth_token = os.getenv('TWILIO_AUTH_TOKEN')
    Twilioclient = Client(account_sid, auth_token)
    client = FaunaClient(
        secret=os.getenv('FAUNA_SECRET_KEY')
    )
    reply = None
    user_exists = client.query(
        q.exists(q.match(q.index("users_by_username"), username)))
    if user_exists:
        reply = prompt(username, question)
        answer = Twilioclient.messages.create(
            body=reply,
            from_=twilio_phone_number,
            to=sender_phone_number
        )
    else:
        client.query(
            q.create(
                q.collection("Users"),
                {
                    "data": {
                        "username": username
                    }
                }   
            )
        )
        reply = prompt(username, question)
        answer = Twilioclient.messages.create(
            body=reply,
            from_=twilio_phone_number,
            to=sender_phone_number
        )
    return str(answer.sid)

```
Inside the `/whatsapp` route, we load the environment variables, retrieve the necessary data from the incoming request, and create the Twilio and FaunaDB clients. We check if the user exists in FaunaDB using `q.exists` and `q.match`. If the user exists, we call the `prompt` function to generate a reply and send the reply back to the user using Twilio's `messages.create()` method. If the user doesn't exist, we create a new document in the "Users" collection and follow the same process as above.

## Running the Flask App
Finally, let's run the Flask app:
```python
if __name__ == '__main__':
    app.run(debug=True)

```
We use the `if __name__ == '__main__':` conditional statement to ensure that the app only runs if the script is executed directly, not imported as a module. The `debug=True` is set to in order to enable debug mode for better error messages during development. Now run `python app.py` in your terminal, it will run on `localhost:5000`. You can now go ahead to ngrok and run `ngrok http 5000`.
![ngrok image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw5hwmr409v7uk18smai.png)
Copy the circled link and paste it in your WhatsApp sandbox
![Twilio whatsapp sandbox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ukuer90zuxmof90lsnsc.png)
There you have it, you now have your own personal WhatsApp fitness and diet bot

![WhatsApp bot at work](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebxj4b904mzqvtb5qoj8.jpg)

![WhatsApp bot at work](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agyeiuhnvzx2riyvqejn.jpg)

## Conclusion
In this article, you learned how to build a chatbot using Python, FaunaDB, OpenAI, and Twilio. We covered the code step by step, from initializing the Flask app to handling WhatsApp messages and generating responses using OpenAI's ChatCompletion API. You can use this code as a starting point to develop your own chatbot with additional features and integrations.

Happy Building!!