Creating Chatbot with Webhooks Using Python (Flask) and DialogFlow

Engineering@ZenOfAI
May 14 · 10 min read

In my previous article, we have learned how to build a chatbot for Travel agent using DialogFlow. Now in this article, we’re going to learn Webhooks and how Fulfillment works in DialogFlow.

What is Webhook?

Webhooks are “user-defined HTTP callbacks”. A webhook is an HTTP request that is sent automatically whenever certain criteria are fulfilled. The request is done as an HTTP POST request. A webhook can be created in any server-side programming language like Python, PHP or Node.js. You can read more about webhook here.

Why Webhook in DialogFlow?

We use webhooks when we want to create some useful chatbot with complex actions or we want dynamic responses. In Dialoflow, a webhook can be used to fetch data from your server whenever a certain intent having webhook enabled is invoked. The information from the intent is passed to the webhook service to receive the result. (We will learn more about this later in this article)

In the below image you can see the interaction between DialogFlow and Webhook.

Prerequisite

Before we start, make sure you have installed the following tools.

  1. Download and install python from here as per your supported OS.
  2. Make sure you have the right version of python by typing below commands in your command line.
  3. Install Flask using following command — pip install Flask
  4. We are going to use Ngrok that can be used to check webhooks from your local server. You can download it from here.

Now we can start writing code for our project.

First, let’s create a structure of our application using the command line:

# Create folders
$ mkdir -p travel_assistance && cd travel_assistance && mkdir templates static
# Create files
$ touch index.py static/{custom.js,style.css} templates/index.html requirements.txt .flaskenv .env

After running the above commands you can see the structure of our app as below

If you want you can create an app structure manually as shown above.

Now copy the below libraries in the requirements.txt file and add the following libraries that we needed.

Flask==1.0.2requests==2.18.4dialogflow==0.4.0python-dotenv==0.8.2

Creating a virtual environment :

From your command line navigate to your project root directory (travel_assistanse) and then execute the below command:

$ python3 -m venv env

or

$ python -m venv env

After that activate the virtual environment using below command:

$ source env/bin/activate

If you are using Windows, activate the virtual environment with below command:

$ env/Scripts/activate

Now let’s configure your Flask environment in .flaskenv by adding the below lines.

FLASK_APP=index.pyFLASK_ENV=development

This will instruct Flask to use index.py as the main entry file and start up the project in development mode.

Now we are ready to code for Flask App. Copy the below code into your index.py file.

# /index.py
from flask import Flask, request, jsonify, render_template
import os
import dialogflow
import requests
import json
import pusher

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

# run Flask app
if __name__ == "__main__":
app.run()

In the above code, we have created a root ‘/’ which will render the index.html file that presents in the templates folder.

Creating UI for our Travel Assistance chatbot:

Let’s create a simple chatbot interface where users can send messages and also on the same page they can see a reply that sent from the bot.

Now go to the templates/index.html file and copy the below code in it.

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>Travel Assistance</title>
</head>
<body>
<div class="container h-100">
<div class="row align-items-center h-100">
<div class="col-md-8 col-sm-12 mx-auto">
<div class="h-100 justify-content-center">
<div class="chat-container" style="overflow: auto; max-height: 80vh">
<!-- chat messages -->
<div class="chat-message col-md-5 offset-md-7 bot-message">
Welcome to Travel portal. How can I help you?
</div>
</div>
<form id="target">
<input class="input" type="text" value="" placeholder="Enter message..." id="input_message"/>
<input type="submit" hidden>
</form>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<script src="{{ url_for('static', filename='custom.js')}}"></script>
</body>
</html>

In the above HTML code, we are using Bootstrap. In the above code, we have imported Bootstrap’s style CDN, we use it in creating jquery and custom.js files later. A default welcome message is displayed when the page loads.

Now copy the following code in your static/style.css file:

body,html {
height: 100%;
}

.chat-container {
/*margin: 0px;*/
padding: 0px;
width: 500px;
/*margin: 35px 0px;*/
margin-left: 15%;
margin-right: 15%;
}

.chat-message {
padding: 6px;
border-radius: 6px;
margin-bottom: 3px;
}

.bot-message {
background: #22b538;
max-width: 300px;
color: white;
margin-left: auto;
}

.human-message {
background: dodgerblue;
max-width: 300px;
color: white;
margin: 13px 1px;
}

