Skip to content

Instantly share code, notes, and snippets.

@muttoni
Created June 5, 2019 13:39
Show Gist options
  • Select an option

  • Save muttoni/c97b4cdfa1ed072977f0a8c114a926fc to your computer and use it in GitHub Desktop.

Select an option

Save muttoni/c97b4cdfa1ed072977f0a8c114a926fc to your computer and use it in GitHub Desktop.
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
const {
getSlotValue
} = require('ask-sdk-core');
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
async handle(handlerInput) {
const persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes();
let sessionAttributes;
if(persistentAttributes) {
sessionAttributes = persistentAttributes;
}
handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
const speechText = `<audio src='soundbank://soundlibrary/nature/amzn_sfx_ocean_wave_on_rocks_1x_02'/> Welcome to My Perfect Getaway. I can help you plan your perfect trip or review a past getaway. How can I help you?`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const PlanGetawayHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'PlanGetaway';
},
handle(handlerInput) {
const duration = handlerInput.requestEnvelope.request.intent.slots.duration.value;
const location = handlerInput.requestEnvelope.request.intent.slots.location.value;
const departure_time = handlerInput.requestEnvelope.request.intent.slots.departure_time.value;
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
if(sessionAttributes && sessionAttributes.trip_count && sessionAttributes.trip_count > 0) {
sessionAttributes.trip_count += 1;
} else {
sessionAttributes.trip_count = 1;
}
const duration2 = getSlotValue(handlerInput.requestEnvelope, 'duration');
console.log('duration:', duration2)
const speechText = `Ok, lets find you a trip to ${location} for ${duration2} leaving ${departure_time}. Buckle up <audio src='soundbank://soundlibrary/transportation/amzn_sfx_car_click_seatbelt_01'/>, and let's go! <audio src='soundbank://soundlibrary/transportation/amzn_sfx_airplane_takeoff_whoosh_01'/>`;
handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
return handlerInput.responseBuilder
.speak(speechText)
.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speechText = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
|| handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
},
async handle(handlerInput) {
const speechText = 'Goodbye!';
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
handlerInput.attributesManager.setPersistentAttributes(sessionAttributes);
await handlerInput.attributesManager.savePersistentAttributes();
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const intentName = handlerInput.requestEnvelope.request.intent.name;
const speechText = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speechText)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.message}`);
const speechText = `Sorry, I couldn't understand what you said. Please try again.`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
// This handler acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
PlanGetawayHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
.addErrorHandlers(
ErrorHandler)
.withPersistenceAdapter(
new persistenceAdapter.S3PersistenceAdapter({bucketName:process.env.S3_PERSISTENCE_BUCKET})
)
.lambda();
{
"interactionModel": {
"languageModel": {
"invocationName": "my perfect getaway",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "PlanGetaway",
"slots": [
{
"name": "duration",
"type": "AMAZON.DURATION",
"samples": [
"how about {duration}",
"lets stay {duration}",
"{duration}"
]
},
{
"name": "location",
"type": "AMAZON.City",
"samples": [
"let's go to {location}",
"{location}"
]
},
{
"name": "departure_time",
"type": "AMAZON.DATE",
"samples": [
"{departure_time}"
]
},
{
"name": "theme",
"type": "THEMES"
}
],
"samples": [
"help me plan a {theme} vacation",
"plan a {theme} getaway",
"i want to leave {departure_time}",
"to go to {location}",
"plan a trip to {location}",
"i want to go to {location}",
"plan a {duration} day getaway",
"i am bored",
"get me out of here",
"find me a holiday",
"i want a holiday",
"plan a trip",
"i want to get away"
]
}
],
"types": [
{
"name": "THEMES",
"values": [
{
"name": {
"value": "relaxing",
"synonyms": [
"doing nothing",
"recharge",
"chill out",
"chilled",
"relax"
]
}
},
{
"name": {
"value": "adrenaline",
"synonyms": [
"crazy",
"exploration",
"adventure"
]
}
},
{
"name": {
"value": "romantic",
"synonyms": [
"honeymoon",
"couples",
"passionate",
"love",
"romance"
]
}
}
]
}
]
},
"dialog": {
"intents": [
{
"name": "PlanGetaway",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "duration",
"type": "AMAZON.DURATION",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.682835773707.818990482642"
}
},
{
"name": "location",
"type": "AMAZON.City",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.682835773707.1416995003727"
}
},
{
"name": "departure_time",
"type": "AMAZON.DATE",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.682835773707.1248430058630"
}
},
{
"name": "theme",
"type": "THEMES",
"confirmationRequired": false,
"elicitationRequired": false,
"prompts": {}
}
]
}
],
"delegationStrategy": "ALWAYS"
},
"prompts": [
{
"id": "Elicit.Slot.682835773707.818990482642",
"variations": [
{
"type": "PlainText",
"value": "How long?"
},
{
"type": "PlainText",
"value": "How many days do you want to escape?"
},
{
"type": "PlainText",
"value": "How long shall the getaway be?"
},
{
"type": "PlainText",
"value": "How long do you want to go on vacation for?"
}
]
},
{
"id": "Elicit.Slot.682835773707.1416995003727",
"variations": [
{
"type": "PlainText",
"value": "What's your destination?"
},
{
"type": "PlainText",
"value": "Where to?"
},
{
"type": "PlainText",
"value": "Where do you wanna go?"
}
]
},
{
"id": "Elicit.Slot.682835773707.1248430058630",
"variations": [
{
"type": "PlainText",
"value": "When do you want to leave?"
},
{
"type": "PlainText",
"value": "I love {location} . When do you want to leave?"
},
{
"type": "PlainText",
"value": "{location} it is. When do you want to leave?"
}
]
}
]
}
}
{
"name": "hello-world",
"version": "0.9.0",
"description": "alexa utility for quickly building skills",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Amazon Alexa",
"license": "ISC",
"dependencies": {
"ask-sdk-core": "*",
"ask-sdk-s3-persistence-adapter": "*",
"ask-sdk-model": "^1.4.1",
"aws-sdk": "^2.326.0"
}
}
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
const {
getSlotValue
} = require('ask-sdk-core');
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
const speechText = 'Welcome, you can say Hello or Help. Which would you like to try?';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const SwearIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'SwearIntent';
},
handle(handlerInput) {
const phrase = getSlotValue(handlerInput.requestEnvelope, 'phrase');
const nonowords = [
'damn',
'dang',
'heck',
'shoot',
'fudge'
];
let count = 0;
nonowords.map(function(item) {
const regex = RegExp(item,'g');
const match = phrase.match(regex);
count += (match || []).length;
});
const speechText = `You used ${count} bad words!`;
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
if(sessionAttributes && sessionAttributes.swear_count && sessionAttributes.swear_count > 0) {
sessionAttributes.swear_count += count;
} else {
sessionAttributes.swear_count = count;
}
return handlerInput.responseBuilder
.speak(speechText)
.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speechText = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
|| handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speechText = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const intentName = handlerInput.requestEnvelope.request.intent.name;
const speechText = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speechText)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.message}`);
const speechText = `Sorry, I couldn't understand what you said. Please try again.`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const requestPresistenceInterceptor = {
async process(handlerInput) {
let persistentAttributes;
if (handlerInput.requestEnvelope.session['new']) {
try {
persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes() || {};
handlerInput.attributesManager.setSessionAttributes(persistentAttributes);
} catch (e) {
console.log('RequestInterceptor:: could not retrieve persistent attributes');
}
}
}
}
const responsePersistenceInterceptor = {
async process(handlerInput, responseOutput) {
const ses = (typeof responseOutput.shouldEndSession === "undefined" ? true : responseOutput.shouldEndSession);
if (ses || handlerInput.requestEnvelope.request.type === 'SessionEndedRequest') { // skill was stopped or timed out
let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
handlerInput.attributesManager.setPersistentAttributes(sessionAttributes);
await handlerInput.attributesManager.savePersistentAttributes();
}
}
}
// This handler acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
SwearIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
.addRequestInterceptors(requestPresistenceInterceptor)
.addResponseInterceptors(responsePersistenceInterceptor)
.addErrorHandlers(
ErrorHandler)
.withPersistenceAdapter(
new persistenceAdapter.S3PersistenceAdapter({bucketName:process.env.S3_PERSISTENCE_BUCKET})
)
.lambda();
{
"interactionModel": {
"languageModel": {
"invocationName": "swear buddy",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "SwearIntent",
"slots": [
{
"name": "phrase",
"type": "AMAZON.SearchQuery"
}
],
"samples": [
"to {phrase}",
"that {phrase}"
]
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
}
],
"types": []
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment