Unable to process alexa intent - node.js

My code is not running, can anybody help.
Unable to speak out the text, can i return handler input response. The test function is a http call which may take tieme.
function test(url, number)
{
return 5;
}
function speak(handlerInput) {
return handlerInput.responseBuilder
.getResponse();
}
const NumberFactIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'NumberFactIntent';
},
handle(handlerInput) {
const theNumber = handlerInput.requestEnvelope.request.intent.slots.number.value;
const repromptOutput = " Would you like another fact?";
const URL = "http://numbersapi.com";
test(URL, theNumber).then(function (data) {
console.log("data is " + data);
handlerInput.responseBuilder
.speak("Test Data")
.reprompt(repromptOutput)
return speak(handlerInput);
}).catch(function (data) {
console.log("error is " + data);
handlerInput.responseBuilder
.speak(`I wasn't able to find a fact for ${theNumber}` )
.reprompt(repromptOutput)
return speak(handlerInput);
});
}
};

First of all your test function doesn't return a promise. I don't know if this is intentional and you just cut api calling code to make it simpler, but it should return a promise if you want to use then on it.
If it returns a promise in your full example, then what are you missing is adding a return before test. Also you should return handlerInput from inside of your promise. Code should look like this (i'll remove some of the code, that is not relevant):
const NumberFactIntentHandler = {
canHandle(handlerInput) {},
handle(handlerInput) {
const repromptOutput = " Would you like another fact?";
const URL = "http://numbersapi.com";
return test(URL, theNumber).then(function (data) {
return handlerInput.responseBuilder
.speak("Test Data")
.reprompt(repromptOutput)
}).catch(function (data) {
return handlerInput.responseBuilder
.speak(`I wasn't able to find a fact for ${theNumber}` )
.reprompt(repromptOutput)
});
}
};
Now you might be wondering why do you need those return's. This is because JS functions implicitly returns undefined, so in this case you have to explicitly tell what should be returned from handle function. Same applies to inside of the promise.

This Code might Help You!!
//use request for http call
function fun(url) {
return new Promise((resolve, reject) => {
request.get(url,(err, res, body) => {
console.log("url-fetched");
return resolve(body);
});
});
}
const NumberFactIntentHandler = {
canHandle(handlerInput) {..
},
async handle(handlerInput) {
const theNumber =handlerInput.requestEnvelope.request.intent.slots.number.value;
const repromptOutput = " Would you like another fact?";
const URL = "http://numbersapi.com";
let data = await fun(url);
return handlerInput.responseBuilder
.speak(data)
.reprompt('is there any thing i can do for you?')
.withSimpleCard('Hello', speechText)
.getResponse();
};

Related

how to return response of axios in async function [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I want to return the response of axios but get the console.log as undefined.
getFirstPageData();
async function getFirstPageData() {
const res = await getPageData(1) ;
console.log("res--->", res);
}
async function getPageData(page_num){
let pageNum = page_num;
console.log("page_num" , pageNum);
let path = "https://......"
axios.get(path).then(
(response) => {
var result = response.data;
console.log(result);
return result;
},
(error) => {
console.log(error);
}
);
}
Is this a problem in await or async. any way returning this response.
You'll need to return the promise that axios.get() returns, this will keep the promise chain intact.
I would also suggest throwing in the reject handler to propagate any error to the top level, again keeping the chain intact.
getFirstPageData();
async function getFirstPageData() {
try {
const res = await getPageData(1) ;
console.log("res--->", res);
} catch (error) {
console.error('An error occurred:', error);
}
}
async function getPageData(page_num){
let pageNum = page_num;
console.log("page_num" , pageNum);
let path = "https://......"
return axios.get(path).then(
(response) => {
var result = response.data;
console.log(result);
return result;
},
(error) => {
console.error(error);
throw error;
}
);
}
In light of my comment on your post and what you are trying to do.
Your getPageData should return the promise from axios like the following.
function getPageData(page_num){
let pageNum = page_num;
console.log("page_num" , pageNum);
let path = "https://......"
return axios.get(path).then(
(response) => {
var result = response.data;
console.log(result);
return result;
},
(error) => {
console.log(error);
}
);
}
Loose the async from the function getPageData since it is not an asynchronous function. it returns a promise that you will get data from in your const res = await getPageData(1) ;

How to return a promise using web sockets?

I have this function:
function authentification(jsonHeder: any): Promise<boolean> {
return new Promise(function(resolve, reject) {
const ws = new WebSocket('ws://localhost:8000');
if (!jsonHeder.email || !jsonHeder.password) reject(new Error('bad request'));
ws.onopen = async function() {
ws.send(JSON.stringify({
command: '-userLogin',
instanceIdentifier: jsonHeder.email,
password: jsonHeder.password
})
);
};
ws.onmessage = async function(message) {
const json = JSON.parse(message.data.toString());
if (json.isError === false) {
resolve();
} else {
reject(new Error('bad request'));
}
}
});
}
and I'm trying to get the approval for an authentification, and when I am trying to print the result I receive a Promise { }.
the call function is like this:
const result = authentification(jsonHeder);
console.log('result: ', result);
and when I'm trying to put await operator I received a compile error. What can I do?

ERROR: INVALID_RESPONSE - Data is returning from promise but can't get Alexa to speak/show card

I have two https requests in nodejs, the first to confirm a user is authorized to access the data, and the second is to return the data.
I have console logs throughout and I see the data is successfully being returned in the promise, but Alexa won't speak/show the card. There are no errors in cloudwatch.
I don't fully understand the promise syntax so I'm sure I'm missing something simple.
I've been changing up the syntax, trying async/await, and nothing seems to be working.
EDITED CODE - with some help, I was able to better lay out my code. I am now getting the error in cloud watch: ERROR: INVALID_RESPONSE, An exception occurred while dispatching the request to the skill.
Note: this error is occuring because right now I am FORCING an error, aka the top part (if redacted.errors).
messages.NO_ACCESS is currently set to ---- "Hmm, it seems like you don't have access to that data."
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
let resp, resultData;
return new Promise((resolve, reject) => {
checkAuthenticationStatus(handlerInput, function(json){
if(REDACTED.errors) {
console.log('unauthed', REDACTED.error)
reject(handlerInput.responseBuilder.speak(messages.NO_ACCESS).withSimpleCard('Unauthorized Request', messages.NO_ACCESS).getResponse());
} else if(REDACTED.noerror && REDACTED.noerror.data == 'true'){
const url = new URL(REDACTED.url);
const resp = httpsGetIntent(handlerInput, url, (theResult) => {
resultData = theResult;
console.log('resolved with ---->', d)
return resolve(handlerInput.responseBuilder.speak("The result was" + d).withSimpleCard('Hello World', d).getResponse());
})
}
})
});
},
};
Here's the part of the code where the data IS (resultData and d are the same thing and both return data) returned but she doesn't speak/show card:
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
let resp, resultData;
return new Promise((resolve, reject) => {
checkAuthenticationStatus(handlerInput, function(json){
if(REDACTED) {
reject(handlerInput.responseBuilder.speak(messages.NO_ACCESS).withSimpleCard('Unauthorized Request', messages.NO_ACCESS).getResponse())
} else if(REDACTED && REDACTED == 'true'){
const url = new URL(REDACTED);
resp = httpsGetIntent(handlerInput, url, (theResult) => {
resultData = theResult;
console.log(resultData)
}).then((d) => {
console.log(d)
resolve(handlerInput.responseBuilder.speak("The result was" + d.response.outputSpeech.text).withSimpleCard('Hello World', d.response.outputSpeech.text).getResponse());
}).catch((err) => { console.log(err)});
}
});
});
},
};
There are a few things you have to consider and are not clear in the question.
What is REDACTED here, where does that value come from ? you have not mentioned it anywhere.
Let's assume it's a global variable for the sake of convenience.
If REDACTED is false for any reason then your code is never gonna executed. Because the condition is never met. So that promise neither rejects nor resolves.
If am assuming you have mistakenly written REDACTED in the first if statement, it should be !REDACTED probably.
resp = httpsGetIntent(handlerInput, url, theResult => {
resultData = theResult;
console.log(resultData);
})
.then(d => {
console.log(d);
resolve(
handlerInput.responseBuilder
.speak("The result was" + d.response.outputSpeech.text)
.withSimpleCard("Hello World", d.response.outputSpeech.text)
.getResponse()
);
})
.catch(err => {
console.log(err);
});
Here the httpsGetIntent is also not correct, You are having a callback method which receives theResult and yet attaching a then method, which doesn't make any sense. Moreover resultData is not doing anything here.
Assuming that checkAuthenticationStatus and httpGetIntent both have a callback pattern, You could write something like this
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "IntentRequest";
},
handle(handlerInput) {
const {
requestEnvelope,
serviceClientFactory,
responseBuilder
} = handlerInput;
let resp, resultData;
return new Promise((resolve, reject) => {
checkAuthenticationStatus(handlerInput, function (json) {
if (!REDACTED) {
reject(
handlerInput.responseBuilder
.speak(messages.NO_ACCESS)
.withSimpleCard("Unauthorized Request", messages.NO_ACCESS)
.getResponse()
);
} else if (REDACTED && REDACTED == "true") {
const url = new URL(REDACTED);
httpsGetIntent(handlerInput, url, theResult => {
resultData = theResult;
console.log(resultData);
return resolve(
handlerInput.responseBuilder
.speak("The result was" + d.response.outputSpeech.text)
.withSimpleCard("Hello World", d.response.outputSpeech.text)
.getResponse()
);
})
}
});
});
}
};
In arrow notation, .then((d) => resolve(x)) will return the resolve, because the call to resolve is on the same line, but if you .then(() => { // more than one line }) I think you just need to explicitly call return.
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
// do you need resp here?
let resp, resultData;
return new Promise((resolve, reject) => {
checkAuthenticationStatus(handlerInput, function(json){
// does REDACTED know about json?
if(!REDACTED) {
reject(handlerInput.responseBuilder
.speak(messages.NO_ACCESS)
.withSimpleCard('Unauthorized Request', messages.NO_ACCESS)
.getResponse());
} else if(REDACTED && REDACTED == 'true') {
const url = new URL(REDACTED);
httpsGetIntent(handlerInput, url, (resultData) => {
console.log(resultData);
return resultData;
}).then((d) => {
resolve(handlerInput.responseBuilder
.speak("The result was" + d.response.outputSpeech.text)
.withSimpleCard("Hello World", d.response.outputSpeech.text)
.getResponse());
}).catch((err) => { console.log(err)});
}
});
});
}
},

