Webhook call failed - dialogflow-es

I have created new agent and enables the fulfillment for Default Welcome Intent and also enabled the inline editor but when I invoked my welcome intent in simulator then it gives me default response instead of fulfillment response.
Am I doing something wrong?
this is my code
'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'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});

I have changed my code and now it's working
this is my new code
'use strict';
const functions = require('firebase-functions');
const {dialogflow} = require ('actions-on-google');
const {Suggestions} = require ('actions-on-google');
const WELCOME_INTENT = 'Default Welcome Intent';
const FALLBACK_INTENT = 'Default Fallback Intent';
const app = dialogflow();
app.intent(FALLBACK_INTENT, (conv) => {
conv.ask("Sorry! Could you please repeat that?");
});
app.intent(WELCOME_INTENT, (conv) => {
conv.ask("Hello!");
conv.ask("What you would like to do?");
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

Related

Invalid or unknown request type (not a Dialogflow v1 or v2 webhook request)

I am working with dialogflow for a chatbot app.
But when I run NodeJS always response with error
Invalid or unknown request type (not a Dialogflow v1 or v2 webhook request)
in the line where I declare the agent.
This is the original code. What is wrong?? :(
'use strict';
const express = require("express");
bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const { WebhookClient } = require("dialogflow-fulfillment");
var port = process.env.PORT || 3000;
app.post("/order", express.json(), function (req, res) {
// Error on next line
const agent = new WebhookClient({ request: req, response: res });
console.log('Dialogflow Request headers: ' + JSON.stringify(req.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(req.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?`);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
})
app.get('/hello', function (req, res) {
res.send ('Hello World');
})
app.listen(port, () => {
console.log("Listening port: " + port);
})

Context not working in web app dialogflow using web speech API

I am using socket.io for bi-directional communication between client-server side , i have also used web speech api for speech synthesis and recognition in my web app ,though it fulfills default responses and it gives output properly , but during contextual conversation my intents doesn't match with context and results in fallback intent.
It works fine on dialogflow "try" console but doesn't work on my webapp , please can anyone help..!
here's my app.js file :-
const path = require("path");
const express = require("express");
const colors = require("colors");
const dotenv = require("dotenv");
const socketio = require("socket.io");
const dialogflow = require("#google-cloud/dialogflow");
const uuid = require("uuid");
const app = express();
dotenv.config({ path: "./config/config.env" });
app.use(express.static(path.join(__dirname, "views")));
app.use(express.static(path.join(__dirname, "public")));
const PORT = process.env.PORT || 3000;
const server = app.listen(
PORT,
console.log(
`Server is runnig on ${process.env.NODE_ENV} mode at port ${PORT} for ${process.env.PROJECT_ID}`.yellow
.bold
)
);
const io = socketio(server);
io.on("connection", function (socket) {
console.log("a user connected");
socket.on("chat message", (message) => {
console.log(message);
const callapibot = async (projectId = process.env.PROJECT_ID) => {
try {
const sessionId = uuid.v4();
const sessionClient = new dialogflow.SessionsClient({
keyFilename: "/home/abat/Downloads/kiosk-cwbx-8e7bd8645085.json",
});
const sessionPath = sessionClient.projectAgentSessionPath(
projectId,
sessionId,
);
const request = {
session: sessionPath,
queryInput: {
text: {
text: message,
languageCode: "en-US",
},
},
};
const responses = await sessionClient.detectIntent(request);
console.log("Detected intent");
const result = responses[0].queryResult;
socket.emit("bot reply", result.fulfillmentText);
console.log(result);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`)
}
} catch (error) {
console.log(error);
}
};
callapibot();
});
});
here's my script.js file :-
const btn = document.querySelector("button");
const outputme = document.querySelector(".output-you");
const outputbot = document.querySelector(".output-bot");
const socket = io();
const SpeechRecognition =
window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.lang = "en-US";
recognition.interimResults = false;
btn.addEventListener("click", () => {
recognition.start();
});
recognition.onresult = function (event) {
const last = event.results.length - 1;
const text = event.results[last][0].transcript;
console.log(text);
outputme.textContent = text;
socket.emit("chat message", text);
};
const botReply = (text) => {
const synth = window.speechSynthesis;
const voices = synth.getVoices();
const utterance = new SpeechSynthesisUtterance();
utterance.voice = voices[4];
utterance.lang = "hi-IN";
utterance.text = text;
utterance.pitch = 1;
utterance.volume = 1;
synth.speak(utterance);
};
socket.on("bot reply", (text) => {
outputbot.textContent = text;
botReply(text);
});
Does my code need modifications for handling contexts for dialogflow ?
Got the answer , it was because the session id was inside the async function() , i then removed it from there and placed it on the top
const sessionId = uuid.v4();
Don't put this under the async function , that's it.

Using sessions to share data between different files and routes

