alexa-sdk
Version:
Alexa Skill SDK for Node.js
402 lines (365 loc) • 14.6 kB
JavaScript
'use strict';
const packageInfo = require('../package.json');
const attributesHelper = require('./DynamoAttributesHelper');
module.exports = (function () {
return {
':tell': function (speechOutput) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
shouldEndSession: true
});
this.emit(':responseReady');
},
':ask': function (speechOutput, repromptSpeech) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
shouldEndSession: false
});
this.emit(':responseReady');
},
':askWithCard': function(speechOutput, repromptSpeech, cardTitle, cardContent, imageObj) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
cardTitle: cardTitle,
cardContent: cardContent,
cardImage: imageObj,
shouldEndSession: false
});
this.emit(':responseReady');
},
':tellWithCard': function(speechOutput, cardTitle, cardContent, imageObj) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
cardTitle: cardTitle,
cardContent: cardContent,
cardImage: imageObj,
shouldEndSession: true
});
this.emit(':responseReady');
},
':tellWithLinkAccountCard': function(speechOutput) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
cardType: 'LinkAccount',
shouldEndSession: true
});
this.emit(':responseReady');
},
':askWithLinkAccountCard': function(speechOutput, repromptSpeech) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
cardType: 'LinkAccount',
shouldEndSession: false
});
this.emit(':responseReady');
},
':askWithPermissionCard': function(speechOutput, repromptSpeech, permissions) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
permissions: permissions,
cardType: 'AskForPermissionsConsent',
shouldEndSession: false
});
this.emit(':responseReady');
},
':tellWithPermissionCard': function(speechOutput, permissions) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
permissions: permissions,
cardType: 'AskForPermissionsConsent',
shouldEndSession: true
});
this.emit(':responseReady');
},
':delegate': function(updatedIntent) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
directives: getDialogDirectives('Dialog.Delegate', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady');
},
':elicitSlot': function (slotName, speechOutput, repromptSpeech, updatedIntent) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
directives: getDialogDirectives('Dialog.ElicitSlot', updatedIntent, slotName),
shouldEndSession: false
});
this.emit(':responseReady');
},
':elicitSlotWithCard': function (slotName, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
cardTitle: cardTitle,
cardContent: cardContent,
cardImage: imageObj,
directives: getDialogDirectives('Dialog.ElicitSlot', updatedIntent, slotName),
shouldEndSession: false
});
this.emit(':responseReady');
},
':confirmSlot': function (slotName, speechOutput, repromptSpeech, updatedIntent) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
directives: getDialogDirectives('Dialog.ConfirmSlot', updatedIntent, slotName),
shouldEndSession: false
});
this.emit(':responseReady');
},
':confirmSlotWithCard': function (slotName, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
cardTitle: cardTitle,
cardContent: cardContent,
cardImage: imageObj,
directives: getDialogDirectives('Dialog.ConfirmSlot', updatedIntent, slotName),
shouldEndSession: false
});
this.emit(':responseReady');
},
':confirmIntent': function (speechOutput, repromptSpeech, updatedIntent) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
directives: getDialogDirectives('Dialog.ConfirmIntent', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady');
},
':confirmIntentWithCard': function (speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj) {
if(this.isOverridden()) {
return;
}
this.handler.response = buildSpeechletResponse({
sessionAttributes: this.attributes,
output: getSSMLResponse(speechOutput),
reprompt: getSSMLResponse(repromptSpeech),
cardTitle: cardTitle,
cardContent: cardContent,
cardImage: imageObj,
directives: getDialogDirectives('Dialog.ConfirmIntent', updatedIntent, null),
shouldEndSession: false
});
this.emit(':responseReady');
},
':responseReady': function () {
if (this.isOverridden()) {
return;
}
if(this.handler.state) {
this.handler.response.sessionAttributes.STATE = this.handler.state;
}
this.handler.response.userAgent = `ask-nodejs/${packageInfo.version} Node/${process.version}`;
if (this.handler.dynamoDBTableName) {
return this.emit(':saveState');
}
if(typeof this.callback === 'undefined') {
this.context.succeed(this.handler.response);
} else {
this.callback(null, this.handler.response);
}
},
':saveState': function(forceSave) {
if (this.isOverridden()) {
return;
}
if(forceSave && this.handler.state){
this.attributes.STATE = this.handler.state;
}
let userId = '';
// Long-form audio enabled skills use event.context
if (this.event.context) {
userId = this.event.context.System.user.userId;
} else if (this.event.session) {
userId = this.event.session.user.userId;
}
if(this.handler.saveBeforeResponse || forceSave || this.handler.response.response.shouldEndSession) {
attributesHelper(this.dynamoDBClient).set(this.handler.dynamoDBTableName, userId, this.attributes,
(err) => {
if(err) {
return this.emit(':saveStateError', err);
}
// To save the state when AudioPlayer Requests come without sending a response.
if (Object.keys(this.handler.response).length === 0 && this.handler.response.constructor === Object) {
this.handler.response = true;
}
if(typeof this.callback === 'undefined') {
this.context.succeed(this.handler.response);
} else {
this.callback(null, this.handler.response);
}
});
} else {
if(typeof this.callback === 'undefined') {
this.context.succeed(this.handler.response || true);
} else {
this.callback(null, this.handler.response || true);
}
}
},
':saveStateError': function(err) {
if(this.isOverridden()) {
return;
}
console.log(`Error saving state: ${err}\n${err.stack}`);
if(typeof this.callback === 'undefined') {
this.context.fail(err);
} else {
this.callback(err);
}
}
};
})();
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) {
const 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
};
}
const returnResult = {
version: '1.0',
response: alexaResponse
};
if (options.sessionAttributes) {
returnResult.sessionAttributes = options.sessionAttributes;
}
return returnResult;
}
// TODO: check for ssml content in card
function getSSMLResponse(message) {
if (message == null) { // jshint ignore:line
return null;
} else {
return {
type: 'SSML',
speech: `<speak> ${message} </speak>`
};
}
}
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];
}