Alexa responding before data is returned

I'm new to promises, async/await, and Alexa/lambda so bear with me.
My function is returning prior to the data being returned. I had a similar issue where I was getting an error, but have since edited my function quite a bit and therefore asking a new question. I'm now no longer getting an error, but instead my data is being returned first, then the promise is executing.
I've tried re-writing the promise/function after reading many, many SO, google, and amazon developer forums. Nothing seems to be working for me.
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
let responseData, promise;
checkAuthenticationStatus(handlerInput,function(json){
console.log('waited!')
if(json.error) {
return handlerInput.responseBuilder.speak(messages.NO_ACCESS).withSimpleCard('Unauthorized Request', messages.NO_ACCESS).getResponse();
} else if(json.noerror && json.noerror.okay == 'true'){
console.log('starting to get intent data')
const url = new URL(json.noerror.okay.path);
promise = new Promise((resolve, reject) => {
console.log('start promise')
return httpsGetIntent(handlerInput, url).then((resultData) => {
console.log(resultData)
responseData = resultData;
resolve(responseData)
console.log('inside promise, no error, prior to return data')
})
}).then((result) => { console.log('result', result)})
return handlerInput.responseBuilder.speak('Test').getResponse();
}
});
console.log('response data', responseData)
let result = await promise;
return result;
},
};
Out of my many console.logs() added for debugging, they print as follows:
- 'response data'
- 'waited!'
- 'starting to get intent data'
- 'start promise'
- resultData
- 'inside promise, no error, prior to return data'
While it's not fully fleshed out (I need to address the error handling portion) I wanted to share my solution here in case anyone stumbled upon this post and needed some help.
I moved the promise outside the checkAuthentication function and returned the data when it was processed. I then chained the promise with .then() and passed it the data returned, and prompted Alexa to speak.
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
let responseData, promise;
return new Promise((resolve, reject) => {
checkAuthenticationStatus(handlerInput, async function(json) {
if (json.error) {
return handlerInput.responseBuilder.speak(messages.NO_ACCESS).withSimpleCard('Unauthorized Request', messages.NO_ACCESS).getResponse();
} else if (json.noerror && json.noerror.okay == 'true') {
const url = new URL(json.noerror.okay.path);
let resultD = await httpsGetIntent(handlerInput, url, function(resultData) {
if (resultData) {
return resolve(resultData);
} else {
return resolve(handlerInput.responseBuilder.speak('False Test').getResponse());
}
})
}
})
}).then((data) => {
return handlerInput.responseBuilder.speak(data.response.outputSpeech.text).getResponse();
});
},
};
Almost_Ashleigh, based on your own answer, some ideas in which I have :
made the assumption that httpsGetIntent() returns a Promsie that delivers resultData.
promisified checkAuthenticationStatus() by writing the adaptor checkAuthenticationStatusAsync().
consolidated the .speak() commands into final .then() and .catch() clauses.
allowed specific errors to be tailored by decorating with a .title property, which is used in the final .catch() as a cardTitle. Any undecorated errors will default to 'Sorry' (or whatever you want).
// in a suitable scope ...
function checkAuthenticationStatusAsync(handlerInput) {
return new Promise((resolve, reject) {
checkAuthenticationStatus(handlerInput, (json) => {
if (json.error || !json.noerror || json.noerror.okay !== 'true') {
let err = new Error(messages.NO_ACCESS); // or maybe a separate error message per error case?
err.title = 'Unauthorized Request';
reject(err);
} else {
resolve(json);
}
});
});
}
// ... and ...
const IntentRequest = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
return checkAuthenticationStatusAsync(handlerInput)
.then((json) => httpsGetIntent(handlerInput, new URL(json.noerror.okay.path)))
.then((resultData) => {
if (resultData) {
// success path ends here
return handlerInput.responseBuilder.speak(resultData.response.outputSpeech.text).getResponse();
} else {
throw new Error('No result data returned'); // throws to the .catch() below
}
})
.catch(err => {
// all errors end up here.
return handlerInput.responseBuilder.speak(err.message).withSimpleCard(err.title || 'Sorry', err.message).getResponse();
throw err; // to keep handle's caller informed
});
},
};
Note, this is a way, not necessarily the way, to write the code. Please feel free to raid for ideas.
Your best friend is async/await. Please use something like this or like this to access APIs.

