Dialogflow CX webhook for fulfilment to reply user using nodejs - node.js

I tried using dialogflow-fulfillment library but I guess it is for Dialogflow ES so now I am using
#google-cloud/dialogflow-cx library but I don't know how to use this library for webhook connection to reply to users using fulfilments, there is very little material available for Dialogflow CX.
// use credentials or keyFilename i'm using keyFile
credentials: {
private_key: "-----BEGIN PRIVATE KEY-----==\n-----END PRIVATE KEY-----\n",
client_email:"pro1a3711.iam.gserviceaccount.com",
},
keyFilename: './pr.json'
}
const {SessionsClient} = require('#google-cloud/dialogflow-cx');
const projectId = 'pro1-293711';
const location = 'global';
const agentId = 'da2271f5-0221-4dce-98d3-efa----9dd';
const languageCode = 'en';
const query = ['hello'];
// Imports the Google Cloud Some API library
//console.log(WebhooksClient)
const client = new SessionsClient(config);
//console.log("client",client)
async function detectIntentText() {
const sessionId = Math.random().toString(36).substring(7);
const sessionPath = client.projectLocationAgentSessionPath(
projectId,
location,
agentId,
sessionId
);
console.info(sessionPath);
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
},
languageCode,
},
};
const [response] = await client.detectIntent(request);
console.log(`User Query: ${query}`);
for (const message of response.queryResult.responseMessages) {
if (message.text) {
console.log(`Agent Response: ${message.text.text}`);
}
}
if (response.queryResult.match.intent) {
console.log(
`Matched Intent: ${response.queryResult.match.intent.displayName}`
);
}
console.log(
`Current Page: ${response.queryResult.currentPage.displayName}`
);
}
detectIntentText()```

Note that the dialogflow-fulfillment library only supports Dialogflow ES and the #google-cloud/dialogflow-cx library is only used for node.js applications to access Dialogflow CX API.
As there are no fulfillment libraries available yet for Dialogflow CX, you can refer to the Dialogflow CX webhook request and webhook response for building webhook services for your Dialogflow CX agent.
You can also refer to the sample webhook service code for Dialogflow CX using Node.js and express below:
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.post("/webhook", (request, response) => {
let tag = request.body.fulfillmentInfo.tag;
let jsonResponse = {};
if (tag == "welcome tag") {
//fulfillment response to be sent to the agent if the request tag is equal to "welcome tag"
jsonResponse = {
fulfillment_response: {
messages: [
{
text: {
//fulfillment text response to be sent to the agent
text: ["Hi! This is a webhook response"]
}
}
]
}
};
} else {
jsonResponse = {
//fulfillment text response to be sent to the agent if there are no defined responses for the specified tag
fulfillment_response: {
messages: [
{
text: {
////fulfillment text response to be sent to the agent
text: [
`There are no fulfillment responses defined for "${tag}"" tag`
]
}
}
]
}
};
}
response.json(jsonResponse);
});
const listener = app.listen(process.env.PORT, () => {
console.log("Your app is listening on port " + listener.address().port);
});

Related

For some reason when using the dialogflow detectIntent googleapi, I keep getting default fallback intent even though other api's like listIntents work

Has anyone experienced this issue?
I am sure the dialogflow connection works because listing the intents works which is (intentsClient.listIntents(request)) and it gives me back all the intents from my dialogflow es agent. So it looks like the credentials should be fine.
Any help would be highly appreciated,
Thanks
const dialogflow = require('#google-cloud/dialogflow').v2;
const uuid = require('uuid');
const sessionId = uuid.v4();
const sessionClient = new dialogflow.SessionsClient({ keyFilename: '..........' });
const sessionPath = sessionClient.projectAgentSessionPath(
projectId,
sessionId
);
const query = 'What are the pets rules';
async function detectIntentForQuestion() {
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: 'en-US',
},
},
queryParams: {
sentimentAnalysisRequestConfig: {
analyzeQueryTextSentiment: true,
},
},
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
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.');
}
if (result.sentimentAnalysisResult) {
console.log('Detected sentiment');
console.log(
` Score: ${result.sentimentAnalysisResult.queryTextSentiment.score}`
);
console.log(
` Magnitude: ${result.sentimentAnalysisResult.queryTextSentiment.magnitude}`
);
} else {
console.log('No sentiment Analysis Found');
}
};
you are setting languageCode: 'en-US' for language. Does your agent support this language? If it is set using a different language (ie. Spanish), then it is expected to give you back only the fallback intent response.
other thing that you can do is checking at https://dialogflow.cloud.google.com/#/agent/<your agent name>/history and in your last interactions, you click on the 3 dots in the agent response and go to Raw interaction log. There you can see the information the agent got from your code, how it was interpreted by the agent and how it answered you back.

TikTok oAuth API auth code is always expired

