Building a Fitness and diet Chatbot with FaunaDB, OpenAI, and Twilio in Python

Building a Fitness and diet Chatbot with FaunaDB, OpenAI, and Twilio in Python

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:

Setting Up the Fauna database

To begin using Fauna, 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. For efficient communication with the chatbot, it is necessary to utilize the Fauna database for storing and retrieving user messages.

Creating the database

Faunadashboard

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

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

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

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

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

Next, you will click on the NEW KEY button that will navigate you to the page below:

Fauna

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

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

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:

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:

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:

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:

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:

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:

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:

@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:

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

Copy the circled link and paste it in your WhatsApp sandbox

Twilio whatsapp sandbox

There you have it, you now have your own personal WhatsApp fitness and diet bot

WhatsApp bot at work

WhatsApp bot at work

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!!