How could I detect an Intent with a context ? (node.js SDK for Dialogflow API V2) - node.js

How could I detect an Intent with a context ?
I defined an Intent "Book" with an input Context "Activity", then trained my bot with "Book this activity in my schedule".
I don't want to use dialog flow to manage context flow because it may evolve from other inputs in my app. So I want pass it as a parameter for each intent detection.
I use node.js SDK for Dialogflow API V2.
I precise it works fine with REST API v1 ... I think i'm stuck with API v2 Contexts :)
I'm looking to do exactly the same as the following code with API v2
//code below is working fine
import apiai from 'apiai';// Dialogflow SDK v1
import uuidv4 from 'uuid/v4';
const sessionId = uuidv4();
const api = apiai('myToken');
const lang = 'en-US';
const request = api.textRequest('Book this activity in my schedule', {
sessionId,
lang,
contexts : [{ name : 'activity' }],
});
request.on('response', function(response) {
console.log(response);
});
request.on('error', function(error) {
console.log(error);
});
request.end();
I didn't found documentation or exemple to do that except API documentation so I probably did something wrong
Whenever I pass a context as a queryParams or create a Context it don't work.
It also seems that context creation from API don't work at all.
import dialogflow from 'dialogflow';//Dialogflow SDK v2
import uuidv4 from 'uuid/v4';
const projectId = 'myID';
const sessionId = uuidv4();
const languageCode = 'en-US';
const sessionClient = new dialogflow.SessionsClient();
const contextClient = new dialogflow.ContextsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
...
First try returned my Default Fallback Intent :
const request = {
session : sessionPath,
queryParams : {
context : {
parent : sessionPath,
name : contextClient.contextPath(
projectId,
sessionId,
'activity'
),
},
},
queryInput : {
text : {
text : 'Book this activity in my schedule',
languageCode : languageCode,
},
},
};
sessionClient.detectIntent(request)
.then(responses => {
const result = responses[0].queryResult;
if (result.intent) {
console.log(result.intent.displayName);
} else {
console.log(' No intent matched.');
}
});
Second try returned my Default Fallback Intent :
contextClient
.createContext({
parent : sessionPath,
context : {
name : contextClient.contextPath(
projectId,
sessionId,
'activity'
),
},
})
.then(() => contextClient.listContexts({ parent : sessionPath }))
.then(contexts => {
console.log(contexts);//returned an empty list
return sessionClient.detectIntent(request);
}).then(responses => {
const result = responses[0].queryResult;
if (result.intent) {
console.log(result.intent.displayName);
} else {
console.log(' No intent matched.');
}
});
Anybody see what's wrong with it ?
Help needed :)

Had same issue.
Found that I need to send "lifespanCount" greated than 0.
{
"session": "projects\/verifier-26084\/agent\/sessions\/1gg5b76c37f7111b",
"queryParams":
{
"contexts": [
{
"name": "projects\/verifier-26084\/agent\/sessions\/1gg5b76c37f7111b\/contexts\/ask-children",
"lifespanCount": 1
}]
},
"queryInput":
{
"text":
{
"text": "\u0442\u0440\u0438",
"languageCode": "ru-RU"
}
}
}

Related

Stripe API - stipe.setupIntents doesn't exist

I am trying to delete setup intent via
stripe.setupIntents.delete
but that method doesn't exist. Any idea what am I doing wrong (I am looking at the official documents, stripe.setupIntents is what I need here).
I have the following code:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('{{ env('STRIPE_KEY') }}');
const elements = stripe.elements();
const cardElement = elements.create('card', {
hidePostalCode: true,
});
cardElement.mount('#card-element');
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
e.preventDefault()
if (cardHolderName.value.length < 3) {
$(cardHolderName).addClass('is-invalid')
.parent()
.append('<span class="text-danger">Please Insert Card Holder Name.</span>');
return;
} else {
$(cardHolderName).removeClass('is-invalid');
$(cardHolderName).next('.text-danger').remove();
}
let id = $(cardButton).data('id')
let url = $(cardButton).data('url')
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: cardHolderName.value
}
}
}
)
if (error) {
let errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
let billingForm = $('#billing-form')
$.ajax({
url: url,
method: 'POST',
cache: false,
data: {
cardholder: billingForm.find('#cardholder').val(),
locum_organization: billingForm.find('#locum_organization').val(),
billing_addresses: billingForm.find('#billing_addresses').val(),
intent: setupIntent,
id: id
},
success: function(response) {
},
fail: function(xhr, textStatus, errorThrown){
console.log(errorThrown);
},
error(response){
stripe.setupIntents.cancel(
setupIntent.id,
function(err, setupIntent) {
// asynchronously called
})
}
});
}
});
</script>
You can only cancel a setup intent; not delete it. Also, setup intents can only be cancelled server-side. Right now it looks like you're trying to cancel it client-side with Stripe.js (which doesn't have a cancel setup intent method). It also isn't necessary to cancel a setup intent after it's gone through. In fact, you can't cancel a setup intent at all unless it's in one of these four processing states:
requires_payment_method, requires_capture, requires_confirmation, requires_action.
https://stripe.com/docs/api/setup_intents/cancel?lang=node
Your integration seems fine other than that. I would recommend leaving out the cancellation step entirely, and you should be good to go!

