我正在研究一项Alexa技能(适用于Node.js的ASK-SDK v.2),该技能可以从Airtable数据库执行GET。用户要求输入角色名称,然后技能将对照此数据库进行检查以检索简短的描述。
但是,当我在Skill Builder上进行一些测试时,即使我说出在JSON INPUT中产生ER_SUCCESS_MATCH的角色名称,JSON OUTPUT也会将解析后的值(实际上是任何有效名称)返回为UNDEFINED。无论我查询的字符名称是什么,我都听到的消息是“我在数据库中找不到未定义的字符。”
即使我相信我在代码中提供了正确的API密钥和AirtableGet密钥,我仍然认为该技能无法连接到我的Airtable基地以获得角色名称。此外,我应该在我的AWS Lambda日志上看到一个“ IN AIRTABLE GET”条目,就在IN CHARACTER HANDLER和通常的END RequestID之间,但是到目前为止,我从IN CHARACTER HANDLER跳到了END RequestID,所以Airtable db似乎没有被调用。
这是完整的Index.js代码(减去技能编号,API密钥和角色名称列密钥)。你能发现任何错误吗?预先感谢您的帮助!
'use strict';
const Alexa = require('ask-sdk');
const https = require('https');
const APP_ID = "amzn1.ask.skill.xxxxxxxxxxxxxxxxxxxx";
const EmptyHandler = {
canHandle(handlerInput) {
return false;
},
handle(handlerInput, error) {
return handlerInput.responseBuilder
.speak()
.reprompt()
.getResponse();
}
};
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "LaunchRequest";
},
handle(handlerInput, error) {
console.log("IN LAUNCH REQUEST");
return handlerInput.responseBuilder
.speak("Accessing the Skill. Say any name.")
.reprompt("Go on, start.")
.getResponse();
},
};
const CharacterHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "IntentRequest" &&
handlerInput.requestEnvelope.request.intent.name === "CharacterIntent";
},
handle(handlerInput, error) {
console.log("IN CHARACTER HANDLER");
var spokenValue = getSpokenValue(handlerInput.requestEnvelope, "character");
var resolvedValues = getResolvedValues(handlerInput.requestEnvelope, "character");
//NO MATCHES FOUND
if (resolvedValues === undefined)
{
return handlerInput.responseBuilder
.speak("I can't find " + spokenValue + " in my database." + getRandomQuestion())
.reprompt("" + spokenValue + " is not in my database. " + getRandomQuestion())
.getResponse();
}
//ONLY ONE MATCH FOUND
else if (resolvedValues.length === 1)
{
var filter = "&filterByFormula=%7BName%7D%3D%22" + encodeURIComponent(resolvedValues[0].value.name) + "%22";
return new Promise((resolve) => {
airtableGet("appXXXXXXXXXX", "Character", filter, (record) => {
console.log("AIRTABLE RECORD = " + JSON.stringify(record));
var speechText = "Accessing to " + spokenValue + "<break time='.5s'/>" + record.records[0].fields.VoiceDescription;
console.log("RESPONSE BUILDER = " + JSON.stringify(handlerInput.responseBuilder));
resolve(handlerInput.responseBuilder
.speak(speechText)
.reprompt(getRandomQuestion())
.getResponse());
});
});
}
//MORE THAN ONE MATCH FOUND. DISAMBIGUATE.
else if (resolvedValues.length > 1)
{
var valuesString = getValuesString(resolvedValues);
return handlerInput.responseBuilder
.speak("You asked for " + spokenValue + ", but I have different matches for that name. Do you mean " + valuesString + "?")
.reprompt("Do you want to talk about" + valuesString + "?")
.getResponse();
}
}
};
const HelpHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "IntentRequest" &&
handlerInput.requestEnvelope.request.intent.name === "AMAZON.HelpIntent";
},
handle(handlerInput, error) {
console.log("IN " + handlerInput.requestEnvelope.request.intent.name.toUpperCase())
return handlerInput.responseBuilder
.speak("I know a lot of things about these characters. Tell me something about them.")
.reprompt("Go on.")
.getResponse();
}
};
const StopHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "IntentRequest" &&
(handlerInput.requestEnvelope.request.intent.name === "AMAZON.StopIntent" ||
handlerInput.requestEnvelope.request.intent.name === "AMAZON.CancelIntent");
},
handle(handlerInput, error) {
console.log("IN " + handlerInput.requestEnvelope.request.intent.name.toUpperCase())
return handlerInput.responseBuilder
.speak("Okey. See you!")
.getResponse();
}
};
const SessionEndedHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
console.log("IN SESSIONENDEDHANDLER");
return handlerInput.responseBuilder
.speak(getRandomGoodbye())
.getResponse();
}
};
function getRandomQuestion()
{
var questions = ["Say another name when you're ready.", "Tell me another name.", "Say another one.", "Any other names?"];
var random = getRandom(0, questions.length-1);
return "<break time='.5s'/>" + questions[random];
}
function getRandom(min, max)
{
return Math.floor(Math.random() * (max-min+1)+min);
}
function getSpokenValue(envelope, slotName)
{
if (envelope &&
envelope.request &&
envelope.request.intent &&
envelope.request.intent.slots &&
envelope.request.intent.slots[slotName] &&
envelope.request.intent.slots[slotName].value)
{
return envelope.request.intent.slots[slotName].value;
}
else return undefined;
}
function getResolvedValues(envelope, slotName)
{
if (envelope &&
envelope.request &&
envelope.request.intent &&
envelope.request.intent.slots &&
envelope.request.intent.slots[slotName] &&
envelope.request.intent.slots[slotName].resolutions &&
envelope.request.intent.slots[slotName].resolutions.resolutionsPerAuthority &&
envelope.request.intent.slots[slotName].resolutions.resolutionsPerAuthority[0] &&
envelope.request.intent.slots[slotName].resolutions.resolutionsPerAuthority[0].values)
{
return envelope.request.intent.slots[slotName].resolutions.resolutionsPerAuthority[0].values;
}
else return undefined;
}
function getSlotName(envelope)
{
if (envelope &&
envelope.request &&
envelope.request.intent &&
envelope.request.intent.slots &&
envelope.request.intent.slots[0] &&
envelope.request.intent.slots[0].name)
{
return envelope.request.intent.slots[0].name;
}
else return undefined;
}
function getValuesString(values)
{
var string = "";
for (var i = 0;i<values.length; i++)
{
if (i != 0) string += ", ";
if (i === (values.length-1)) string += " or ";
string += values[i].value.name;
}
return string;
}
function airtableGet(base, table, filter, callback) {
console.log("IN AIRTABLE GET");
console.log("BASE = " + base);
console.log("TABLE = " + table);
console.log("FILTER = " + filter);
var options = {
host: "api.airtable.com",
port: 443,
path: "/v0/" + base + "/" + table + "?api_key=keyXXXXXXXXXX" + filter,
method: 'GET',
};
console.log("PATH = https://" + options.host + options.path);
var req = https.request(options, res => {
res.setEncoding('utf8');
var returnData = "";
res.on('data', chunk => {
returnData = returnData + chunk;
});
res.on('end', () => {
var data = JSON.parse(returnData);
console.log("DATA = " + JSON.stringify(data));
callback(data);
});
});
req.end();
}
const RequestLog = {
process(handlerInput) {
console.log("REQUEST ENVELOPE = " + JSON.stringify(handlerInput.requestEnvelope));
return;
}
};
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log("Error handled: " + JSON.stringify(error.message));
console.log('handlerInput:' + JSON.stringify(handlerInput));
return handlerInput.responseBuilder
.speak('I am sorry, something went wrong on my side.')
.getResponse();
},
};
exports.handler = Alexa.SkillBuilders.standard()
.addRequestHandlers(
LaunchRequestHandler,
CharacterHandler,
HelpHandler,
StopHandler,
SessionEndedHandler
)
.addRequestInterceptors(RequestLog)
.addErrorHandlers(ErrorHandler)
.lambda();