MalformedResponse when asking for Permission - node.js

I am struggling on creating a very basic example asking the user for permission. The setup is
Intents:
"request_permission":
- phrase "where am I?"
- action: "request_permission"
- events: <empty>
"user_info":
- phrase: <empty>
- action: "user_info"
- events: "actions_intent_PERMISSION"
Following the official example using the node.js fulfillment library, I write:
'use strict';
const functions = require('firebase-functions');
const {Permission, DialogflowApp} = require('actions-on-google');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion, SimpleResponse} = require('dialogflow-fulfillment');
const app = dialogflow({debug: true});
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
const app = new DialogflowApp({request, response});
function welcome_intent(agent) {agent.add(`welcome intent`); }
function fallback_intent(agent) {agent.add(`fallback intent.`); }
function request_permission(agent){
let conv = agent.conv();
conv.ask(new Permission({
context: 'I need your location',
permissions: 'DEVICE_COARSE_LOCATION'
}))
agent.add(conv);
}
function user_info(agent){
if (app.isPermissionGranted()) {
const zipCode = app.getDeviceLocation().zipCode;
if (zipCode) {
app.tell(`your zip code ise ${zipCode}`);
} else {
app.tell('I cannot tell your zip code.');
}
} else {
app.tell('Without permission I cannot tell the zip code');
}
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome_intent);
intentMap.set('Default Fallback Intent', fallback_intent);
intentMap.set('request_permission', request_permission);
intentMap.set('user_info', user_info);
agent.handleRequest(intentMap);
});
Still, when starting the simulator and activating my app and then asking "where am I?" I get
MalformedResponse
'final_response' must be set.
And yes, I did check the firebase console logs and the "Fulfillment"-Slider "Enable webhook call for this event" is on.
I wouldn't ask here if there would a V2 compatible example with a very basic permission request. I am aware of the answer in Dialogflow v2 API + Actions v2 API: MalformedResponse 'final_response' must be set

Related

How to get user first_name in telegram chatbot build with dialogflow_es

