【问题标题】:Recall DynamoDB with Alexa query使用 Alexa 查询调用 DynamoDB
【发布时间】:2018-03-01 13:18:45
【问题描述】:

我正在尝试使用 Alexa 创建一项技能,以使用扫描或查询功能(或两者)从我的 DynamoDB 表中读取数据。

我表中的列是日期、时间和电影名称。

我是新手,但我已经设法将我的 Lambda 函数链接到 Alexa。我还创建了一个单独的 Lambda 函数,它会在我配置测试事件时从我的表中调用数据,因此当我输入特定日期时,它会调用相应的电影和时间。但是现在我想在 Alexa 中实现它,但不确定如何。

这是我当前的代码

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

        }
    });

}

///////////////////////////////////////////////////////////////////////////////

这是我的 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
};

我可以启动应用程序,并且我有一个 Lambda 函数,当我将测试事件配置为查找某个日期的电影但我无法让它与 Alexa 一起使用时,它可以独立运行。

谁能帮助我或指出我哪里出错了

更新*************

这是我的意图架构的设置方式

{
  "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"
    }
  ]
}

【问题讨论】:

    标签: amazon-web-services aws-lambda amazon-dynamodb alexa alexa-skills-kit


    【解决方案1】:

    第 1 部分 - 权限

    您可能无法从您的技能中读取 DynamoDB 的一个可能原因是权限。

    您应该仔细检查您分配给技能 lambda 的 IAM 角色,以确保它具有从 DynamoDB 读取的权限。

    一些参考资料:

    第 2 部分 - 实际的技能处理程序 Lambda

    我重新阅读了您的问题,我对您谈论设置第二个 Lambda 以从 Dynamo 读取数据的部分感到困惑。您不应该有两个 Lambda 函数 - 只有一个将处理来自 Alexa 的请求,并且在该函数中您应该在调用 Dynamo 后将响应返回给 Alexa。

    现在,具体来说。在您的第一个代码 sn-p 中,您有:

    exports.handler = getItems;
    
    exports.handler = (event, context, callback) => {
        // here you have your handler to handle alexa responses 
    }
    

    立即突出的一件事是,您首先将处理程序设置为getItems,然后重置回应该响应 Alexa 的处理程序。

    我猜正在发生的另一件事是,有时该技能会起作用,例如当您第一次启动它时,可能如果您说“帮助”,但在其他情况下它不会,例如当您向它发送“电影”时" 意图。

    这是因为从 Alexa 请求到你的技能的入口点是exports.handler,它基本上定义为一个具有三个参数的函数(就像 ac 程序的 void main(int argc,char *argv[]) )。

    第一个参数 - event 是你技能的输入。 Alexa 将在此处提供信息,例如请求类型、是否为意图、意图名称、会话信息等。

    第二个和第三个参数 - contextcallback 用于从 lambda 函数返回控制权,具体取决于节点运行时。对于 Note v4 及更高版本,您使用回调,对于旧版本,您使用上下文。

    您可以使用类似这样的方式发送成功响应:

    if(typeof callback === 'undefined') {
         context.succeed("successful response message");
    } else {
         callback(null, "successful response message");
    }
    

    像这样发送失败响应

    if(typeof callback === 'undefined') {
         context.fail("failure response message");
    } else {
         callback("failure response message", null);
    }
    

    总而言之,这是一个始终响应您的技能调用的基本 Lambda 处理程序:

    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");
        }
      }
    };
    

    到此为止,该技能应该是功能性的,并且应该能够处理您的所有请求。假设您已正确配置交互模型并且 cinema 意图已发送到您的技能,那么您可以使用 dynamo 客户端响应来自表的数据。

    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");
        }
      }
    };
    

    【讨论】:

    • 好的 - 我已经更新了答案 - 希望它能让事情变得更清晰
    • 很高兴答案有帮助(也许考虑支持;),是的,如果您在加载数据时遇到错误,这意味着来自 handleCinemaIntent 的回调返回一个 err 对象,因此您必须查看日志CloudWatch 中的 lambda 以查看究竟是什么错误。在此处发表评论或添加其他问题
    • 不,您应该能够转到 Lambda 控制台以获取您的技能,并打开监控选项卡,然后单击“在 CloudWatch 中查看”,这将带您进入显示控制台日志消息来自您的技能;会有一个从“handleCinemaIntent”记录下来,显示从发电机获取项目的问题
    • 是的,类似的,虽然我怀疑错误来自不同的调用——也许你正在测试 lambda 调用?这似乎不是实际的技能调用 - 错误告诉您事件对象实际上并不包含“请求”参数,尽管所有 Alexa 技能请求都包含,因此 lambda 调用不能来自实际技能请求
    • 您应该在日志中查找类似以下内容的日志:“未能加载数据项”,然后查看错误是什么;很可能是关于该数据项的结构.. 但我不能说没有错误消息
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 2023-02-20
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 2018-05-05
    相关资源
    最近更新 更多