How can I set dynamic args in Text response?
not from user phrases
I have a case:
bot says: "Do you find a work?"
user says: "Yes"
Text response:
bot says: "I can offer you vacancy {random vacancy}"
You can't do it with a default text response, in fact, you need to enable webhook call for that intent in the fulfillment section. I'll show you how my intent looks like.
Here you have the webhook code:
'use strict';
const http = require('http');
const request2 = require('request');
exports.dialogflowFirebaseFulfillment = (req, res) => {
console.log('Dialogflow Request body: ' + JSON.stringify(req.body));
let action = req.body.queryResult['intent']['displayName'];
switch(action){
case "work":
// Get the city from a database
let city="Randomcity";
// Get the job from a database
let job="randomjob";
res.send(JSON.stringify({ 'fulfillmentText': "I can offer you this fantastic job in "+city+" city, doing "+job}));
break;
//Add more cases if you have more intents
}
}
And this is the result:
There are two cases :
1. If you want to return random arguments, then you can simply set all possible arguments in the responses and DialogFlow will randomly select a response a send it to user.
If the arguments are based on some criteria, then you need to enable webhook and you need to return the response from the webhook. This is the recommended option. And this is how fulfillment works.
Hope it helps.
Related
I am building several bots with DialogFlow and Hangouts Chat integration.
How can I retrieve the user email of the user spraking to the bot ?
When I see the request sent from Hangouts chat to Dialogflow I cannot see any information about the user, it's like an anonymous request.
Has anybody find a workaround for that ?
It can be retrieved using events:
For each event like a message, a new user added or removed to the chat you can call event.user and it has the following fields:
event.user.name: The user name
event.user.email: The user email
event.user.displayName: The 'display' user name
event.user.avatarUrl: The user avatar's img url
For example, a working code using onMessage(event) function, for each interaction with the bot, the bot will answer with the user name and the message:
function onMessage(event) {
if (event.type == "MESSAGE" || event.space.type == "DM" ) {
var message = event.user.displayName + " said " + event.message.argumentText;
return { "text": message };
}
}
Response:
Benoit said How to get usernames on Hangouts bots?
A diagram of the JSON event format on Hangouts:
More documentation on hangouts events format and examples of use
Ok. Figured it out...
Indeed, you'll need to handle the intent using a Fullfilment.
In the default fullfilment function, you'll see this bit of code:
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));
//... REST OF THE CODE WHERE YOU 'LL HANDLE THE INTENTS
});
The event info that you normally get out of an apps script chat bot is in the
request.body
For example email:
const email = request.body.originalDetectIntentRequest.payload.data.event.user.email;
In that user object, you'll also find:
email
avatarUrl
displayName
Ok, solution here is to enable fulfilment and process this as a cloud function. The input json of the cloud function contains all the event json.
I wanted to reply back "Hello #name" without using cloud function
I created a simple webhook to fulfill a Google Action intent using Actions on Google Client Library. This webhook is hosted on an AWS Lambda function with this code:
'use strict';
// Import the Dialogflow module from the Actions on Google client library.
const {dialogflow} = require('actions-on-google');
// Instantiate the Dialogflow client.
const app = dialogflow({debug: true});
// Handle the Dialogflow intent named 'favorite color'.
// The intent collects a parameter named 'color'.
app.intent('favorite color', (conv, {color}) => {
const luckyNumber = color.length;
// Respond with the user's lucky number and end the conversation.
conv.close('Your lucky number is ' + luckyNumber);
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.fulfillment = app;
My issue is the that the response comes back to the assistant in this form:
{
"statusCode": 200,
"body": "{\"payload\":{\"google\":{\"expectUserResponse\":false,\"richResponse\":{\"items\":[{\"simpleResponse\":{\"textToSpeech\":\"Your lucky number is 3\"}}]}}},\"fulfillmentText\":\"Your lucky number is 3\"}",
"headers": {
"content-type": "application/json;charset=utf-8"
}
}
As you can see, the body comes with the escape letter inserted which causes the fulfillment to fail.
I tried the following:
JSON.stringify(conv.close('Your lucky number is ' + luckyNumber));
JSON.parse(conv.close('Your lucky number is ' + luckyNumber));
JSON.parse(conv.close('Your lucky number is ' + luckyNumber).body);
Nothing changed as I think I need to reach the payload part.
Turns out there's an checkbox option in AWS API Gateway called: Use Lambda Proxy Integration.
When selected it returns the JSON as is from my code without extra formatting.
After trying and trying countless times, I ask for your help to call a Dialogflow event (GoogleHome) with a specific GoogleHome device.
Through nodeJS I managed to successfully call a Dialogflow event and I get the fullfillment response. All perfect, only I have to let my GoogleHome device speak with fullfillment, I do not need a text-only answer.
My goal is to let my GoogleHome device speak first, without the word "Ok, Google" and wait for a response from the user.
I did not find anything on the web, my attempts stop to invoke the Dialogflow event and have a console response.
This is the code i have tried for fullfillment
test: async function () {
console.log("[funcGHTalk|test] CALLED");
const projectId = "[[projectid]]";
const LANGUAGE_CODE = 'it-IT';
let eventName = "[[eventname]]";
const sessionId = uuid.v4();
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
event: {
name: eventName,
languageCode: LANGUAGE_CODE
},
},
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
console.log(result);
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
}
The code you have written is using the Dialogflow Detect Intent API. This is meant to run on consoles and servers to send a message to Dialogflow, which will parse it, determine which Intent it matches, call fulfillment with that information, and return all the results.
You don't need to run this on a Google Home, since the Google Assistant does all this for you.
What I think you're looking for is to develop fulfillment with Actions on Google and the Dialogflow Fulfillment API. This handles things on the other end - after Dialogflow determines what Intent matches what the user has said, and if that Intent has fulfillment enabled, it will send the information to your webhook which is running on a cloud server somewhere. You would then process it, send a reply (either using the actions-on-google library or the dialogflow-fulfillment library is easiest), and it would send it back to the Assistant.
You indicated that you want the Action to "let my GoogleHome device speak first, without the word "Ok, Google" and wait for a response from the user". This is much more complicated, and not really possible to do with the Google Home device right now. Most Actions have the user initiating the conversation with "Ok Google, talk to my test app" or whatever the name of the Action is.
You don't indicate how you expect to trigger the Home to begin talking, but you may wish to look into notifications to see if those fit your model, however notifications don't work with the Home right now, just the Assistant on mobile devices.
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.
I want to access the transcription text that has been generated by transcribe in my Twilio account under transcriptions as I want to compare user recorded response as text
twiml.say('Hi there! Please speak your response after the beep,-Get ready!')
.record({
transcribe:true,
timeout:5,
maxLength:30,
transcribeCallback:'/recording',
action:'/recording'
});
app.post('/recording', (request,response) => {
if(transcriptionText=='yes'){
twiml.say('thank you for positive response');
}
response.type('text/xml');
response.send(twiml.toString());
});
Twilio developer evangelist here.
When using transcription with <Record>, once the recording is complete the call will continue on to make a request to the action attribute synchronously. Whatever you return from the action attribute URL will control the call.
The actual transcription, however, takes a bit more time and when you get a webhook to the transcribeCallback URL it is done asynchronously, outside the context of the call. So, returning TwiML will not affect the call at all.
You will get the transcription text by inspecting the body of the request. There are plenty of parameters sent to the transcribeCallback, but the one you are looking for is the TranscriptionText. In your Node.js app, which looks like Express to me, you can get hold of it by calling request.body.TranscriptionText.
If you do want to affect the call when you receive the transcribe callback you will need to use the REST API to modify the call and redirect it to some new TwiML.
Let me know if that helps at all.
[edit]
From the comments I can see you are trying to drive a part of the call from a spoken response. The transcribeCallback URL isn't called immediately as the transcription needs to be done, so you need an action URL that you can send your caller to while you wait.
So, adjust your recording route to have different endpoints for action and transcribeCallback:
app.post("/voice", (request, response) => {
var twiml = new twilio.TwimlResponse();
twiml.say('Hi there! Please speak your response after the beep,-Get ready!')
.record({
transcribe:true,
timeout:5,
maxLength:30,
transcribeCallback:'/transcribe',
action:'/recording'
});
response.type('text/xml');
response.send(twiml.toString());
})
Then your recording endpoint will need to keep the user waiting while Twilio transcribes the text.
app.post('/recording', (request,response) => {
var twiml = new twilio.TwimlResponse();
// A message for the user
twiml.say('Please wait while we transcribe your answer.');
twiml.pause();
// Then redirect around again while we wait
twiml.redirect('/recording');
response.type('text/xml');
response.send(twiml.toString());
});
Finally, when you get the transcribe callback you can figure out the course from the transcribed text somehow and then redirect the live call into a new endpoint that carries on the call with the new information.
app.post('/transcribe', (request, response) => {
var text = request.body.TranscriptionText;
var callSid = require.body.CallSid;
// Do something with the text
var courseId = getCourseFromText(text);
var accountSid = '{{ account_sid }}'; // Your Account SID from www.twilio.com/console
var authToken = '{{ auth_token }}'; // Your Auth Token from www.twilio.com/console
var client = new twilio.RestClient(accountSid, authToken);
// Redirect the call
client.calls(callSid).update({
url: '/course?courseId=' + courseId,
method: 'POST'
}, (err, res) => {
response.sendStatus(200);
})
});