Skip to content

Instantly share code, notes, and snippets.

@vidanov
Created September 23, 2019 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vidanov/acd480dde956768a0c6b26ac093e8daf to your computer and use it in GitHub Desktop.
Save vidanov/acd480dde956768a0c6b26ac093e8daf to your computer and use it in GitHub Desktop.
'use strict';
const Alexa = require('ask-sdk');
const Util = require('util.js');
/***********
Data: Customize the data below as you please.
***********/
const SKILL_NAME = "Am I A Toaster";
const HELP_MESSAGE_BEFORE_START = "Answer five yes or no questions, and I will tell you what kitchen appliance you are. Are you ready to start?";
const HELP_MESSAGE_AFTER_START = "Just respond with yes or no and I'll give you the result in the end.";
const HELP_REPROMPT = "Your kitchen appliance will be revealed after you answer all my yes or no questions.";
const STOP_MESSAGE = "If it's too hot, you've gotta get out of the kitchen.";
const MISUNDERSTOOD_INSTRUCTIONS_ANSWER = "Please answer with either yes or no.";
const imgUrl = (imgName) => imgName;
//const BACKGROUND_IMAGE_URL = "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/default.jpg";
const BACKGROUND_IMAGE_URL = imgUrl("default.jpg");
const BACKGROUND_GOODBYE_IMAGE_URL = "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/goodbye.jpg";
// const BACKGROUND_GOODBYE_IMAGE_URL = imgUrl("goodbye.jpg");
const BACKGROUND_HELP_IMAGE_URL = "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/help.jpg";
// const BACKGROUND_HELP_IMAGE_URL = imgUrl("help.jpg");
const WELCOME_MESSAGE = "If you answer all my questions, we can definitely find out whether you are a toaster or not. Are you ready to start?";
const INITIAL_QUESTION_INTROS = [
"Ok, let's go!",
"<say-as interpret-as='interjection'>Alrighty</say-as>! First question, coming right up!",
"Can't wait. <say-as interpret-as='interjection'>Ahem</say-as>.",
"<say-as interpret-as='interjection'>Woo-hoo!</say-as>."
];
const QUESTION_INTROS = [
"Wowzers.",
"Cool beans.",
"Fascinating!",
"<say-as interpret-as='interjection'>Hmm</say-as>, makes sense.",
"We have a lot in common.",
"Yes, of course.",
"You surprise me.",
"Spot on.",
"One hundred percent.",
"Very insightful."
];
const UNDECISIVE_RESPONSES = [
"<say-as interpret-as='interjection'>Beep beep</say-as>. I'll just flip a coin",
"<say-as interpret-as='interjection'>Bee doe bee doe</say-as>. I'll take this one.",
"<say-as interpret-as='interjection'>Well</say-as>...let's just say you did",
"<say-as interpret-as='interjection'>Yo</say-as>. Don't make me do all the work here, k?.",
"<say-as interpret-as='interjection'>Hmm</say-as>. I feel like I know which one you were gonna pick.",
];
const RESULT_MESSAGE = "Just as I suspected. You are"; // the name of the result is inserted here.
const RESULT_MESSAGE_SHORT = "You are "; // the name of the result is inserted here.
const PLAY_AGAIN_REQUEST = "Do you want to play again?";
const resultList = {
result1: {
name: "a toaster",
display_name: "Toaster",
audio_message: "Toasters are dependable and known for brief flashes of excitement.",
description: "Toasters are easy and dependable friends. They can be counted on to do what they say but also love having a good time, after all their work is done, of course.",
img: {
//largeImageUrl: "https://coach-courses-us.s3.amazonaws.com/public/courses/voice/Example%20images%20skill%203/Red-knobbed.starfish.1200.jpg"
largeImageUrl: imgUrl("toaster1.jpg"),
}
},
result2: {
name: "a teapot",
display_name: "Teapot",
audio_message: "People know they can come to you for comfort, but if you get too hot... look out!",
description: "You are less flashy than some others, but few are as deeply loved. You are a warm gathering place where people know they will find warmth and care. But you're also not afraid to boil over and give the world a piece of your mind!",
img: {
//largeImageUrl: "https://coach-courses-us.s3.amazonaws.com/public/courses/voice/Example%20images%20skill%203/Aceria_anthocoptes.1200.jpg"
largeImageUrl: imgUrl("teapot1.jpg"),
}
},
result3: {
name: "a mixer",
display_name: "Hand Mixer",
audio_message: "Mixers aren't afraid to make a splash and bust a move!",
description: "You love to dive right in and make your presence known. Beauty and bravery this bold is known to get a little messy. But it's always worth it in the end.",
img: {
//largeImageUrl: "https://coach-courses-us.s3.amazonaws.com/public/courses/voice/Example%20images%20skill%203/Anodorhynchus_hyacinthinus.1200.jpg"
largeImageUrl: imgUrl("mixer1.jpg"),
}
},
result4: {
name: "a stove",
display_name: "Stove",
audio_message: "Stoves know lots of ways to get the job done and get it done right.",
description: "You do you best work when the pressure is on. You're good at multi-tasking and never give up on a challenge. You don't always get it right, sometimes things really go up in smoke, but everyone wants you on their team.",
img: {
//largeImageUrl: "https://coach-courses-us.s3.amazonaws.com/public/courses/voice/Example%20images%20skill%203/Male_goat.1200.jpg"
largeImageUrl: imgUrl("stove1.jpg"),
}
},
result5: {
name: "a blender",
display_name: "Blender",
audio_message: "You know what you want and you go hard. Classic and always hungry.",
description: "When you're a hammer, the whole world looks like a nail. Well, you're a blender that's ready to pulverize whatever comes your way. You've perfected your skillset and are always down for a challenge.",
img: {
//largeImageUrl: "https://coach-courses-us.s3.amazonaws.com/public/courses/voice/Example%20images%20skill%203/Bufo_boreas.1200.jpg"
largeImageUrl: imgUrl("blender1.jpg"),
}
}
};
const questions = [{
question: "Do you like spending time socializing with others?",
questionDisplay: "Do you like spending time socializing?",
//background: "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/q1.jpg",
background: imgUrl("question1.jpg"),
points: {
result1: 4,
result2: 0,
result3: 5,
result4: 3,
result5: 1
}
},
{
question: "Do you enjoy sunbathing?",
questionDisplay: "Do you enjoy sunbathing?",
//background: "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/q2.jpg",
background: imgUrl("question2.jpg"),
points: {
result1: 4,
result2: 1,
result3: 2,
result4: 3,
result5: 5
}
},
{
question: "Do you enjoy reading a good book more than going out to a party?",
questionDisplay: "Do you enjoy a book more than a party?",
//background: "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/q3.jpg",
background: imgUrl("question3.jpg"),
points: {
result1: 0,
result2: 5,
result3: 1,
result4: 3,
result5: 4
}
},
{
question: "Do you like doing sports?",
questionDisplay: "Do you like doing sports?",
//background: "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/q4.jpg",
background: imgUrl("question4.jpg"),
points: {
result1: 2,
result2: 3,
result3: 4,
result4: 4,
result5: 5
}
},
{
question: "Do you prefer vacationing in the forest instead of on the beach?",
questionDisplay: "A beach vs a forest. Forest? Yes or no?",
//background: "https://s3.amazonaws.com/coach-courses-us/public/courses/voice/2.7/q5.jpg",
background: imgUrl("question5.jpg"),
points: {
result1: 0,
result2: 5,
result3: 3,
result4: 4,
result5: 5
}
}
];
/***********
Execution Code: Avoid editing the code below if you don't know JavaScript.
***********/
// Private methods (this is the actual code logic behind the app)
const newSessionHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
var isCurrentlyPlayingOrGettingResult = false;
if (sessionAttributes.state) {
isCurrentlyPlayingOrGettingResult = true;
}
return handlerInput.requestEnvelope.request.type === `LaunchRequest` || (!isCurrentlyPlayingOrGettingResult && request.type === 'IntentRequest' && (request.intent.name === 'AMAZON.YesIntent' || request.intent.name === 'AMAZON.NoIntent'));
},
handle(handlerInput) {
console.log('--------------------------------------- New session')
const request = handlerInput.requestEnvelope.request;
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
if (handlerInput.requestEnvelope.request.type === `LaunchRequest`) {
_initializeApp(sessionAttributes);
return buildResponse(handlerInput, WELCOME_MESSAGE, WELCOME_MESSAGE, SKILL_NAME);
}
if (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.YesIntent') {
sessionAttributes.state = states.QUIZMODE;
const systemSpeak = _nextQuestionOrResult(handlerInput);
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background,systemSpeak.displayText);
}
if (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.NoIntent') {
console.log('--------------------------------------- Exit session')
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes.state = '';
return buildResponse(handlerInput, STOP_MESSAGE, '', SKILL_NAME, BACKGROUND_GOODBYE_IMAGE_URL,STOP_MESSAGE);
}
if (request.type === 'IntentRequest' && request.intent.name === 'UndecisiveIntent') {
const outputSpeech = MISUNDERSTOOD_INSTRUCTIONS_ANSWER;
return buildResponse(handlerInput, outputSpeech, outputSpeech, SKILL_NAME, BACKGROUND_IMAGE_URL,"");
}
},
};
const nextQuestionIntent = (handlerInput, prependMessage) => {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes.questionProgress++;
var currentQuestion = questions[sessionAttributes.questionProgress].question;
return {
prompt: `${prependMessage} ${_randomQuestionIntro(sessionAttributes)} ${currentQuestion}`,
reprompt: HELP_MESSAGE_AFTER_START,
displayText: questions[sessionAttributes.questionProgress].questionDisplay,
background: questions[sessionAttributes.questionProgress].background
};
}
const resultIntent = (handlerInput, prependMessage) => {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
const resultPoints = sessionAttributes.resultPoints;
const result = Object.keys(resultPoints).reduce((o, i) => resultPoints[o] > resultPoints[i] ? o : i);
const resultMessage = `${prependMessage} ${RESULT_MESSAGE} ${resultList[result].name}. ${resultList[result].audio_message}. ${PLAY_AGAIN_REQUEST}`;
return {
prompt: resultMessage,
reprompt: PLAY_AGAIN_REQUEST,
displayText: `${RESULT_MESSAGE_SHORT} ${resultList[result].name}`,
background: resultList[result].img.largeImageUrl
}
// this.emit(':askWithCard', resultMessage, PLAY_AGAIN_REQUEST, resultList[result].display_name, resultList[result].description, resultList[result].img);
// ^speechOutput ^repromptSpeech ^cardTitle ^cardContent ^imageObj
}
const _randomIndexOfArray = (array) => Math.floor(Math.random() * array.length);
const _randomOfArray = (array) => array[_randomIndexOfArray(array)];
const _adder = (a, b) => a + b;
const _subtracter = (a, b) => a - b;
// Handle user input and intents:
const states = {
QUIZMODE: "_QUIZMODE",
RESULTMODE: "_RESULTMODE"
}
const quizModeHandler = {
canHandle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
var isCurrentlyPlaying = false;
if (sessionAttributes.state && sessionAttributes.state === states.QUIZMODE) {
isCurrentlyPlaying = true;
}
return isCurrentlyPlaying;
},
handle(handlerInput) {
console.log('---------------------------------------Quiz Mode')
const request = handlerInput.requestEnvelope.request;
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
var prependMessage = '';
if (request.type === 'IntentRequest' && request.intent.name === 'NextQuestionIntent') {
const systemSpeak = nextQuestionIntent(handlerInput, prependMessage);
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background,systemSpeak.displayText);
}
if (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.YesIntent') {
_applyresultPoints(sessionAttributes, _adder);
sessionAttributes.state = states.QUIZMODE;
const systemSpeak = _nextQuestionOrResult(handlerInput);
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background,systemSpeak.displayText);
}
if (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.NoIntent') {
_applyresultPoints(sessionAttributes, _subtracter);
sessionAttributes.state = states.QUIZMODE;
const systemSpeak = _nextQuestionOrResult(handlerInput);
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background,systemSpeak.displayText);
}
if (request.type === 'IntentRequest' && request.intent.name === 'UndecisiveIntent') {
Math.round(Math.random()) ? _applyresultPoints(sessionAttributes, _adder) : _applyresultPoints(sessionAttributes, _subtracter);
const systemSpeak = _nextQuestionOrResult(handlerInput, _randomOfArray(UNDECISIVE_RESPONSES));
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background,systemSpeak.displayText);
}
if (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.RepeatIntent') {
var currentQuestion = questions[sessionAttributes.questionProgress].question;
return buildResponse(handlerInput, currentQuestion, HELP_MESSAGE_AFTER_START, SKILL_NAME, BACKGROUND_HELP_IMAGE_URL);
}
},
};
const resultModeHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
var isCurrentlyPlaying = false;
if (sessionAttributes.state &&
sessionAttributes.state === states.QUIZMODE) {
isCurrentlyPlaying = true;
}
return !isCurrentlyPlaying && request.type === 'IntentRequest' && sessionAttributes.state === states.RESULTMODE;
},
handle(handlerInput) {
console.log('--------------------------------------- Result mode')
const request = handlerInput.requestEnvelope.request;
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
if (request.intent.name === 'AMAZON.YesIntent') {
_initializeApp(sessionAttributes);
sessionAttributes.state = states.QUIZMODE;
const systemSpeak = _nextQuestionOrResult(handlerInput);
return buildResponse(handlerInput, systemSpeak.prompt, systemSpeak.reprompt, SKILL_NAME, systemSpeak.background, systemSpeak.displayText);
}
if (request.intent.name === 'AMAZON.NoIntent') {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes.state = '';
return buildResponse(handlerInput, STOP_MESSAGE, '', SKILL_NAME, BACKGROUND_GOODBYE_IMAGE_URL,STOP_MESSAGE);
}
},
};
const ExitHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest' &&
(request.intent.name === 'AMAZON.CancelIntent' ||
request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
console.log('--------------------------------------- Exit session')
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes.state = '';
return buildResponse(handlerInput, STOP_MESSAGE, '', SKILL_NAME, BACKGROUND_GOODBYE_IMAGE_URL,STOP_MESSAGE);
//------------------------------------------------
},
};
const HelpIntentHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest' &&
request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
console.log('--------------------------------------- Help')
const attributesManager = handlerInput.attributesManager;
var speechOutput = '';
const sessionAttributes = attributesManager.getSessionAttributes();
if (sessionAttributes.state === states.QUIZMODE) {
speechOutput = HELP_MESSAGE_AFTER_START;
} else {
speechOutput = HELP_MESSAGE_BEFORE_START;
}
const reprompt = HELP_REPROMPT;
return buildResponse(handlerInput, speechOutput, reprompt, SKILL_NAME, BACKGROUND_HELP_IMAGE_URL);
},
};
const UnhandledHandler = {
canHandle() {
return true;
},
handle(handlerInput) {
console.log('---------------------------------------Unhandled')
const outputSpeech = MISUNDERSTOOD_INSTRUCTIONS_ANSWER;
return buildResponse(handlerInput, outputSpeech, outputSpeech, SKILL_NAME);
},
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
console.log("Inside SessionEndedRequestHandler");
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
console.log(`Session ended with reason: ${JSON.stringify(handlerInput.requestEnvelope)}`);
return handlerInput.responseBuilder.getResponse();
},
};
// Skill brain
const _initializeApp = handler => {
// Set the progress to -1 one in the beginning
handler.questionProgress = -1;
// Assign 0 points to each animal
var initialPoints = {};
Object.keys(resultList).forEach(result => initialPoints[result] = 0);
handler.resultPoints = initialPoints;
};
const _nextQuestionOrResult = (handlerInput, prependMessage = '') => {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = attributesManager.getSessionAttributes();
if (sessionAttributes.questionProgress >= (questions.length - 1)) {
sessionAttributes.state = states.RESULTMODE;
return resultIntent(handlerInput, prependMessage)
} else {
return nextQuestionIntent(handlerInput, prependMessage);
}
};
const _applyresultPoints = (handler, calculate) => {
const currentPoints = handler.resultPoints;
const pointsToAdd = questions[handler.questionProgress].points;
handler.resultPoints = Object.keys(currentPoints).reduce((newPoints, result) => {
newPoints[result] = calculate(currentPoints[result], pointsToAdd[result]);
return newPoints;
}, currentPoints);
};
const _randomQuestionIntro = handler => {
if (handler.questionProgress === 0) {
// return random initial question intro if it's the first question:
return _randomOfArray(INITIAL_QUESTION_INTROS);
} else {
// Assign all question intros to remainingQuestionIntros on the first execution:
var remainingQuestionIntros = remainingQuestionIntros || QUESTION_INTROS;
// randomQuestion will return 0 if the remainingQuestionIntros are empty:
let randomQuestion = remainingQuestionIntros.splice(_randomIndexOfArray(remainingQuestionIntros), 1);
// Remove random Question from rameining question intros and return the removed question. If the remainingQuestions are empty return the first question:
return randomQuestion ? randomQuestion : QUESTION_INTROS[0];
}
};
// Utilities
let buildResponse = (handlerInput, prompt, reprompt, title = SKILL_NAME, image_url = BACKGROUND_IMAGE_URL, displayText = SKILL_NAME, display_type = "BodyTemplate7" ) => {
console.log(title);
if (reprompt) {
handlerInput.responseBuilder.reprompt(reprompt);
} else {
handlerInput.responseBuilder.withShouldEndSession(true);
}
var response;
if (supportsDisplay(handlerInput)) {
response = getDisplay(handlerInput.responseBuilder, title, displayText, image_url, display_type)
}
else{
response = handlerInput.responseBuilder
}
return response
.speak(prompt)
.getResponse();
}
function supportsDisplay(handlerInput) {
var hasDisplay =
handlerInput.requestEnvelope.context &&
handlerInput.requestEnvelope.context.System &&
handlerInput.requestEnvelope.context.System.device &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display
return hasDisplay;
}
function getDisplay(response, title, displayText, image_url, display_type){
if (!image_url.includes('https://')) {
image_url=Util.getS3PreSignedUrl("Media/"+image_url);
}
const image = new Alexa.ImageHelper().addImageInstance(image_url).getImage();
console.log("the display type is => " + display_type);
console.log(image_url);
const myTextContent = new Alexa.RichTextContentHelper()
.withPrimaryText(title+"<br/>")
.withSecondaryText(displayText)
.withTertiaryText("<br/> ")
.getTextContent();
if (display_type === "BodyTemplate7"){
//use background image
response.addRenderTemplateDirective({
type: display_type,
backButton: 'visible',
backgroundImage: image,
title:displayText,
textContent: myTextContent,
});
}
else{
response.addRenderTemplateDirective({
//use 340x340 image on the right with text on the left.
type: display_type,
backButton: 'visible',
image: image,
title:displayText,
textContent: myTextContent,
});
}
return response
}
// Init
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
.addRequestHandlers(
SessionEndedRequestHandler, HelpIntentHandler, ExitHandler, newSessionHandler, quizModeHandler, resultModeHandler, UnhandledHandler
)
//.addErrorHandlers(ErrorHandler)
.lambda();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment