Search code examples
python-3.xactionwebhooksdialogflow-esgoogle-books

How to create a personalized response with dialoglow?


I am trying to build a chatbot with dialogflow which is able to advice books for users. But I really don't find how to build the responses in a python file. I mean, I want that if the intent is "search-book", then it will send few books depending on the gender the user said. Actually, my python file is there :

# -*- coding:utf-8 -*-
# !/usr/bin/env python
# Copyright 2017 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import os
import sys
import json
import yaml
try:
import apiai
except ImportError:
sys.path.append(
    os.path.join(
        os.path.dirname(os.path.realpath(__file__)),
        os.pardir,
        os.pardir
    )
)

import apiai

CLIENT_ACCESS_TOKEN = '197ef97149d449a6962ba5bd5e488607'

def yaml_loader(filepath):
  """Loads a yaml file"""
  with open(filepath, 'r') as file:
     data = yaml.load(file)
  return data
def yaml_dump(filepath, data):
  """Dumps data to a yaml file"""
  with open(filepath, "w") as file:
    yaml.dump(data, file)


def main():
  ai = apiai.ApiAI(CLIENT_ACCESS_TOKEN)
  filepath = "proxy.yaml"
  data = yaml_loader(filepath)

  proxy = data.get('proxy')
  for proxy_protocol, proxy_host in proxy.items():
    os.environ["" + proxy_protocol] = "" + proxy_host

while True:
    print(u"> ", end=u"")
    user_message = input()

    if user_message == u"exit":
        break

    request = ai.text_request()
    request.query = user_message

    response = json.loads(request.getresponse().read())

    result = response['result']
    action = result.get('action')
    actionIncomplete = result.get('actionIncomplete', False)

    print(u"< %s" % response['result']['fulfillment']['speech'])

    if action is not None:
        if action == "search-book":
            parameters = result['parameters']

            text = parameters.get('text')
            Gender = parameters.get('Gender')

            print (
                'text: %s, Gender: %s' %
                (
                    text if text else "null",
                    Gender if Gender else "null",
                )
            )


if __name__ == '__main__':
  main()

For Google books API I found this, and it is working: https://github.com/hoffmann/googlebooks

I already have created an Entity called "gender" and an intent named "search-book"


Solution

  • what you have to do is you need to implement a webhook (a web service) for your intent.

    set the url to your webhook here

    enter image description here

    then go to your intent and enable the webhook for the intent

    enter image description here

    so when some one query for your intent your webhook will get a post request with bellow josn body

    {
      "responseId": "ea3d77e8-ae27-41a4-9e1d-174bd461b68c",
      "session": "projects/your-agents-project-id/agent/sessions/88d13aa8-2999-4f71-b233-39cbf3a824a0",
      "queryResult": {
        "queryText": "user's original query to your agent",
        "parameters": {
          "param": "param value"
        },
        "allRequiredParamsPresent": true,
        "fulfillmentText": "Text defined in Dialogflow's console for the intent that was matched",
        "fulfillmentMessages": [
          {
            "text": {
              "text": [
                "Text defined in Dialogflow's console for the intent that was matched"
              ]
            }
          }
        ],
        "outputContexts": [
          {
            "name": "projects/your-agents-project-id/agent/sessions/88d13aa8-2999-4f71-b233-39cbf3a824a0/contexts/generic",
            "lifespanCount": 5,
            "parameters": {
              "param": "param value"
            }
          }
        ],
        "intent": {
          "name": "projects/your-agents-project-id/agent/intents/29bcd7f8-f717-4261-a8fd-2d3e451b8af8",
          "displayName": "Matched Intent Name"
        },
        "intentDetectionConfidence": 1,
        "diagnosticInfo": {},
        "languageCode": "en"
      },
      "originalDetectIntentRequest": {}
    }
    

    you can get the intent name

    body.queryResult.intent.displayName
    

    also you can get the parameters

    body.queryResult.parameters
    

    since now you have the parameters you need, you can call to your googlebooks api and send the result back to the google dialogflow

    the responce json should be something like this

    {
      "fulfillmentText": "This is a text response",
      "fulfillmentMessages": [
        {
          "card": {
            "title": "card title",
            "subtitle": "card text",
            "imageUri": "https://assistant.google.com/static/images/molecule/Molecule-Formation-stop.png",
            "buttons": [
              {
                "text": "button text",
                "postback": "https://assistant.google.com/"
              }
            ]
          }
        }
      ],
      "source": "example.com",
      "payload": {
        "google": {
          "expectUserResponse": true,
          "richResponse": {
            "items": [
              {
                "simpleResponse": {
                  "textToSpeech": "this is a simple response"
                }
              }
            ]
          }
        },
        "facebook": {
          "text": "Hello, Facebook!"
        },
        "slack": {
          "text": "This is a text response for Slack."
        }
      },
      "outputContexts": [
        {
          "name": "projects/${PROJECT_ID}/agent/sessions/${SESSION_ID}/contexts/context name",
          "lifespanCount": 5,
          "parameters": {
            "param": "param value"
          }
        }
      ],
      "followupEventInput": {
        "name": "event name",
        "languageCode": "en-US",
        "parameters": {
          "param": "param value"
        }
      }
    }
    

    some thing i have done with node js

     'use strict';
     const http = require('http');
    
     exports.bookWebhook = (req, res) => {
    
         if (req.body.queryResult.intent.displayName == "search-book") {
             res.json({
                 'fulfillmentText': getbookDetails(req.body.queryResult.parameters.gender)
             });
         }
     };
    
    
     function getbookDetails(gender) {
         //do you api search for book here
    
         return "heard this book is gooooood";
     }
    

    in getbookDetails function you can call to you api get the book and format the string and return the string.. same should be applied to python as we. only the syntax will be different.