How to get data from firebase Realtime database to Dialogflow chatbot - node.js

I was trying to create a chatbot in Dialogflow es. I want, when the user ask chatbot to show Board id it will read data from firebase Realtime database and show that data to the user.
I have tried this way:-
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const firebaseAdmin = require("firebase-admin");
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
firebaseAdmin.initializeApp({
credential:firebaseAdmin.credential.applicationDefault(),
databaseURL:"https://***************.firebaseio.com",
});
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(`Welcome to akvo agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function getFromFirebase(agent){
return firebaseAdmin.database().ref('board_id').once("value").then((snapshot) => {
var boardid = snapshot.val();
agent.add(boardid);
});
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('board id', getFromFirebase);
agent.handleRequest(intentMap);
});
After deploying this code I got an error.
Error:-"Webhook call failed. Error: DEADLINE_EXCEEDED, State: URL_TIMEOUT, Reason: TIMEOUT_WEB."
Please tell me how can I solve this.

Related

How to integrate Dialogflow to my discord bot along with contexts and followup intents in Node js

I made a fully well functioning chatbot using dialogflow now I wanted to integrate it to my discord bot. I followed a youtube video which integrated only the intents but I couldn't figure out how to do with contexts and follow up intents.
const Discord = require('discord.js');
const client = new Discord.Client();
require('dotenv').config();
const token = process.env.DISCORD_TOKEN;
process.env.GOOGLE_APPLICATION_CREDENTIALS = `${process.env.PWD}/${process.env.REPL_SLUG}/config.json`
const dialogflow = require('#google-cloud/dialogflow');
const uuid = require('uuid');
client.once('ready', () => console.log('ready!'));
async function replyMsg(textMsg) {
projectId = process.env.PROJECT_ID;
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = await sessionClient.projectAgentSessionPath(
projectId,
sessionId
);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: textMsg,
// The language used by the client (en-US)
languageCode: 'en-US',
},
},
};
// 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.`);
}
return await result.fulfillmentText;
}
client.on('message', (message) => {
console.log(message.author.bot);
if (!message.author.bot) {
if (message.mentions.has('784734506812833792'))
replyMsg(message.content).then((res) => {
console.log(res);
message.channel.send("<#" + message.author.id + ">" + ' ' + res);
});
}
});
client.login(token);
instead of this
const sessionId = uuid.v4();
Use user wise Session for contexts and follow up intents
like...
const sessionId = Userid;

What is the proper way to send a response form dialogflow from nodejs webhook?

I'm new to dialogflow and I've been trying to send a response from a nodejs webhook to my DF bot when a record has been added to a firestore DB. I've been searching a lot and I couldn't find either examples or docs that could help me understand.
This is the cloud function that performs the above:
const functions = require('firebase-functions');
const mysql = require('mysql');
const promise_mysql = require('promise-mysql');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug';
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.dialogflowFirebaseFulFillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body>>>>>>>> ' + JSON.stringify(request.body));
const parameters = request.body.queryResult.parameters;
db.collection('reservations').add(parameters).then(() => {
agent.add("Room reversed!")
}).catch((e => {
agent.add('Something went wrong!!!!!')
}))
});
// Other stuff
The issue (I think) is this part:
db.collection('reservations').add(parameters).then(() => {
agent.add("Room reversed!")
}).catch((e => {
agent.add('Something went wrong!!!!!')
}))
It adds the record perfectly but I can't get the dialogflow response to be shown as none of the options: agent.add("Room reversed!"), agent.add('Something went wrong!!!!!')
What am I missing? Thanks.
Method1:
exports.dialogflowFirebaseFulFillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body>>>>>>>> ' + JSON.stringify(request.body));
const parameters = request.body.queryResult.parameters;
db.collection('reservations').add(parameters).then(() => {
agent.add("Room reversed!")
// Other stuff
}).catch((e => {
agent.add('Something went wrong!!!!!')
// Other stuff
}))
});
Method2:
exports.dialogflowFirebaseFulFillment = functions.https.onRequest(async (request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body>>>>>>>> ' + JSON.stringify(request.body));
const parameters = request.body.queryResult.parameters;
try {
await db.collection('reservations').add(parameters)
agent.add("Room reversed!")
} catch (error) {
agent.add('Something went wrong!!!!!')
}
// Other stuff
});

