How to include custom API request in ALEXA intent by using async - node.js

I have following problem which already took me several hours but I can´t fix it.
I want to create an Alexa skill, where the user is asked several questions. The user input afterwards sent to an API.
When I make the request separate everything works well. When it´s included in the intent it doesn´t work. It´s skipped even if I use async.
Here the code:
/* This code has been generated from your interaction model by skillinator.io
/* eslint-disable func-names */
/* eslint quote-props: ["error", "consistent"]*/
// There are three sections, Text Strings, Skill Code, and Helper Function(s).
// You can copy and paste the contents as the code for a new Lambda function, using the alexa-skill-kit-sdk-factskill template.
// This code includes helper functions for compatibility with versions of the SDK prior to 1.0.9, which includes the dialog directives.
// 1. Text strings =====================================================================================================
// Modify these strings and messages to change the behavior of your Lambda function
var request = require('request');
var size;
var heightSlot = 188;
var ageSlot = 35;
var weightSlot = 88;
var gender = "MALE"
var jsonBody = {
"gender" : gender,
"unit": "METRIC",
"height": heightSlot,
"weight": weightSlot,
"age": ageSlot
}
const options = {
method: 'PUT',
uri: "https://custom.apicall.com/api/finder/",
//headers: headers, // headers if your api requires
body: jsonBody,
json: true
};
let speechOutput;
let reprompt;
let welcomeOutput = "This is a placeholder welcome message. This skill includes 6 intents. Try one of your intent utterances to test the skill.";
let welcomeReprompt = "sample re-prompt text";
// 2. Skill Code =======================================================================================================
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
speechOutput = '';
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', welcomeOutput, welcomeReprompt);
},
'AMAZON.HelpIntent': function () {
speechOutput = 'Placeholder response for AMAZON.HelpIntent.';
reprompt = '';
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
speechOutput = 'Placeholder response for AMAZON.CancelIntent';
this.emit(':tell', speechOutput);
},
'AMAZON.StopIntent': function () {
speechOutput = 'Placeholder response for AMAZON.StopIntent.';
this.emit(':tell', speechOutput);
},
'SessionEndedRequest': function () {
speechOutput = '';
//this.emit(':saveState', true);//uncomment to save attributes to db on session end
this.emit(':tell', speechOutput);
},
'AMAZON.FallbackIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "This is a place holder response for the intent named AMAZON.FallbackIntent. This intent has no slots. Anything else?";
this.emit(":ask", speechOutput, speechOutput);
},
'AMAZON.NavigateHomeIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "This is a place holder response for the intent named AMAZON.NavigateHomeIntent. This intent has no slots. Anything else?";
this.emit(":ask", speechOutput, speechOutput);
},
'findOutMyCalorie': function () {
//delegate to Alexa to collect all the required slot values
let filledSlots = delegateSlotCollection.call(this);
speechOutput = '';
//any intent slot variables are listed here for convenience
let genderSlotRaw = this.event.request.intent.slots.gender.value;
console.log(genderSlotRaw);
let genderSlot = resolveCanonical(this.event.request.intent.slots.gender);
console.log(genderSlot);
let ageSlotRaw = this.event.request.intent.slots.age.value;
console.log(ageSlotRaw);
let ageSlot = resolveCanonical(this.event.request.intent.slots.age);
console.log(ageSlot);
let weightSlotRaw = this.event.request.intent.slots.weight.value;
console.log(weightSlotRaw);
let weightSlot = resolveCanonical(this.event.request.intent.slots.weight);
console.log(weightSlot);
let weight_unitSlotRaw = this.event.request.intent.slots.weight_unit.value;
console.log(weight_unitSlotRaw);
let weight_unitSlot = resolveCanonical(this.event.request.intent.slots.weight_unit);
console.log(weight_unitSlot);
let heightSlotRaw = this.event.request.intent.slots.height.value;
console.log(heightSlotRaw);
let heightSlot = resolveCanonical(this.event.request.intent.slots.height);
console.log(heightSlot);
let height_unitSlotRaw = this.event.request.intent.slots.height_unit.value;
console.log(height_unitSlotRaw);
let height_unitSlot = resolveCanonical(this.event.request.intent.slots.height_unit);
console.log(height_unitSlot);
myAsyncFn();
//Your custom intent handling goes here
speechOutput = "Deine Kalorienverbrauch ist " + size;
this.emit(':ask', speechOutput, speechOutput);
//Your custom intent handling goes here
//speechOutput = "This is a place holder response for the intent named findOutMySize, which includes dialogs. This intent has 6 slots, which are gender, age, weight, weight_unit, height, and height_unit. Anything else?";
//this.emit(':ask', speechOutput, speechOutput);
},
'Unhandled': function () {
speechOutput = "The skill didn't quite understand what you wanted. Do you want to try something else?";
this.emit(':ask', speechOutput, speechOutput);
}
};
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
alexa.appId = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
//alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
//alexa.dynamoDBTableName = 'DYNAMODB_TABLE_NAME'; //uncomment this line to save attributes to DB
alexa.execute();
};
// END of Intent Handlers {} ========================================================================================
// 3. Helper Function =================================================================================================
async function myAsyncFn() {
try {
await request(options, function(err, response) {
size = response.body.size;
console.log(response.body.size)
});
}
catch (error) {
console.log(error);
}
}
function resolveCanonical(slot){
//this function looks at the entity resolution part of request and returns the slot value if a synonyms is provided
let canonical;
try{
canonical = slot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
}catch(err){
console.log(err.message);
canonical = slot.value;
};
return canonical;
};
function delegateSlotCollection(){
console.log("in delegateSlotCollection");
console.log("current dialogState: "+this.event.request.dialogState);
if (this.event.request.dialogState === "STARTED") {
console.log("in Beginning");
let updatedIntent= null;
// updatedIntent=this.event.request.intent;
//optionally pre-fill slots: update the intent object with slot values for which
//you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property
//this.emit(":delegate", updatedIntent); //uncomment this is using ASK SDK 1.0.9 or newer
//this code is necessary if using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady', updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
// return a Dialog.Delegate directive with no updatedIntent property.
//this.emit(":delegate"); //uncomment this is using ASK SDK 1.0.9 or newer
//this code necessary is using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', null, null),
shouldEndSession: false
});
this.emit(':responseReady');
} else {
console.log("in completed");
console.log("returning: "+ JSON.stringify(this.event.request.intent));
// Dialog is now complete and all required slots should be filled,
// so call your normal intent handler.
return this.event.request.intent;
}
}
function randomPhrase(array) {
// the argument is an array [] of words or phrases
let i = 0;
i = Math.floor(Math.random() * array.length);
return(array[i]);
}
function isSlotValid(request, slotName){
let slot = request.intent.slots[slotName];
//console.log("request = "+JSON.stringify(request)); //uncomment if you want to see the request
let slotValue;
//if we have a slot, get the text and store it into speechOutput
if (slot && slot.value) {
//we have a value in the slot
slotValue = slot.value.toLowerCase();
return slotValue;
} else {
//we didn't get a value in the slot.
return false;
}
}
//These functions are here to allow dialog directives to work with SDK versions prior to 1.0.9
//will be removed once Lambda templates are updated with the latest SDK
function createSpeechObject(optionsParam) {
if (optionsParam && optionsParam.type === 'SSML') {
return {
type: optionsParam.type,
ssml: optionsParam['speech']
};
} else {
return {
type: optionsParam.type || 'PlainText',
text: optionsParam['speech'] || optionsParam
};
}
}
function buildSpeechletResponse(options) {
let alexaResponse = {
shouldEndSession: options.shouldEndSession
};
if (options.output) {
alexaResponse.outputSpeech = createSpeechObject(options.output);
}
if (options.reprompt) {
alexaResponse.reprompt = {
outputSpeech: createSpeechObject(options.reprompt)
};
}
if (options.directives) {
alexaResponse.directives = options.directives;
}
if (options.cardTitle && options.cardContent) {
alexaResponse.card = {
type: 'Simple',
title: options.cardTitle,
content: options.cardContent
};
if(options.cardImage && (options.cardImage.smallImageUrl || options.cardImage.largeImageUrl)) {
alexaResponse.card.type = 'Standard';
alexaResponse.card['image'] = {};
delete alexaResponse.card.content;
alexaResponse.card.text = options.cardContent;
if(options.cardImage.smallImageUrl) {
alexaResponse.card.image['smallImageUrl'] = options.cardImage.smallImageUrl;
}
if(options.cardImage.largeImageUrl) {
alexaResponse.card.image['largeImageUrl'] = options.cardImage.largeImageUrl;
}
}
} else if (options.cardType === 'LinkAccount') {
alexaResponse.card = {
type: 'LinkAccount'
};
} else if (options.cardType === 'AskForPermissionsConsent') {
alexaResponse.card = {
type: 'AskForPermissionsConsent',
permissions: options.permissions
};
}
let returnResult = {
version: '1.0',
response: alexaResponse
};
if (options.sessionAttributes) {
returnResult.sessionAttributes = options.sessionAttributes;
}
return returnResult;
}
function getDialogDirectives(dialogType, updatedIntent, slotName) {
let directive = {
type: dialogType
};
if (dialogType === 'Dialog.ElicitSlot') {
directive.slotToElicit = slotName;
} else if (dialogType === 'Dialog.ConfirmSlot') {
directive.slotToConfirm = slotName;
}
if (updatedIntent) {
directive.updatedIntent = updatedIntent;
}
return [directive];
}