I am trying to login using TikTok oAuth API
I have a Firebase Cloud Function (Nodejs) set up to complete the oauth flow, based
on the TikTok API Documentation, but when i reach the point (https://open-api.tiktok.com/oauth/access_token) to get the actual user access token it fails and i get an error.
The response i get is status 200 and
{
"data": {
"captcha": "",
"desc_url": "",
"description": "Authorization code expired",
"error_code": 10007
},
"message": "error"
}
The TikTok API always gives me the same authorization code. So i am guessing something is wrong. Any suggestion is welcomed.
Here is the code sample from the backend
The /linkTikTok/oauth and point used to redirect the user to tikTok oauth and the /linkTikTok/validate is used to request the access token. The code runs fine but when it reaches const URL = https://open-api.tiktok.com/oauth/access_token; and actually requests the user access token i get response above.
import * as express from 'express';
import * as cors from 'cors';
import axios from 'axios';
import * as cookieParser from 'cookie-parser';
import { config } from 'firebase-functions';
import { firestore } from 'firebase-admin';
import { colRefs } from '../../constants/db-refs';
const app = express();
app.use(cors());
app.use(cookieParser());
app.listen();
const { client_key, client_secret } = config().tikTokCredentials;
const redirectURI = `https://xxxxx.firebaseapp.com/linkTikTok/validate`;
app.get('/linkTikTok/oauth', async (req, res) => {
// The user's id;
const uid = 'a_user_id';
if (!uid) {
return res.status(401).send('This action requires user authentication');
}
// Random state
const csrfState = Math.random().toString(36).substring(7);
const state: any = {
state: csrfState,
timestamp: firestore.Timestamp.now(),
uid,
};
// A state object kepts in firestore
await colRefs.tikTokAuthState.doc(uid).set(state);
res.cookie('__session', { state: csrfState });
let url = 'https://open-api.tiktok.com/platform/oauth/connect/';
url += `?client_key=${client_key}`;
url += '&scope=user.info.basic,video.list';
url += '&response_type=code';
url += `&redirect_uri=${redirectURI}`;
url += '&state=' + csrfState;
return res.redirect(url);
});
app.get('/linkTikTok/validate', async (req, res) => {
// Query state
const state = req.query.state as string;
if (!state) {
return res.status(403).send('No state found');
}
const code = req.query.code as string;
if (!code) {
return res.status(403).send('No code found');
}
const sessionCookie = req.cookies['__session'] ?? {};
const sessionState = sessionCookie.state;
if (state !== sessionState) {
return res.status(403).send('Wrong state');
}
// Retrieve the uid from firestore
const uid = await (async () => {
const states = (await colRefs.tikTokAuthState.where('state', '==', state).get()).docs.map(d => d.data());
if (states.length !== 0 && states.length > 1) {
console.warn('More than one state');
}
return states[0].uid;
})();
console.log({ uid });
const URL = `https://open-api.tiktok.com/oauth/access_token`;
const params = {
client_key,
client_secret,
code,
grant_type: 'authorization_code',
};
try {
const result = await axios.post<any>(URL, '', {
params,
});
const data = result.data.data;
const {
access_token: accessToken,
refresh_token,
refresh_expires_in,
open_id: openId,
expires_in,
} = data;
if (!accessToken) {
throw new Error('No access token found');
}
// Application logic
...
});
would you share the piece of code you've written so that we could find the spot.
I got the same error in my code, however, in my case, I was doing duplicate authentication with the TikTok API, because I forgot the "code" GET parameter in my URL and when I was saving settings in my app again, the GET parameter fired again the authentication sequence and I got always the "Authorization code expired" error - but only the second time I was making requests.
You should check if you don't also have duplicate authentication requests in your app.

Error : Dialogflow server in 'us' received request for resources located in 'europe-west2-dialogflow.googleapis.com.'

I want to detect an intent with dialogflow, but my dialogflow agent's region is europe-west2 for some reasons. So to specify a location, I use the version v2beta1 of Dialogflow API like described in the documentation. But it doesn't worked and I have the following error Dialogflow server in 'us' received request for resources located in 'europe-west2-dialogflow.googleapis.com.
Code :
const sessionId = crypto.randomBytes(16).toString("hex");
// Create a new dialogflow session
const sessionClient = new Dialogflow.SessionsClient(this.dialogFlowConfig)
const sessionPath = sessionClient.projectLocationAgentSessionPath(this.projectId, "europe-west2-dialogflow.googleapis.com", sessionId);
// The text query request.
const dfRequest = {
session: sessionPath,
queryInput: {
event: {
name: "Welcome",
languageCode: DialogFlowService.LANGUAGE_CODE
}
}
}
try {
const responses = await sessionClient.detectIntent(dfRequest);
const result = responses[0].queryResult!;
Logger.debug(` Query: ${result.queryText}`);
Logger.debug(` Response: ${result.fulfillmentText}`);
if (result.intent) {
Logger.debug(` Intent: ${result.intent.displayName}`);
} else {
Logger.debug(` No intent matched.`);
}
return result
I had the similar issue, but the below config worked to access dialogflow agent's in region europe-west2
Set the Location as "europe-west2"
Also need to set the
SessionsClient's 'apiEndpoint' as
"europe-west2-dialogflow.googleapis.com"
const sessionClient = new dialogflow.SessionsClient({ apiEndpoint: "europe-west2-dialogflow.googleapis.com" });
const sessionPath = sessionClient.projectLocationAgentSessionPath(
projectId,
"europe-west2",
sessionId
);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
},
},
};
Try to use 'europe-west2' as location. More docs here.

