Qiscus Chat SDK by Qiscus
Credits: Qiscus Chat SDK Qiscus

Integrating DialogFlow (API.ai) into Qiscus SDK Chat Application

Just recently on October 10th, Google announced a change in name of API.AI into DialogFlow. There are a couple of new features following this change. Regardless of what has been changed, in this post we are going to share a simple way of how to integrate your agents that is created using DialogFlow into any Qiscus SDK chat application.

As DialogFlow doesn't have a famous 'one-click integration' feature for Qiscus (yet), we need to make a workaround to do so. Fortunately, DialogFlow provides some integration ways through its SDK options, here in the image, you can see several programming languages that you can use for your agent integration.

Image of DialogFlow SDK for integration

For our simplicity purpose, we are going to use our last article on how to create a simple chat application using QiscusSDK and how to integrate a simple echo bot into it as prior requirement to this post. You can find the codes here. Since it uses Python as the webhook, in this article we are going to use DialogFlow SDK for Python as well, called apiai (this might changes later as the effect of name change).

Another thing to note is that we assume you already have an agent or two created in DialogFlow, if not please refer to the basic tuts to create one.

1. Adding DialogFlow SDK Python Package

Alright, let's get started! First thing first, let's add DialogFlow SDK python package (apiai) into our project, come on open setup.py file, and add it,

from setuptools import setup, find_packages

setup(
name='simplebot',
version='1.0',
long_description=__doc__,
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=[
'apiai',
'flask',
'requests'
],
)

and let's import that package into our simplebot/views.py file then instantiate new object from this package,

import apiai
import json
import requests

from flask import render_template, request
from simplebot import app

app_id = "YOUR_APP_ID"
secret_key = "YOUR_SECRET_KEY"
sender_email = "YOUR_BOT_EMAIL"
bot_name = "YOUR_BOT_NAME"
qiscus_sdk_url = "https://{}.qiscus.com".format(app_id)

client_access_token = "YOUR_DIALOGFLOW_CLIENT_ACCESS_TOKEN"

ai = apiai.ApiAI(client_access_token)
...

Notice in the code above, we need client_access_token to initiate new object from the package. So please make sure you already got this token from your agent DialogFlow console and assign it to the client_access_token, you may want to see this image to locate where it is.

image of console

2. Incoming Message and Agent Response

After you got this package set, let's implement it in our index() function. First we will need to get query from Qiscus chat app to be passed to the DialogFlow agent before we send it to get the response from agent.

...

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
payload = request.get_json().get('payload')

room_id = payload.get("room").get("id")
message = payload.get("message").get("text")

# let's prepare the incoming message (query) we want to send to dialogflow agent
conv = ai.text_request()
conv.session_id = "ADD_YOUR_UNIQUE_SESSION_ID(ANY_SESSION_ID_WOULD_WORK)"
conv.query = message

# and let's get the response from dialogflow agent
response = conv.getresponse()
response_json = json.loads(response.read().decode('utf-8'))
response_messages = response_json.get("result").get("fulfillment").get("messages")

post_url = "{}/api/v2/rest/post_comment".format(qiscus_sdk_url)
headers = {"QISCUS_SDK_SECRET": secret_key}

data = {
"sender_email": sender_email,
"room_id": room_id,
"message": message,
"type": "text",
"payload": {}
}

req = requests.post(post_url, headers=headers, params=data)

...

What we need to do next is just to send back any response we got from agent to the Qiscus chat app. This can be anything: plain text or any additional payload. So, before we return it back, we need to know the structure of payload that is returned by our agent at DialogFlow, please have a look at JSON code below, this JSON code is the payload that is returned from our agent in DialogFlow.

{
"id": "354eaf3c-c40e-42e4-acf6-fc2ef0e71799",
"timestamp": "2017-10-26T08:11:58.239Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "yo",
"action": "hi",
"actionIncomplete": false,
"parameters": {},
"contexts": [],
"metadata": {
"intentId": "fe8dbb22-43bf-4cb4-b634-48c3a2af98ae",
"webhookUsed": "false",
"webhookForSlotFillingUsed": "false",
"intentName": "hi"
},
"fulfillment": {
"speech": "Hi! How are you doing?",
"messages": [
{
"type": 0,
"id": "948f5dad-550c-4fa1-93ba-3819d8e42fbb",
"speech": "Hi! How are you doing?"
}
]
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "d70073ae-0cdb-4bc4-b7d9-d8d0b16b98af"
}

From JSON code above, there is a fulfillment field. It has additional children fields that are actual responses from our agent which we are interested in.

Notice the messages field? its value is a list. So we may assume our agent could return one or more messages when we make a request to DialogFlow. This could be another text or additional custom payload, such as payload for button or card. However, to simplify this process, let's handle text message only.

Ok, let's modify our index() function in views.py file again.

...

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
payload = request.get_json().get('payload')

room_id = payload.get("room").get("id")
message = payload.get("message").get("text")

# let's prepare the incoming message (query) we want to send to dialogflow agent
conv = ai.text_request()
conv.session_id = "ADD_YOUR_UNIQUE_SESSION_ID(ANY_SESSION_ID_WOULD_WORK)"
conv.query = message

# and let's get the response from dialogflow agent
response = conv.getresponse()
response_json = json.loads(response.read().decode('utf-8'))
response_messages = response_json.get("result").get("fulfillment").get("messages")

post_url = "{}/api/v2/rest/post_comment".format(qiscus_sdk_url)
headers = {"QISCUS_SDK_SECRET": secret_key}

for msg in response_messages:

data = {
"sender_email": sender_email,
"room_id": room_id,
"message": msg.get("speech"),
"type": "text",
"payload": {}
}

try:
requests.post(post_url, headers=headers, params=data)
except requests.exceptions.RequestException as e:
print(e)
return "Something went wrong!", 401

return "OK", 200
else:
return render_template('index.html', appId=app_d, senderEmail=sender_email)

...

save and let's run our webhook, and you may open http://locahost:5000 in your browser.

$ make run

3. Webhook

Since we've been developing this integration locally, Qiscus SDK won't recognise our webhook even though we are trying to add our local webhook address to the Qiscus SDK. So to help making our webhook open to the internet, we are going to use ngrok add the generated temporary subdomain addresses into our Qiscus SDK, please refer to this for ngrok.

After you did that, please try to have a chat again through generated subdomain address in your browser. Voila, you got a reply from your agent in DialogFlow!

You can find live version of this bot at https://qiscusdialog.herokuapp.com/

Closing

To sum up, this article is only focusing on how we integrate our agent that is created in DialogFlow which returns plain text only. In order to leverage your agent capability, you may want to add additional custom payload as additional fulfillment response. For example you may want to return message that contains image and buttons from DialogFlow. You can refer to Qiscus docs for any type of comments that is supported by Qiscus as well as how to add custom payload in DialogFlow for more information.

Cheers.

This article was updated on 26 October 2017

Comments

Follow @nurulishlah