Do you get an error message? My feeling is that the handler itself needs to be async too.
I solved it like this
const getSpeechOutput = async function (version) {
const gadata = await ga.getGA(gaQueryUsers, 'ga:users')
let speechOutput;
...
return speechOutput
}
const UsersIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'UsersIntent';
},
async handle(handlerInput) {
try {
const speechOutput = await getSpeechOutput("long");
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard(defaulttext.SKILL_NAME, speechOutput)
.getResponse();
} catch (error) {
console.error(error);
}
},
};
module.exports = {
UsersIntentHandler,
getSpeechOutput
}
That the function with the actual api call:
const getGA = async (gaQuery,requestResult) => {
try {
jwt.authorize()
const result = await google.analytics('v3').data.ga.get(gaQuery)
return result.data.totalsForAllResults[requestResult];
} catch (error) {
throw error
}2
}

Related

DirectLine response is not included in function response, execution completes too quickly

I am trying to send a message and get a response using " directLine.activity$" -- although my function execution completes before the directLine.activity$ detects a "received activity".
Below is my entire code. I'm just wondering, how can I wait for the WebSocket to receive at least 1 "received activity" before processing the response.
When I run this code it just quickly executes and says "Executed 'Functions.twilioagogo' (Succeeded, Id=xxxxxx-c1a9-4092-b740-xxxxxx, Duration=272ms)"
const express = require('express');
const { MessagingResponse } = require('twilio').twiml;
const app = express();
//DirectLine
global.XMLHttpRequest = require('xhr2');
global.WebSocket = require('ws');
const { DirectLine } = require('botframework-directlinejs');
const { twiml } = require('twilio');
module.exports = async function (context, req) {
//global variables
var myPhone
var myActivity
var responseCount = 0
const twiml = new MessagingResponse();
var directLine = new DirectLine({
secret: 'xxxxxxxxxxxxxxxxxx',
});
async function myfunction() {
console.log('Inside of myfunction');
console.log("start")
const name = (req.query.name || (req.body && req.body.name));
// const responseMessage = name
// ? "Hello, " + name + ". This HTTP triggered function executed successfully."
// : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";
//new
// twiml.message('The Robots are coming! Head for the hills!');
//get from phone #
var myInfo = req.body.toString().split("&")
for (i = 0; i < myInfo.length; i++) {
if (myInfo[i].toString().includes("From=")) {
myPhone = myInfo[i].toString()
}
}
}
async function myfunction2() {
console.log('Inside of myfunction2');
console.log("start2")
directLine.postActivity({
from: { id: '123'}, // required (from.name is optional)
type: 'message',
text: 'a message for you, Rudy'
}).subscribe(
id => {console.log("Posted activity, assigned ID ", id)
directLine.activity$
.subscribe(
activity => {
if (activity.from.id === "tbot1000v5") {
console.log("received activity ", activity.text)
myActivity = activity.text
console.log("myactivity here " + myActivity)
responseCount++
console.log("ResponseCount is " + responseCount)
twiml.message(myActivity);
console.log("here")
} //end if
}
)
},
error => console.log("Error posting activity", error)
)
} //end myfunction2
async function myfunction3() {
console.log('Inside of myfunction3');
console.log("start3")
}
function start() {
return myfunction();
}
function start2() {
return myfunction2();
}
function start3() {
return myfunction3();
}
// Call start
(async() => {
console.log('before start');
await start();
await start2();
await start3();
console.log("send response begin")
context.res = {
// status: 200, /* Defaults to 200 */
// body: 'The Robots are coming! Head for the hills!'
headers: { 'Content-Type': 'application/xml' },
body: twiml.toString()
};
})();
}