.input {
width: 500px;
/*margin: 35px 0px;*/
margin-left: 15%;
margin-right: 15%;

Now our UI is ready. Let’s see how our UI renders like, to do that first we need to run our flask app from the command line using the following command

 flask run

After running successfully the output in your command line looks as shown below:

The app is running on port 5000, we could see our app with the welcome message at http://127.0.0.1:5000/.


DialogFlow-Fulfillment:

Enabling webhook for intents:

In the previous article, we have created a Travel Agent in DialogFlow. Here we’re going to work on that agent. Visit here to see how to setup Dialoflow account and create Travel Agent.

We need to enable webhook for intents that need to communicate with our server data or to get responses from the server. Now go to our Travel Agent page in DialogFlow, and then go to Book a flight ticket — yes follow-up intent and Enable webhook call for this intent in Fulfillment section.

If you have any responses in Response section delete them because sometimes we will get a response from Response section if there is any but we need a response from webhook for this intent.

Do same for Book a flight ticket — no follow-up intent.

Getting our API keys:

To make use of DialogFlow Python library, it requires an API key. So we need to get it.

  1. Go to Google Cloud Platform.
  2. Select our bot project name — Travel (This is the same as the name we gave our Agent)

3. Copy your Project ID and add it to the .env file as below:

DIALOGFLOW_PROJECT_ID= <your project-id>

4. Next, go to APIs & Services then Credentials.

5. Under Create credentials, click on Service account key.

6. Under Create credentials, click on Service account key

7. Then select JSON under key type.

8. Finally, click on the Create button to download your API key

Your API key will be downloaded automatically.

Now, copy the downloaded JSON file ( Travel-****.json) to the root folder of the project - travel_assistance.

*This JSON file is your Private key, don’t commit in any public repositories.

9. Next, add the path to the file to the .env file:

GOOGLE_APPLICATION_CREDENTIALS=<your JSON file>

When a user asks our bot to book a flight ticket, it will detect the intent of the user and when it receives all required parameters it shall send a request to our webhook with those parameters (city, date, etc.). Using those parameters we will send a response to the user.

Now let’s create a new route which we’ll use as our webhook. Add the following code to index.py:

@app.route('/webhook', methods=['POST'])
def webhook():
data = request.get_json(silent=True)
if data['queryResult']['queryText'] == 'yes':
reply = {
"fulfillmentText": "Ok. Tickets booked successfully.",
}
return jsonify(reply)

elif data['queryResult']['queryText'] == 'no':
reply = {
"fulfillmentText": "Ok. Booking cancelled.",
}
return jsonify(reply)

Webhook responses should be in a proper JSON format, this way DialogFlow can understand what to display to the user. Go to your DialogFlow console and you can check by sending messages from the right side of your screen.

Clicking on DIAGNOSTIC INFO button, we can view all details about the request being sent from DialogFlow and response being sent from our webhook. DIAGNOSTIC INFO can also be very useful for debugging your webhook.


Run the webhook using Ngrok:

Ngrok establishes secure tunnels from a public endpoint such as the internet to a locally running service while capturing all traffic for detailed inspection and replay.

Why Ngrok?

DialogFlow doesn’t support localhost. To integrate it as webhook for DialogFlow, we need to make it live.

In my use case, I will be using Ngrok to expose the flask web server running on my local machine to the internet. I just tell Ngrok what port the web server is listening on(5000).

To run Ngrok, run the following command in the current working directory:

ngrok http <port_number>

After running the above command then your output looks like this:

Copy your https: link that generated by ngrok.


Setup webhook in DialogFlow:

Go to DialogFlow console and navigate to Fulfillment tab in the left sidebar. Then enable the webhook and enter your ngrok link followed by webhook with a ‘/’.

<ngrok link>/webhook

How to send messages to Dialoflow and display responses?

When a user submits a message, we’ll send it to Diagflow to detect the intent of the user. DialogFlow will process the text, then send back a fulfillment response.

Make sure you have enabled webhook in your intents.

Add the following code in your index.py file:

def detect_intent_texts(project_id, session_id, text, language_code):
session_client = dialogflow.SessionsClient()
session = session_client.session_path(project_id, session_id)

if text:
text_input = dialogflow.types.TextInput(
text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(
session=session, query_input=query_input)
return response.query_result.fulfillment_text

Finally, let’s create a route that this text will be submitted to. Add the following code to index.py:

@app.route('/send_message', methods=['POST'])
def send_message():
message = request.form['message']
project_id = os.getenv('DIALOGFLOW_PROJECT_ID')
fulfillment_text = detect_intent_texts(project_id, "unique", message, 'en')
response_text = { "message": fulfillment_text }
return jsonify(response_text)

If you see above route we used “unique” as the second parameter in the detect_intent_texts function. This is meant to be a unique id of the user using our bot so that the bot can maintain session among different users.

Now let’s write a code to display messages in static/custom.js file.

Add the following code in custom.js file:

function submit_message(message) {
$.post( "/send_message", {message: message}, handle_response);

function handle_response(data) {
// append the bot repsonse to the div
$('.chat-container').append(`
<div class="chat-message col-md-5 offset-md-7 bot-message">
${data.message}
</div>
`)
// remove the loading indicator
$( "#loading" ).remove();
}
}

If you see the above code, we created a function called submit_message-that will be invoked once a user submits a message.

Then, we’ll use jQuery to send a message to our route /send_message-from where it will be processed.

Now add the following code to your custom.js file:

$('#target').on('submit', function(e){
e.preventDefault();
const input_message = $('#input_message').val()
// return if the user does not enter any text
if (!input_message) {
return
}

$('.chat-container').append(`
<div class="chat-message col-md-5 human-message">
${input_message}
</div>
`)

// loading
$('.chat-container').append(`
<div class="chat-message text-center col-md-2 offset-md-10 bot-message" id="loading">
<b>...</b>
</div>
`)

// clear the text input
$('#input_message').val('')

// send the message
submit_message(input_message)
});

The above code will listen for when the user submits the message, then call the submit_message function to submit the input text from the user.

That’s Great. Now we are all set to start testing our bot. Go to your command line and restart your flask application and test it out.

In webhook, you could write your own logic to get dynamic responses. For example, if a user wants to book 100 flight tickets, and then we need to get the number of tickets as a parameter, validate with maximum limit and should display something like “Sorry, maximum 7 seats can be booked at a time.”

So in this tutorial, we have learned how to build a chatbot using python flask and DialogFlow. I hope this helps you to build your own chatbot. Good luck!

Any queries, please drop them in the comment section.

This story is authored by Venu Vaka. Venu is a software engineer and machine learning enthusiast.

ZenOfAI

AI | Machine Learning | Big Data

Engineering@ZenOfAI

Written by

ZenOfAI

ZenOfAI

AI | Machine Learning | Big Data