How to create a personalized response with dialoglow? - python-3.x

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"

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
then go to your intent and enable the webhook for the intent
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.

Related

Google Assistant / Dialogflow Back Press Context

from pydialogflow_fulfillment import DialogflowResponse
from flask import Flask, request
app = Flask(__name__)
#app.route('/webhook', methods = ['POST'])
def user():
request_ = request.get_json(force=True)
print(request_)
qr = request_['queryResult']
queryText = qr['queryText']
querys = str(queryText)
print("querys")
print(querys)
if (querys == "GOOGLE_ASSISTANT_WELCOME"):
return{
"payload": {
"google": {
"expectUserResponse": True,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Choose a item"
}
}
]
},
"systemIntent": {
"intent": "actions.intent.OPTION",
"data": {
"#type": "type.googleapis.com/google.actions.v2.OptionValueSpec",
"listSelect": {
"title": "Hello",
"items": [
{
"optionInfo": {
"key": "first title key"
},
"description": "first description",
"image": {
"url": "https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png",
"accessibilityText": "first alt"
},
"title": "first title"
},
{
"optionInfo": {
"key": "second"
},
"description": "second description",
"image": {
"url": "https://lh3.googleusercontent.com/Nu3a6F80WfixUqf_ec_vgXy_c0-0r4VLJRXjVFF_X_CIilEu8B9fT35qyTEj_PEsKw",
"accessibilityText": "second alt"
},
"title": "second title"
}
]
}
}
}
}
}
}
elif (querys == "actions_intent_OPTION"):
request_ = request.get_json(force=True)
# get json data in user request
qr = request_['queryResult']
print(request_)
queryText = qr['queryText']
querys = str(queryText)
print("querys")
print(querys)
dialogflow_response = DialogflowResponse("you r selecting"+querys)
print("Response:\n" +dialogflow_response.get_final_response())
people = dialogflow_response.get_final_response()
print(people)
return people
if __name__ == '__main__':
app.run()
OUTPUT:
querys
actions_intent_OPTION
Response:
{"fulfillmentText": "actions_intent_OPTION", "fulfillmentMessages": [], "source": "webhook", "outputContexts": [], "payload": {"google": {}}}
screenshot screenshot
Above my Code Have.I have a case where I can go back from in between conversation in Dialogflow, When tried in Google assistant, pressing back and selecting another option makes a google search instead of performing the reqired action. I am working with webhook fullfillment.
Example Scenario (same happens with me):
Google Assistant: shows list view "Choose a item"
Me: i select first title
Google Assistant : it shows "you r selecting first title" or"action_intent_OPTION"
Me: then i press back button
Google Assistant : Displays same List View (Choose a item)
Me: then i choose second title
Google Assistant : it shows "can i say that again?".
I need help with being within the context even after back press.
Me: i select first title
Google Assistant : it shows "you r selecting first title" or"action_intent_OPTION"
Me: when i choose second title
Google Assistant : you r selecting second title.
or
Is there a way to get any intents in between the conversation ?
Like, if I was in the middle of a conversation, say after 4-5 questions with google, and if I ask the second question, it is replying - "I missed that, say that again ?", instead i need the 2nd intent to work. (I have used follow up intents in this case)

Having trouble with Google Assistant repeating previous message in Dialogflow