Async Firebase Cloud Function trigger - What to return on catch block?

I have written the trigger below and I'm not sure what I should return in case a catch block is called. I know that Firebase docs say that triggers should always return a Promise...
exports.sendPushNotificationForNewMessage = functions.firestore.document("messages/{messageId}").onCreate(async (snap, context) => {
const message = snap.data()
const chatRoomId = message.chatRoomId
const senderId = message.user.id
const senderUsername = message.user.username
try {
const chatRoom = await admin.firestore().collection("chatRooms").doc(chatRoomId).get()
const receiverId = chatRoom.data().userIds.find(userId => userId != senderId)
const receiver = await admin.firestore().collection("users").doc(receiverId).get()
const deviceToken = receiver.data().deviceToken
if (deviceToken) {
const payload = {
notification: {
title: "popster",
body: `New DM from ${senderUsername} 💬`,
badge: "1",
sound: "pop.m4a"
},
data: {
}
}
console.log(payload);
return admin.messaging().sendToDevice(deviceToken, payload)
} else {
return null
}
} catch (error) {
return null
}
})
The async function wraps your response in a Promise so here your return type is Promise<MessagingDevicesResponse | null> and that will terminate the Cloud Function.
I'm not sure what I should return in case a catch block is called.
Background functions do not return any value/error to client so you can just return null;.
Also checkout this Firecast for more information.