Update Dialogflow "Transfer Call" field from backend ( Node.js )

I try to update phone number in "Transfer Call" field under "Responses" tab ("TELEPHONY" -> "ADD RESPONSES" button ) for given intent using Node.js but I cannot.
New update removes old "Transfer Call" field with the old phone number (which I created by hand in console for testing purposes)
Please help
Here is example code:
const dialogflow = require('dialogflow')
const intentsClient = new dialogflow.IntentsClient({ keyFilename: 'key.json' })
const fulfillmentMessages = [ { platform: 'TELEPHONY',
telephonySynthesizeSpeech: { text: 'Hello World' } },
{ platform: 'TELEPHONY',
telephonyTransferCall: { phoneNumber: '+12132954242' } },
{ text: { text: [ '' ] } } ]
const intent = {
name: 'projects/example/agent/intents/2ef3e0b6-6cd7-4d5b-a8ca-ce11111125e019',
displayName: 'Test',
fulfillmentMessages: fulfillmentMessages
}
const updateIntentRequest = { intent: intent }
intentsClient.updateIntent(updateIntentRequest).then((data) =>{ console.log(data)}, (e) => {
console.log(e) })
Detailed response can be found here however heres the correct code sample (tested and working)
const dialogflow = require('dialogflow');
const intentsClient = new dialogflow.v2beta1.IntentsClient({ keyFilename: 'key.json' }) //Note that dialogflow will be using v2beta1 api
const message_to_set = [
{
platform: 10,
telephonySynthesizeSpeech: {
text : 'Hello World'
},
telephonyTransferCall: {
phoneNumber: '+12132954242'
}
}
]
const intent = {
name: 'projects/example/agent/intents/2ef3e0b6-6cd7-4d5b-a8ca-ce11111125e019',
displayName: 'Forward',
messages: message_to_set //Note parameter was switched from fulfillmentMessages to messages
}
const updateIntentRequest = { intent: intent }
intentsClient.updateIntent(updateIntentRequest).then((data) =>{ console.log(data)}, (e) => { console.log(e) })

How to intergrate lex with lambda in amazon aws?

I have a simple bot with the following logic.
Bot: select one of the following item your interested
(response card)
-ecommerce
-travel etc
Human : clicks eg travel
Bot: response card
-marketing
-digital
Here is what I have in my lambda function
'use strict';
exports.handler = (event, context, callback) => {
const sessionAttributes = event.sessionAttributes;
const slots = event.currentIntent.slots;
const videommerceType = slots.videommerceType;
// predefined list of available pizza
const validData = ['ecommerce', 'startup', 'lead generation', 'crm', 'travel'];
// negative check: if valid slot value is not obtained, inform lex that user is expected
// respond with a slot value
if (videommerceType && !(videommerceType === "") && validData.indexOf(videommerceType.toLowerCase()) === -1) {
let response = {
sessionAttributes: event.sessionAttributes,
dialogAction: {
type : "'ElicitSlot",
message: {
contentType: "PlainText or SSML",
content: "Message to convey to the user. For example, Thanks, your pizza has been ordered."
},
responseCard: {
version: "1",
contentType: "application/vnd.amazonaws.card.generic",
genericAttachments: [{
title: "card-title",
subTitle: "card-sub-title",
imageUrl: "URL of the image to be shown",
attachmentLinkUrl: "URL of the attachment to be associated with the card",
buttons: [{
text: "button-text",
value: "Value sent to server on button click"
}]
}]
}
}
}
callback(null, response);
}
let response = {
sessionAttributes: sessionAttributes,
dialogAction: {
type: "Delegate",
slots: event.currentIntent.slots
}
}
callback(null, response);
};
Unfortunately this not working, on lex bot I get the following error
Intent videommerceIntent is ReadyForFulfillment: name:jim videommerceType:ecommerce
What is wrong with my code? any help or similar working demo would be appreciated , thanks

set input or output context dialogflow nodejs v2

