Problems getting slot in AWS Lambda function - javascript

I am trying to build a simple Alexa Skill. Now, I want to access some of my slot values. This is my code. When I uncomment even the line that I define Alexa in, my skill will not work. Also, if I only uncomment the line defining var text, I still get "there was a problem with the skills response". Const gives the same output. I am using custom slots called recipe. How can I access the slots in my lambda function? Thanks.
const breakfast = {
"bacon and eggs":["bacon","egg"],
"buttered toast":["bread", "butter"]
};
const lunch = {
"ham sandwich":["ham","cheese"]
};
const dinner = { "Steak and eggs": ['steak','eggs']};
//const Alexa = require('ask-sdk-core');
exports.handler = (event, context, callback) => {
try {
if (event.request.type === 'LaunchRequest') {
callback(null, buildResponse('Hello from Lambda'));
} else if (event.request.type === 'IntentRequest') {
const intentName = event.request.intent.name;
if (intentName === 'breakfast') {
callback(null, buildResponse(Object.keys(breakfast)+"") );
}
else if (intentName === 'lunch') {
callback(null, buildResponse(Object.keys(lunch)+"") );
}
else if (intentName === 'dinner') {
callback(null, buildResponse(Object.keys(dinner)+"") );
}
else if (intentName ==='requestRecipe'){
//var text = this.event.request.intent.slots.recipe.value;
//const meal = Alexa.getSlotValue(intentName, "meal")
callback(null, buildResponse("Recipe requested") );
}
else {
callback(null, buildResponse("Sorry, i don't understand"));
}
} else if (event.request.type === 'SessionEndedRequest') {
callback(null, buildResponse('Session Ended'));
}
} catch (e) {
context.fail(`Exception: ${e}`);
}
};
function buildResponse(response) {
return {
version: '1.0',
response: {
outputSpeech: {
type: 'PlainText',
text: response,
},
shouldEndSession: false,
},
sessionAttributes: {},
};
}

for a bit of context: my lambda has the endpoint of what my alexa hosted skill was, and the alexa skill has the endpoint of the lambda. when I say const gives the same output, i mean instead of using var, when I use const, it does the same thing. The JSON file that i get as a reply is empty brackets.
I found the issue behind my problem. Instead of using
//var text = this.event.request.intent.slots.recipe.value;
i simply did var text = event.request.intent.slots.recipe.value; I am now able to use text in building a response or something like that.

Related

Axios Async Await Function returns 'undefined' results (Using while Loop)

I am trying to get data from an api using axios.
I am first getting the token, and then using the token to make the request. Since there is a limit on how much information can be responded, I have to use a while loop to get all the data and store it all to an empty array.
However, I am getting a bunch of 'undefined', I read other similar articles online with regard to this return, and most of them is because of "no return", but since I am using a while loop, where can I return the data?
const getDailySales = async (req, res) => {
try {
const res_token = await axios.post(
`https://cysms.wuuxiang.com/api/auth/accesstoken?appid=${process.env.TCSL_APPID}&accessid=${process.env.TCSL_ACCESSID}&response_type=token`
);
const token = res_token.data.access_token;
var list = [];
var pageTotal = true;
var pageNo = 1;
while (pageTotal) {
var salesData = await axios.post(
`https://cysms.wuuxiang.com/api/datatransfer/getserialdata?centerId=${process.env.TCSL_CENTERID}&settleDate=2022-09-30&pageNo=${pageNo}&pageSize=20&shopId=12345`
{},
{
headers: {
access_token: `${token}`,
accessid: `${process.env.TCSL_ACCESSID}`,
granttype: "client",
},
}
);
list.push(salesData);
console.log(salesData.data.data.billList.shop_name);
if (salesData.data.data.pageInfo.pageTotal !== pageNo) {
pageNo += 1;
} else {
pageTotal = false;
}
}
} catch (error) {
console.log(error);
}
};
Above implementation would work.
Return list just before catch and after ending of the while loop
} else {
pageTotal = false;
}
}
return list;
} catch (error) {
console.log(error);
}
};
Few suggestions
Use let/const instead of var.
Can elaborate more error handling
return list like this:
return res.status(200).json({list});

