DialogFlow if statement conv.ask in app.intent - node.js

I want to run this code in dialogflow ,basically i want to use if condition
for example.
if my intent "Done" reply response [Text Response] =>'Now add 2 more and say got it' then
it switch to my another intent called "alright" so the response should be "you no. is 1" and end the converstation.
similary
if my intent "Done" reply response [Text Response] =>'Now add 4 more and say got it' then
it switch to my another intent called "alright" so the response should be "you no. is 2" and end the converstation.
and goes on.
just look at the SS Below
enter image description here
enter image description here
'use strict';
// Import the Dialogflow module from the Actions on Google client library.
const {dialogflow} = require('actions-on-google');
// Import the firebase-functions package for deployment.
const functions = require('firebase-functions');
// Instantiate the Dialogflow client.
const app = dialogflow({debug: true});
var numr=0;
app.intent('done',(conv,output)=>{
if(conv.ask ==='Now add 2 more, and say got it'){
numr=1;
return numr;
}
else{
numr=2;
return numr;
}
});
app.intent('Alright',(conv)=>{
conv.close('your number is '+ numr);
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

There are a number of issues with your code that make it difficult to understand exactly what you're doing. However, there are a few things to note. I'm assuming you're using JavaScript here.
You have a syntax error in how you are using else. The else statement should be followed by an execution block. You look like you're following it with a condition. Perhaps you are trying to use else if there?
conv.ask is a function. You're then assigning a string to it which would remove it as a function. I don't think this is what you mean to do here.
In the Intent Handler for the "done" Intent, you're not sending anything back to the user using the conv.ask() function since you just removed that as a function.
Setting a global variable does not guarantee the value of it will be preserved in between Intent Handler calls. If you want a value saved during a conversation, you should use a Context or conv.data or other means.

Related

How to verify JWT in middleware function dialogflow

We are sending a HTTP Header with a JWT Token, we have configured this header in dialogflow console.
We want to verify this token in a previous step to send the request to a specific intent, for example welcome_intent.
We are using "middleware" as this previous step, and the verification is correct and it's applied to every communication. And we want to know, how
In case the JWT is wrong, we want to return an error and not continue with the associated intent, for example: welcome_intent.
We have tried to end the flow with "conv.close" in "middleware", but we have seen that the flow continues and it goes to the intent associated with the query.
How can we get out of the flow and return an error in the middleware function?
const {
dialogflow
} = require('actions-on-google');
const fulfillment = dialogflow({
clientId: "clientIdDialogflow",
debug: true
});
const jwt = require('jsonwebtoken');
fulfillment.middleware(async (conv) => {
let tokenIncorrect = await utils.verifyJWT(conv);
if (tokenIncorrect) {
conv.close("Lo siento pero no puedes continuar con la conversación.");
}
});
// Intents functions
fulfillment.intent("welcome_intent", .....);
You should be able to throw an UnauthorizedError to terminate the conversation inside intent handlers.
Here are some relevant docs with some example code: https://actions-on-google.github.io/actions-on-google-nodejs/2.12.0/classes/_service_actionssdk_conversation_conversation_.unauthorizederror.html
However, this won't work from a middleware. As you can see in https://github.com/actions-on-google/actions-on-google-nodejs/blob/9f8c250a385990d28705b3658364c74aa3c19adb/src/service/actionssdk/actionssdk.ts#L345-L350, middleware are applied before the UnauthorizedError handling wrapping the call to the intent handler: https://github.com/actions-on-google/actions-on-google-nodejs/blob/9f8c250a385990d28705b3658364c74aa3c19adb/src/service/actionssdk/actionssdk.ts#L370-L389
As implemented, middleware cannot be used to gracefully end fulfillment directly. You can, however, modify the conv object. For example, you could change the target intent (conv.intent = 'UNAUTHORIZED') in these cases and then add a handler for that intent that always throws an UnauthorizedError.

Where should I look into for the error "MalformedResponse Failed to parse Dialogflow response into AppResponse because of empty speech response

Got the error
MalformedResponse Failed to parse Dialogflow response into AppResponse because of empty speech response
had read Failed to parse Dialogflow response into AppResponse because of empty speech response for Ssml response but still did not get the point.
I'm quite a newbie.
Tried to follow the code provided in "Query Data Scalably for Actions on Google using Cloud Firestore", but got the error.
//Copyright 2018 Google LLC.SPDX-License-Identifier: Apache-2.0
'use strict';
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const app = dialogflow({debug: true});
admin.initializeApp();
const db = admin.firestore();
const collectionRef = db.collection('restaurants');
app.intent('ask_recipe_intent', (conv, {name}) => {
const term = name.toLowerCase();
const termRef = collectionRef.doc(`${term}`);
return termRef.get()
.then((snapshot) => {
const {city, name} = snapshot.data();
conv.ask(`Here you go, ${name}, ${city}. ` +
`What else do you want to know?`);
}).catch((e) => {
console.log('error:', e);
conv.close('Sorry, try again and tell me another food.');
});
});
exports.actionsOracle = functions.https.onRequest(app);
I'm trying to understand what
`${term}`
is and how this is used in Firebase? I don't have any document named "term".
You have a few unrelated questions here.
The line
collectionRef.doc(`${term}`);
is overly complex. You can safely rewrite it as
collectionRef.doc(term);
since the backtick-quote doesn't do anything in this case. Backtick quotes in recent versions of JavaScript do expression expansion inside the ${} part. So evaluating the expression
`${term}`
just evaluates to whatever term is. So the result of that function is to create a reference to a document in Firestore named by term, which is just the lowercase version of the name parameter from your Dialogflow Intent.
Which leads us to the error you're getting. That typically happens if you fail to send a reply. Failing to send a reply can happen for a number of reasons, the two most common being
You didn't call conv.ask() or conv.close()
You are doing an asynchronous operation (such as calling a database) without returning a Promise
However, in your case, it looks like you're doing both.
It seems possible that your function is generating an error before it reaches the database call. The most likely possibility in this case might be the line
const term = name.toLowerCase();
which could be causing an error if name isn't defined, meaning it wasn't a parameter in the Dialogflow Intent.
You may wish to consult the following two articles which also look into debugging Actions on Google Intent fulfillments:
Debugging Common Actions on Google Errors
Hey Google, I Need Help With My Action

Dialogflow - Google Assistant: setFollowupEvent after showing a message

Here it is my use case: somewhere in my fulfillment, while handling the Intent A, I want to call Intent B using setFollowupEvent. Something like:
function intentA_Handler(){
....
agent.add('This is the response when calling Intent A');
agent.setFollowupEvent('call_intent_B_event');
}
function intentB_Handler(){
agent.add('I am in intent B now.');
....
}
What I'm expecting:
The assistant Shows & Speaks out the string This is the response when calling Intent A
Then calling agent.setFollowupEvent('call_intent_B_event'); and Showing & Speaking out the string I am in intent B now.
What happens:
The assistant immediately shows & speaks out to me the string I am in intent B now and omits the first string This is the response when calling Intent A
Already tried:
function intentA_Handler(){
....
new Promise((resolve, reject) => {
agent.add('This is the response when calling Intent A');
resolve();
});
agent.setFollowupEvent('call_intent_B_event');
}
But still the same result. Any idea how to achieve my goal?
This is the expected behavior. The followup event goes back to Dialogflow and not the Actions on Google platform. The second intent response will go back to the assistant as the only response coming from the agent.
If you use the Actions on Google client library (https://developers.google.com/actions/reference/nodejsv2/overview), the it has a way to pass parameters with the follow up event: https://actions-on-google.github.io/actions-on-google-nodejs/classes/dialogflow.dialogflowconversation.html#followup
You could use the parameter values to track additional information you want to include in the final response.
In general - you don't need to use followup events if you are doing things through fulfillment. Remember - Intents usually represent the user doing or saying something, not what you want the reply to be.
If you need to call some code in both the Intent Handler for A and for B - just call that code as another function. If you want to conclude with the same message in both A and B - then you can just call add() with the messages you want in both A and B - either directly or by calling a common function. Perhaps something like this:
function addMessageB(){
agent.add('This was from adding message B.');
}
function intentA_Handler(){
....
agent.add('This is the response when calling Intent A');
addMessageB();
}
function intentB_Handler(){
addMessageB();
....
}
There are limitations (you can only add() two Simple Messages, for example), but this would be the general approach.

dialogflow fullfilment and firebase response time

I am trying to build a simple chatbot with DialogFlow.
My aim is to give information from user question, like : where can I slackline above water in croatia ? I have two parameters (croatia, waterline) and a list of slackline places.
So I need a data base to retrieve information from parameters. DialogFlow allows fulfillment with Firebase. I build a database with places (name, country, type of slack) and enable webhook call for my intent.
I use Inline Editor and index.js
const parameters = request.body.queryResult.parameters;
var country = parameters.country.toString();
function show(snap) {
console.log('snap');
agent.add(JSON.stringify(snap.val(),null,2));
}
function slkplc(agent) {
var testRef;
firebase.database().ref('slackplace').once('value',show);
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
But I do not get the expected result while trying it on DialogFlow or Google Assistant. The function show is asynchronously called but too late and the response is not available for DialogFlow :
I see three way to deal with this problem :
use blocking call to database : another database ?
treat asynchronous message with DialogFlow ???
response to user that an error occured.
The third that I choose, but it is always on error.
After trying several things to wait data from database response, the only thing I managed is to freeze the response, therefore the timeout of DialogFlow - 5s -and Firebase - 60s - were reached.
A workaround
Another way to do it is to separate database acquisition and request/response from DialogFlow. The data of database is collected outside of the dialogflowFirebaseFulfillment
var data;
var inidata = firebase.database().ref().on('value',function(snap) {
console.log('snap');
data = snap.val();
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
...
function slkplc(agent) {
agent.add(JSON.stringify(data,null,2));
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
}
Now I can do what I want with data, and I am able to find the place where I can practice waterline in croatia. But there is always something weird, the data of the database is duplicated ...
The "right" solution is option 2 that you suggest: since you're doing an asynchronous call, you need to handle this correctly when working with the dialogflow-fulfillment library.
Basically, if your handler makes an asynchronous call, it needs to be asynchronous as well. To indicate to the handleRequest() method that your handler is async, you need to return a Promise object.
Firebase's once() method returns a Promise if you don't pass it a callback function. You can take advantage of this, return that Promise, and also handle what you want it to do as part of a .then() clause. It might look something like this:
function slkplc(agent) {
var testRef;
return firebase.database().ref('slackplace').once('value')
.then( snap => {
var val = snap.val();
return agent.add( JSON.stringify( val, null, 2 ) );
});
}
The important part isn't just that you use a Promise, but also that you return that Promise.

Dialogflow IntentHandler not found for intent: myIntent (Dialogflow V2)

Since I upgraded my Dialogflow to use v2 API, I get the following error:
Dialogflow IntentHandler not found for intent: myIntent
For some reasons, my intent is no longer recognized altough the action name is identical - myIntent.
This is how I'm using my NodeJS (express V4) app to return a response:
dialogflowApp.intent('myIntent', (conv, {version}) => {
conv.close('test');
});
What could have gone wrong?
Make sure that myIntent is spelled the same in Dialogflow and in your NodeJS webhook function, otherwise you'll get this error. This is how I write and access my functions in the webhook:
//Function execution
dialogflowApp.intent('myIntent', myIntentFunction);
//Function definition
function myIntentFunction(conv, {version}){
conv.close('test');
}
V2 Actions SDK uses the Intent name instead of the Action Name. The Intent name can be found in your request, or directly from DialogFlow intent interface
DialogFlow V1 to V2 Migration Documentation
In V2 you have to use the intent name instead of the action name. First you define this at the beginning of the index file:
'use strict';
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});
Then you have to add the code for each intent:
app.intent('intentName1', (conv, {parameterName}) => {
conv.close('Answer text');
});
app.intent('intentName2', (conv, {parameterName}) => {
conv.ask('Answer text');
});
Finally, at the end of the index file, it is necessary to set the DialogflowApp object to handle the HTTPS POST request. Like this:
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
In your example:
dialogflowApp.intent('myIntent', (conv, {version}) => {
conv.close('test');
});
You should check that you have defined 'dialogflowApp' at the begining of index file:
const dialogflowApp = dialogflow({debug: true});
Then you have two options:
Replace 'myIntent' with the name of the intent
Change your intent name to be 'myIntent'
IMPORTANT: You have to make sure that the intent name in dialogflow and that in the code are exactly the same, since it is case sentive.
'version' should be the name of a parameter received from that intent.
Also check that you have this at the end of the index file:
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(dialogflowApp);
Hope this can help! :)
Try to put your intent handler outside your express '/' post route:
dialogflowApp.intent('myIntent', (conv, {version}) => {
conv.close('test');
});
express().post('/', function(req, res)) {
})
According to this comment on Github, Actions-on-Google V2 does not match on the action, but instead matches on the intent.name. I have yet to find a smooth way of finding the name property of a Dialogfow intent, but I have been able to find it by looking at the Diagnostic info after testing it in the right column.
Note: What you named the intent at the top of the screen is the Display Name, not the name.
Update: Part of the intent.name can be found in the URL after opening the intent. The intent.name currently is given in the following format:
projects/<username>/agent/intents/<intent-uuid>
The intent-uuid can be found in the last part of the path:
https://console.dialogflow.com/api-client/#/agent/<agent-uuid>/editIntent/<intent-uuid>/
While this is also not ideal either, it can make things a little easier.
Try also to remove space from the intent name defined in DialogFlow and in your javascript function call. This solve the problem in my case (running in a Azure function)
before :
app.intent('Open Car Gate', conv => {
conv.close('OK, je vais ouvrir le portail en grand !');
});
after :
app.intent('OpenCarGate', conv => {
conv.close('OK, je vais ouvrir le portail en grand !');
});

Resources