Последние пару часов я пытался решить эту проблему, но безуспешно.Вот мой код:
Лямбда-код:
/*eslint-disable func-names */
/* eslint quote-props: ["error", "consistent"]*/
// There are three sections, Text Strings, Skill Code, and Helper Function(s).
// You can copy and paste the contents as the code for a new Lambda function, using the alexa-skill-kit-sdk-factskill template.
// This code includes helper functions for compatibility with versions of the SDK prior to 1.0.9, which includes the dialog directives.
// 1. Text strings =====================================================================================================
// Modify these strings and messages to change the behavior of your Lambda function
let speechOutput;
let reprompt;
let welcomeOutput = "Welcome to Tanpura! You can ask me to play at any pitch. How can I help you today?";
let welcomeReprompt = "sample re-prompt text";
// 2. Skill Code =======================================================================================================
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
speechOutput = '';
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', welcomeOutput, welcomeReprompt);
},
'AMAZON.HelpIntent': function () {
speechOutput = 'You can ask me to play at any pitch, such as C, C sharp, D, D sharp, and so on. How can I help you?';
reprompt = '';
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
speechOutput = 'Would you like me to play another pitch?';
this.emit(':tell', speechOutput);
},
'AMAZON.StopIntent': function () {
speechOutput = 'I hope I helped you. Goodbye!';
this.emit(':tell', speechOutput);
},
'SessionEndedRequest': function () {
speechOutput = '';
//this.emit(':saveState',true);//uncomment to save attributes to db on session end
this.emit(':tell', speechOutput);
},
'AMAZON.FallbackIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "I currently do not support your request. However, you can ask me to play at any pitch, and I will do so. How can I help?";
this.emit(":ask", speechOutput, speechOutput);
},
'AMAZON.NavigateHomeIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
//Your custom intent handling goes here
speechOutput = "Welcome to Tanpura! You can ask me to play at any pitch. How can I help you today?";
this.emit(":ask", speechOutput, speechOutput);
},
'PlayNoteIntent': function () {
speechOutput = '';
//any intent slot variables are listed here for convenience
let noteSlot = resolveCanonical(this.event.request.intent.slots.note);
console.log("User selected pitch: " + noteSlot);
let accidentalSlot = resolveCanonical(this.event.request.intent.slots.accidental);
console.log("User selected accidental: " + accidentalSlot);
var notes = {
'a': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Gsharp.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_A.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Asharp.mp3'
},
'b': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Asharp.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_B.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_C.mp3'
},
'c': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_B.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_C.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Csharp.mp3'
},
'd': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Csharp.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_D.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Dsharp.mp3'
},
'e': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Dsharp.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_E.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_F.mp3'
},
'f': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_E.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_F.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Fsharp.mp3'
},
'g': {
'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Fsharp.mp3',
'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_G.mp3',
'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Gsharp.mp3'
}
}
var note = noteSlot.toLowerCase();
var speechReprompt = "";
if (noteSlot && notes[note]){
var audio = '';
var accidental = 'natural';
if(accidentalSlot && accidental.indexOf(accidentalSlot) > -1){
accidental = accidentalSlot;
}
var audioSrc = notes[note][accidental];
speechOutput = "Ok. I will play " + noteSlot + accidental + <audio src="' + audioSrc + '" />;
speechReprompt = "Would you like me to continue playing?";
}
else{
speechOutput = "The note you have requested is not supported yet.";
speechReprompt = "However, Tanpura does support A, B, C, D, E, F, G, and all of the accidentals in between.";
}
//Your custom intent handling goes here
this.emit(":ask", speechOutput, speechOutput);
},
'Unhandled': function () {
speechOutput = "Tanpura didn't quite understand what you wanted. Please try rephrasing your request.";
this.emit(':ask', speechOutput, speechOutput);
}
};
exports.handler = (event, context) => {
const alexa = Alexa.handler(event, context);
alexa.appId = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
//alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
//alexa.dynamoDBTableName='DYNAMODB_TABLE_NAME';//uncomment this line to save attributes to DB
alexa.execute();
};
// END of Intent Handlers {} ========================================================================================
// 3. Helper Function =================================================================================================
function resolveCanonical(slot){
//this function looks at the entity resolution part of request and returns the slot value if a synonyms is provided
let canonical;
try{
canonical = slot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
}catch(err){
console.log(err.message);
canonical = slot.value;
};
return canonical;
};
function delegateSlotCollection(){
console.log("in delegateSlotCollection");
console.log("current dialogState: "+this.event.request.dialogState);
if (this.event.request.dialogState === "STARTED") {
console.log("in Beginning");
let updatedIntent= null;
// updatedIntent=this.event.request.intent;
//optionally pre-fill slots: update the intent object with slot values for which
//you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property
//this.emit(":delegate", updatedIntent); //uncomment this is using ASK SDK 1.0.9 or newer
//this code is necessary if using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady', updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
// return a Dialog.Delegate directive with no updatedIntent property.
//this.emit(":delegate"); //uncomment this is using ASK SDK 1.0.9 or newer
//this code necessary is using ASK SDK versions prior to 1.0.9
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', null, null),
shouldEndSession: false
});
this.emit(':responseReady');
} else {
console.log("in completed");
console.log("returning: "+ JSON.stringify(this.event.request.intent));
// Dialog is now complete and all required slots should be filled,
// so call your normal intent handler.
return this.event.request.intent;
}
}
function randomPhrase(array) {
// the argument is an array [] of words or phrases
let i = 0;
i = Math.floor(Math.random() * array.length);
return(array[i]);
}
function isSlotValid(request, slotName){
let slot = request.intent.slots[slotName];
//console.log("request = "+JSON.stringify(request)); //uncomment if you want to see the request
let slotValue;
//if we have a slot, get the text and store it into speechOutput
if (slot && slot.value) {
//we have a value in the slot
slotValue = slot.value.toLowerCase();
return slotValue;
} else {
//we didn't get a value in the slot.
return false;
}
}
//These functions are here to allow dialog directives to work with SDK versions prior to 1.0.9
//will be removed once Lambda templates are updated with the latest SDK
function createSpeechObject(optionsParam) {
if (optionsParam && optionsParam.type === 'SSML') {
return {
type: optionsParam.type,
ssml: optionsParam['speech']
};
} else {
return {
type: optionsParam.type || 'PlainText',
text: optionsParam['speech'] || optionsParam
};
}
}
function buildSpeechletResponse(options) {
let alexaResponse = {
shouldEndSession: options.shouldEndSession
};
if (options.output) {
alexaResponse.outputSpeech = createSpeechObject(options.output);
}
if (options.reprompt) {
alexaResponse.reprompt = {
outputSpeech: createSpeechObject(options.reprompt)
};
}
if (options.directives) {
alexaResponse.directives = options.directives;
}
if (options.cardTitle && options.cardContent) {
alexaResponse.card = {
type: 'Simple',
title: options.cardTitle,
content: options.cardContent
};
if(options.cardImage && (options.cardImage.smallImageUrl || options.cardImage.largeImageUrl)) {
alexaResponse.card.type = 'Standard';
alexaResponse.card['image'] = {};
delete alexaResponse.card.content;
alexaResponse.card.text = options.cardContent;
if(options.cardImage.smallImageUrl) {
alexaResponse.card.image['smallImageUrl'] = options.cardImage.smallImageUrl;
}
if(options.cardImage.largeImageUrl) {
alexaResponse.card.image['largeImageUrl'] = options.cardImage.largeImageUrl;
}
}
} else if (options.cardType === 'LinkAccount') {
alexaResponse.card = {
type: 'LinkAccount'
};
} else if (options.cardType === 'AskForPermissionsConsent') {
alexaResponse.card = {
type: 'AskForPermissionsConsent',
permissions: options.permissions
};
}
let returnResult = {
version: '1.0',
response: alexaResponse
};
if (options.sessionAttributes) {
returnResult.sessionAttributes = options.sessionAttributes;
}
return returnResult;
}
function getDialogDirectives(dialogType, updatedIntent, slotName) {
let directive = {
type: dialogType
};
if (dialogType === 'Dialog.ElicitSlot') {
directive.slotToElicit = slotName;
} else if (dialogType === 'Dialog.ConfirmSlot') {
directive.slotToConfirm = slotName;
}
if (updatedIntent) {
directive.updatedIntent = updatedIntent;
}
return [directive];
}
Alexa Разработчик JSON Ввод:
{
"version": "1.0",
"session": {
"new": true,
"sessionId": "amzn1.echo-api.session.1456bfda-a9d3-457f-88a7-bc5387d774db",
"application": {
"applicationId": "amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2"
},
"user": {
"userId": "amzn1.ask.account.AGD7V7GZTLU4DQH623OMU5MUBR2FGWXKDVW2OPNYYRWKIYJCHQBCSKVNQHEPOEXQWO33Q4OTJ6LSIRLYT3TN33OAK3W7LLNNYPU5S3MVKPMPNH2XDWYJ7DBWCFZRXY4STCPFKVL2FADYZE4TXS53Z5AXBPN6344R6VG6GD365TFQTCPPKABC5IKM46UZXUX3BPR4TQ4KEYO6LTA"
}
},
"context": {
"System": {
"application": {
"applicationId": "amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2"
},
"user": {
"userId": "amzn1.ask.account.AGD7V7GZTLU4DQH623OMU5MUBR2FGWXKDVW2OPNYYRWKIYJCHQBCSKVNQHEPOEXQWO33Q4OTJ6LSIRLYT3TN33OAK3W7LLNNYPU5S3MVKPMPNH2XDWYJ7DBWCFZRXY4STCPFKVL2FADYZE4TXS53Z5AXBPN6344R6VG6GD365TFQTCPPKABC5IKM46UZXUX3BPR4TQ4KEYO6LTA"
},
"device": {
"deviceId": "amzn1.ask.device.AFHBRIBVUWYIR2ESXPKWP3G3PHYK4W5VW4NF55KH5ZXD27WMSPBPU7YLJQJWM2YQDZBH7VWGXCLNQKESUNWWGI6CJUWUUSWUKVBZWZC5LBNXMCDY2IOZAZUYWHYXT5VLLA7XC3OP2WY7RXE2LPRHM5E4BIMR662M5MZKJH4WRPUFS3HVIFRDK",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOiJodHRwczovL2FwaS5hbWF6b25hbGV4YS5jb20iLCJpc3MiOiJBbGV4YVNraWxsS2l0Iiwic3ViIjoiYW16bjEuYXNrLnNraWxsLjBjMjM3MTMyLTg4MTUtNDAyNS1hMjNmLWNhNmRmNjg4YmNkMiIsImV4cCI6MTUzOTU1MzE2NywiaWF0IjoxNTM5NTQ5NTY3LCJuYmYiOjE1Mzk1NDk1NjcsInByaXZhdGVDbGFpbXMiOnsiY29uc2VudFRva2VuIjpudWxsLCJkZXZpY2VJZCI6ImFtem4xLmFzay5kZXZpY2UuQUZIQlJJQlZVV1lJUjJFU1hQS1dQM0czUEhZSzRXNVZXNE5GNTVLSDVaWEQyN1dNU1BCUFU3WUxKUUpXTTJZUURaQkg3VldHWENMTlFLRVNVTldXR0k2Q0pVV1VVU1dVS1ZCWldaQzVMQk5YTUNEWTJJT1pBWlVZV0hZWFQ1VkxMQTdYQzNPUDJXWTdSWEUyTFBSSE01RTRCSU1SNjYyTTVNWktKSDRXUlBVRlMzSFZJRlJESyIsInVzZXJJZCI6ImFtem4xLmFzay5hY2NvdW50LkFHRDdWN0daVExVNERRSDYyM09NVTVNVUJSMkZHV1hLRFZXMk9QTllZUldLSVlKQ0hRQkNTS1ZOUUhFUE9FWFFXTzMzUTRPVEo2TFNJUkxZVDNUTjMzT0FLM1c3TExOTllQVTVTM01WS1BNUE5IMlhEV1lKN0RCV0NGWlJYWTRTVENQRktWTDJGQURZWkU0VFhTNTNaNUFYQlBONjM0NFI2Vkc2R0QzNjVURlFUQ1BQS0FCQzVJS000NlVaWFVYM0JQUjRUUTRLRVlPNkxUQSJ9fQ.KAHvIOOUP4k-73lNMxRnOToYjrUbeHuLRDQGzMFi9dVEiwc2QpvpMZpLNpG5rCtoqB2-OfC48KbK5u67nW6X9QO6DSoNTBfPKUatIHB6pUWbArdv-FliUO69SQMomjLtLzC86_jnZ8TqvNavjb5I5hOGnmCe5Fv2IY5HgBw0h07Dq3ZT4i_4edcnhX9zYJretTEydF0L3JA7GTithgtAGFxbBqbiDTKRMlaGUGBWAkZkHy8FPWsAmvfTwRaNL7F3LAEbGH2QJlyoPQR7jYij7CsnlRAEv-3Ur1kFaMEdhDNA9fcn2JI4TVf1umy0fL66dHWq3omk2p5I4FyrJ3a8SQ"
}
},
"request": {
"type": "IntentRequest",
"requestId": "amzn1.echo-api.request.80cc2899-2fa2-4828-99ba-1c25d8cce05b",
"timestamp": "2018-10-14T20:39:27Z",
"locale": "en-US",
"intent": {
"name": "PlayNoteIntent",
"confirmationStatus": "NONE",
"slots": {
"note": {
"name": "note",
"value": "an",
"resolutions": {
"resolutionsPerAuthority": [
{
"authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2.Note",
"status": {
"code": "ER_SUCCESS_NO_MATCH"
}
}
]
},
"confirmationStatus": "NONE",
"source": "USER"
},
"accidental": {
"name": "accidental",
"value": "e",
"resolutions": {
"resolutionsPerAuthority": [
{
"authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2.accidental",
"status": {
"code": "ER_SUCCESS_NO_MATCH"
}
}
]
},
"confirmationStatus": "NONE",
"source": "USER"
}
}
}
}
}
И, наконец, JSON скила:
{
"interactionModel": {
"languageModel": {
"invocationName": "tanpura",
"intents": [
{
"name": "AMAZON.FallbackIntent",
"samples": []
},
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "PlayNoteIntent",
"slots": [
{
"name": "note",
"type": "Note"
},
{
"name": "accidental",
"type": "accidental"
}
],
"samples": [
"for an {note} {accidental}",
"for a {note} {accidental}",
"play {note} {accidental}",
"Start an {note} {accidental}",
"Give me an {note} {accidental}",
"Make an {note} {accidental}",
"Put on an {note} {accidental}",
"Sing at an {note} {accidental}",
"Sing at a {note} {accidental}",
"Create an {note} {accidental}",
"Lets hear an {note} {accidental}",
"Play an {note} {accidental}",
"Lets hear a {note} {accidental}",
"Sing at {note} {accidental}",
"Create a {note} {accidental}",
"Make a {note} {accidental}",
"Put on a {note} {accidental}",
"Initiate a {note} {accidental}",
"Give me a {note} {accidental}",
"Start a {note} {accidental}",
"Play a {note} {accidental}"
]
}
],
"types": [
{
"name": "Note",
"values": [
{
"name": {
"value": "B"
}
},
{
"name": {
"value": "A#"
}
},
{
"name": {
"value": "A"
}
},
{
"name": {
"value": "G#"
}
},
{
"name": {
"value": "G"
}
},
{
"name": {
"value": "F"
}
},
{
"name": {
"value": "E"
}
},
{
"name": {
"value": "D#"
}
},
{
"name": {
"value": "D"
}
},
{
"name": {
"value": "C#"
}
},
{
"name": {
"value": "C"
}
}
]
},
{
"name": "accidental",
"values": [
{
"name": {
"value": "natural"
}
},
{
"name": {
"value": "flat"
}
},
{
"name": {
"value": "sharp"
}
}
]
}
]
}
}
}
Код работал изначально, но после того, как я сделал несколько изменений в коде Lambda, я снова и снова получал один и тот же ответ, которыйне имеет смысла.Я думаю, что проблема может заключаться в том факте, что я добавил дополнительное значение для случайного значения, и я добавил естественное значение, прежде чем перестраивать свой лямбда-код на сайте skillinator.io.Буду признателен за любую помощь, так как я весь день боролся с этим кодом.