using slots in skill intent to search dynamodb - amazon-web-services

I'm new to alexa, nodejs and coding in general but i am currently trying to create a skill to find machine states from my dynamodb table using date and time.
I currently have set up my skill to get alexa and lambda to understand my slot values but im not sure how i can use these values for a dynamodb query and have alexa call out the state for that corresponding time
My table is setup with a primary and sort key which are date and time and i have a third column for machine state.
I'm not sure if i should set up a custom slot for machine state as it would be easy to do since there are only 4 possible states.
Here is the code i currently have, please feel free to clean parts up or explain how you came to my solution.
const awsSDK = require('aws-sdk');
const updatedincident = 'updatedincident';
const docClient = new awsSDK.DynamoDB.DocumentClient();
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({
region: "'us-east-1'"
});
const params = {
TableName: "updatedincident",
Key:{ date: "2018-03-28",
time: "04:23",
state: "Blocked Primary"
}
};
let GetMachineStateIntent = (context, callback) => {
var params = {
TableName: "updatedincident",
Key: {
date: "2018-03-28",
time: "04:23",
state: "Blocked Primary"
}
};
dbClient.get(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from your database",
endSession: false
});
} else {
console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2));
// assuming the item has an attribute called "state"..
sendResponse(context, callback, {
output: data.Item.state,
endSession: false
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill, I can tell you about the status of machines at different times. what data are you looking for?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
if (request.type === "IntentRequest"
// make sure the name of the intent matches the one in interaction model
&& request.intent.name == "GetMachineStateIntent") {
var dateSlot = request.intent.slots.Date != null ?
request.intent.slots.Date.value : "unknown date";
var timeSlot = request.intent.slots.Time != null ?
request.intent.slots.Time.value : "unknown time";
// respond with speech saying back what the skill thinks the user requested
sendResponse(context, callback, {
output: "You wanted the machine state at "
+ timeSlot + " on " + dateSlot,
endSession: false
});
var ConfirmationHandlers = Alexa.CreateStateHandler(states.CONFIRMATIONMODE, {
'YesIntent': function () {
this.emit("GetMachineStateIntent");
},
'AMAZON.NoIntent': function () {
this.response.speak(GetMachineStateIntent);
this.emit(':responseReady');
}
});
}
let options = {};
if (request.intent.name === "GetMachineStateIntent") {
GetMachineStateIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened or states of machines in the past",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// an unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};
Update ************
Response im getting currently in Skill I/O in alexa
"request": {
"type": "IntentRequest",
"requestId": "amzn1.echo-api.request.c515c39e-4ce1-4f28-97ed-30536fa593b9",
"timestamp": "2018-05-15T08:55:25Z",
"locale": "en-GB",
"intent": {
"name": "GetMachineStateIntent",
"confirmationStatus": "NONE",
"slots": {
"Time": {
"name": "Time",
"value": "04:23",
"confirmationStatus": "NONE"
},
"Date": {
"name": "Date",
"value": "2018-03-28",
"confirmationStatus": "NONE"
}
}
},
"dialogState": "STARTED"
}
}

A couple of observations:
First
In the branch of code that handles the GetMachineStateIntent you had added code to create state handlers but they weren't properly wired. At best that code would do nothing, at worst it might actually cause some problems. Remove that.
// take the following lines of code out
var ConfirmationHandlers = Alexa.CreateStateHandler(states.CONFIRMATIONMODE, {
'YesIntent': function () {
this.emit("GetMachineStateIntent");
},
'AMAZON.NoIntent': function () {
this.response.speak(GetMachineStateIntent);
this.emit(':responseReady');
}
});
Second The query parameters that you are passing to your DynamoDB query are hard-coded. That means you are always going to get the same result. You need to pass the values of the slots your receive in the intent into the parameters for the query.
var params = {
TableName: "updatedincident",
Key: {
date: "2018-03-28",
time: "04:23",
state: "Blocked Primary"
}
};
Those are hard-coded. You only need to specify the primary key (date) and the sort key ('time'), so you can remove the state. And for date and time you have to change the values to be dynamically passed in from the dateSlot and timeSlot.
Third In the branch of code that handles requests of type IntentRequest you are handling the GetMachineStateIntent twice and the code is a bit redundant. Rewrite that as follows:
...
} else if (request.type === "IntentRequest") {
if (request.intent.name === "GetMachineStateIntent") {
GetMachineStateIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened or states of machines in the past",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
} else if (request.type === "SessionEndedRequest") {
Fourth
This is the most difficult to explain. When you query the machine state, you will be providing a date and a time but presumably, the machine state might not be stored in the database with a time stamp that matches the time value in your query exactly. So you have to do a query that is essentially the equivalent of "what is the machine state on date X, at the most recent time before or equal to Y"
It is that "most recent time before or equal to Y" part that is tricky. You have to create a query on your table that expresses that and you will also have to change the way you store timestamps in the table, from string, to a numeric format so that you can easily express this inequality.
I'm going to show here just how to pass the dateSlot and timeSlot to make the query but I recommend you look into getting this to work (perhaps ask specific questions if you get stuck).
Here's your code with the modifications I mentioned:
const awsSDK = require('aws-sdk');
const updatedincident = 'updatedincident';
const docClient = new awsSDK.DynamoDB.DocumentClient();
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({
region: "'us-east-1'"
});
let GetMachineStateIntent = (context, callback, dateSlot, timeSlot) => {
var params = {
TableName: "updatedincident",
KeyConditionExpression: '#d = :dVal and #t < :tVal',
ExpressionAttributeValues: {
':dVal': dateSlot,
':tVal': timeSlot
},
ExpressionAttributeNames: {
'#d': 'date',
'#t': 'time'
},
ScanIndexForward: false // gets values in reverse order by time
};
dbClient.query(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from your database",
endSession: false
});
} else {
let dataItem = data.Items[0];
console.log('loaded data item:\n' + JSON.stringify(dataItem, null, 2));
// assuming the item has an attribute called "state"..
sendResponse(context, callback, {
output: dataItem.state,
endSession: false
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill, I can tell you about the status of machines at different times. what data are you looking for?",
endSession: false
});
} else if (request.type === "IntentRequest") {
if (request.intent.name === "GetMachineStateIntent") {
var dateSlot = request.intent.slots.Date != null
? request.intent.slots.Date.value : null;
var timeSlot = request.intent.slots.Time != null
? request.intent.slots.Time.value : null;
// pass the slot values to the GetMachineStateIntent function
GetMachineStateIntent(context, callback, dateSlot, timeSlot);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened or states of machines in the past",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// an unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};

Related

Recall DynamoDB with Alexa query

I'm trying to create a skill with Alexa to read data from my DynamoDB table using either the scan or query function (or both).
The columns in my table are date, time and film name.
I'm new to this but I've managed to link my Lambda function to Alexa. I have also created a separate Lambda function that will recall data from my table when i configure test events, so when I input a specific date it will recall the corresponding film and time. However now I want to implement this into Alexa and am not sure how.
Here is my current code
console.log('Loading function');
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dclient = new AWS.DynamoDB.DocumentClient();
var getItems = (event, context, callback)=>{
dclient.get(event.params,(error,data)=>{
if(error){
callback(null,"error occurerd");
}
else{
callback(null,data);
}
});
};
exports.handler = getItems;
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
context.succeed(buildResponse({
speechText: "Welcome to H.S.S.M.I skill, what would you like to find",
repromptText: "I repeat, Welcome to my skill, what would you like to find",
endSession: false
}));
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "cinema") {
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
options.speechText = "ok, good bye.";
options.endSession = true;
context.succeed(buildResponse(options));
}
else if (request.intent.name === "AMAZON.HelpIntent") {
options.speechText = "My skill will read your table depending on what is asked. For example, you can ask what about a specific date. Please refer to skill description for all possible utterences.";
options.repromptText = "What is the data sign you want to know about today? If you want to exit from my skill please say stop or cancel.";
options.endSession = false;
context.succeed(buildResponse(options));
}
else {
context.fail("Unknown Intent");
}
}
else if (request.type === "SessionEndedRequest") {
options.endSession = true;
context.succeed();
}
else {
context.fail("Unknown Intent type");
}
} catch (e) {
}
};
function buildResponse(options) {
var response = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.speechText}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
response.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.repromptText}</prosody></speak>`
}
};
}
return response;
}
function readDynamoItem(params, callback) {
var AWS = require('aws-sdk');
AWS.config.update({region: AWSregion});
var dynamodb = new AWS.DynamoDB();
console.log('reading item from DynamoDB table');
dynamodb.scan(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else{
console.log(data); // successful response
callback(JSON.stringify(data));
}
});
var docClient = new AWS.DynamoDB.DocumentClient();
//Get item by key
docClient.get(params, (err, data) => {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
callback(data.Item.message); // this particular row has an attribute called message
}
});
}
///////////////////////////////////////////////////////////////////////////////
And here is my DBHandler
var AWS = require('aws-sdk');
AWS.config.update({
region: "'us-east-1'"
});
let docClient = new AWS.DynamoDB.DocumentClient();
var table = "Cinema";
let getItems = (Id,callback) => {
var params = {
TableName: "cinema",
Key: {
"date": "2018-01-04",
"filmname": "rugrats"
}
};
docClient.get(params, function (err, data) {
callback(err, data);
});
};
module.exports = {
getItems
};
I can launch the app and I have a Lambda function that works on its own when I configure the test event to find a movie from a certain date but I cannot get it to work with Alexa.
Can anyone help me or indicate where I'm going wrong
UPDATE*************
Here is how my intent schema is setup
{
"intents": [
{
"slots": [
{
"name": "sincedate",
"type": "AMAZON.DATE"
}
],
"intent": "date"
},
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"slots": [
{
"name": "sincedate",
"type": "AMAZON.DATE"
}
],
"intent": "cinema"
},
{
"intent": "MyIntent"
}
]
}
Part 1 - Permissions
One possible reason why you may not be able to read from DynamoDB in your skill is because of permissions.
You should double check the IAM role that you assigned to your skill lambda to make sure that it has permissions to read from DynamoDB.
Some references:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/using-identity-based-policies.html
https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-an-aws-lambda-function.html#define-new-role
Part 2 - The actual Skill handler Lambda
I re-read your question and I'm confused about the part where you talk about setting up a second Lambda to read data from Dynamo. You shouldn't have two Lambda functions - just one that will handle requests from Alexa and in that function you should return your response to Alexa, after making a call to Dynamo.
Now, to the specifics. In your first code snippet, you have:
exports.handler = getItems;
exports.handler = (event, context, callback) => {
// here you have your handler to handle alexa responses
}
One thing that stands out immediately is that you are first setting the handler to getItems and then resetting back to the handler that is supposed to respond to the Alexa.
The other thing that I'm guessing is happening is that sometimes the skill works, like when you first launch it, and probably if you say Help but in other cases it doesn't, like when you send it the "cinema" intent.
This is because the entry point from the Alexa request to your skill is the exports.handler which is basically defined as a function with three parameters (it's like the void main(int argc,char *argv[]) of a c program).
The first parameter - event is the input to your skill. Alexa will supply information here such as the request type, if it's an intent, the intent name, session information etc.
The second and third parameters - context and callback are what you use to return control from your lambda function, depending on the node runtime. For Note v4 and newer, you use callback, for older versions you use context.
You can use something like this to send a success response back:
if(typeof callback === 'undefined') {
context.succeed("successful response message");
} else {
callback(null, "successful response message");
}
And something like this to send a failure response
if(typeof callback === 'undefined') {
context.fail("failure response message");
} else {
callback("failure response message", null);
}
Putting it all together, here's a basic Lambda handler that always responds to your skill invocations:
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what do you want to find?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "cinema") {
// this is where we will wire up the dynamo call
// for now, just send a simple response and end the session
sendResponse(context, callback, {
output: "cinema not implemented yet!",
endSession: true
});
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about films",
reprompt: "what can I help you with?"
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// un unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};
Getting to this point, the skill should be functional and it should be able to handle all your requests. Assuming that you have configured your interaction model correctly and that the cinema intent gets sent to your skill, then here's how you can use dynamo client to respond with data from a table.
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
let handleCinemaIntent = (context, callback) => {
let params = {
TableName: "cinema",
Key: {
"date": "2018-01-04",
"filmname": "rugrats"
}
};
dbClient.get(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from Dynamo",
endSession: true
});
} else {
console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2))
// assuming the item has an attribute called "message"..
sendResponse(context, callback, {
output: data.Item.message,
endSession: true
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what do you want to find?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "cinema") {
handleCinemaIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about films",
reprompt: "what can I help you with?"
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// un unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};

failed to load data item: { "message": "The provided key element does not match the schema", "code":

Im trying to create a skill to pull data from my dynamoDB table and feed it back to me through alexa.
I want to be able to ask Alexa about a certain date and recall the information from this date.
im currently getting the following error with the code I currently have
failed to load data item: { "message": "The provided key element does not match the schema", "code":
Here is my current code:
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({
region: "'us-east-1'"
});
let handleCinemaIntent = (context, callback) => {
let params = {
TableName: "cinema",
Key: {
date: "20180102",
message: "Inception",
time: "14:00"
}
};
dbClient.get(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from your database",
endSession: true
});
} else {
console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2));
// assuming the item has an attribute called "message"..
sendResponse(context, callback, {
output: data.Item.message,
endSession: true
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what do you want to find?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "cinema") {
handleCinemaIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about films",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// un unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};
If it helps my table in dyanamodb has the columns
Date | Message | Time
I can also post my intent schema if required
Thank you
A DynamoDB table cannot have a primary key of 3 attributes (as your code suggests; date, message and time). It can either have a partition key or a partition key + sort key as its primary key.

Read data from DynamoDB and recall with Alexa

I'm trying to create a skill with Alexa to read data from my DynamoDB table using either the scan or query function (or both).
The columns in my table are date, time and film name.
I'm new to this but I've managed to link my Lambda function to Alexa. I have also created a separate Lambda function that will recall data from my table when I configure test events, so when I input a specific date it will recall the corresponding film and time. However now i want to implement this into Alexa and am not sure how.
Here is my current code
console.log('Loading function');
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dclient = new AWS.DynamoDB.DocumentClient();
var getItems = (event, context, callback)=>{
dclient.get(event.params,(error,data)=>{
if(error){
callback(null,"error occurerd");
}
else{
callback(null,data);
}
});
};
exports.handler = getItems;
and the code i have to launch the skill in alexa is
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
context.succeed(buildResponse({
speechText: "Welcome to H.S.S.M.I skill, what would you like to find",
repromptText: "I repeat, Welcome to my skill, what would you like to find",
endSession: false
}));
}
else if (request.type === "SessionEndedRequest") {
options.endSession = true;
context.succeed();
}
else {
context.fail("Unknown Intent type");
}
} catch (e) {
}
};
function buildResponse(options) {
var response = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.speechText}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
response.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.repromptText}</prosody></speak>`
}
};
}
return response;
}
I don't mind changing my table from DynamoDB to something else so long as I can recall the data.
Please try below code,
var DBHandler = require("./DBHandler")
var DBHandler = require("./DBHandler")
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
context.succeed(buildResponse({
speechText: "Welcome to home remedy. Please tell me what is the problem you want remedy for?",
repromptText: "You can say for example, home remedy for headache",
endSession: false
}));
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "Cinema") {
DBHandler.getItems(ID, function (err, data) {
if (err) {
context.fail(err);
} else {
if (data.Item !== undefined) {
options.speechText = data.Item.YOURFIELDHERE
options.endSession = true;
context.succeed(buildResponse(options));
} else {
options.speechText = `I am Sorry, I couldn't find any data! `
options.endSession = true;
context.succeed(buildResponse(options));
}
}
callback(null, data)
});
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
options.speechText = "ok, thanks for using my skill.";
options.endSession = true;
context.succeed(buildResponse(options));
}
else {
context.fail("Unknown Intent")
}
}
else if (request.type === "SessionEndedRequest") {
options.endSession = true;
context.succeed();
}
else {
context.fail("Unknown Intent type")
}
} catch (e) {
}
};
function buildResponse(options) {
var response = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.speechText}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
response.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.repromptText}</prosody></speak>`
}
};
}
return response;
}
and DBHandler would be,
const AWS = require('aws-sdk');
AWS.config.update({
region: "Location"
});
var docClient = new AWS.DynamoDB.DocumentClient()
var table = "TableName";
var getItems = (Id,callback) => {
var params = {
TableName: table,
Key: {
"Id": Id
}
};
docClient.get(params, function (err, data) {
callback(err, data);
});
};
module.exports = {
getItems
};

Recall dynamodb table with Alexa

Im trying to create a skill that will scan or query my dynamodb table which includes a date column, filmanme, and time.
Here is the code i have so far.
console.log('Loading function');
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dclient = new AWS.DynamoDB.DocumentClient();
var getItems = (event, context, callback)=>{
dclient.get(event.params,(error,data)=>{
if(error){
callback(null,"error occurerd");
}
else{
callback(null,data);
}
});
};
exports.handler = getItems;
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
context.succeed(buildResponse({
speechText: "Welcome to H.S.S.M.I skill, what would you like to find",
repromptText: "I repeat, Welcome to my skill, what would you like to find",
endSession: false
}));
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "cinema") {
if (request.intent.slots.cimema !== undefined)
var sign = request.intent.slots.cinema.value;
//Check sign is valid
if (sign === undefined || sign === null) {
options.speechText = " sorry, i didn't understant your question. can you say that again?";
options.endSession = false;
context.succeed(buildResponse(options));
return;
}
if (request.intent.slots.zodiac !== undefined && !ValidateZodiacSign(sign)) {
options.speechText = ` The Zoadiac sign ${sign} is not a valid one. Please tell a valid zodiac sign .`;
options.endSession = false;
context.succeed(buildResponse(options));
return;
}
cinema(sign, function (cinema, error) {
if (error) {
context.fail(error);
options.speechText = "There has been a problem with the request.";
options.endSession = true;
context.succeed(buildResponse(options));
} else {
options.speechText = todaysFortune;
options.speechText += " . Have a nice day ahead . ";
options.sign = sign;
options.cardText = todaysFortune;
options.endSession = true;
context.succeed(buildResponse(options));
}
});
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
options.speechText = "ok, good bye.";
options.endSession = true;
context.succeed(buildResponse(options));
}
else if (request.intent.name === "AMAZON.HelpIntent") {
options.speechText = "My skill will read your table depending on what is asked. For example, you can ask what about a specific date. Please refer to skill description for all possible utterences.";
options.repromptText = "What is the data sign you want to know about today? If you want to exit from my skill please say stop or cancel."
options.endSession = false;
context.succeed(buildResponse(options));
}
else {
context.fail("Unknown Intent")
}
}
else if (request.type === "SessionEndedRequest") {
options.endSession = true;
context.succeed();
}
else {
context.fail("Unknown Intent type");
}
} catch (e) {
}
};
function buildResponse(options) {
var response = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.speechText}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
response.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.repromptText}</prosody></speak>`
}
};
}
return response;
}
function readDynamoItem(params, callback) {
var AWS = require('aws-sdk');
AWS.config.update({region: AWSregion});
var dynamodb = new AWS.DynamoDB();
console.log('reading item from DynamoDB table');
dynamodb.scan(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else{
console.log(data); // successful response
callback(JSON.stringify(data));
}
});
var docClient = new AWS.DynamoDB.DocumentClient();
//Get item by key
docClient.get(params, (err, data) => {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
callback(data.Item.message); // this particular row has an attribute called message
}
});
}
///////////////////////////////////////////////////////////////////////////////
and here is my DBHandlder
const AWS = require('aws-sdk');
AWS.config.update({
region: "'us-east-1'"
});
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "Cinema";
var getItems = (Id,callback) => {
var params = {
TableName: "cinema",
Key: {
"Id": Id
}
};
docClient.get(params, function (err, data) {
callback(err, data);
});
};
module.exports = {
getItems
};
i'm very new to this and i cant find muchsupport online for the skill i want to produce or anything similar.
I created a lambda function which works and will find the corresponding movie when i configure the test function to find a specified date, however this does not work with alexa
any input is helpful.

