I have a default fallback intent that has a followup fallback intent that is attached to it (its a followup and also linked via output and input contexts). When I use the Dialogflow console to test it this feature it works well. However, when I go through my API I don't get the same effect. Can someone tell me where I am going wrong in my code?
"use strict";
const express = require("express");
const bodyParser = require("body-parser");
const app = express().use(bodyParser.json()); // creates http server
const { JWT } = require("google-auth-library");
const dialogflow = require("dialogflow");
const uuid = require("uuid");
const token = "token"; // type here your verification token
const result = "";
app.get("/", (req, res) => {
// check if verification token is correct
if (req.query.token !== token) {
return res.sendStatus(401);
}
// return challenge
return res.end(req.query.challenge);
});
app.post("/", (req, res) => {
// check if verification token is correct
if (req.query.token !== token) {
return res.sendStatus(401);
}
// print request body
console.log(req.body);
//var request = req;
//console.log(req.body.result.parameters.testing);
console.log(req.body.result.resolvedQuery);
runSample();
/**
* Send a query to the dialogflow agent, and return the query result.
* #param {string} projectId The project to be used
*/
async function runSample(projectId = "projectid") {
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: req.body.result.resolvedQuery,
// 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}`);
//first not matched user query
if (result.intent.displayName === 'Default Fallback Intent')
{
result.fulfillmentText = 'Sorry, can you rephase your question?';
//result.fulfillmentText = 'Sorry, I am unable to answer your question';
}
if (result.intent.displayName === 'Default Fallback Intent - fallback')
{
//result.fulfillmentText = 'Sorry, can you rephase your question?';
result.fulfillmentText = 'Sorry, I am unable to answer your question';
}
// return a text response
const data = {
responses: [
{
type: "text",
elements: [result.fulfillmentText]
}
]
};
// return a text response
res.json(data);
} else {
console.log(` No intent matched.`);
}
}
});
// listen for requests :)
const listener = app.listen(3000, function() {
console.log("Your app is listening on port " + listener.address().port);
});
It does not look like you are either looking at the Context that is sent back as part of result, nor sending any Context in your call to sessionClient.detectIntent(request);. Dialogflow only maintains state in Contexts, in the same way a web server might maintain state through Cookies, so it does not know that an Output Context was set unless you send it as an Input Context the next round.
Because of this, every Fallback ends up being treated as the "Default Fallback Intent".
Related
I'm trying to implement a reminders dialogflow agent in node js that reminds the user on his google calendar upcoming events. however I'm getting an No responses defined for platform: null error when calling the intent for listing the upcoming events.
This is my code:
const express = require('express');
const google = require('googleapis').google;
const jwt = require('jsonwebtoken');
const dfff = require('dialogflow-fulfillment')
const {googlec} = require('googleapis');
// Google's OAuth2 client
const OAuth2 = google.auth.OAuth2;
// Including our config file
const CONFIG = require('./config');
// Creating our express application
const app = express();
// Allowing ourselves to use cookies
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// Setting up EJS Views
app.set('view engine', 'ejs');
app.set('views', __dirname);
console.log(dfff)
app.get('/', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
// Obtain the google login link to which we'll send our users to give us access
const loginLink = oauth2Client.generateAuthUrl({
access_type: 'offline', // Indicates that we need to be able to access data continously without the user constantly giving us consent
scope: CONFIG.oauth2Credentials.scopes // Using the access scopes from our config file
});
return res.render("index", { loginLink: loginLink });
});
app.get('/auth_callback', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
if (req.query.error) {
// The user did not give us permission.
return res.redirect('/error');
} else {
oauth2Client.getToken(req.query.code, function(err, token) {
if (err)
return res.redirect('/');
// Store the credentials given by google into a jsonwebtoken in a cookie called 'jwt'
res.cookie('jwt', jwt.sign(token, CONFIG.JWTsecret));
return res.redirect('/');
});
}
});
app.post('/', express.json(),(req,res)=>{
//if (!req.cookies.jwt) {
// We haven't logged in
//return res.redirect('/');
//}
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
const calendar = google.calendar({version: 'v3' , auth:oauth2Client});
const agent = new dfff.WebhookClient({
request : req,
response : res
})
function welcome(agent){
agent.add("Hi")
}
function listEvents(agent){
calendar.events.list({
'calendarId': 'primary',
'auth':oauth2Client,
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'singleEvents': true,
'orderBy': 'startTime'
}).then((err,response)=> {
let events = response.result.items;
if (events.length > 0) {
for (let i = 0; i < events.length; i++) {
var event = events[i];
var when = event.start.dateTime;
if (!when) {
when = event.start.date;
}
return agent.add('Be ready for '+ event.summary + ' event '+ 'at ' + when )
}}
else {
return agent.add('You dont have any upcoming events.');
}
});
}
let intenMap = new Map()
intenMap.set('Default_Welcome_Intent',welcome)
intenMap.set('Remind_me',listEvents)
agent.handleRequest(intenMap)
});
// Listen on the port defined in the config file
app.listen(CONFIG.port, function () {
console.log(`Listening on port ${CONFIG.port}`);
});
Whenever listEvents function is processed I'm getting (Error: No responses defined for platform null) any idea why?
The issue is that listEvents() does an asynchronous operation that returns a Promise (the call to calendar.events.list()), which you handle through the .then() block, but you don't have a way for the agent.handleRequest() function to know this and to wait for the Promise to complete. To do this, you need to return a Promise.
Fortunately, in your case, the solution is straightforward, since the call and then() chain return a Promise, you can just return it. This would be done by adding the return keyword before calendar.events.list(). It might look something like this:
function listEvents(agent){
return calendar.events.list({
// Parameters go here
}).then((err,response)=> {
// Code to handle response and return a message go here
});
}
I'm trying to create a simple AI chatbot command for my discord bot and it's working out, however, my Dialogflow doesn't seem to utilize any changes that I made like new intents or different responses. it always just returns the text from before the changes or just doesn't return anything at all.
I might be really stupid.
This is my code:
const Discord = require("discord.js")
const axios = require('axios');
const uuid = require('uuid');
const dialogflow = require('#google-cloud/dialogflow');
require("dotenv").config();
const projectId = "mydialogprojectid";
console.log(process.env.GOOGLE_APPLICATION_CREDENTIALS)
const config = require(`../config.json`)
exports.run = async(client, message, args) => {
message.channel.startTyping();
// A unique identifier for the given session
const sessionId = uuid.v4();
console.log(sessionId)
// Create a new session
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.projectAgentSessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: args.join(' '),
// The language used by the client (en-US)
languageCode: 'en-US',
},
},
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
console.log(result)
message.channel.stopTyping();
return message.channel.send(result.fulfillmentText ? result.fulfillmentText : "Something went wrong, forgive me please! I'm still in beta.")
}
I have the same issue it works on the Dialogflow console etc but when using it in discord it wouldnt find the intent etc like you described.
At first i thought this was just some cache issue. I will update this if i find a solution.
I am developing a bot that sends users a message every hour on skype. It makes an API call and then sends a message containing the parsed message. I started with the welcomebot sample. I then used the switch statement to fire a setInterval()to send the message activity when the user sends a certain message but the message keeps sending even after I call clearinterval() I set to a different string. How can I make clearInterval() work? Or am I going about it the wrong way?
Index.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required packages
const path = require('path');
const restify = require('restify');
const axios = require('axios');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter, UserState, ConversationState, MemoryStorage } = require('botbuilder');
const { WelcomeBot } = require('./bots/welcomeBot');
// Read botFilePath and botFileSecret from .env file
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create bot adapter.
// See https://aka.ms/about-bot-adapter to learn more about bot adapter.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppID,
appPassword: process.env.MicrosoftAppPassword
});
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};
// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
// A bot requires a state store to persist the dialog and user state between messages.
// For local development, in-memory storage is used.
// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot
// is restarted, anything stored in memory will be gone.
const memoryStorage = new MemoryStorage();
const userState = new UserState(memoryStorage);
const conversationState = new ConversationState(memoryStorage);
// Create the main dialog.
const conversationReferences = {};
const bot = new WelcomeBot(userState, conversationReferences, conversationState);
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
// Listen for incoming activities and route them to your bot main dialog.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// route to main dialog.
await bot.run(context);
});
});
const getBalance = async () => {
try {
return await axios.get('https://balance.com/getbalance', { //this returns a json object
headers: {
Authorization: xxxxx
}
});
} catch (error) {
console.error(error);
}
};
const retrieveBalance = async () => {
const balance = await getBalance();
if (balance.data.message) {
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async turnContext => {
// If you encounter permission-related errors when sending this message, see
// https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity(balance.data.message);
});
}
}
};
module.exports.retrieveBalance = retrieveBalance;
Bot.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required Bot Framework classes.
const { ActionTypes, ActivityHandler, CardFactory, TurnContext } = require('botbuilder');
const balance = require('../index.js');
// Welcomed User property name
const CONVERSATION_DATA_PROPERTY = 'conversationData';
class WelcomeBot extends ActivityHandler {
/**
*
* #param {UserState} //state to persist boolean flag to indicate
* if the bot had already welcomed the user
*/
constructor(userState, conversationReferences, conversationState) {
super();
// Creates a new user property accessor.
// See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors.
this.userState = userState;
this.conversationReferences = conversationReferences;
this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// The state management objects for the conversation and user state.
this.conversationState = conversationState;
this.onConversationUpdate(async (context, next) => {
this.addConversationReference(context.activity);
await next();
});
this.onMessage(async (context, next) => {
this.addConversationReference(context.activity);
// Add message details to the conversation data.
// Display state data.
// Read UserState. If the 'DidBotWelcomedUser' does not exist (first time ever for a user)
// set the default to false.
// const didBotWelcomedUser = await this.welcomedUserProperty.get(context, false);
// Your bot should proactively send a welcome message to a personal chat the first time
// (and only the first time) a user initiates a personal chat with your bot.
// if (didBotWelcomedUser === false) {
// // The channel should send the user name in the 'From' object
// await context.sendActivity( 'This bot will send you your paystack balance every 5 minutes. To agree reply \'ok\'');
// // Set the flag indicating the bot handled the user's first message.
// await this.welcomedUserProperty.set(context, true);
// }
// This example uses an exact match on user's input utterance.
// Consider using LUIS or QnA for Natural Language Processing.
var text = context.activity.text.toLowerCase();
var myint;
switch (text) {
case 'ok':
myint = setInterval(async () => {
await balance.retrieveBalance();
}, 1000);
// eslint-disable-next-line template-curly-spacing
break;
case 'balance':
await balance.retrieveBalance();
break;
case 'no':
clearInterval(myint);
break;
case 'hello':
case 'hi':
await context.sendActivity(`You said "${ context.activity.text }"`);
break;
case 'info':
case 'help':
await this.sendIntroCard(context);
break;
default:
await context.sendActivity('This is a simple notification bot. ');
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
// Sends welcome messages to conversation members when they join the conversation.
// Messages are only sent to conversation members who aren't the bot.
this.onMembersAdded(async (context, next) => {
// Iterate over all new members added to the conversation
for (const idx in context.activity.membersAdded) {
// Greet anyone that was not the target (recipient) of this message.
// Since the bot is the recipient for events from the channel,
// context.activity.membersAdded === context.activity.recipient.Id indicates the
// bot was added to the conversation, and the opposite indicates this is a user.
if (context.activity.membersAdded[idx].id !== context.activity.recipient.id) {
await context.sendActivity('Welcome to the Nag Bot. If you want to get nagged with your balance type \'ok\', if you want your balance alone type \'balance. for info type \'info\'');
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);
// Save state changes
await this.userState.saveChanges(context);
await this.conversationState.saveChanges(context, false);
}
addConversationReference(activity) {
const conversationReference = TurnContext.getConversationReference(activity);
this.conversationReferences[conversationReference.conversation.id] = conversationReference;
}
async sendIntroCard(context) {
const card = CardFactory.heroCard(
'Welcome to Nag Bot balance checker!',
'Welcome to Paystack Nagbot.',
['https://aka.ms/bf-welcome-card-image'],
[
{
type: ActionTypes.OpenUrl,
title: 'Open your dashboard',
value: 'https://dashboard.paystack.com'
},
{
type: ActionTypes.OpenUrl,
title: 'Ask a question on twitter',
value: 'https://twitter.com/paystack'
},
{
type: ActionTypes.OpenUrl,
title: 'View docs',
value: 'https://developers.paystack.co/reference'
}
]
);
await context.sendActivity({ attachments: [card] });
}
}
module.exports.WelcomeBot = WelcomeBot;
Im wondering if there is a way to keep track of contexts of the intents as they follow. I know that you can use the output context of the previous intent as the input context of the followup intent. but this means i have to keep track of the lifespan as well. Is there another way???
var context = [];
async function TextRecognition(queryText) {
const sessionId = uuid.v4();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: queryText,
// The language used by the client (en-US)
languageCode: 'en-US',
},
},
};
if (contextList !== null && contextList.length !== 0) {
console.log(contextList);
request.queryParams = {
contexts: context,
};
}
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
if (result.intent) {
if (typeof result.outputContexts[0] !== 'undefined') {
result.outputContexts.forEach(context => {
context.push(context);
});
}
return result.fulfillmentText;
} else {
console.log(`No intent matched.`);
}
}
found the answer :) there wasnt a problem in the first place just make sure to create sessionid for each client you dont need to keep track of anythiing dialogflow does it for you :p
How to solve "Wit is not a constructor" error coming from Node.js while executing code given by node-wit and wit.ai documentation.
// Setting up our bot
const wit = new Wit(WIT_TOKEN, actions);
I tried all the ways by upgrading and downgrading npm/node versions, but no luck.
Update: Please find the index.js source I used,
Do I need to change anything in this?
module.exports = {
Logger: require('./lib/logger.js').Logger,
logLevels: require('./lib/logger.js').logLevels,
Wit: require('./lib/wit.js').Wit,
}
'use strict';
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
const Logger = require('node-wit').Logger;
const levels = require('node-wit').logLevels;
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.listen((process.env.PORT || 3000));
//const Wit = require('node-wit').Wit;
const WIT_TOKEN = process.env.WIT_TOKEN;
const FB_PAGE_TOKEN = process.env.FB_PAGE_TOKEN;
const Wit = require('node-wit').Wit;
// Server frontpage
app.get('/', function (req, res) {
debugger;
res.send('This is TestBot Server');
});
// Messenger API specific code
// See the Send API reference
// https://developers.facebook.com/docs/messenger-platform/send-api-reference
const fbReq = request.defaults({
uri: 'https://graph.facebook.com/me/messages',
method: 'POST',
json: true,
qs: { access_token: FB_PAGE_TOKEN },
headers: {'Content-Type': 'application/json'},
});
const fbMessage = (recipientId, msg, cb) => {
const opts = {
form: {
recipient: {
id: recipientId,
},
message: {
text: msg,
},
},
};
fbReq(opts, (err, resp, data) => {
if (cb) {
cb(err || data.error && data.error.message, data);
}
});
};
// See the Webhook reference
// https://developers.facebook.com/docs/messenger-platform/webhook-reference
const getFirstMessagingEntry = (body) => {
const val = body.object == 'page' &&
body.entry &&
Array.isArray(body.entry) &&
body.entry.length > 0 &&
body.entry[0] &&
body.entry[0].id === FB_PAGE_ID &&
body.entry[0].messaging &&
Array.isArray(body.entry[0].messaging) &&
body.entry[0].messaging.length > 0 &&
body.entry[0].messaging[0]
;
return val || null;
};
// Wit.ai bot specific code
// This will contain all user sessions.
// Each session has an entry:
// sessionId -> {fbid: facebookUserId, context: sessionState}
const sessions = {};
const findOrCreateSession = (fbid) => {
var sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {fbid: fbid, context: {}};
}
return sessionId;
};
// Our bot actions
const actions = {
say(sessionId, context, message, cb) {
// Our bot has something to say!
// Let's retrieve the Facebook user whose session belongs to
const recipientId = sessions[sessionId].fbid;
if (recipientId) {
// Yay, we found our recipient!
// Let's forward our bot response to her.
fbMessage(recipientId, message, (err, data) => {
if (err) {
console.log(
'Oops! An error occurred while forwarding the response to',
recipientId,
':',
err
);
}
// Let's give the wheel back to our bot
cb();
});
} else {
console.log('Oops! Couldn\'t find user for session:', sessionId);
// Giving the wheel back to our bot
cb();
}
},
merge(sessionId, context, entities, message, cb) {
cb(context);
},
error(sessionId, context, error) {
console.log(error.message);
},
// You should implement your custom actions here
// See https://wit.ai/docs/quickstart
};
const wit = new Wit(WIT_TOKEN, actions);
// Message handler
app.post('/webhook', (req, res) => {
// Parsing the Messenger API response
// Setting up our bot
//const wit = new Wit(WIT_TOKEN, actions);
const messaging = getFirstMessagingEntry(req.body);
if (messaging && messaging.message && messaging.message.text) {
// Yay! We got a new message!
// We retrieve the Facebook user ID of the sender
const sender = messaging.sender.id;
// We retrieve the user's current session, or create one if it doesn't exist
// This is needed for our bot to figure out the conversation history
const sessionId = findOrCreateSession(sender);
// We retrieve the message content
const msg = messaging.message.text;
const atts = messaging.message.attachments;
if (atts) {
// We received an attachment
// Let's reply with an automatic message
fbMessage(
sender,
'Sorry I can only process text messages for now.'
);
} else if (msg) {
// We received a text message
// Let's forward the message to the Wit.ai Bot Engine
// This will run all actions until our bot has nothing left to do
wit.runActions(
sessionId, // the user's current session
msg, // the user's message
sessions[sessionId].context, // the user's current session state
(error, context) => {
if (error) {
console.log('Oops! Got an error from Wit:', error);
} else {
// Our bot did everything it has to do.
// Now it's waiting for further messages to proceed.
console.log('Waiting for futher messages.');
// Based on the session state, you might want to reset the session.
// This depends heavily on the business logic of your bot.
// Example:
// if (context['done']) {
// delete sessions[sessionId];
// }
// Updating the user's current session state
sessions[sessionId].context = context;
}
}
);
}
}
res.sendStatus(200);
});
There are two typical causes of your issue, either forgetting to require your module or forgetting to npm install it. Check if you:
Forgot to require('node-wit') and obtain the constructor from the returned object:
const Wit = require('node-wit').Wit
Properly required Wit but forgot to npm install node-wit
For everyone who are using messenger.js as your index.js use this:
const Wit = require('./lib/wit');
const log = require('./lib/log');
Please check your node_modules directory for node-wit package.
If node-wit is present then please require it before trying to create its instance.
const {Wit} = require('node-wit');
witHandler = new Wit({
accessToken: accessToken
});