Relatively new to node.js, I am wondering if anyone could help me please.
My MERN stack is working fine and authentication is working.
I am integrating Google's Dialogflow into a MERN stack and I would like to pass a user's username to the fulfillmentRoutes.js file.
The fulfillmentRoutes.js is as follows (I removed unnecessary code, it is just the bare bones);
fulFillmentRoutes.js
const {WebhookClient, Payload, Card} = require('dialogflow-fulfillment');
const express = require('express');
const router = express.Router();
const jwtDecode = require('jwt-decode');
const chatbot = require('../chatbot/chatbot');
const mongoose = require('mongoose');
const passport = require('passport');
const keys = require('../config/keys');
const Coupon = mongoose.model('coupon');
const Counter = mongoose.model('counters');
const Define = mongoose.model('define');
const sourceFile = require('./api/users.js');
const Themes = require('../models/Themes');
const Time = require('../models/Time');
const Questionexp = require('../models/Questionexp');
var cookieParser = require('cookie-parser');
var session = require('express-session');
module.exports = app => {
app.post('/api/df_text_query', async (req, res) => {
let responses = await chatbot.textQuery(req.body.text, req.body.userID, req.body.parameters);
res.send(responses[0].queryResult);
});
app.post('/api/df_event_query', async (req, res) => {
let responses = await chatbot.eventQuery(req.body.event, req.body.userID, req.body.parameters);
res.send(responses[0].queryResult);
});
app.post('/', async (req, res) => {
const agent = new WebhookClient({ request: req, response: res });
async function learn(agent) {
Demand.findOne({'course': agent.parameters.courses}, function(err, course) {
if (course !== null ) {
course.counter++;
course.save();
} else {
const demand = new Demand({course: agent.parameters.courses});
demand.save();
}
});
let responseText = `You want to learn about ${agent.parameters.courses}.
Here is a link to all of my courses: https://www.udemy.com/user/jana-bergant`;
let coupon = await Coupon.findOne({'course': agent.parameters.courses});
if (coupon !== null ) {
responseText = `You want to learn about ${agent.parameters.courses}.
Here is a link to the course: ${coupon.link}`;
}
agent.add(responseText);
}
async function welcome(agent) {
agent.add(“Hello!);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Welcome Intent - custom', continuesession);
agent.handleRequest(intentMap);
});
}
The file above file sits in a folder outside the api folder, but inside the routes folder.
I want to share a user's username is personalise conversation using the fulfillment module above.
I have declared a session in my server.js file which sits in the main app root.
server.js
app.use(session({
resave: true,
saveUninitialized: true,
secret: "secret"
}));
In my users.js file in ./routes/api directory, I did the following in users.js
users.js
router.get('/current', passport.authenticate('jwt', { session: false }),
(req, res) => {
req.session.test = req.user.email;
res.send(req.session.test);
res.json({
id: req.user.id,
firstname: req.user.firstname,
lastname: req.user.lastname,
email: req.user.email,
week: req.user.week,
age: req.user.age,
time: req.user.time,
bookmark: req.user.bookmark,
alertDate: req.user.alertDate
});
}
);
However, when I use session extract line (below) in the fulfillmentRoutes.js (file quoted at the beginning) I get undefined.
username = req.session.test;`
Any help would be appreciated please.
Thank you,
P

How to split functions into another file in index.js

I would like to split functions to different js files, using webhook hosted in firebase.
Because i anticipate i will be writing more functions int the future.
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 hello(agent) {
console.log("hello);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('hello',hello);
agent.handleRequest(intentMap);
});
Your index.js
const functions = require('firebase-functions');
const { WebhookClient } = require('dialogflow-fulfillment');
const welcome = require('./welcome')
const fallback = require('./fallback')
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));
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
You can split files like welcome, fallback
=> welcome.js
const welcome = (agent) => {
agent.add(`Welcome to my agent!`);
}
module.exports = welcome
=> fallback.js
const fallback = (agent) => {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
module.exports = fallback

dialogflow-fulfillment-library and express, what to res on?

I'm trying to use the dialog-fulfillment-library with express, without firebase functions. I'm having trouble finding how to res on the agent though.
const { WebhookClient, Card, Suggestion } = require('dialogflow-fulfillment');
module.exports = function () {
let self = {};
self.create = function (req, res, next) {
const agent = new WebhookClient({request: req, response: res});
agent.add(new Suggestion(`Quick Reply`));
agent.add(new Card({
title: `Title: this is a card title`,
imageUrl: 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png',
text: `This is the body text of a card. You can even use line\n breaks and emoji! 💁`,
buttonText: 'This is a button',
buttonUrl: 'https://assistant.google.com/'
})
);
res.json(agent);
};
return self;
};
I get a TypeError: Converting circular structure to JSON
I've tried decycling the agent but then it doesn't work on the dialogflow side.
using:
res.send(JSON.stringify(agent, decycle()));
returns: Webhook call failed. Error: Failed to parse webhook JSON response: Cannot find field: request_ in message google.cloud.dialogflow.v2.WebhookResponse.
Has anyone used it in this way or is it not possible?
I have submitted a Pull Request for the same.
Following code works for me.
package.json
{
"name": "Test_Agent",
"version": "0.0.1",
"description": "Test Agent webhook",
"main": "server.js",
"author": "Abhinav Tyagi, New Delhi, India",
"dependencies": {
"dialogflow-fulfillment": "^0.4.1",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"actions-on-google": "^2.2.0"
}
}
server.js
'use strict';
const {WebhookClient} = require('dialogflow-fulfillment');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
function welcome (agent) {
agent.add(`Welcome to Express.JS webhook!`);
}
function fallback (agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function WebhookProcessing(req, res) {
const agent = new WebhookClient({request: req, response: res});
console.info(`agent set`);
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
// intentMap.set('<INTENT_NAME_HERE>', yourFunctionHandler);
agent.handleRequest(intentMap);
res.status(200).send(agent);
}
// Webhook
app.post('/', function (req, res) {
console.info(`\n\n>>>>>>> S E R V E R H I T <<<<<<<`);
WebhookProcessing(req, res);
});
app.listen(8080, function () {
console.info(`Webhook listening on port 8080!`)
});
Make sure to use both action-on-google and dialogflow-fulfillment modules.
The intentMap keys is the intent.displayName.

Resources