How to use graph API with react-native-fbsdk?

I read the document, both on github and Facebook developers docs.
There is only sample, nothing more. No API document.
The code to make a Graph API request is
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
And the callback
_responseInfoCallback(error: ?Object, result: ?Object) {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
alert('Success fetching data: ' + result.toString());
}
}
And here is the function to make a Graph API request
testRequestGraphAPI(){
const infoRequest = new GraphRequest(
'/me',
null,
this._responseInfoCallback,
);
new GraphRequestManager().addRequest(infoRequest).start();
}
However, I can't find any further document. I have no idea what each parameters do.
The result for these codes above is this.
I also don't know how to get the result.
However, when I try to modify '\me' to 'me?fields=id,name', It failed.
Although I have asked for permission
<LoginButton
publishPermissions={["publish_actions,user_birthday, user_religion_politics, user_relationships, user_relationship_details, user_hometown, user_location, user_likes, user_education_history, user_work_history, user_website, user_managed_groups, user_events, user_photos, user_videos, user_friends, user_about_me, user_status, user_games_activity, user_tagged_places, user_posts, user_actions.video, user_actions.news, user_actions.books, user_actions.music, user_actions.fitness, public_profile, basic_info"]}
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
meow_accesstoken = data.accessToken
alert(meow_accesstoken.toString())
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
But it does not print out what error, just object Object.
So, the problem is that I don't understand the sample code which Facebook provide with no explanation.
Here is my question that I really need you help me:
First at all, please check the javascript code that I currently looking at?
How to use graph API in react-native-fbsdk to retrieve some user information (example: full name) and successfully display it (use alert) ?
What each parameters in GraphRequest() do ?
What is the structure of error object and result object in _responseInfoCallback ?
SOLUTION
Thanks to #Samuel answer, I have updated my code
testRequestGraphAPI: function(){
const infoRequest = new GraphRequest(
'/me',
{
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name' // what you want to get
},
access_token: {
string: meow_accesstoken.toString() // put your accessToken here
}
}
},
this._responseInfoCallback // make sure you define _responseInfoCallback in same class
);
new GraphRequestManager().addRequest(infoRequest).start();
}
And the callback
_responseInfoCallback: function(error: ?Object, result: ?Object) {
alert("meow response");
if (error) {
alert('Error fetching data: ' + error.toString());
console.log(Object.keys(error));// print all enumerable
console.log(error.errorMessage); // print error message
// error.toString() will not work correctly in this case
// so let use JSON.stringify()
meow_json = JSON.stringify(error); // error object => json
console.log(meow_json); // print JSON
} else {
alert('Success fetching data: ' + result.toString());
console.log(Object.keys(result));
meow_json = JSON.stringify(result); // result => JSON
console.log(meow_json); // print JSON
}
}
*Note: For console.log(), you need to use "Debug JS remotely" then open Chrome developer tools to see the log.
Unfortunately the react-native-fbsdk documentation is not updated and the examples do not work well.
I got the same problem and I solved it by try and error.
To solve your problem you'll need to change your GraphRequest adding params and fields to it like this:
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
You'll need to enable the Remote JS Debug to see the console.log() info.
https://facebook.github.io/react-native/docs/debugging.html
And probably you need to get some permissions to get more info than names and email so it's a good idea to look the Facebook Graph API Documentation: https://developers.facebook.com/docs/graph-api/overview/
Reference:
https://github.com/facebook/react-native-fbsdk/issues/105#issuecomment-206501550
Here is an example of a custom button if you want to make one :)
FbLoginButton() {
LoginManager
.logInWithReadPermissions(['public_profile'])
.then(function (result) {
if (result.isCancelled) {
alert('Login cancelled');
} else {
AccessToken
.getCurrentAccessToken()
.then((data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
alert('Success fetching data: ' + result.toString());
}
}
const infoRequest = new GraphRequest('/me', {
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
}, responseInfoCallback);
// Start the graph request.
new GraphRequestManager()
.addRequest(infoRequest)
.start()
})
}
}, function (error) {
alert('Login fail with error: ' + error);
});
}
Thank you #Samuel.
I finally succeed to get user information from Facebook login because of your help!
But I struggled to figure out how can I get username and email literally from the result object cause I am a newbie in React & Javascript.
P.S. result["name"] is the point because it is object!!
So I added some code to yours for other people like me.
If you don't like using your code, just tell me that.
<LoginButton
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
let accessToken = data.accessToken
alert(accessToken.toString())
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error)
alert('Error fetching data: ' + error.toString());
} else {
console.log(result)
// Here's my code
alert('Success fetching data: ' + result["name"].toString() +
", " + result["email"].toString());
/*
if(your DB already got this email or something unique) {
// SignIn()
}
// when your DB doesn't have this email
else {
// Do signUp() with this infomation and SignIn()
}
*/
}
}
const infoRequest = new GraphRequest(
'/me',
{
accessToken: accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name'
}
}
},
responseInfoCallback
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start()
}
)
}
}
}
onLogoutFinished={() => alert("logout.")}/>
My code was not retriving the user email, if you are having the same problem, just put 'email' in parameter's logInWithPermission
Not Working
LoginManager.logInWithPermissions(['public_profile']).then(...)
Working
LoginManager.logInWithPermissions(['public_profile', 'email']).then(...)
All Function
loginWithFacebook = () => {
LoginManager.logInWithPermissions(['public_profile', 'email']).then(
login => {
if (login.isCancelled) {
console.log('Login Cancelado');
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
const accessToken = data.accessToken.toString()
this.getInfoFromToken(accessToken)
})
}
},
error => {
console.log('Erro no login ', console.error(error)
)
}
)
}
getInfoFromToken = token => {
const PROFILE_REQUEST_PARAMS = {
fields: {
string: 'id, name, first_name, last_name, birthday, email'
},
}
const profileRequest = new GraphRequest('/me', { token, parameters: PROFILE_REQUEST_PARAMS },
(error, result) => {
if (error) {
console.log('Login Info has an error:', error)
} else {
console.log(result)
}
},
)
new GraphRequestManager().addRequest(profileRequest).start()
}
try this
import { GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
export const GetInfoUSer = () => {
return new Promise((resolve, reject) => {
const infoRequest = new GraphRequest('/me', null, ((error, result) => {
if (error) {
reject(error)
} else {
resolve(result)
}
}))
new GraphRequestManager().addRequest(infoRequest).start();
})
}
and then
onLoginConFacebook = () => {
LoginManager.logInWithReadPermissions(['public_profile']).then(result => {
if (result.isCancelled) {
console.log(':(')
} else {
AccessToken.getCurrentAccessToken().then((data) => {
let myAccessToken = data.accessToken.toString();
GetInfoUSer().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
).catch(error => {
console.log(':(')
})
}
})
}