I am using dialogflow NPM module and I want to send input/output context but I am not sure how to do.
I know I can do in google-assistant NPM with
I can set contexts with parameter using below method,
const parameters = { // Custom parameters to pass with context
welcome: true,
};
conv.contexts.set('welcome-context', 5, parameters);
First, some clarification on packages
The google-assistant package is one person's implementation/wrapper for the Assistant SDK, allowing you to embed the Assistant in any program or device. This is like building your own Google Home. (This package isn't from Google, it appears, but does wrap the official protobuf API, so you don't have to compile it yourself.)
The actions-on-google package is for making a webhook that works either with the Action SDK or Dialogflow. This is for making a program that works with the Assistant and sends back responses. (ie - it is for writing the code that responds to "Hey Google, talk to my app")
The dialogflow package lets you work with Dialogflow agents, including configuring them and sending queries to them to see which Intents match the queries. This is a very rough equivalent of the "google-assistant" package, but does a lot more.
The dialogflow-fulfillment package is for making a webhook that provides logic and responses for any of the platforms that Dialogflow supports. It is the equivalent of the actions-on-google package.
The code fragment you provided looks like it is from the "actions-on-google" package, and is setting the output context as part of the reply.
The equivalent of your code using the "dialogflow-fulfillment" package would be
const parameters = { // Custom parameters to pass with context
welcome: true,
};
agent.context.set('welcome-context', 5, parameters);
Note that it is "context", without the "s" in this package.
Similarly, to get an input context, you would use
agent.context.get('name-of-context;);
(You may see some examples that use something like agent.setContext(). These methods have been deprecated in favor of the above.)
In order to send a context using the Dialogflow NPM module, you have to first create a context, using dialogflow.ContextsClient and then send it on the query.
To convert the parameters to the format required by Dialogflow you will need to use this module: pb-util
const dialogflow = require('dialogflow');
const { struct } = require('pb-util');
const projectId = 'projectId';
const contextsClient = new dialogflow.ContextsClient();
const sessionClient = new dialogflow.SessionsClient();
async function createContext(sessionId, contextId, parameters, lifespanCount = 5) {
const sessionPath = contextsClient.sessionPath(projectId, sessionId);
const contextPath = contextsClient.contextPath(
projectId,
sessionId,
contextId
);
const request = {
parent: sessionPath,
context: {
name: contextPath,
parameters: struct.encode(parameters)
lifespanCount
}
};
const [context] = await contextsClient.createContext(request);
return context;
}
function sendQuery(sessionId, query, context) {
const session = sessionClient.sessionPath(projectId, sessionId);
const request = {
session,
queryInput: {
text: {
text: query
}
},
queryParams: {
contexts: [context] // You can pass multiple contexts if you wish
}
};
return sessionClient.detectIntent(request);
}
(async() => {
const parameters = { // Custom parameters to pass with context
welcome: true
};
const sessionId = 'my-session';
const context = await createContext(sessionId, 'welcome-context', parameters);
const response = await sendQuery(sessionId, 'Hi', context);
console.log(response);
})();
Have in mind that you send an input context. The output context is generated in the intent.
If you have trouble authenticating the clients SessionClient & ContextClient you can check this other question: Dialogflow easy way for authorization
Building on Marco's excellent answer-- here's a simpler version:
package.json
{
"name": "credtest",
"version": "1.0.0",
"dependencies": {
"dialogflow": "^1.2.0",
"pb-util": "^0.1.3"
}
}
index.js
const {ContextsClient} = require('dialogflow')
const { struct } = require('pb-util');
// REPLACE THESE:
/*
1. Service Account Credential file (KEEP SAFE)
// where/how to get this key: https://cloud.google.com/iam/docs/creating-managing-service-account-keys
// For different auth options, see Marco Casagrande's explainer: https://stackoverflow.com/questions/50545943/dialogflow-easy-way-for-authorization/50546430#50546430)
// Note: JSON hard-coded below for copy/paste, just import/require in when using for real
*/
const credential = {
"type": "__REPLACE__ME___",
"project_id": "__REPLACE__ME___",
"private_key_id": "__REPLACE__ME___",
"private_key": "__REPLACE__ME___",
"client_email": "__REPLACE__ME___",
"client_id": "__REPLACE__ME___",
"auth_uri": "__REPLACE__ME___",
"token_uri": "__REPLACE__ME___",
"auth_provider_x509_cert_url": "__REPLACE__ME___",
"client_x509_cert_url": "__REPLACE__ME___"
}
/*
2. Project ID, Google cloud project id
Tip: notice the service-account naturally already has your product id
*/
const project_id = '__REPLACE__ME___' // This is your google cloud project
const client = new ContextsClient({
credentials: credential
});
createContext('123456789', {name: 'bongo_context', lifespanCount: 3, parameters: {a:1, b:2, c:3 }} )
function createContext(sessionID, {name, lifespanCount, parameters = {} } = {} ) {
const formattedParent = client.sessionPath(project_id, sessionID);
const formattedContextName = `projects/${project_id}/agent/sessions/${sessionID}/contexts/${name}`
const context = {
"name": formattedContextName,
"lifespanCount": 2,
"parameters": struct.encode(parameters)
}
const request = {
parent: formattedParent,
context: context,
}
client.createContext(request)
.then(responses => {
const response = responses[0];
console.log('context now active:', response)
})
.catch(err => {
console.error(err);
});
}

Firebase CloudFunctions: Querying Nodes

I am exploring Cloud Functions with FCM to do a push notification to my iOS app device. I have the following structure and I am listening for people who registered to an event. I want to extract the eventHost so that I can go to my users node to find the userUID and eventually his deviceID, and send a push notification to him.
{
"events" : {
"XXX" : {
"eventHost" : "YYY", //<-- How do I get this node?
"eventID" : "XXX",
"registered" : { //<-- Listening for this node
"ASKDJHAIUCHA" : {
"name" : "Emma Watson",
"userUID" : "ASKDJHAIUCHA"
}
},
},
},
"users" : {
"YYY" : {
"deviceID" : "1234456",
"name" : "Andrew Garfield",
"userUID" : "YYY"
},
}
}
My code for the Cloud Functions as such so far:
exports.sendNotification = functions.database.ref('/events/{eventId}/registered')
.onWrite(event => {
const register = event.data.val();
const eventHost = functions.database.ref('/events/' + event.params.eventId + '/eventHost')
console.log('sendNotifications', eventHost);
const payload = {
notification: {
title: "Event Registration",
body: "Someone registered to your event!"
}
};
const options = {
priority: "high"
};
return admin.messaging().sendToDevice("theDeviceID", payload, options)
});
And my Swift portion when adding value into the database is as such:
#IBAction func registerButtonDidTap(_ sender: Any) {
let personDict = [FIRConstants.UserInfo.Name: user.userName,
FIRConstants.UserInfo.UserUID: user.userUID]
let registerPerson = [user.userUID!: personDict as AnyObject] as [String: AnyObject]
let values = ["registered": registerPerson]
FIRHelperClient.sharedInstance.checkIfEventHasRegistrants(ref, event.eventID!) { (has, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let has = has {
if has {
self.ref.child("events").child(self.event.eventID!).child("registered").updateChildValues(registerPerson)
} else {
self.ref.child("events").child(self.event.eventID!).updateChildValues(values)
}
}
}
}
}
My cloud functions is definitely not complete as currently I am hardcoding theDeviceID. As I am pretty inexperience with Node.js and am trying to write both the iOS code in Swift and the server side code, so pls pardon me if this question is elementary. Some advice here would be helpful, thanks.
You'll need to read the host, which your code currently doesn't do.
exports.sendNotification = functions.database.ref('/events/{eventId}/registered')
.onWrite(event => {
const register = event.data.val();
const eventHostRef = functions.database.ref('/events/' + event.params.eventId + '/eventHost')
return eventHostRef.once('value', (eventHostSnapshot) => {
const eventHost = eventHostSnapshot.val();
console.log('sendNotifications', eventHost);
const payload = {
notification: {
title: "Event Registration",
body: "Someone registered to your event!"
}
};
const options = {
priority: "high"
};
return admin.messaging().sendToDevice("theDeviceID", payload, options)
});
});
I highly recommend that you spend some time learning how to interact with the Firebase Database through JavaScript before continuing though. This doesn't have to be through Cloud Functions. You could also use the Firebase Database Admin SDK from Node.js on your client or take the Firebase codelab for web. Whichever one you take, it will ensure you're much better prepared for interacting with the database through Cloud Functions.
As a final warning: you're nesting multiple data types under a single list. This is not recommended, since it leads to all kinds of problems down the line. Instead, I would pull the registered users into their own top-level node, so that you get:
{
"events" : {
"XXX" : {
"eventHost" : "YYY",
"eventID" : "XXX"
}
},
"registered" : {
"XXX" : {
"ASKDJHAIUCHA" : {
"name" : "Emma Watson",
"userUID" : "ASKDJHAIUCHA"
}
}
},
"users" : {
"YYY" : {
"deviceID" : "1234456",
"name" : "Andrew Garfield",
"userUID" : "YYY"
}
}
}

Resources