This content originally appeared on Twilio Blog and was authored by Ezzeddin Abdullah
Owning an inventory is a big deal only if you're able to manage it efficiently.
You can manage it on your own but you'll lose a lot of your time keeping track of products there. Or you can hire someone else to work but they might not be honest enough.
Today, you're going to learn how to manage your products in the inventory with a WhatsApp chatbot. With this chatbot, you'll retain your time and get a reliable result. This chatbot will help you engage with clients ordering your products and will tell them exactly about the status of the product — if it's there in the inventory or not.
In this tutorial, you will build this service using FastAPI, Twilio's WhatsApp messaging API, Pyngrok, and SQLAlchemy.
You'll use Twilio's API to access the WhatsApp messaging product to let the clients send messages via WhatsApp and start the chatbot. This chatbot is built using a FastAPI backend. It is then communicated with Pyngrok, which is a Python wrapper for ngrok that puts the FastAPI localhost on the internet. This chatbot will fetch data from the PostgreSQL database and update inventory data in the database using the SQLAlchemy ORM.
At the end of this tutorial, you'll be able to create a chatbot like so:
The app is straightforward. You have a product in your inventory. When the client orders that product, the chatbot replies to place that order for them or says it's not available.
Saying that the product is not available means one of two options:
- This particular product is finished and it's no longer in the inventory.
- Or, your inventory didn't have that particular product in the first place.
Prerequisites
To follow along with this tutorial, you need to have the following:
- Python 3.6+ installed
- PostgreSQL installed
- A Twilio account set up. If you're new, create a free account here.
- A smartphone with WhatsApp installed
- A basic knowledge of FastAPI
- A basic knowledge of what ORM is. If you have no idea what ORM is, consult this wiki page!
Create your inventory
Start with setting up your database. Say, you have two products in your inventory:
- 2 collapsible umbrellas
- 1 hybrid smartwatch
To be able to create these two records in the database, you should define a schema and then create a table based on that schema.
Initiate the Python project and the PostgreSQL database
Download and set up PostgreSQL if you haven’t already. Create a new database — for this project, we would call it mydb
.
createdb mydb
Create a new directory called inventory_chatbot and initiate a virtual environment inside:
mkdir inventory_chatbot
cd inventory_chatbot
python3 -m venv venv; . venv/bin/activate; pip install --upgrade pip
For now, you only need to install these two libraries:
pip install sqlalchemy psycopg2-binary
The psycopg2-binary
library is a binary version of psycopg2
, which is a Python driver for the PostgreSQL database.
Set up the SQLAlchemy model
For this project, you will use the SQLAlchemy ORM to access a PostgreSQL database engine and interact with it. Create a new file called models.py. Model the data and create the schema in the models.py file:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.engine import URL
from sqlalchemy.orm import declarative_base, sessionmaker
url = URL.create(
drivername="postgresql",
username="postgres",
password="postgres",
host="localhost",
database="mydb",
port=5432
)
engine = create_engine(url)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
name = Column(String)
amount = Column(Integer)
Base.metadata.create_all(engine)
As you can see, the url
object defines the database URL with the following options:
- The
drivername
as the database engine - The
username
andpassword
of your database - The
host
in this example is localhost - The
database
is the name of the database you need to access; in this tutorial it'smydb
- The
port
which is5432
by default for PostgreSQL
You can choose your own specifications depending on your case.
You then define the engine
instance which is the starting point of your application. That instance is passed into the sessionmaker
function to create a Session
class which is instantiated to create a session
object.
The session
object manages the persistence layer and does the ORM operations.
The Product
class inherits from the Base
class, which declares the mapped classes into tables. Inside that Product
table, there is a definition of its name and its attributes.
Similarly, if you want to define another table, you can create a class that inherits from the base class and declare your attributes.
The Base.metadata.create_all()
function creates all the tables defined in the mapped classes.
Insert data in SQLAlchemy
Now, insert records into the products table. To do that, create a file called insert_db.py and add the following code to the file:
from models import Product, session
umbrella = Product(name="Collapsible Umbrella", amount=2)
smartwatch = Product(name="Hybrid Smartwatch", amount=1)
session.add_all([umbrella, smartwatch])
session.commit()
The umbrella
and smartwatch
objects are products in the database. The former amounts to two in the inventory, while the latter is just one piece.
In the code above, you add them into the database with the session.add_all()
method and then commit it into the PostgreSQL database with session.commit()
.
To run this script, make sure you have the Python virtual environment activated, then run the script with this command:
python insert_db.py
To check if everything worked correctly, you can access your PostgreSQL database through a SQL client or a PostgreSQL client like psql
.
Alternatively, you can of course use any database administration tool like pgAdmin, or a multi-platform database tool like DBeaver.
Here is how you can use psql
to explore the data in your database.
Running psql -U newuser -d mydb
lets you use the psql
client to connect to the database named mydb
as the user newuser
. You can replace newuser
with your own username. Take a look at the psql
documentation to learn more about the commands you can run.
Once you are logged in and connected to your database, you can run the following query to list the products in your inventory:
select * from products;
Below is what the psql
log statements might look like if you're running them on a Linux machine:
$ sudo -u postgres psql
[sudo] password for <username>:
psql (10.19 (<operating_system>))
Type "help" for help.
postgres=# \c mydb
You are now connected to database "mydb" as user "postgres".
mydb=# select * from products;
id | name | amount
----+----------------------+--------
1 | Collapsible Umbrella | 2
2 | Hybrid Smartwatch | 1
(2 rows)
As you can see, both records are now created.
Once you are finished, you can enter \q
and then press the Enter key to quit psql.
Create the chatbot
The database is now set up and the tiny inventory is already there. Now you need to track the orders for this simple data. Let's configure the Twilio WhatsApp API first.
Configure the Twilio Sandbox for WhatsApp
Assuming you've already set up a new Twilio account, go to the Twilio Console and choose the Messaging tab on the left panel. Under Try it out, choose Send a WhatsApp message. You'll see a phone number "+14155238886" with a code to join next to it. 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 send the message from WhatsApp 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.
Set up the FastAPI backend
To set up FastAPI, navigate to the project directory and create a new file called main.py. Inside that file add this very minimal FastAPI backend:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
return {"message": "hello"}
To run this backend, you need to install fastapi
(for the API) and uvicorn
(the Python ASGI web server):
pip install fastapi uvicorn
To run the app:
uvicorn main:app --reload
Open your browser to localhost:8000
. The result you should see is a JSON response of {"message": "hello"}
.
To connect Twilio with 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.
To use it, open a new terminal window, activate the same virtual environment you created earlier, and then install pyngrok
, which is a Python wrapper for Ngrok:
pip install pyngrok
Leave the FastAPI app running on port 8000, and run this ngrok
command:
ngrok http 8000
This will create a tunnel between your localhost port 8000 and a public domain created on the ngrok.io site. When a client visits your Ngrok forwarding URL, the Ngrok service will automatically forward that request to your backend.
Go to the URL (preferably, with the https prefix) as shown below:
After you click on the forwarding URL, Ngrok will redirect you to your FastAPI app’s index endpoint.
Configure the Twilio webhook
To be able to receive a reply when you message the Twilio WhatsApp sandbox number, you need to configure a webhook known to Twilio.
To do that, head over to the Twilio Console and choose the Messaging tab on the left panel. Under the Settings tab, choose WhatsApp sandbox settings.
Copy the ngrok.io forwarding URL and append /message
. Paste it into the box next to WHEN A MESSAGE COMES IN:
The full URL will be something like: https://d8c1-197-36-101-223.ngrok.io/message
.
Note: The /message
is the endpoint we will set up in the FastAPI application. This endpoint will have the chatbot logic.
Once you finish, click the Save button.
Authenticate your Twilio account
Earlier, you created the /index
endpoint to just test that your backend is working with Ngrok. From this point onwards, you won’t need it anymore.
Before setting up the /message
endpoint, authenticate your Twilio account to be able to use the Twilio client. Open the main.py file and add the following highlighted lines of code:
import os
from dotenv import load_dotenv
from twilio.rest import Client
from fastapi import FastAPI
load_dotenv()
account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
client = Client(account_sid, auth_token)
app = FastAPI()
In your Twilio console, find your Account SID and Auth Token. In the next step, you will set these as environment variables.
You will use dotenv
to read and set the environment variables and twilio
to create the Twilio client. Install these two packages in your virtual environment with the following:
pip install twilio python-dotenv
To use the environment variables, create a .env file and fill it like so, replacing the placeholder text with your values from the Console:
TWILIO_ACCOUNT_SID=<your-twilio-account-sid>
TWILIO_AUTH_TOKEN=<your-twilio-auth-token>
Now, the app is ready to use Twilio capabilities to create the chatbot through WhatsApp.
Add the chatbot logic
Open the main.py file and update the imports at the top of the file as follows:
import os
import uuid
from dotenv import load_dotenv
from twilio.rest import Client
from fastapi import FastAPI, Form
from models import Product, session
load_dotenv()
Since you're using Form
data here, you'll also need to install one more Python package in your activated virtual environment:
pip install python-multipart
Now, in main.py, below the index()
endpoint, add the following logic:
def send_message(body_text):
client.messages.create(
from_="whatsapp:+14155238886", body=body_text, to="whatsapp:+<your-country-code><your-phone-number>"
)
@app.post("/message")
async def reply(Body: str = Form()):
message = Body.lower()
products = session.query(Product)
for product in products:
if product.name.lower() in message:
if product.amount >= 1:
id = str(uuid.uuid4())
message = f"Your order is placed for {product.name}. This is the tracking id: {id}."
ordered_product = products.filter(Product.name == product.name).first()
ordered_product.amount -= 1
session.commit()
return send_message(message)
message = "The product you mentioned is not available at the moment."
return send_message(message)
The send_message()
function takes the text message that you want to send from the Twilio WhatsApp sandbox account to your phone number. Change the placeholder text in the to
option value to your phone number in E.164 format (country code followed by your phone number).
The /message
endpoint will receive a POST request that takes the Form
as an argument, which represents the body of the incoming message. The message
instance is a lowered-case version of the body text in the WhatsApp message coming from the client.
The products
object is a Query object in SQLAlchemy. Here, you loop over each product in the database. You check if the incoming message contains the names of any products in your database, and then, if it does, check if that product is available in the inventory (if its amount is greater than 1). If the product is available, you prepare a useful message that informs the client that their order is placed, and generate a new unique id for tracking (using the uuid
library).
Once the product is ordered, you reduce its amount in the database by 1 and commit the new change to the database. Then you send the message to the user.
If no product matches the text in the client's message, or if the product matches but its amount in the inventory is zero, you then send the user a message that the product is not available at the moment.
Test the final app
Earlier, you defined that there were 2 different products in the inventory:
- 2 collapsible umbrellas
- 1 hybrid smartwatch
Let's test the app by sending a rubbish message to the app. What should its response be?
Yes, this text: "The product you mentioned is not available at the moment."
Now, send "collapsible umbrella". The chatbot responds with:
"Your order is placed for Collapsible Umbrella. This is the tracking id: <unique-identifier>"
Send this message next: "I want another collapsible umbrella". The chatbot then responds with a similar message to the previous one, with another tracking id.
If you message with "I want one more collapsible umbrella", you'll get back: "The product you mentioned is not available at the moment." because the last two pieces were already ordered and there is no more of this product available in the inventory now.
You can try your own experiments with the other products and investigate even further with your own specifications.
Finally, if your tech stack is Flask, not FastAPI, be sure to check out this blog post that explains how to create a chatbot using WhatsApp and access third-party APIs.
Ezz is an AWS Certified Machine Learning Specialist and a Data Platform Engineer. He helps tech companies generate developer leads through his technical blog posts. Check out his website for more.
This content originally appeared on Twilio Blog and was authored by Ezzeddin Abdullah
Ezzeddin Abdullah | Sciencx (2022-10-20T22:28:36+00:00) How to Build an Inventory Chatbot on WhatsApp with FastAPI. Retrieved from https://www.scien.cx/2022/10/20/how-to-build-an-inventory-chatbot-on-whatsapp-with-fastapi/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.