How to use session entity in dialogflow's inline editor? - dialogflow-es

My problem is passing parameters between intents' hooks.
As example: at one hook I made a request for my own backend and receive a sessionId. I need pass the sessionId to an another intent for second request in it's hook.
How I can do it? I can't find any examples, documentations or best practices.
Thanks for your attention!

You can do this with contexts.
In your first intent you do something like this:
function firstIntentHandler(agent){
return axios.get(`https://yourserver.com`)
.then(function (response) {
agent.add(response.data.sessionId);
const context = {'name': 'yourcontextname', 'lifespan': 3, 'parameters':
{'sessionid': response.data.sessionId }};
agent.setContext(context);
}).catch(function (error) {
agent.add("An error occured.");
});
}
sessionId is the variable name in your json data sent by your server.
In your other intent you can access this data like this:
function otherIntentHandler(agent) {
const context = agent.getContext("yourcontextname");
const sessionid = context.parameters.sessionid;
// do something with this session id
});
}
Be careful with camelcase for your context name. I have the feeling that uppercase letters are stored as lowercase...

Related

Is it possible to modify the pug.js-parameter object in an express middleware-callback

Currently I'm on a legacy application using pug.js as view engine in a node.js express-app.
I want to implement a generic way to display feedback messages. I want to be able to display messages (successes, errors), even if the handler does reply with a redirect.
This is what I want:
handlePostRequest(req, res){
// do stuff with the post request
doStuff(req.body);
//This should be done of course somewhere else.
req.session.successes=req.session.successes|[];
//save some success-message for the user
req.session.successes.push("Your post has been saved. Thank you!");
//but reply with a 302
res.redirect(req.headers.referer);
}
//a get request. maybe the handler above redirected here
handleGetRequest(req,res){
// we do NOT get the successes here. Just the 'pure' data.
const renderData=getRenderData();
res.render('fancy-pug-template', renderData);
}
fancyMiddlewareForMessages(req, res, next){
//how to implement getRenderDataByBlackMagic()????
const renderData = getRenderDataByBlackMagic();
//set the messages
renderData.successes = req.session.successes;
//empty saved messages
req.session.successes = [];
next();
}
Obviously, I do not want to polute every handler which actually renders a template with some logic which retrieves the messages and adds them to the parameter object. I would like to move this cross-cutting concern in a middleware callback or something like that.
So, the question is: Can this be achieved? How? I'm fairly new to pug.js, maybe I'm overlooking something obvious.
Ok, I found a way. This is what I did:
const requestStorage = new AsyncLocalStorage<Request>();
function patchRenderFunction(req: Request, res: Response, next: NextFunction) {
const render = res.render;
res.render = function (view: string, options?: any, callback?: (err: Error, html: string) => void) {
const messages = new MessageManager(req);
//merge errorMessages
options.errorMessages = mergeMessageArrays(options.errorMessages, messages.errors);
//same for successMessages
options.successMessages = mergeMessageArrays(options.successMessages, messages.successes);
render.bind(this)(view, options, callback);
};
requestStorage.run(req, () => {
next();
});
}
export function applyAutomaticRenderAttributes(app: Express): void {
app.use(patchRenderFunction);
}
export function successMessage(message: string, req?: Request) {
if (!req) {
req = requestStorage.getStore();
}
if (!req) {
console.error('No request found in async storage. This should not happen. Please report this issue. (successMessage)');
return;
}
new MessageManager(req).addSuccessMessage(message);
}
//export function errorMessage(...) omitted
The MessageManager uses the requests session to store messages. It also filters them in some respect. I'm using the session because the application runs clustered (thank you, pm2). Since the session is stored in the db via express-mysql-session, I avoid problems with non-sticky sessions.

How can I get an express server to send the data retrieved by a separate provider to a client?

I'm currently writing a small program in Node.js for an express server. I am using providers to facilitate the separation of concerns. What I'm having trouble with is figuring out how to send a return value from the provider function back to the express server and then to the client. I'm probably missing something with the asynchronous code but can't seem to figure out what. I'd be grateful if you could guide me in the right direction! Sorry if this is an obvious question, I'm very new to programming.
This is the code skeleton for the main.js:
server.get("/someurl*", (req, res) => {
let name = req.query.name;
let id = req.query.id;
if (
(isValid(name) == true) &&
(isValid(id) == true)
) {
let provider = new getSomething();
provider.getMethod(id);
res.send(provider.getMethod(id));
};
});
This is the code skeleton for provider.js:
class getSomething extends abstractClass {
getMethod(id) {
this.id = id;
// Acquiring data from database based on id
…
if (err)
return (‘error’);
return data;
};
};
What I do not understand is how can I send the data back to the express server (main.js) and then send that data back to the client with res.send. The way I'm doing it above doesn't work as it returns an undefined value. I cannot do res.send directly from the provider as it should only be responsible for retrieving data from the database and the server instance is defined in main.js.
Thank you very much in advance!
In your provider class I would ammend the getMethod to be something more like this:
getMethod(id) {
this.id = id;
//do something. update this.data
if (err) this.error = err;
//optional for method chaining: return this;
}
Class methods can be used to modify the properties of the class, which you can then send to the client.
then in your endpoint:
server.get(“/someurl*”, (req, res) => {
const name = req.query.name; //use consts for values that you dont want to change
const id = req.query.id;
if ((isValid(name) == true) && (isValid(id) == true)) {
const provider = new getSomething();
provider.getMethod(id);
if (provider.error) return res.status(500).json({error: provider.error});
return res.status(200).json({data: provider.data});
};
return res.status(500).error({error: 'query data not valid'});
});
this is extracting the data you require from the query object, then if everything is valid (im asuming some other function you have for validation) will instantiate a new instance of your class, run the method, and if theres an error send this back to the client otherwise send back data to the client.
If the data is not valid it also sends back a response to the client. In any case you want to send something back to the client.