Send web push notifications to specific users conditionally

I am willing to use web-push notifications on my web app. I have already setup serviceWorkers on the front-end(React) and implemented web-push notifications on my backend(NodeJS). Now I just need to send notifications which are user specific, means only specific users should receive those notifications.
For e.g. In my web app's backend I will be receiving some live values. Say, there is a collection named
"users" where all the user's data will be stored. Now these users will have a field named "device" where the user will receive numeric values which will be updated within 40-50 seconds.
Now, their will be a threshold for these values. Say, for e.g. if the value reaches above 200 then that specific user should receive a push notification, letting them know that the device has reached it's limit.
How is it possible for me to create such user specific push notifications where the notification will be sent to only that user who's device value has reached above 200 ?. P.S I am using Mongoose for the database.
FrontEnd code(react.js)
sw.js:
self.addEventListener("notificationclick", function (event) {
// on Notification click
let notification = event.notification;
let action = event.action;
console.log("Notification====>", notification);
if (action === "confirm") {
console.log("Confirm clicked");
notification.close(); // Closes the notifcation
} else {
event.waitUntil(
clients.matchAll().then(function (clis) {
var client = clis.find(function (c) {
return c.visibilityState === "visible";
});
if (client !== undefined) {
// found open window
client.navigate("http://localhost:3000"); // means website opens on the same tab where user is on
client.focus();
} else {
// if client's window was not open
clients.openWindow("http://localhost:3000"); // when browser window is closed, open website
}
notification.close();
})
);
console.log(action); // name of action, basically id
}
});
self.addEventListener("notificationclose", function (event) {
console.log("Notification closed", event);
});
// triggers when we get an incoming push message
self.addEventListener("push", function (event) {
console.log("Push notifications recieved from eventListner", event);
var data = { title: "New!", content: "New things" };
if (event.data) {
// check if payload exists(from backend)
data = JSON.parse(event.data.text()); // recieve payload & store
}
var options = {
body: data.content,
icon: "https://iconarchive.com/download/i90141/icons8/windows-8/Cinema-Avengers.ico",
tag: "id1",
renotify: true,
};
event.waitUntil(self.registration.showNotification(data.title, options));
});
swReg.js:
if ("serviceWorker" in navigator) {
console.log("Registering service worker");
navigator.serviceWorker
.register("/sw.js")
.then(() => {
console.log("Service Worker has been registered");
})
.catch((err) => console.error(err));
}
function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function displayConfirmNotification() {
if ("serviceWorker" in navigator) {
const options = {
body: "After subscription managing done",
// icon: "/src/assets/img/pattern_react.png",
// tag:"" ==> in advanced options.
vibrate: [100, 50, 200],
// badge:""
tag: "confirm",
renotify: true,
actions: [
{ action: "confirm", title: "okay" }, // optnl icon:""
{ action: "cancel", title: "cancel" },
],
};
navigator.serviceWorker.ready.then(function (swreg) {
swreg.showNotification("Successfully subscribed sW", options);
});
}
}
function configPushSub() {
if (!("serviceWorker" in navigator)) {
return;
}
var reg;
navigator.serviceWorker.ready
.then(function (swreg) {
// access to sW registration
reg = swreg;
return swreg.pushManager.getSubscription(); // returns any existing subscription
})
.then(function (sub) {
// sub holds the current subscription, if subscription doesn't exist then it returns null
if (sub === null) {
// Create a new subscription
var vapidPublicKey = KEY;
var convertedPublicKey = urlBase64ToUint8Array(vapidPublicKey);
return reg.pushManager.subscribe({
userVisibleOnly: true, // for security
applicationServerKey: convertedPublicKey, // for security & server storage
}); // create new subscription
} else {
// We already have a subscription
}
})
.then(function (newSub) {
// have to pass this subscription(new one) to backend
console.log("New subb =======>", newSub);
return fetch("http://localhost:8000/subscribeToPushNotifications", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
subscriptionObj: newSub,
}),
});
})
.then(function (res) {
if (res.ok) {
displayConfirmNotification();
}
})
.catch(function (e) {
console.log("err while subbing====>", e);
});
}
function askForNotificationPermission() {
Notification.requestPermission(function (result) {
console.log("User's choice", result);
if (result !== "granted") {
console.log("Permission rights not granted");
} else {
configPushSub();
// displayConfirmNotification();
}
});
}
if ("Notification" in window) {
askForNotificationPermission();
}
Backend:
API to subscribe:
exports.subscribeToPushNotifications = async (req, res) => {
const { subscriptionObj } = req.body;
// console.log("Subscription object=====>", subscriptionObj);
if (subscriptionObj != undefined || subscriptionObj != null) {
let newSubscription = new Subscription({
pushSubscription: subscriptionObj,
});
await newSubscription.save();
if (newSubscription) {
console.log(newSubscription);
return res.status(200).send("Subscription made");
} else {
console.log("Not subbed");
return res.status(400).send("Subscription not made");
}
} else {
console.log("Sub obj is null");
return res.status(400).send("Sub obj was null");
}
};
Checking if values are more than the threshold and then sending notification:(For example purposes). This is an example for single user only.
exports.checkStatus = async () => {
schedule.scheduleJob("*/10 * * * * *", async () => {
let subscriptions = await Subscription.find({});
let Email = "james#mail.com";
let findUser = await User.findOne({ Email });
if (findUser) {
if (findUser.device > 200) // findUser.device contains the value
{
for (let i = 0; i < subscriptions.length; i++) { //Notification will be sent to all users which I don't want.
webpush.sendNotification(
subscriptions[i].pushSubscription,
JSON.stringify({
title: "Alert",
content: "Value has reached it's limit",
})
);
}
}
}
});
};
How can I make this work such that only those users who's device's value has gone above 200 will only receive the notification and not all the subscribed users.

