1

我正在研究从 Airtable 数据库执行 GET 的 Alexa Skill(ASK-SDK v.2 for Node.js)。用户要求输入角色名称,Skill 会根据该数据库对其进行检查,以检索简短的描述作为结果。

但是,当我在 Skill Builder 上进行一些测试时,即使我在我的 JSON INPUT 中说出一个 ER_SUCCESS_MATCH 的字符名称,JSON OUTPUT 也会将解析的值(实际上是任何有效的名称)返回为 UNDEFINED。我听到的消息是“我在我的数据库中找不到 UNDEFINED。”,无论我询问的角色名称是什么。

我相信技能无法连接到我的 Airtable 基础以获取角色名称,即使我相信我在我的代码上提供了正确的 API 密钥和 AirtableGet 密钥。此外,我应该在我的 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();
4

0 回答 0