I'm new to Dialogflow and want to understand how to extract a user's first_name in a Telegram chatbot I created. I built a chatbot using intents + inline editor (code below) and learned how to get users' data from responses using agent.parameters, but still can't code anything to extract parts of user's payload data to get first_name without asking for it. I used the following code but it didn't work:
let display_name = agent.originalRequest.payload.data.message.from.first_name
Would be super grateful if someone can explain the right code function to extract this data!
My full inline code is below.
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const admin = require ('firebase-admin');
admin.initializeApp();
admin.auth();
const db = admin.firestore();
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function welcome(agent) {
agent.add(`Hi! Do you want to create an account?`);
}
function createUser(agent) {
agent.add(`Thank you! You have successfully created your account`);
let uid = makeid(28);
let email = agent.parameters.email;
let password = agent.parameters.user_password;
let display_name = agent.parameters.display_name;
admin.auth().createUser({uid: uid, email: email, password: password});
db.collection('users').doc(uid).set(
{uid: uid,
display_name: display_name,
});
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('Welcome Intent', welcome);
intentMap.set('account.creation', createUser);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
This is the code that worked for me
let payload = agent.originalRequest;
let firstname = payload.payload.data.from.first_name;

Display data from JSON external API and display to DIALOGFLOW using AXIOS

I need help with Displaying the response I get from my API to Dialogflow UI. Here is my code. I am currently using WebHook to connect Dialogflow to backend in Heroku.
My code
const functions = require('firebase-functions');
var admin = require("firebase-admin");
var serviceAccount = require("../../reactpageagent-dxug-firebase-adminsdk-26f6q-e1563ff30f.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://reactpageagent-dxug.firebaseio.com"
});
const { WebhookClient } = require('dialogflow-fulfillment');
const { Card, Suggestion } = require('dialogflow-fulfillment');
const axios = require('axios');
module.exports = (request, response) => {
const agent = new WebhookClient({ request, response });
function welcome(agent) {
agent.add('Welcome to my agent');
}
function rhymingWordHandler(agent) {
const word = agent.parameters.word;
agent.add(`Here are the rhyming words for ${word}`)
axios.get(`https://api.datamuse.com/words?rel_rhy=${word}`)
.then((result) => {
console.log(result.data);
result.data.map(wordObj => {
console.log(wordObj.word);
agent.add(JSON.stringify(wordObj.word));
return;
// agent.end(`${wordObj.word}`);
});
});
};
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('rhymingWord', rhymingWordHandler);
agent.handleRequest(intentMap);
}
When I console.log my the result. I get the data from the API in my console.log output, but the Data is not displayed in Dialogflow UI I also do not get any error.
Heroku log
I had real trouble with the result.data.map line of code.
In the end, I avoided it and instead processed result.data after the .then((result) => { line by checking the array length that my API returned, and if it was > 0, loop through it to output each line individually, using agent.add. If the array length was 0, I used agent.add to display a message saying 'No records found'. I used a catch to log any errors (again, using agent.add to send an error message to the user).

How to read URL for API CALL in Inline of Dialogflow?

I made some API call as node.js and I checked it works well in my local.
So I tried to put into Dialogflow.
But It doesn't give any variable.
This is the errors.
Error: could not handle the request
I explain my procedure below.
[1]
This works well in my local with node.js.
It gives a value as ['aa','bb','cc',...,'dd']
const request = require('request');
const url = "https://datos.madrid.es/portal/site/egob/menuitem.ac61933d6ee3c31cae77ae7784f1a5a0/?vgnextoid=00149033f2201410VgnVCM100000171f5a0aRCRD&format=json&file=0&filename=201132-0-museos&mgmtid=118f2fdbecc63410VgnVCM1000000b205a0aRCRD&preview=full";
function information(){
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200)
{
}
var bd = body['#graph'];
var model = [];
var i = i;
for (i = 0; i < Object.keys(bd).length; i++)
{
model[i] = bd[i].title;
}
return model;
});
}
[2]
Therefore I made some code on inline editor to check it works well with this kind of value.
I attached a function most below in this code.
When I type 'hello', it says '1 , 2'. That works well.
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const request = require('request');
process.env.DEBUG = 'dialogflow:debug';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function hello(agent) {
var model2 = information();
agent.add(model2);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('hello', hello);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
function information(){
return ['1','2'];
}
[3]
At last, I tried to do with my API call.
But It doesn't give any value.
It has every package moduel also.
I wonder why it doesn't work on same situation.
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const request = require('request');
const url = "https://datos.madrid.es/portal/site/egob/menuitem.ac61933d6ee3c31cae77ae7784f1a5a0/?vgnextoid=00149033f2201410VgnVCM100000171f5a0aRCRD&format=json&file=0&filename=201132-0-museos&mgmtid=118f2fdbecc63410VgnVCM1000000b205a0aRCRD&preview=full";
process.env.DEBUG = 'dialogflow:debug';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function hello(agent) {
var model2 = information();
agent.add(model2);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('hello', hello);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
function information(){
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200)
{
}
var bd = body['#graph'];
var model = [];
var i = i;
for (i = 0; i < Object.keys(bd).length; i++)
{
model[i] = bd[i].title;
}
return model;
});
}
You don't show the error you're getting, but there are likely two problems with what you're trying to do.
The first is that request returns its information asynchronously, through a callback. The dialogflow-fulfillment library requires that any Intent Handler that does asynchronous operations return a Promise.
While you can wrap this in a Promise, even easier is to use a version of request that will work directly with Promises, such as request-promise-native. (See, for example, https://stackoverflow.com/a/49751176/1405634)
Your other issue is that if you're using the built-in code editor for Dialogflow, you're working on top of Cloud Functions for Firebase. By default, network calls are only allowed inside Google's network. In order to access external addresses, you need to make sure you're using a paid plan such as the Blaze plan. There is, however, a free tier which is sufficient for development and most testing, and likely even light production.
You can make an app on heroku and send the fullfilment through to it, instead of using inclide code editor for google cloud.

Unable to get device coarse location when testing with google mini device

Hi I'm facing an issue that, i'm unable to get device coarse location when i tested on google mini device with same dialogflow account.I'm able to get user's name but not device location. I'm frustrated regarding this issue.Please help to resolve this.
Here is my code,
const express = require('express');
const bodyParser = require('body-parser');
const {dialogflow,Permission} = require('actions-on-google');
const app = dialogflow({
clientId : 'xyz'
});
app.intent('Default Welcome Intent', conv => {
const options = {
context: 'To locate you',
permissions: ['NAME','DEVICE_COARSE_LOCATION'],
};
conv.ask(new Permission(options));
});
app.intent('User_info',(conv, params, permissionGranted) => {
console.log(conv);
var location=conv.device.location;
console.log(location);
if(permissionGranted){
const name = conv.user.name;
console.log(name);
var resp="you are located at"+conv.device.location.city;
conv.ask(resp);
}else{
conv.close("sorry I'm unable to locate you right now. Okay bye now");
}
});
const expressApp=express().use(bodyParser.json())
expressApp.post('/',app);
expressApp.listen(3000);
I just tested the DEVICE_COARSE_LOCATION permission, and found an empty object in my Firebase logs where I expected to see the location.
Have you tried using DEVICE_PRECISE_LOCATION instead? Here's a sample how that works:
// Handle the Dialogflow intent named 'Default Welcome Intent'.
app.intent('Default Welcome Intent', (conv) => {
conv.ask(new Permission({
context: 'Hi there, to get to know you better',
permissions: ['NAME', 'DEVICE_PRECISE_LOCATION']
}));
});
// Handle the Dialogflow intent named 'actions_intent_PERMISSION'. If user
// agreed to PERMISSION prompt, then boolean value 'permissionGranted' is true.
app.intent('actions_intent_PERMISSION', (conv, params, permissionGranted) => {
if (!permissionGranted) {
conv.ask(`Ok, no worries. What's your favorite color?`);
conv.ask(new Suggestions('Blue', 'Red', 'Green'));
} else {
console.log(conv);
conv.data.userName = conv.user.name.given;
conv.data.userLatitude = conv.device.location.coordinates.latitude;
conv.data.userLongitude = conv.device.location.coordinates.longitude;
conv.ask(`Thanks, ${conv.data.userName} at latitude ${conv.data.userLatitude} and longitude ${conv.data.userLongitude}. What's your favorite color?`);
}
});

How to connect Dialogflow to Cloud Firestore via the Inline Editor in Dialogflow?

I have a Cloud Firestore database that stores the number of inhabitants of all cities in England in 2017.
Then I have a Dialogflow. Whenever I tell the name of a city to Dialogflow, I want it to get the number of inhabitants in that city from Firestore and return it to Dialogflow.
Specifically, I want to implement this via the Inline Editor.
Question: What lines of code do I need to add to the code below in order to make this happen?
So here is the code that I write in the Inline Editor in Dialogflow > Fulfillment > index.js:
'use strict';
const functions = require('firebase-functions');
const firebaseAdmin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const App = require('actions-on-google').DialogflowApp;
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Hello and welcome!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
Here is some sample code showing how to connect Firebase's Firestore database to Dialogflow fulfillment hosting on Firebase functions:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function writeToDb (agent) {
// Get parameter from Dialogflow with the string to add to the database
const databaseEntry = agent.parameters.databaseEntry;
// Get the database collection 'dialogflow' and document 'agent' and store
// the document {entry: "<value of database entry>"} in the 'agent' document
const dialogflowAgentRef = db.collection('dialogflow').doc('agent');
return db.runTransaction(t => {
t.set(dialogflowAgentRef, {entry: databaseEntry});
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`Wrote "${databaseEntry}" to the Firestore database.`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${databaseEntry}" to the Firestore database.`);
});
}
function readFromDb (agent) {
// Get the database collection 'dialogflow' and document 'agent'
const dialogflowAgentDoc = db.collection('dialogflow').doc('agent');
// Get the value of 'entry' in the document and send it to the user
return dialogflowAgentDoc.get()
.then(doc => {
if (!doc.exists) {
agent.add('No data found in the database!');
} else {
agent.add(doc.data().entry);
}
return Promise.resolve('Read complete');
}).catch(() => {
agent.add('Error reading entry from the Firestore database.');
agent.add('Please add a entry to the database first by saying, "Write <your phrase> to the database"');
});
}
// Map from Dialogflow intent names to functions to be run when the intent is matched
let intentMap = new Map();
intentMap.set('ReadFromFirestore', readFromDb);
intentMap.set('WriteToFirestore', writeToDb);
agent.handleRequest(intentMap);
});
This came from Dialogflow's Firestore sample located here: https://github.com/dialogflow/fulfillment-firestore-nodejs

Resources