Session expiring in Dialogflow

I came to know that context expires in 15 minutes but is there any way to solve it manually i.e by storing the previous conversation in dB so can we handle that session expiring issue or else the whole conversation(output context) under that session ID will get clear and need to start from the first.
exports.fulfillmenttext = functions.https.onRequest((req,res) =>{
const answer1 = req.body.Text;
console.log("Text said by the user",answer1);
const uid = answer1.substring(0,28);
console.log("uid1 is",uid);
const answer = answer1.substring(28);
console.log("answer is",answer);
const sessionId = uid;
var count,questvalue;
runSample();
async function runSample(projectId = 'xxxxxxx') {
const languageCode = 'en-US';
const credentials = {
client_email: 'xxxxxxxxxx',
private_key: 'xxxxxxxxx'
};
//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: sessionPath,
queryInput: {
text: {
text: answer,
languageCode,
},
},
};
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
let action = result.action;
console.log("action is"+action);
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
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('User').doc(uid).collection("Popop").doc(uid).collection('Answers').doc('Earlyyears').update(data1);
},
'family': () => {
console.log('familyaction1', action);
let mname1 = JSON.stringify(result.parameters.fields.M_Name.stringValue);
let mname_string = mname1.toString().replace(/"/g,"");
var data20 = {
MName: mname_string
};
var setDoc20 = admin.firestore().collection('User').doc(uid).collection("Popop").doc(uid).collection('Answers').doc('Family').update(data20);
}
};
if (action === 'early') {
console.log('1');
actionHandlers[action]();
}
else if (action === 'family') {
console.log('2');
actionHandlers[action]();
}
res.status(200).send({"question":result.fulfillmentText,"action":action});
} else {
console.log(` No intent matched.`);
res.status(400).send({"action":"empty"});
}
}
});
I stumbled upon this problem as well. My solution was to save the userID and save the contexts to Firestore.
UPDATE:
This is how I stored Dialogflow's contexts in Firestore:
function saveContexts(userId, contexts) {
let UID = userId;
//get all contexts + parameters
if (contexts === undefined) {
console.log("contexts are undefined! returning");
return false;
}
db.collection("user-contexts-prod").doc(UID).set({
dateCreated: new Date(),
contexts: JSON.stringify(contexts)
})
.then(function () {
console.log("success!");
return true;
})
.catch(function (error) {
console.log("error writing document..", error);
return false;
});
}
Retrieving user contexts:
async function getContexts(userId) {
let UID = userId;
let docRef = db.collection("user-contexts-prod").doc(UID);
return docRef.get()
.then(res => {
if (res.exists) {
let contexts = JSON.parse(res.data().contexts);
console.log("<><> parsed contexts <><>: ");
console.log(contexts);
return contexts;
} else {
console.log(" UID DOES NOT EXIST!");
return false;
}
})
}
You can set the contexts again by looping over them and using the contextClient to create new contexts. Or use this method to loop through the contexts and find the one you need:
contexts.forEach(function(context) {
if (context.name === 'projects/{DIALOGFLOWPROJECTID}/agent/sessions/' + senderId + '/contexts/{CONTEXTNAME}') {
sessionData = context.parameters;
// all data that you saved in CONTEXTNAME is now available in the sessionData variable
}
});
Original answer:
Whenever a user started talking that didn't have any active contexts I check if I had the userID stored in my Database. If this user existed in my DB I retrieved the user information with all his data like this:
knownUser = await db.isKnownUser(senderId);
if (knownUser) {
//knownUser
console.log("Known user");
let userData = db.getUserDataById(senderId)
//initialize contexts with data you need
payload = returningUser_useSameData();
messenger.send(payload, senderId);
dashbot.logBotMessage(payload.toString, sessionId, intentName);
break;
} else {
//newUser
console.log("new user");
createContext('await_fillInTogether', '', sessionPath, sessionId, 1);
createContext('session', '', sessionPath, sessionId, 500);
payload = fillInTogetherNewUser();
messenger.send(payload, senderId);
dashbot.logBotMessage(payload.toString, sessionId, intentName);
break;
}