I'm working on a very simple Dialogflow with about 15-20 intents. All of these intents use a text response except one. The only intent that does not use a text response is called 'repeat'. The intent (repeat) should be able to repeat whatever was previously said by Google Assistant.
I've tried to set this up using Multivocal but have not been successful. When I type a command into the test simulator I'll get the initial response, but when I follow up with 'repeat' the default response of 'Not available' is returned. The webhook times out when I look at the Diagnostic Info. My sense is that I've configured something wrong because I've read these answers and not been able to solve my problem:
How to repeat last response of bot in dialogflow
Dialogflow - Repeat last sentence (voice) for Social Robot Elderly
Use multivocal libary to configure repeat intent in Dialogflow for VUI
I'm using the inline editor within Dialogflow my index.js looks like:
const Multivocal = require('multivocal');
const conf = {
Local: {
en: {
Response: {
"Action.multivocal.repeat": "Let me try again",
}
}
}
};
new Multivocal.Config.Simple( conf );
exports.webhook = Multivocal.processFirebaseWebhook;
exports.dialogflowFirebaseFulfillment = Multivocal.processFirebaseWebhook;
And my package.json includes the Multivocal dependency:
"multivocal": "^0.15.0"
My understanding based on the above SO questions is that these config values would be enough and I don't need to do any coding, but I'm clearly screwing something (many things?) up. How can I get the prior response in Google Assistant to repeat when a user says 'repeat' or something similar? Multivocal seems like a simple solution, if I can do it that way.
Additional logs:
Fulfillment request (removed project id information):
{
"responseId": "--",
"queryResult": {
"queryText": "repeat",
"action": "multivocal.repeat",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
""
]
}
}
],
"outputContexts": [
{
"name": "project info",
"parameters": {
"no-input": 0,
"no-match": 0
}
}
],
"intent": {
"name": "project info",
"displayName": "repeat"
},
"intentDetectionConfidence": 1,
"languageCode": "en"
},
"originalDetectIntentRequest": {
"payload": {}
},
"session": "project info"
}
Raw API response (removed project and response id)
{
"responseId": "",
"queryResult": {
"queryText": "repeat",
"action": "multivocal.repeat",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
""
]
}
}
],
"intent": {
"name": "projects info",
"displayName": "repeat"
},
"intentDetectionConfidence": 1,
"diagnosticInfo": {
"webhook_latency_ms": 4992
},
"languageCode": "en"
},
"webhookStatus": {
"code": 4,
"message": "Webhook call failed. Error: DEADLINE_EXCEEDED."
}
}
My simple intent that I've added based on the recommendation that for repeat to work on an intent it must use fulfillment and not based text response in Dialogflow
Here is my index.js file using the inline editor with suggestion to add text response in the config:
const conf = {
Local: {
en: {
Response: {
"Intent.help": [
"I'm sorry, I'm not able to help you.",
"You, John, Paul, George, and Ringo ey?"
],
"Action.multivocal.repeat": "Let me try again"
}
}
}
};
This line at the end of my index.js seems odd to me, but may be unrelated:
exports.webhook = Multivocal.processFirebaseWebhook;
exports.dialogflowFirebaseFulfillment = Multivocal.processFirebaseWebhook;
It sounds like you're triggering the Fallback Intent. You also need an Intent defined in Dialogflow that has an Action set to "multivocal.repeat". That might look something like this:
In the dialogflow directory of the npm package (or on github) you'll find a zip file with this and several other "standard" Intents that you can use with mulivocal.
Additionally, all the other Intents that you want to be repeated must use fulfillment to send the response (the library doesn't know what might be sent unless it can send it itself). The simplest way to do this is to enable fulfillment on each, and move the text responses from their Dialogflow screens into the configuration under an entry such as "Intent.name" (replacing "name" with the name of the Intent) or "Action.name" if you set an action name for them.
So your configuration might be something like
const conf = {
Local: {
en: {
Response: {
"Intent.welcome": [
"Hello there!",
"Welcome to my Action!"
],
"Action.multivocal.repeat": [
"Let me try again"
]
}
}
}
};

Custom Payload with dialogflow on google plateform

I am trying to sent custom payload in an dialogflot intent. When i am selecting the custom payload option available under google assistant it gives the following predefined json format : -
{
"google": {
}
}
now i am not aware about what i need to put in there in order to get a response from here. Any guide will be helpful
There are some compulsory Keys to be added in Rich Response JSON.
You must have Suggestion Chips and a Simple Response to maintain the follow-up of your Action. AoG rejects any action with missing Suggestion Chips or Follow-Up Response.
Refer to this JSON for Basic Card Response:
{
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Here's an example of a basic card."
}
},
{
"basicCard": {
"title": "Title: this is a title",
"subtitle": "This is a subtitle",
"formattedText": "This is a basic card. Text in a basic card can include \"quotes\" and\n most other unicode characters including emojis. Basic cards also support\n some markdown formatting like *emphasis* or _italics_, **strong** or\n __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other\n things like line \nbreaks",
"image": {
"url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
"accessibilityText": "Image alternate text"
},
"buttons": [
{
"title": "This is a button",
"openUrlAction": {
"url": "https://assistant.google.com/"
}
}
],
"imageDisplayOptions": "CROPPED"
}
},
{
"simpleResponse": {
"textToSpeech": "Which response would you like to see next?"
}
}
]
}
}
}
}
You can refer to the specific Rich Response JSON for your Action in the following Documentation:
https://developers.google.com/assistant/conversational/rich-responses#df-json-basic-card