How to read multiple json file using fs and bulk request

I'm using elasticsearch search engine with my react app, I was reading one file at the backend as you see in the code and it work perfectly, but now I want to read three different JSON files to three different indexes using the "fs" package and bulk request, can you please help me?
the code:
// Start reading the json file
fs.readFile("DocRes.json", { encoding: "utf-8" }, function (err, data) {
if (err) {
throw err;
}
// Build up a giant bulk request for elasticsearch.
bulk_request = data.split("\n").reduce(function (bulk_request, line) {
var obj, ncar;
try {
obj = JSON.parse(line);
} catch (e) {
console.log("Done reading 1");
return bulk_request;
}
// Rework the data slightly
ncar = {
id: obj.id,
name: obj.name,
summary: obj.summary,
image: obj.image,
approvetool: obj.approvetool,
num: obj.num,
date: obj.date,
};
bulk_request.push({
index: { _index: "ncar_index", _type: "ncar", _id: ncar.id },
});
bulk_request.push(ncar);
return bulk_request;
}, []);
// A little voodoo to simulate synchronous insert
var busy = false;
var callback = function (err, resp) {
if (err) {
console.log(err);
}
busy = false;
};
// Recursively whittle away at bulk_request, 1000 at a time.
var perhaps_insert = function () {
if (!busy) {
busy = true;
client.bulk(
{
body: bulk_request.slice(0, 1000),
},
callback
);
bulk_request = bulk_request.slice(1000);
console.log(bulk_request.length);
}
if (bulk_request.length > 0) {
setTimeout(perhaps_insert, 100);
} else {
console.log("Inserted all records.");
}
};
perhaps_insert();
});
You can create multiple promises for each file read and feed it to the elastic search bulk_request.
const fsPromises = require('fs').promises,
files = ['filename1', 'filename1'],
response = [];
const fetchFile = async (filename) => {
return new Promise((resolve, reject) => {
const path = path.join(__dirname, filename);
try {
const data = await fsPromises.readFile(path)); // make sure path is correct
resolve(data);
} catch (e) {
reject(e)
}
});
files.forEach((fileName) => results.push(fetchFile()));
Promise.all(results).then(data => console.log(data)).catch(e => console.log(e));
}
Once you get data from all the promises pass it to the elastic search.

How do I specify which API data I want to use?