How to override feathersjs defaults service methods

I have a feathersjs service created using the feathers generate service command. I want to override its definition of create method.
This is my service class
/* eslint-disable no-unused-vars */
// Initializes the `userGroup` service on path `/usergroup`
const createService = require('feathers-sequelize');
const createModel = require('../../models/user-group.model');
const hooks = require('./user-group.hooks');
const filters = require('./user-group.filters');
const async = require('async');
module.exports = function () {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'usergroup',
Model,
paginate,
create: fnCreateGroup // documentation says this allows you to create your own create method
};
function fnCreateGroup(data, params) {
let json = {
done: false
};
let permissionIds = Object.keys(data.permissionList), inIds = [];
for (let i in permissionIds) {
if (data.permissionList[permissionIds[i]]) {
inIds.push(parseInt(permissionIds[i]));
}
}
if (inIds.length === 0) {
json.cause = 'You must provide the permission List';
return Promise.resolve(json);
}
async.parallel([
function (cb) {
Model.create({
groupName: data.groupName
}).then(function (ug) {
cb(null, ug);
}, function (err) {
cb(err, null);
});
},
function (cb) {
app.models.permissions.findAll({
where: {
id: {
$in: inIds
}
}
}).then(function (plist) {
cb(null, plist);
});
}
]
, function (err, results) {
if (typeof err !== 'undefined' && err !== null) {
json.err = err;
return Promise.resolve(json);
} else {
let permissions = results[1], group = results[0];
for (let i = 0; i < permissions.length; i++) {
group.addPermission(permissions[i]);
}
json.done = true;
json.id = group.id;
return Promise.resolve(json);
}
});
}
// Initialize our service with any options it requires
app.use('/usergroup', createService(options));
// Get our initialized service so that we can register hooks and filters
let service = app.service('usergroup');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
The test on create method shows the result of original method and not mine.
What can I do?
How to override and extend existing services is documented in the database adapter common API. You can use hooks by setting hook.result or extend the existing ES6 class:
const Service = require( 'feathers-sequelize').Service;
class MyService extends Service {
create(data, params) {
data.created_at = new Date();
return super.create(data, params);
}
}
app.use('/todos', new MyService({
paginate: {
default: 2,
max: 4
}
}));

Resources