How to fix "error": "'parameter_name'" when using IBM Cloud Function's REST API?

I have an action in IBM Cloud Functions that only receives one parameter: "frame". I'm using Postman to test the REST API endpoint provided with the action. However, when I provide the "frame" parameter it returns the following:
"response": {
"result": {
"error": "'frame'"
},
"status": "application error",
"success": false
}
I've experienced this problem when I invoke this action in the IBM Cloud Functions' console. I resolve it by erasing a space in the input modal and adding it again, then it works like a charm in the console. However, I can't do the same thing with an HTTP request.
The way I'm currently doing the HTTP request is like this:
POST https://us-south.functions.cloud.ibm.com/api/v1/namespaces/{namespace}/actions/{action_name}?blocking=true&frame={value}
The action should return the result I'm expecting but it doesn't do that right now. Please help me, any answers would be great!
EDIT:
This is the action's code:
import requests, base64, json, cv2
from PIL import Image
from six import BytesIO
def json_to_dict(json_str):
return json.loads(json.dumps(json_str))
def frame_to_bytes(frame):
frame_im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_im = Image.fromarray(frame_im)
stream = BytesIO()
pil_im.save(stream, format="JPEG")
stream.seek(0)
img_for_post = stream.read()
img_base64 = base64.b64encode(img_for_post)
return img_base64
def main(dict):
cap = cv2.VideoCapture(dict['frame'])
if not cap.isOpened():
return { "error": "Unable to open video source" }
ret, frame = cap.read()
if ret is False:
return { "error": "Unable to read video source" }
# openALPR API part
OPENALPR_SECRET_KEY = {my_secret_key}
url = "https://api.openalpr.com/v2/recognize_bytes?recognize_vehicle=1&country=us&secret_key=%s" % (
OPENALPR_SECRET_KEY)
r = requests.post(url, data=frame_to_bytes(frame))
resp = json_to_dict(r.json())
print(resp)
if not resp['results']:
return { "error": "Plate number not recognized" }
plates = []
for plate in resp['results']:
if plate['confidence'] < 75:
pass
else:
print(plate['plate'])
plates.append(plate['plate'])
return { "plates": plates }
This is the activation response (the status returned was 502 Bad Gateway according to Postman):
{
"activationId": "5a83396b9f53447483396b9f53e47452",
"annotations": [
{
"key": "path",
"value": "{namespace}/{name}"
},
{
"key": "waitTime",
"value": 5531
},
{
"key": "kind",
"value": "python:3.7"
},
{
"key": "timeout",
"value": false
},
{
"key": "limits",
"value": {
"concurrency": 1,
"logs": 10,
"memory": 1024,
"timeout": 60000
}
},
{
"key": "initTime",
"value": 3226
}
],
"duration": 3596,
"end": 1560669652454,
"logs": [],
"name": "{name}",
"namespace": "{namesapce}",
"publish": false,
"response": {
"result": {
"error": "'frame'"
},
"status": "application error",
"success": false
},
"start": 1560669648858,
"subject": "{my_email}",
"version": "0.0.7"
}
EDIT 2:
I've also tried to enable it as a web action to see if it changes anything. However, it's no use. When I use this HTTP request:
https://us-south.functions.cloud.ibm.com/api/v1/web/{namespace}/default/{action_name}?frame={value}
I get:
{
"code": "e1c36666f4db1884c48f028ef58243fc",
"error": "Response is not valid 'message/http'."
}
which is understandable since what my functions returns is json. However, when I use this HTTP request:
https://us-south.functions.cloud.ibm.com/api/v1/web/{namespace}/default/{action_name}.json?frame={value}
I get:
{
"code": "010fc0efaa29f96b47f92735ff763f50",
"error": "Response is not valid 'application/json'."
}
I really don't know what to do here
After googling a bit I found something that works for me right now although it might not work for everyone. Apache has a python "client" example for using an action's REST API which uses the requests library.
Thing is that in order to use it you need to provide your API KEY, which I don't know how to get by any other means than getting it directly from the IBM Cloud CLI. Since I'm trying to access the function from a web server, I would need to save the key in an environment variable or save it in a text file and access it from there or install the CLI on the server, login with my credentials and call ibmcloud wsk property get --auth.
Also, this method didn't work with the web action endpoint when I tried it.