I am developing an Alexa Skill (using v2 and javascript) and I'm attempting to make an API GET call to the USDA API.
At this point I've copy/pasted from here and I'm using the USDA API example from here (with some minor changes). as an attempt just to get the connection to work and return anything that proves it worked.
The error I currently receive is: Error handled: TypeError: Cannot read property 'generalSearchInput' of undefined at Object.handle (/var/task/index.js:39:32)
at process._tickCallback (internal/process/next_tick.js:68:7)
Directly before the error I get the return of: every entry about Cottage Cheese that is in the USDA API, I.E. WAY too much from the console.log(response). I just want the first one, or even just the name.
Console logs have told me that I am receiving data back from the call, so I know it is working. My question is: How do I set the speechOutput to the specific piece of information I need, rather than the entire API object that is returned?
The speechOutput that I'm looking for should say: Cottage Cheese
The response that I am receiving is: Output from Alexa saying:
Sorry, I had trouble doing what you asked. Please try again
index.js
const Alexa = require('ask-sdk-core');
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'Welcome to food points! What food would you like to know about?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const FoodPointsIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'FoodPointsIntent';
},
async handle(handlerInput) {
console.log("THIS.EVENT = " + JSON.stringify(this.event));
var speakOutput = 'Sorry, there was an error';
//var https = require('https');
const { requestEnvelope } = handlerInput;
const userInput = Alexa.getSlotValue(requestEnvelope, 'FoodQuery');
const response = await httpGet();
console.log(response);
/*
const food = userInput;
speakOutput = food;
*/
speakOutput = response.value.generalSearchInput;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
const TestIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'TestIntent';
},
handle(handlerInput) {
console.log("THIS.EVENT = " + JSON.stringify(this.event));
var speakOutput = 'Sorry, there was an error';
const { requestEnvelope } = handlerInput;
const userInput = Alexa.getSlotValue(requestEnvelope, 'TestQuery');
const food = userInput;
speakOutput = food;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
function httpGet() {
return new Promise(((resolve, reject) => {
var options = {
host: 'api.nal.usda.gov',
port: 443,
path: '/fdc/v1/foods/search?api_key=DEMO_KEY&query=Cheddar%20Cheese',
method: 'GET',
};
var https = require('https');
const request = https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
}
/****************************REMEMBER TO UPDATE THIS*************************/
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speakOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
},
handle(handlerInput) {
const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
const speakOutput = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.stack}`);
const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
// The SkillBuilder acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
FoodPointsIntentHandler,
TestIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addErrorHandlers(
ErrorHandler,
)
.lambda();
I figured it out. Hopefully this can help somebody who may be stuck.
I was calling the API data incorrectly.
The code that did what I needed
var nvArray = [];
for(var i = 0; i < response.foods[0].foodNutrients.length; i++) {
var nvArrayString = [response.foods[0].foodNutrients[i].nutrientName] + '';
var nvShortString = nvArrayString.split(',', 1);
nvArray.push(nvShortString);
}
var nvArraySpeakOutputString = nvArray.join();
The actual piece of code that fixed my errors
response.foods[0].foodNutrients[i].nutrientName
I was looking at the wrong part of the data being given to me, and thus calling something that didn't exist, i.e. response.value.generalSearchInput.
Additional information that may be helpful
nvArray is used so that I can put multiple variables from the data into one place (using push and pop).
This allowed me to split the data into shorter pieces each time the loop runs (used to keep the output from being a wall of text for each item).
nvArraySpeakOutputString is the nvArray joined together, allowing Alexa to speak it all as one variable, preventing me from having to type
varA + '. next ' + varB
and so on for each item.
Anyway, hope this helps somebody at some point!

Firebase: Firestore transactions not defined?

I'm new to Firebase and am reading the docs to learn.
I am currently using Firestore instead of the Database and to be honest, I am not super sure about the pro / cons of each.
In their tutorials for reading and writing data to a Database they have the following code regarding transactions:
function toggleStar(postRef, uid) {
postRef.transaction(function(post) {
if (post) {
if (post.stars && post.stars[uid]) {
post.starCount--;
post.stars[uid] = null;
} else {
post.starCount++;
if (!post.stars) {
post.stars = {};
}
post.stars[uid] = true;
}
}
return post;
});
}
which is meant to mitigate race conditions / corruption to the variable stars in this case.
My question is what is the Firestore equivalent of transaction e.g.
import firebase from 'firebase'
const postId = 1
const firestorePostRef = firebase.firestore().collection('posts').doc(postId)
// throws an error that firestorePostRef.transaction is not defined
firestorePostRef.transaction( (post) => {
if (post) {
// ...
}
})
Firebase Firestore has the same functionality. Read data and at the same operation write over something like :
// Create a reference to the SF doc.
var sfDocRef = db.collection("cities").doc("SF");
db.runTransaction(function(transaction) {
return transaction.get(sfDocRef).then(function(sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
var newPopulation = sfDoc.data().population + 1;
if (newPopulation <= 1000000) {
transaction.update(sfDocRef, { population: newPopulation });
return newPopulation;
} else {
return Promise.reject("Sorry! Population is too big.");
}
});
}).then(function(newPopulation) {
console.log("Population increased to ", newPopulation);
}).catch(function(err) {
// This will be an "population is too big" error.
console.error(err);
});
Here the related documents link

wit.ai Bot Engine Stories connected to hubot

I am trying to get the new wit.ai Bot Engine connected to hubot with Javascript.
Unfortunately I am not a JS Developer so I'm struggling.
Here's the code I have:
'use strict';
const Wit = require('../../../node-wit').Wit;
const firstEntityValue = (entities, entity) => {
const val = entities && entities[entity] &&
Array.isArray(entities[entity]) &&
entities[entity].length > 0 &&
entities[entity][0].value
;
if (!val) {
return null;
}
return typeof val === 'object' ? val.value : val;
};
const actions = {
say: (sessionId, msg, cb) => {
console.log(msg);
cb();
},
merge: (context, entities, cb) => {
const loc = firstEntityValue(entities, "location");
if (loc) context.loc = loc;
cb(context);
},
error: (sessionId, msg) => {
console.log('Oops, I don\'t know what to do.');
},
'fetch-weather': (context, cb) => {
// Here should go the api call, e.g.:
// context.forecast = apiCall(context.loc)
context.forecast = 'sunny';
cb(context);
},
};
const client = new Wit('MY_TOKEN_HERE', actions);
client.interactive();
module.exports = function(robot) {
robot.respond(/hey\s+(.+$)/i, function(msg){
var match = msg.match[1];
msg.send("I've heared: " + match);
console.log(match)
process.stdout.write(match);
});
}
The script listens to "hey botname" and outputs what was written after this. My Problem is I have no clue how to get this input sent to the wit client. Using this script in bash without the hubot stuff works fine towards wit as this is based on the quick start example from wit.ai.
The other issue I'm facing is that I would like to have hubot listen in a private channel with every user and have it respond to every message without prefix. Just as the node example does in the console.
Help is highly apprechiated!
Ok, after fiddling a while I made this work.
Here's what my hubot script looks like now:
'use strict';
const Wit = require('../../../node-wit').Wit;
var room;
const firstEntityValue = (entities, entity) => {
const val = entities && entities[entity] &&
Array.isArray(entities[entity]) &&
entities[entity].length > 0 &&
entities[entity][0].value
;
if (!val) {
return null;
}
return typeof val === 'object' ? val.value : val;
};
const actions = {
say: (sessionId, msg, cb) => {
console.log(msg);
room.send(msg)
cb();
},
merge: (context, entities, cb) => {
const loc = firstEntityValue(entities, "location");
if (loc) context.loc = loc;
cb(context);
},
error: (sessionId, msg) => {
console.log('Oops, I don\'t know what to do.');
room.send('Oops, I don\'t know what to do.')
},
};
const client = new Wit('MY_TOKEN_HERE', actions);
//client.interactive();
module.exports = function(robot) {
robot.listen(function(msg) {
var userID = msg.user.id;
var roomID = msg.user.room;
// is this a direct chat(private room)?
if(roomID.indexOf(userID) >= 0){
if(typeof msg.text == "string"){
client.pxMessage(msg.text);
}
}
return true;
},
function(response){
// save room for replys
room = response;
});
}
additionally I made an awful hack to wit.js to get this work. I added the following function as I was not able to use the available methods to get this working. Basically callbacks and session were holding me back:
this.pxMessage = (message) => {
const sessionId = uuid.v1();
const context = {};
const steps = 5;
this.runActions(
sessionId,
message,
context,
(error, context) => {
if (error) {
l.error(error);
}
rl.prompt();
},
steps
);
}
If someone would take this further and implement it properly I would love to see the result. This hack works and we now have a really smart bot in our rocket.chat who understands natural language and learns every day.
You can directly use this module, it seems to be working well: https://www.npmjs.com/package/hubot-wit
I have just now finished integration. You just need to provide the environment variable WIT_TOKEN and it works beautifully!

Categories

Resources