How to make an asynchronous api call for Alexa Skill application with a Lambda function?

I want to call an api from a Lambda function. My handler is triggered by an intent which includes two required slots. Therefore I don't know in advance whether I will be returning a Dialog.Delegate directive or my response from the api request. How do I promise these return values by the time the intent handler is called?
This is my handler:
const FlightDelayIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'MyIntent';
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
if (request.dialogState != "COMPLETED"){
return handlerInput.responseBuilder
.addDelegateDirective(request.intent)
.getResponse();
} else {
// Make asynchronous api call, wait for the response and return.
var query = 'someTestStringFromASlot';
httpGet(query, (result) => {
return handlerInput.responseBuilder
.speak('I found something' + result)
.reprompt('test')
.withSimpleCard('Hello World', 'test')
.getResponse();
});
}
},
};
This is my helper function which makes the request:
const https = require('https');
function httpGet(query, callback) {
var options = {
host: 'myHost',
path: 'someTestPath/' + query,
method: 'GET',
headers: {
'theId': 'myId'
}
};
var req = https.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', chunk => {
responseString = responseString + chunk;
});
//return the data when streaming is complete
res.on('end', () => {
console.log('==> Answering: ');
callback(responseString);
});
});
req.end();
}
So I suspect I will have to use promises and put an "async" in front of my handle function? I am very new to all of this so I don't know the implications of this, especially considering the fact that I have two different return values, one directly and the other one delayed. How would I solve this?
Thank you in advance.
As you suspected, your handler code is finishing before the asynchronous call to http.request, hence the Alexa SDK receives no return value from the handle function and will return an invalid response to Alexa.
I slightly modified your code to run it locally on a laptop to illustrate the issue :
const https = require('https');
function httpGet(query, callback) {
var options = {
host: 'httpbin.org',
path: 'anything/' + query,
method: 'GET',
headers: {
'theId': 'myId'
}
};
var req = https.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', chunk => {
responseString = responseString + chunk;
});
//return the data when streaming is complete
res.on('end', () => {
console.log('==> Answering: ');
callback(responseString);
});
});
req.end();
}
function FlightDelayIntentHandler() {
// canHandle(handlerInput) {
// return handlerInput.requestEnvelope.request.type === 'IntentRequest'
// && handlerInput.requestEnvelope.request.intent.name === 'MyIntent';
// },
// handle(handlerInput) {
// const request = handlerInput.requestEnvelope.request;
// if (request.dialogState != "COMPLETED"){
// return handlerInput.responseBuilder
// .addDelegateDirective(request.intent)
// .getResponse();
// } else {
// Make asynchronous api call, wait for the response and return.
var query = 'someTestStringFromASlot';
httpGet(query, (result) => {
console.log("I found something " + result);
// return handlerInput.responseBuilder
// .speak('I found something' + result)
// .reprompt('test')
// .withSimpleCard('Hello World', 'test')
// .getResponse();
});
console.log("end of function reached before httpGet will return");
// }
// }
}
FlightDelayIntentHandler();
To run this code, do not forget npm install http , then node test.js. It produces
stormacq:~/Desktop/temp $ node test.js
end of function reached before httpGet will return
==> Answering:
I found something {
"args": {},
"data": "",
...
So, the key is to wait for http get to return before to return a response to Alexa. For that, I propose to modify your httpGet function to return a promise instead using callbacks.
Modified code is like this (I kept your original code as comment)
const https = require('https');
async function httpGet(query) {
return new Promise( (resolve, reject) => {
var options = {
host: 'httpbin.org',
path: 'anything/' + query,
method: 'GET',
headers: {
'theId': 'myId'
}
};
var req = https.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', chunk => {
responseString = responseString + chunk;
});
//return the data when streaming is complete
res.on('end', () => {
console.log('==> Answering: ');
resolve(responseString);
});
//should handle errors as well and call reject()!
});
req.end();
});
}
async function FlightDelayIntentHandler() {
// canHandle(handlerInput) {
// return handlerInput.requestEnvelope.request.type === 'IntentRequest'
// && handlerInput.requestEnvelope.request.intent.name === 'MyIntent';
// },
// handle(handlerInput) {
// const request = handlerInput.requestEnvelope.request;
// if (request.dialogState != "COMPLETED"){
// return handlerInput.responseBuilder
// .addDelegateDirective(request.intent)
// .getResponse();
// } else {
// Make asynchronous api call, wait for the response and return.
var query = 'someTestStringFromASlot';
var result = await httpGet(query);
console.log("I found something " + result);
// return handlerInput.responseBuilder
// .speak('I found something' + result)
// .reprompt('test')
// .withSimpleCard('Hello World', 'test')
// .getResponse();
//});
console.log("end of function reached AFTER httpGet will return");
// }
// }
}
FlightDelayIntentHandler();
Running this code produces :
stormacq:~/Desktop/temp $ node test.js
==> Answering:
I found something{
"args": {},
"data": "",
...
end of function reached AFTER httpGet will return

Resources