Python - youtube.playlistItems().delete() doesn't work with [Deleted video] (YouTube API v3)

I want to clean the remnant [Deleted video] of several playlist of my YouTube channel. I'm using this code but it doesn't work.
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
CLIENT_SECRETS_FILE = "client_secrets.json"
YOUTUBE_READ_WRITE_SCOPE = "https://www.googleapis.com/auth/youtube"
def get_authenticated_service(args):
flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE,
scope=YOUTUBE_READ_WRITE_SCOPE,
message=MISSING_CLIENT_SECRETS_MESSAGE)
storage = Storage("%s-oauth2.json" % sys.argv[0])
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run_flow(flow, storage, args)
return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
http=credentials.authorize(httplib2.Http()))
if __name__ == "__main__":
try:
args = argparser.parse_args()
youtube = get_authenticated_service(args)
youtube.playlistItems().delete(id="xxxxxxxxx").execute()
except HttpError as e:
print ("\nAn HTTP error %d occurred:\n%s" % (e.resp.status, e.content))
I get this error massage (403)(Forbidden)
The request is not properly authorized to delete the specified playlist item
{
"error": {
"errors": [
{
"domain": "youtube.playlistItem",
"reason": "playlistItemsNotAccessible",
"message": "Forbidden",
"locationType": "parameter",
"location": "id"
}
],
"code": 403,
"message": "Forbidden"
}
}
Even using this (Try this API) from here:
https://developers.google.com/youtube/v3/docs/playlistItems/delete?hl=en-419
or here
https://developers.google.com/youtube/v3/docs/playlistItems/delete?hl=es-419
My credentials, my developer Key and my client_secrets.json file are good, becouse i used it before and its works.
Someone knows what is happend? Or someone knows other way to remove "Deleted video" from playlist using Python + Youtube API v3?
The problem was solved:
If you execute PlaylistItems().list(), you get this response.
"items": [
{
"kind": "youtube#playlistItem",
"etag": "\"DuHzAJ-eQIiCIp7p4ldoVcVAOeY/Ktqi5NIapmys1w2V0FiorhFR-Uk\"",
"id": "UExES3pRck8tTUFDZndHV3Z0eXVaVHZXNENxNTNGYV9wNC4wMTcyMDhGQUE4NTIzM0Y5",
"snippet": {
"publishedAt": "2018-06-06T13:43:17.000Z",
"channelId": "xxxxxxxxxxxxxxxxxx",
"title": "Deleted video",
"description": "This video is unavailable.",
"channelTitle": "xxxxxxxxxxxxxxxxxx",
"playlistId": "xxxxxxxxxxxxxxxxxxxxxxx",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "D6NOeUfxCnM"
}
for delete items from playlist you must to use this
"id": "UExES3pRck8tTUFDZndHV3Z0eXVaVHZXNENxNTNGYV9wNC4wMTcyMDhGQUE4NTIzM0Y5",
if you use this "videoId": "D6NOeUfxCnM" you get the error massage (403)(Forbidden)

Resources