Custom Dialogflow Fulfillment response

I am trying to return an object as a response from Dialogflow fulfillment.
I wanted to return some object to be handled later by my front end but I am getting an error:
Error: Unknown response type: "{"fruit1": "apple", "fruit2":
"orange"}";
It's likely that I may be doing it wrong. Here's the code that I have, problem is on function intent1f.
use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
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(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function intent1f(agent) {
var someObject = {"fruit1": "apple",
"fruit2": "orange"};
agent.add(someObject);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('intent1', intent1f);
// intentMap.set('your intent name here', googleAssistantHandler);
agent.handleRequest(intentMap);
});
Is there a way where I can return an object / self defined json via Dialogflow Fulfillment? agent.add() seems to only accept strings but I may be wrong.
function intent1f(agent) {
var someObject = {
"fruit1": "apple",
"fruit2": "orange"
};
agent.add(JSON.stringify(someObject));
}
On your client side you can use JSON.parse() to have same object.

How to deal with the different type of errors that returns from the cloud function?

I have written the cloud functions which sends the response either with the statuscode 200 or 400 but sometimes I am getting this error
Function execution took 219 ms, finished with status: 'connection error'
Error: function crashed out of request scope Function invocation was interrupted.
So the problem is I need to know send this error message with some statuscode as the response of the cloud function.
const functions = require('firebase-functions');
const dialogflow = require('dialogflow');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
var db = admin.firestore();
const {WebhookClient} = require('dialogflow-fulfillment');
var UID = new Object();
exports.fulfillmenttext = functions.https.onRequest((req,res) =>{
const answer1 = req.body.Text;
const uid = answer1.substring(0,28);
const answer = answer1.substring(28);
const sessionId = uid;
var count,questvalue;
const promise = db.collection("***").doc('***').collection("**").doc("uid").get();
promise.then(doc => {
snapshot.forEach(function(doc) {
if (doc.exists) {
count = doc.data().count;
if(count == 1){
var updatequest = title[questvalue];
res.status(200).send({"question":updatequest,"question_number":questvalue});
return;
}
else{
runSample();
}
}
else {
console.log("No such document!");
}
});
}).catch(function(error) {
console.log("Error getting document:", error);
});
async function runSample() {
const languageCode = 'en-US';
const projectId = 'xxxxxxx';
const credentials = {
client_email: 'xxxxxxx',
private_key:
'xxxxxxx',
};
//Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient({
projectId,
credentials,
});
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
//session: context1,
session: sessionPath,
queryInput: {
text: {
text: answer,
languageCode,
},
},
};
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
let action = result.action;
if (result.intent) {
const question = result.fulfillmentText;
console.log("question is",question);
const actionHandlers = {
'early': () => {
console.log('earlyaction1', action);
let name1 = JSON.stringify(result.parameters.fields.Name.stringValue);
name1 = name1.toString().replace(/"/g,"");
var data1 = {
Name: name1
};
var setDoc1 = admin.firestore().collection('**').doc('uid').collection("***").doc('uid').collection('**').doc('**').update(data1);
},
};
if (action === 'early') {
console.log('1');
actionHandlers[action]();
}
res.status(200).send({"question":result.fulfillmentText,"action":action,"question_number":title.indexOf(question)});
} else {
console.log(` No intent matched.`);
res.status(400).send({"action":"empty"});
}
}
});
That message is telling you that you have some async code that was still running after the function terminated normally by sending a response to the client. That bit of async code crashed. Since the function already terminated by sending a response, there's nothing you can do to send another response. There can only be one response per function invocation.
What you'll have to do is review your code and make sure you're 1) handling promises correctly and 2) not intentionally trying to leave any work going after sending the response, since Cloud Functions does not support that.

The bot is not working in Azure web chat after deploying back