Actions on Google context not being passed to next intent

I am trying to pass values from one intent to another.
This is how I set the context:
app.intent('welcome', async (conv) => {
const parameters = {
userId: "12345"
};
conv.contexts.set('userDoc', 5, parameters);
conv.followup('second', {})
})
This is how I try to retrieve the context parameters:
app.intent('second', (conv) => {
console.log(conv.userDoc); // returns undefined
})
However, conv.userDoc returns undefined.
This is my dialogflow setup:
Contexts are set as part of the reply that is sent back. Calling conv.followup() discards everything from the reply except for the event type and parameters that are sent.
In general - you probably don't want to actually use conv.followup() anyway. If you feel you need to return something from your webhook - just return it.

dialogflow fullfilment and firebase response time

I am trying to build a simple chatbot with DialogFlow.
My aim is to give information from user question, like : where can I slackline above water in croatia ? I have two parameters (croatia, waterline) and a list of slackline places.
So I need a data base to retrieve information from parameters. DialogFlow allows fulfillment with Firebase. I build a database with places (name, country, type of slack) and enable webhook call for my intent.
I use Inline Editor and index.js
const parameters = request.body.queryResult.parameters;
var country = parameters.country.toString();
function show(snap) {
console.log('snap');
agent.add(JSON.stringify(snap.val(),null,2));
}
function slkplc(agent) {
var testRef;
firebase.database().ref('slackplace').once('value',show);
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
But I do not get the expected result while trying it on DialogFlow or Google Assistant. The function show is asynchronously called but too late and the response is not available for DialogFlow :
I see three way to deal with this problem :
use blocking call to database : another database ?
treat asynchronous message with DialogFlow ???
response to user that an error occured.
The third that I choose, but it is always on error.
After trying several things to wait data from database response, the only thing I managed is to freeze the response, therefore the timeout of DialogFlow - 5s -and Firebase - 60s - were reached.
A workaround
Another way to do it is to separate database acquisition and request/response from DialogFlow. The data of database is collected outside of the dialogflowFirebaseFulfillment
var data;
var inidata = firebase.database().ref().on('value',function(snap) {
console.log('snap');
data = snap.val();
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
...
function slkplc(agent) {
agent.add(JSON.stringify(data,null,2));
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
}
Now I can do what I want with data, and I am able to find the place where I can practice waterline in croatia. But there is always something weird, the data of the database is duplicated ...
The "right" solution is option 2 that you suggest: since you're doing an asynchronous call, you need to handle this correctly when working with the dialogflow-fulfillment library.
Basically, if your handler makes an asynchronous call, it needs to be asynchronous as well. To indicate to the handleRequest() method that your handler is async, you need to return a Promise object.
Firebase's once() method returns a Promise if you don't pass it a callback function. You can take advantage of this, return that Promise, and also handle what you want it to do as part of a .then() clause. It might look something like this:
function slkplc(agent) {
var testRef;
return firebase.database().ref('slackplace').once('value')
.then( snap => {
var val = snap.val();
return agent.add( JSON.stringify( val, null, 2 ) );
});
}
The important part isn't just that you use a Promise, but also that you return that Promise.

How to get multiple socket attributes in socket.io?

I'm building a chat app on which, for every client connection, i need to set some attributes to every client instance using socket.io. When i save the attribute, i use:
client.set('name', name, function () {});
client.set('email', email, function () {});
....
and it runs fine.
When i need to get all the client properties, i have not found a better way than this:
client.get("name",function(err,name) {
client.get("email",function(err,email) {
.......
}
}
I need to nest all the "get" to asynchronously get data; but if i had 10 properties to get, do i need to nest all the 10 items? There must be a better way to do it, can anyone help me?
I don't attach attributes to the socket.
io.sockets.on('connection', function(socket) {
var username = "xx";
var email = "xx";
socket.on('doX', function(data) {
socket.emit('ackX', {username: username, email: email});
});
});
I don't know if it's the best solution, but I have seen many examples like that.
EDIT : socket.io - getting more than one field for a socket?
The correct answer may fit your needs

Resources