Related
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");
}
}
};
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");
}
}
};
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.
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
};
Is there a way to transfer the messages I get from SQS and send them over to Dynamodb? I've tried making a Lambda function using CloudWatch to trigger it every minute. I'm open to using any other services in AWS to complete this task. I'm sure there's a simple explanation to this that I'm just overlooking.
*Edit my code does not work, I'm looking for either a fix to my code or another solution to accomplish this.
**Edit got it working.
'use strict';
const AWS = require('aws-sdk');
const SQS = new AWS.SQS({ apiVersion: '2012-11-05' });
const Lambda = new AWS.Lambda({ apiVersion: '2015-03-31' });
const QUEUE_URL = 'SQS_URL';
const PROCESS_MESSAGE = 'process-message';
const DYNAMO_TABLE = 'TABLE_NAME';
function poll(functionName, callback) {
const params = {
QueueUrl: QUEUE_URL,
MaxNumberOfMessages: 10,
VisibilityTimeout: 10
};
// batch request messages
SQS.receiveMessage(params, function(err, data) {
if (err) {
return callback(err);
}
// parse each message
data.Messages.forEach(parseSQSMessage);
})
.promise()
.then(function(){
return Lambda.invokeAsync({})
.promise()
.then(function(data){
console.log('Recursion');
})
}
)
.then(function(){context.succeed()}).catch(function(err){context.fail(err, err.stack)});
}
// send each event in message to dynamoDB.
// remove message from queue
function parseSQSMessage(msg, index, array) {
// delete SQS message
var params = {
QueueUrl: QUEUE_URL,
ReceiptHandle: msg.ReceiptHandle
};
SQS.deleteMessage(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
// store atomic event JSON directly to dynamoDB
function storeEvent(event) {
var params = {
TableName : DYNAMO_TABLE,
Item: event
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.put(params, function(err, data) {
if (err) console.log(err);
else console.log(data);
});
}
exports.handler = (event, context, callback) => {
try {
// invoked by schedule
poll(context.functionName, callback);
} catch (err) {
callback(err);
}
};
var aws = require( "aws-sdk" );
// get configuration defaults from config file.
var tableName = 'Table_Name';
var queueUrl = 'SQS_URL';
var dbClient = new aws.DynamoDB.DocumentClient();
var sqsClient = new aws.SQS();
// get config values from dynamodb - if the config values are found, then override existing values
// this will occur on every execution of the lambda which will allow real time configuration changes.
var updateConfig = function updateConfigValues(invokedFunction, cb) {
var params = {
TableName: "Table_NAME",
Key: {
"KEY": "KEY"
}
};
dbClient.get(params, function(err, data) {
if(err) {
console.log("ERR_DYNAMODB_GET", err, params);
}
else if(!data || !data.Item) {
console.log("INFO_DYNAMODB_NOCONFIG", params);
}
else {
queueUrl = data.Item.config.queueUrl;
tableName = data.Item.config.tableName;
}
return cb(err);
});
};
// save the email to dynamodb using conditional write to ignore addresses already in the db
var saveEmail = function saveEmail(messageBody, cb) {
var params = {
TableName:tableName,
Item:messageBody,
ConditionExpression : "attribute_not_exists(clickId)",
};
dbClient.put(params, function(err, data) {
cb(err, data);
});
};
var deleteMessage = function deleteMessage(receiptHandle, cb) {
var params = {
QueueUrl: queueUrl,
ReceiptHandle: receiptHandle
};
sqsClient.deleteMessage(params, function(err, data) {
cb(err, data);
});
}
exports.handler = function(event, context) {
updateConfig(context.invokedFunctionArn, function(err) {
if(err) {
context.done(err);
return;
}
console.log("INFO_LAMBDA_EVENT", event);
console.log("INFO_LAMBDA_CONTEXT", context);
sqsClient.receiveMessage({MaxNumberOfMessages:10 , QueueUrl: queueUrl}, function(err, data) {
if(err) {
console.log("ERR_SQS_RECEIVEMESSAGE", err);
context.done(null);
}
else {
if (data && data.Messages) {
console.log("INFO_SQS_RESULT", " message received");
var message = JSON.parse(data.Messages[0].Body);
var messageBody = message.Message;
messageBody = JSON.parse(messageBody);
// loops though the messages and replaces any empty strings with "N/A"
messageBody.forEach((item) => {
var item = item;
var custom = item.customVariables;
for (i = 0; i < custom.length; i++) {
if(custom[i] === ''){
custom[i] = 'N/A';
}
item.customVariables = custom;
}
for(variable in item) {
if(item[variable] === ""){
item[variable] = "N/A";
console.log(item);
}
}
var messageBody = item;
});
var messageBody = messageBody[0];
// Logs out the new messageBody
console.log("FIXED - ", messageBody);
// Checks for errors and delets from que after sent
saveEmail(messageBody, function(err, data) {
if (err && err.code && err.code === "ConditionalCheckFailedException") {
console.error("INFO_DYNAMODB_SAVE", messageBody + " already subscribed");
deleteMessage(message.MessageId, function(err) {
if(!err) {
console.error("INFO_SQS_MESSAGE_DELETE", "receipt handle: " + message.MessageId, "successful");
} else {
console.error("ERR_SQS_MESSAGE_DELETE", "receipt handle: " + message.MessageId, err);
}
context.done(err);
});
}
else if (err) {
console.error("ERR_DYNAMODB_SAVE", "receipt handle: " + message.MessageId, err);
context.done(err);
}
else {
console.log("INFO_DYNAMODB_SAVE", "email_saved", "receipt handle: " + message.MessageId, messageBody.Message);
deleteMessage(message.MessageId, function(err) {
if(!err) {
console.error("INFO_SQS_MESSAGE_DELETE", "receipt handle: " + message.MessageId, "successful");
} else {
console.error("ERR_SQS_MESSAGE_DELETE", "receipt handle: " + message.MessageId, err);
}
context.done(err);
});
}
});
} else {
console.log("INFO_SQS_RESULT", "0 messages received");
context.done(null);
}
}
});
});
}