I tried to develop a bot locally (created in Azure) with dispatch model to integrate LUIS and a knowledge base in a single bot developed in Node.js.
I used sample code available on Github to get a response for each intent.
I deployed this bot back to Azure using Visual Studio Code but on the portal, web chat is not working. It just keeps loading, saying Waiting for the bot to be ready.
Here the dispatchBot.js file code -
const { ActivityHandler } = require('botbuilder');
const { LuisRecognizer, QnAMaker } = require('botbuilder-ai');
class DispatchBot extends ActivityHandler {
/**
* #param {any} logger object for logging events, defaults to console if none is provided
*/
constructor(logger) {
super();
if (!logger) {
logger = console;
logger.log('[DispatchBot]: logger not passed in, defaulting to console');
}
const dispatchRecognizer = new LuisRecognizer({
applicationId: process.env.LuisAppId,
endpointKey: process.env.LuisAPIKey,
endpoint: `https://${ process.env.LuisAPIHostName }.api.cognitive.microsoft.com`
}, {
includeAllIntents: true,
includeInstanceData: true
}, true);
const qnaMaker = new QnAMaker({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
endpointKey: process.env.QnAAuthKey,
host: process.env.QnAEndpointHostName
});
this.logger = logger;
this.dispatchRecognizer = dispatchRecognizer;
this.qnaMaker = qnaMaker;
this.onMessage(async (context, next) => {
this.logger.log('Processing Message Activity.');
// First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
const recognizerResult = await dispatchRecognizer.recognize(context);
// Top intent tell us which cognitive service to use.
const intent = LuisRecognizer.topIntent(recognizerResult);
// Next, we call the dispatcher with the top intent.
await this.dispatchToTopIntentAsync(context, intent, recognizerResult);
await next();
});
this.onMembersAdded(async (context, next) => {
const welcomeText = 'Type a greeting or a question about the weather to get started.';
const membersAdded = context.activity.membersAdded;
for (let member of membersAdded) {
if (member.id !== context.activity.recipient.id) {
await context.sendActivity(`Welcome to Dispatch bot ${ member.name }. ${ welcomeText }`);
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
async dispatchToTopIntentAsync(context, intent, recognizerResult) {
switch (intent) {
case 'l_luis':
await this.processLuis(context, recognizerResult.luisResult);
break;
case 'q_sample-qna':
await this.processSampleQnA(context);
break;
default:
this.logger.log(`Dispatch unrecognized intent: ${ intent }.`);
await context.sendActivity(`Dispatch unrecognized intent: ${ intent }.`);
break;
}
}
async processLuis(context, luisResult) {
this.logger.log('processLuis');
// Retrieve LUIS result for Process Automation.
const result = luisResult.connectedServiceResult;
const intent = result.topScoringIntent.intent;
await context.sendActivity(`Luis top intent ${ intent }.`);
await context.sendActivity(`Luis intents detected: ${ luisResult.intents.map((intentObj) => intentObj.intent).join('\n\n') }.`);
if (luisResult.entities.length > 0) {
await context.sendActivity(`Luis entities were found in the message: ${ luisResult.entities.map((entityObj) => entityObj.entity).join('\n\n') }.`);
}
}
async processSampleQnA(context) {
this.logger.log('processSampleQnA');
const results = await this.qnaMaker.getAnswers(context);
if (results.length > 0) {
await context.sendActivity(`${ results[0].answer }`);
} else {
await context.sendActivity('Sorry, could not find an answer in the Q and A system.');
}
}
}
module.exports.DispatchBot = DispatchBot;
Here is app.js file code -
const path = require('path');
const restify = require('restify');
var { BotFrameworkAdapter} = require('botbuilder');
const { DispatchBot } = require('./bots/dispatchBot');
var botbuilder_azure = require('botbuilder-azure');
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
const connector = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
connector.onTurnError = async (context, error) => {
console.error(`\n [onTurnError]: ${ error }`);
await context.sendActivity(`Oops. Something went wrong!`);
};
var tableName = 'botdata';
var azureTableClient = new botbuilder_azure.AzureTableClient(tableName, process.env['AzureWebJobsStorage']);
var tableStorage = new botbuilder_azure.AzureBotStorage({ gzipData: false }, azureTableClient);
// Pass in a logger to the bot. For this sample, the logger is the console, but alternatives such as Application Insights and Event Hub exist for storing the logs of the bot.
const logger = console;
// Create the main dialog.
let bot = new DispatchBot(logger);
let server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
});
server.post('/api/messages', (req, res) => {
// Route received a request to adapter for processing
connector.processActivity(req, res, async (turnContext) => {
// route to bot activity handler.
await bot.run(turnContext);
});
});
When I try running this bot on Visual studio code, the program executes successfully and I am getting the restify port address. I am not able to test in Emulator because it is not working due to some errors in emulator..
Any help or suggestions will be very helpful.

Resources