Dialogflow easy way for authorization

Does exist an easy way to connect Dialogflow agent to node.js code? When I use this code with the correct projectID taken from the Dialogflow agent's settings page, I have the following error:
Error: Unexpected error while acquiring application default credentials: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information.
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
I visited the page, but for what I want I think is quite confused (they quotes other API and a lot of setting), how can I solve this?
I want to take informations from a file and loading all without installing third party APIs.
It is not very well documented, but the easiest way to authenticate is using the JSON file provided on your google cloud platform console.
const sessionClient = new dialogflow.SessionsClient({
keyFilename: '/path/to/google.json'
});
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
This also works for all the other clients. ContextsClients, EntityTypesClient and so on.
I am writing the code, which worked for me. Please follow all the steps provided in Reference link 2 and for coding purpose you can use the snippet provided.
I have also added the sample JSON of Google Cloud Oauth
References:
https://www.npmjs.com/package/dialogflow#samples
https://medium.com/#tzahi/how-to-setup-dialogflow-v2-authentication-programmatically-with-node-js-b37fa4815d89
//Downloaded JSON format
{
"type": "service_account",
"project_id": "mybot",
"private_key_id": "123456asd",
"private_key": "YOURKEY",
"client_email": "yourID#mybot.iam.gserviceaccount.com",
"client_id": "098091234",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/yourID%40mybot.iam.gserviceaccount.com"
}
//------*********************---------------------------
//
const projectId = 'mybot';
//https://dialogflow.com/docs/agents#settings
// generate session id (currently hard coded)
const sessionId = '981dbc33-7c54-5419-2cce-edf90efd2170';
const query = 'hello';
const languageCode = 'en-US';
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
let privateKey = 'YourKey';
// as per goolgle json
let clientEmail = "yourID#mybot.iam.gserviceaccount.com";
let config = {
credentials: {
private_key: privateKey,
client_email: clientEmail
}
}
const sessionClient = new dialogflow.SessionsClient(config);
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
},
},
};
// Send request and log result
sessionClient
.detectIntent(request)
.then(responses => {
console.log('Detected intent');
const result = responses[0].queryResult;
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.`);
}
})
.catch(err => {
console.error('ERROR:', err);
});
I have the same issue few months ago, check this, this is how i solve it.
From your JSON that Google Cloud extract this lines.
const dialogflow = require('dialogflow');
const LANGUAGE_CODE = 'en-US'
const projectId = 'projectid';
const sessionId = 'sessionId';
const query = 'text to check';
let privateKey = "private key JSON";
let clientEmail = "email acount from JSON";
let config = {
credentials: {
private_key: privateKey,
client_email: clientEmail
}
};
sessionClient = new dialogflow.SessionsClient(config);
async function sendTextMessageToDialogFlow(textMessage, sessionId) {
// Define session path
const sessionPath = this.sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: textMessage,
languageCode: LANGUAGE_CODE
}
}
}
try {
let responses = await this.sessionClient.detectIntent(request)
console.log('DialogFlow.sendTextMessageToDialogFlow: Detected intent', responses);
return responses
} catch (err) {
console.error('DialogFlow.sendTextMessageToDialogFlow ERROR:', err);
throw err
}
};
sendTextMessageToDialogFlow(query, sessionId)
Since the original question, the documentation for Dialogflow authentication has been improved. You should find all your answers here:
Authentication and access contro
I follow the above solutions with little changes :
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient({
keyFilename: require("path").join('config/google-credential.json')
});
const sessionPath = sessionClient.sessionPath(process.env.DIALOGFLOW_PROJECTID, sessionId);

Dialogflow Node SDK permission fail (dialogflow.sessions.detectIntent)

I got a problem with Node DialogFlow API integration.
I can not have the necessary permissions to make calls to the API, although I have followed all possible documentation.
Here is my Agent Service Account from Dialogflow admin :
Then the Service Account roles :
The "Client de l'API Dialogflow" role detail, where we can see "dialogflow.sessions.detectIntent" :
And finally, the Node error after downloading the JSON file corresponding to the Service Account :
Am I missing something ?
I tried to generate other JSON files, create other Service Accounts, nothing worked...
The Node script is simple, just pasted from the official tutorial :
const projectId = 'newagent-9e77e '; //https://dialogflow.com/docs/agents#settings
const sessionId = 'quickstart-session-id';
const query = 'bonjour';
const languageCode = 'fr-FR';
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient();
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
},
},
};
// Send request and log result
sessionClient
.detectIntent(request)
.then(responses => {
console.log('Detected intent');
const result = responses[0].queryResult;
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.`);
}
})
.catch(err => {
console.error('ERROR:', err);
});
Thanks !
The problem was on space on the "projectId" constant.
Shame on me.

Resources