diff --git a/lambda/custom/index.js b/lambda/custom/index.js index f5e1582..9077441 100755 --- a/lambda/custom/index.js +++ b/lambda/custom/index.js @@ -1,22 +1,41 @@ +// Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Licensed under the Amazon Software License +// http://aws.amazon.com/asl/ + /* eslint-disable func-names */ /* eslint-disable no-console */ /* eslint-disable no-restricted-syntax */ -// IMPORTANT: Please note that this template uses Dispay Directives, +// IMPORTANT: Please note that this template uses Display Directives, // Display Interface for your skill should be enabled through the Amazon developer console // See this screenshot - https://alexa.design/enabledisplay const Alexa = require('ask-sdk-core'); +const i18n = require('i18next'); +const sprintf = require('i18next-sprintf-postprocessor'); + +const languageStrings = { + en: require('./languages/en.js'), +}; + +const states = { + START: '_START', + QUIZ: '_QUIZ', +}; + +const useCardsFlag = true; /* INTENT HANDLERS */ const LaunchRequestHandler = { canHandle(handlerInput) { - return handlerInput.requestEnvelope.request.type === `LaunchRequest`; + return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); + return handlerInput.responseBuilder - .speak(welcomeMessage) - .reprompt(helpMessage) + .speak(requestAttributes.t('WELCOME_MESSAGE')) + .reprompt(requestAttributes.t('HELP_MESSAGE')) .getResponse(); }, }; @@ -24,85 +43,86 @@ const LaunchRequestHandler = { const QuizHandler = { canHandle(handlerInput) { const request = handlerInput.requestEnvelope.request; - console.log("Inside QuizHandler"); + console.log('Inside QuizHandler'); console.log(JSON.stringify(request)); - return request.type === "IntentRequest" && - (request.intent.name === "QuizIntent" || request.intent.name === "AMAZON.StartOverIntent"); + return request.type === 'IntentRequest' && + (request.intent.name === 'QuizIntent' || request.intent.name === 'AMAZON.StartOverIntent'); }, handle(handlerInput) { - console.log("Inside QuizHandler - handle"); + console.log('Inside QuizHandler - handle'); const attributes = handlerInput.attributesManager.getSessionAttributes(); const response = handlerInput.responseBuilder; + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); + attributes.state = states.QUIZ; attributes.counter = 0; attributes.quizScore = 0; - var question = askQuestion(handlerInput); - var speakOutput = startQuizMessage + question; - var repromptOutput = question; + const question = askQuestion(handlerInput); + const speakOutput = requestAttributes.t('START_QUIZ_MESSAGE') + question; + const repromptOutput = question; const item = attributes.quizItem; const property = attributes.quizProperty; if (supportsDisplay(handlerInput)) { const title = `Question #${attributes.counter}`; - const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(getQuestionWithoutOrdinal(property, item)).getTextContent(); const backgroundImage = new Alexa.ImageHelper().addImageInstance(getBackgroundImage(attributes.quizItem.Abbreviation)).getImage(); const itemList = []; - getAndShuffleMultipleChoiceAnswers(attributes.selectedItemIndex, item, property).forEach((x, i) => { - itemList.push( - { - "token" : x, - "textContent" : new Alexa.PlainTextContentHelper().withPrimaryText(x).getTextContent(), - } - ); + getAndShuffleMultipleChoiceAnswers(attributes.selectedItemIndex, item, property).forEach((x) => { + itemList.push({ + token: x, + textContent: new Alexa.PlainTextContentHelper().withPrimaryText(x).getTextContent(), + }); }); response.addRenderTemplateDirective({ - type : 'ListTemplate1', - token : 'Question', - backButton : 'hidden', + type: 'ListTemplate1', + token: 'Question', + backButton: 'hidden', backgroundImage, title, - listItems : itemList, + listItems: itemList, }); } return response.speak(speakOutput) - .reprompt(repromptOutput) - .getResponse(); + .reprompt(repromptOutput) + .getResponse(); }, }; const DefinitionHandler = { canHandle(handlerInput) { - console.log("Inside DefinitionHandler"); + console.log('Inside DefinitionHandler'); const attributes = handlerInput.attributesManager.getSessionAttributes(); const request = handlerInput.requestEnvelope.request; return attributes.state !== states.QUIZ && - request.type === 'IntentRequest' && - request.intent.name === 'AnswerIntent'; + request.type === 'IntentRequest' && + request.intent.name === 'AnswerIntent'; }, handle(handlerInput) { - console.log("Inside DefinitionHandler - handle"); - //GRABBING ALL SLOT VALUES AND RETURNING THE MATCHING DATA OBJECT. + console.log('Inside DefinitionHandler - handle'); + // GRABBING ALL SLOT VALUES AND RETURNING THE MATCHING DATA OBJECT. const item = getItem(handlerInput.requestEnvelope.request.intent.slots); const response = handlerInput.responseBuilder; + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); - //IF THE DATA WAS FOUND + // IF THE DATA WAS FOUND if (item && item[Object.getOwnPropertyNames(data[0])[0]] !== undefined) { if (useCardsFlag) { response.withStandardCard( getCardTitle(item), getTextDescription(item), getSmallImage(item), - getLargeImage(item)) + getLargeImage(item), + ); } - if(supportsDisplay(handlerInput)) { + if (supportsDisplay(handlerInput)) { const image = new Alexa.ImageHelper().addImageInstance(getLargeImage(item)).getImage(); const title = getCardTitle(item); - const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(getTextDescription(item, "
")).getTextContent(); + const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(getTextDescription(item, '
')).getTextContent(); response.addRenderTemplateDirective({ type: 'BodyTemplate2', backButton: 'visible', @@ -111,37 +131,46 @@ const DefinitionHandler = { textContent: primaryText, }); } - return response.speak(getSpeechDescription(item)) - .reprompt(repromptSpeech) - .getResponse(); - } - //IF THE DATA WAS NOT FOUND - else - { - return response.speak(getBadAnswer(item)) - .reprompt(getBadAnswer(item)) - .getResponse(); + + const repromptOutput = requestAttributes.t('GET_DESCRIPTION_REPROMPT'); + const speakOutput = requestAttributes.t( + 'GET_DESCRIPTION_SPEECH', item.StateName, item.StatehoodOrder, item.StatehoodYear, + item.StateName, item.Capital, item.StateName, item.Abbreviation, item.StateName, + ) + repromptOutput; + + return response + .speak(speakOutput) + .reprompt(repromptOutput) + .getResponse(); } - } + // IF THE DATA WAS NOT FOUND + return response + .speak(requestAttributes.t('GET_BAD_ANSWER'), item, requestAttributes.t('HELP_MESSAGE')) + .reprompt(requestAttributes.t('GET_BAD_ANSWER'), item, requestAttributes.t('HELP_MESSAGE')) + .getResponse(); + }, }; const QuizAnswerHandler = { canHandle(handlerInput) { - console.log("Inside QuizAnswerHandler"); + console.log('Inside QuizAnswerHandler'); const attributes = handlerInput.attributesManager.getSessionAttributes(); const request = handlerInput.requestEnvelope.request; return attributes.state === states.QUIZ && - request.type === 'IntentRequest' && - request.intent.name === 'AnswerIntent'; + request.type === 'IntentRequest' && + request.intent.name === 'AnswerIntent'; }, handle(handlerInput) { - console.log("Inside QuizAnswerHandler - handle"); + console.log('Inside QuizAnswerHandler - handle'); + const attributes = handlerInput.attributesManager.getSessionAttributes(); const response = handlerInput.responseBuilder; + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); + + let speakOutput = ''; + let repromptOutput = ''; - var speakOutput = ``; - var repromptOutput = ``; const item = attributes.quizItem; const property = attributes.quizProperty; const isCorrect = compareSlots(handlerInput.requestEnvelope.request.intent.slots, item[property]); @@ -154,72 +183,71 @@ const QuizAnswerHandler = { speakOutput = getSpeechCon(false); } - speakOutput += getAnswer(property, item); - var question = ``; - //IF YOUR QUESTION COUNT IS LESS THAN 10, WE NEED TO ASK ANOTHER QUESTION. + speakOutput += getAnswer(property, item, handlerInput); + let question = ''; + // IF YOUR QUESTION COUNT IS LESS THAN 10, WE NEED TO ASK ANOTHER QUESTION. if (attributes.counter < 10) { - speakOutput += getCurrentScore(attributes.quizScore, attributes.counter); + speakOutput += requestAttributes.t('GET_CURRENT_SCORE', attributes.quizScore, attributes.counter); question = askQuestion(handlerInput); speakOutput += question; repromptOutput = question; if (supportsDisplay(handlerInput)) { const title = `Question #${attributes.counter}`; - const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(getQuestionWithoutOrdinal(attributes.quizProperty, attributes.quizItem)).getTextContent(); const backgroundImage = new Alexa.ImageHelper().addImageInstance(getBackgroundImage(attributes.quizItem.Abbreviation)).getImage(); const itemList = []; - getAndShuffleMultipleChoiceAnswers(attributes.selectedItemIndex, attributes.quizItem, attributes.quizProperty).forEach((x, i) => { - itemList.push( - { - "token" : x, - "textContent" : new Alexa.PlainTextContentHelper().withPrimaryText(x).getTextContent(), - } - ); + getAndShuffleMultipleChoiceAnswers(attributes.selectedItemIndex, attributes.quizItem, attributes.quizProperty).forEach((x) => { + itemList.push({ + token: x, + textContent: new Alexa.PlainTextContentHelper().withPrimaryText(x).getTextContent(), + }); }); response.addRenderTemplateDirective({ - type : 'ListTemplate1', - token : 'Question', - backButton : 'hidden', + type: 'ListTemplate1', + token: 'Question', + backButton: 'hidden', backgroundImage, title, - listItems : itemList, + listItems: itemList, }); } return response.speak(speakOutput) - .reprompt(repromptOutput) - .getResponse(); + .reprompt(repromptOutput) + .getResponse(); } - else { - speakOutput += getFinalScore(attributes.quizScore, attributes.counter) + exitSkillMessage; - if(supportsDisplay(handlerInput)) { - const title = 'Thank you for playing'; - const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(getFinalScore(attributes.quizScore, attributes.counter)).getTextContent(); - response.addRenderTemplateDirective({ - type : 'BodyTemplate1', - backButton: 'hidden', - title, - textContent: primaryText, - }); - } - return response.speak(speakOutput).getResponse(); + const finalScore = requestAttributes.t('GET_FINAL_SCORE', attributes.quizScore, attributes.counter); + speakOutput += finalScore + requestAttributes.t('EXIT_SKILL_MESSAGE'); + if (supportsDisplay(handlerInput)) { + const title = requestAttributes.t('CARD_TITLE'); + const primaryText = new Alexa.RichTextContentHelper().withPrimaryText(finalScore).getTextContent(); + response.addRenderTemplateDirective({ + type: 'BodyTemplate1', + backButton: 'hidden', + title, + textContent: primaryText, + }); } + return response + .speak(speakOutput) + .getResponse(); }, }; const RepeatHandler = { canHandle(handlerInput) { - console.log("Inside RepeatHandler"); + console.log('Inside RepeatHandler'); const attributes = handlerInput.attributesManager.getSessionAttributes(); const request = handlerInput.requestEnvelope.request; return attributes.state === states.QUIZ && - request.type === 'IntentRequest' && - request.intent.name === 'AMAZON.RepeatHandler'; + request.type === 'IntentRequest' && + request.intent.name === 'AMAZON.RepeatIntent'; }, handle(handlerInput) { - console.log("Inside RepeatHandler - handle"); + console.log('Inside RepeatHandler - handle'); const attributes = handlerInput.attributesManager.getSessionAttributes(); - const question = getQuestion(attributes.counter, attributes.quizproperty, attributes.quizitem); + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); + const question = requestAttributes.t('GET_QUESTION', attributes.counter, formatCasing(attributes.quizProperty), attributes.quizItem.StateName); return handlerInput.responseBuilder .speak(question) @@ -230,42 +258,43 @@ const RepeatHandler = { const HelpHandler = { canHandle(handlerInput) { - console.log("Inside HelpHandler"); + console.log('Inside HelpHandler'); const request = handlerInput.requestEnvelope.request; return request.type === 'IntentRequest' && - request.intent.name === 'AMAZON.HelpHandler'; + request.intent.name === 'AMAZON.HelpHandler'; }, handle(handlerInput) { - console.log("Inside HelpHandler - handle"); + console.log('Inside HelpHandler - handle'); + const requestAttributes = handlerInput.requestEnvelope.getRequestAttributes(); return handlerInput.responseBuilder - .speak(helpMessage) - .reprompt(helpMessage) + .speak(requestAttributes.t('HELP_MESSAGE')) + .reprompt(requestAttributes.t('HELP_MESSAGE')) .getResponse(); }, }; const ExitHandler = { canHandle(handlerInput) { - console.log("Inside ExitHandler"); - const attributes = handlerInput.attributesManager.getSessionAttributes(); + console.log('Inside ExitHandler'); const request = handlerInput.requestEnvelope.request; - return request.type === `IntentRequest` && ( - request.intent.name === 'AMAZON.StopIntent' || - request.intent.name === 'AMAZON.PauseIntent' || - request.intent.name === 'AMAZON.CancelIntent' - ); + return request.type === 'IntentRequest' && ( + request.intent.name === 'AMAZON.StopIntent' || + request.intent.name === 'AMAZON.PauseIntent' || + request.intent.name === 'AMAZON.CancelIntent' + ); }, handle(handlerInput) { + const requestAttributes = handlerInput.requestEnvelope.getRequestAttributes(); return handlerInput.responseBuilder - .speak(exitSkillMessage) + .speak(requestAttributes.t('EXIT_SKILL_MESSAGE')) .getResponse(); }, }; const SessionEndedRequestHandler = { canHandle(handlerInput) { - console.log("Inside SessionEndedRequestHandler"); + console.log('Inside SessionEndedRequestHandler'); return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest'; }, handle(handlerInput) { @@ -276,117 +305,91 @@ const SessionEndedRequestHandler = { const ErrorHandler = { canHandle() { - console.log("Inside ErrorHandler"); + console.log('Inside ErrorHandler'); return true; }, handle(handlerInput, error) { - console.log("Inside ErrorHandler - handle"); + console.log('Inside ErrorHandler - handle'); console.log(`Error handled: ${JSON.stringify(error)}`); console.log(`Handler Input: ${JSON.stringify(handlerInput)}`); return handlerInput.responseBuilder - .speak(helpMessage) - .reprompt(helpMessage) + .speak('HELP_MESSAGE') + .reprompt('HELP_MESSAGE') .getResponse(); }, }; /* CONSTANTS */ -const skillBuilder = Alexa.SkillBuilders.custom(); -const imagePath = "https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/quiz-game/state_flag/{0}x{1}/{2}._TTH_.png"; -const backgroundImagePath = "https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/quiz-game/state_flag/{0}x{1}/{2}._TTH_.png" -const speechConsCorrect = ['Booya', 'All righty', 'Bam', 'Bazinga', 'Bingo', 'Boom', 'Bravo', 'Cha Ching', 'Cheers', 'Dynomite', 'Hip hip hooray', 'Hurrah', 'Hurray', 'Huzzah', 'Oh dear. Just kidding. Hurray', 'Kaboom', 'Kaching', 'Oh snap', 'Phew','Righto', 'Way to go', 'Well done', 'Whee', 'Woo hoo', 'Yay', 'Wowza', 'Yowsa']; -const speechConsWrong = ['Argh', 'Aw man', 'Blarg', 'Blast', 'Boo', 'Bummer', 'Darn', "D'oh", 'Dun dun dun', 'Eek', 'Honk', 'Le sigh', 'Mamma mia', 'Oh boy', 'Oh dear', 'Oof', 'Ouch', 'Ruh roh', 'Shucks', 'Uh oh', 'Wah wah', 'Whoops a daisy', 'Yikes']; +const backgroundImagePath = 'https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/quiz-game/state_flag/{0}x{1}/{2}._TTH_.png'; +const speechConsCorrect = ['Booya', 'All righty', 'Bam', 'Bazinga', 'Bingo', 'Boom', 'Bravo', 'Cha Ching', 'Cheers', 'Dynomite', 'Hip hip hooray', 'Hurrah', 'Hurray', 'Huzzah', 'Oh dear. Just kidding. Hurray', 'Kaboom', 'Kaching', 'Oh snap', 'Phew', 'Righto', 'Way to go', 'Well done', 'Whee', 'Woo hoo', 'Yay', 'Wowza', 'Yowsa']; +const speechConsWrong = ['Argh', 'Aw man', 'Blarg', 'Blast', 'Boo', 'Bummer', 'Darn', 'D\'oh', 'Dun dun dun', 'Eek', 'Honk', 'Le sigh', 'Mamma mia', 'Oh boy', 'Oh dear', 'Oof', 'Ouch', 'Ruh roh', 'Shucks', 'Uh oh', 'Wah wah', 'Whoops a daisy', 'Yikes']; const data = [ - {StateName: 'Alabama', Abbreviation: 'AL', Capital: 'Montgomery', StatehoodYear: 1819, StatehoodOrder: 22}, - {StateName: 'Alaska', Abbreviation: 'AK', Capital: 'Juneau', StatehoodYear: 1959, StatehoodOrder: 49}, - {StateName: 'Arizona', Abbreviation: 'AZ', Capital: 'Phoenix', StatehoodYear: 1912, StatehoodOrder: 48}, - {StateName: 'Arkansas', Abbreviation: 'AR', Capital: 'Little Rock', StatehoodYear: 1836, StatehoodOrder: 25}, - {StateName: 'California', Abbreviation: 'CA', Capital: 'Sacramento', StatehoodYear: 1850, StatehoodOrder: 31}, - {StateName: 'Colorado', Abbreviation: 'CO', Capital: 'Denver', StatehoodYear: 1876, StatehoodOrder: 38}, - {StateName: 'Connecticut', Abbreviation: 'CT', Capital: 'Hartford', StatehoodYear: 1788, StatehoodOrder: 5}, - {StateName: 'Delaware', Abbreviation: 'DE', Capital: 'Dover', StatehoodYear: 1787, StatehoodOrder: 1}, - {StateName: 'Florida', Abbreviation: 'FL', Capital: 'Tallahassee', StatehoodYear: 1845, StatehoodOrder: 27}, - {StateName: 'Georgia', Abbreviation: 'GA', Capital: 'Atlanta', StatehoodYear: 1788, StatehoodOrder: 4}, - {StateName: 'Hawaii', Abbreviation: 'HI', Capital: 'Honolulu', StatehoodYear: 1959, StatehoodOrder: 50}, - {StateName: 'Idaho', Abbreviation: 'ID', Capital: 'Boise', StatehoodYear: 1890, StatehoodOrder: 43}, - {StateName: 'Illinois', Abbreviation: 'IL', Capital: 'Springfield', StatehoodYear: 1818, StatehoodOrder: 21}, - {StateName: 'Indiana', Abbreviation: 'IN', Capital: 'Indianapolis', StatehoodYear: 1816, StatehoodOrder: 19}, - {StateName: 'Iowa', Abbreviation: 'IA', Capital: 'Des Moines', StatehoodYear: 1846, StatehoodOrder: 29}, - {StateName: 'Kansas', Abbreviation: 'KS', Capital: 'Topeka', StatehoodYear: 1861, StatehoodOrder: 34}, - {StateName: 'Kentucky', Abbreviation: 'KY', Capital: 'Frankfort', StatehoodYear: 1792, StatehoodOrder: 15}, - {StateName: 'Louisiana', Abbreviation: 'LA', Capital: 'Baton Rouge', StatehoodYear: 1812, StatehoodOrder: 18}, - {StateName: 'Maine', Abbreviation: 'ME', Capital: 'Augusta', StatehoodYear: 1820, StatehoodOrder: 23}, - {StateName: 'Maryland', Abbreviation: 'MD', Capital: 'Annapolis', StatehoodYear: 1788, StatehoodOrder: 7}, - {StateName: 'Massachusetts', Abbreviation: 'MA', Capital: 'Boston', StatehoodYear: 1788, StatehoodOrder: 6}, - {StateName: 'Michigan', Abbreviation: 'MI', Capital: 'Lansing', StatehoodYear: 1837, StatehoodOrder: 26}, - {StateName: 'Minnesota', Abbreviation: 'MN', Capital: 'St. Paul', StatehoodYear: 1858, StatehoodOrder: 32}, - {StateName: 'Mississippi', Abbreviation: 'MS', Capital: 'Jackson', StatehoodYear: 1817, StatehoodOrder: 20}, - {StateName: 'Missouri', Abbreviation: 'MO', Capital: 'Jefferson City', StatehoodYear: 1821, StatehoodOrder: 24}, - {StateName: 'Montana', Abbreviation: 'MT', Capital: 'Helena', StatehoodYear: 1889, StatehoodOrder: 41}, - {StateName: 'Nebraska', Abbreviation: 'NE', Capital: 'Lincoln', StatehoodYear: 1867, StatehoodOrder: 37}, - {StateName: 'Nevada', Abbreviation: 'NV', Capital: 'Carson City', StatehoodYear: 1864, StatehoodOrder: 36}, - {StateName: 'New Hampshire', Abbreviation: 'NH', Capital: 'Concord', StatehoodYear: 1788, StatehoodOrder: 9}, - {StateName: 'New Jersey', Abbreviation: 'NJ', Capital: 'Trenton', StatehoodYear: 1787, StatehoodOrder: 3}, - {StateName: 'New Mexico', Abbreviation: 'NM', Capital: 'Santa Fe', StatehoodYear: 1912, StatehoodOrder: 47}, - {StateName: 'New York', Abbreviation: 'NY', Capital: 'Albany', StatehoodYear: 1788, StatehoodOrder: 11}, - {StateName: 'North Carolina', Abbreviation: 'NC', Capital: 'Raleigh', StatehoodYear: 1789, StatehoodOrder: 12}, - {StateName: 'North Dakota', Abbreviation: 'ND', Capital: 'Bismarck', StatehoodYear: 1889, StatehoodOrder: 39}, - {StateName: 'Ohio', Abbreviation: 'OH', Capital: 'Columbus', StatehoodYear: 1803, StatehoodOrder: 17}, - {StateName: 'Oklahoma', Abbreviation: 'OK', Capital: 'Oklahoma City', StatehoodYear: 1907, StatehoodOrder: 46}, - {StateName: 'Oregon', Abbreviation: 'OR', Capital: 'Salem', StatehoodYear: 1859, StatehoodOrder: 33}, - {StateName: 'Pennsylvania', Abbreviation: 'PA', Capital: 'Harrisburg', StatehoodYear: 1787, StatehoodOrder: 2}, - {StateName: 'Rhode Island', Abbreviation: 'RI', Capital: 'Providence', StatehoodYear: 1790, StatehoodOrder: 13}, - {StateName: 'South Carolina', Abbreviation: 'SC', Capital: 'Columbia', StatehoodYear: 1788, StatehoodOrder: 8}, - {StateName: 'South Dakota', Abbreviation: 'SD', Capital: 'Pierre', StatehoodYear: 1889, StatehoodOrder: 40}, - {StateName: 'Tennessee', Abbreviation: 'TN', Capital: 'Nashville', StatehoodYear: 1796, StatehoodOrder: 16}, - {StateName: 'Texas', Abbreviation: 'TX', Capital: 'Austin', StatehoodYear: 1845, StatehoodOrder: 28}, - {StateName: 'Utah', Abbreviation: 'UT', Capital: 'Salt Lake City', StatehoodYear: 1896, StatehoodOrder: 45}, - {StateName: 'Vermont', Abbreviation: 'VT', Capital: 'Montpelier', StatehoodYear: 1791, StatehoodOrder: 14}, - {StateName: 'Virginia', Abbreviation: 'VA', Capital: 'Richmond', StatehoodYear: 1788, StatehoodOrder: 10}, - {StateName: 'Washington', Abbreviation: 'WA', Capital: 'Olympia', StatehoodYear: 1889, StatehoodOrder: 42}, - {StateName: 'West Virginia', Abbreviation: 'WV', Capital: 'Charleston', StatehoodYear: 1863, StatehoodOrder: 35}, - {StateName: 'Wisconsin', Abbreviation: 'WI', Capital: 'Madison', StatehoodYear: 1848, StatehoodOrder: 30}, - {StateName: 'Wyoming', Abbreviation: 'WY', Capital: 'Cheyenne', StatehoodYear: 1890, StatehoodOrder: 44}, + { StateName: 'Alabama', Abbreviation: 'AL', Capital: 'Montgomery', StatehoodYear: 1819, StatehoodOrder: 22 }, + { StateName: 'Alaska', Abbreviation: 'AK', Capital: 'Juneau', StatehoodYear: 1959, StatehoodOrder: 49 }, + { StateName: 'Arizona', Abbreviation: 'AZ', Capital: 'Phoenix', StatehoodYear: 1912, StatehoodOrder: 48 }, + { StateName: 'Arkansas', Abbreviation: 'AR', Capital: 'Little Rock', StatehoodYear: 1836, StatehoodOrder: 25 }, + { StateName: 'California', Abbreviation: 'CA', Capital: 'Sacramento', StatehoodYear: 1850, StatehoodOrder: 31 }, + { StateName: 'Colorado', Abbreviation: 'CO', Capital: 'Denver', StatehoodYear: 1876, StatehoodOrder: 38 }, + { StateName: 'Connecticut', Abbreviation: 'CT', Capital: 'Hartford', StatehoodYear: 1788, StatehoodOrder: 5 }, + { StateName: 'Delaware', Abbreviation: 'DE', Capital: 'Dover', StatehoodYear: 1787, StatehoodOrder: 1 }, + { StateName: 'Florida', Abbreviation: 'FL', Capital: 'Tallahassee', StatehoodYear: 1845, StatehoodOrder: 27 }, + { StateName: 'Georgia', Abbreviation: 'GA', Capital: 'Atlanta', StatehoodYear: 1788, StatehoodOrder: 4 }, + { StateName: 'Hawaii', Abbreviation: 'HI', Capital: 'Honolulu', StatehoodYear: 1959, StatehoodOrder: 50 }, + { StateName: 'Idaho', Abbreviation: 'ID', Capital: 'Boise', StatehoodYear: 1890, StatehoodOrder: 43 }, + { StateName: 'Illinois', Abbreviation: 'IL', Capital: 'Springfield', StatehoodYear: 1818, StatehoodOrder: 21 }, + { StateName: 'Indiana', Abbreviation: 'IN', Capital: 'Indianapolis', StatehoodYear: 1816, StatehoodOrder: 19 }, + { StateName: 'Iowa', Abbreviation: 'IA', Capital: 'Des Moines', StatehoodYear: 1846, StatehoodOrder: 29 }, + { StateName: 'Kansas', Abbreviation: 'KS', Capital: 'Topeka', StatehoodYear: 1861, StatehoodOrder: 34 }, + { StateName: 'Kentucky', Abbreviation: 'KY', Capital: 'Frankfort', StatehoodYear: 1792, StatehoodOrder: 15 }, + { StateName: 'Louisiana', Abbreviation: 'LA', Capital: 'Baton Rouge', StatehoodYear: 1812, StatehoodOrder: 18 }, + { StateName: 'Maine', Abbreviation: 'ME', Capital: 'Augusta', StatehoodYear: 1820, StatehoodOrder: 23 }, + { StateName: 'Maryland', Abbreviation: 'MD', Capital: 'Annapolis', StatehoodYear: 1788, StatehoodOrder: 7 }, + { StateName: 'Massachusetts', Abbreviation: 'MA', Capital: 'Boston', StatehoodYear: 1788, StatehoodOrder: 6 }, + { StateName: 'Michigan', Abbreviation: 'MI', Capital: 'Lansing', StatehoodYear: 1837, StatehoodOrder: 26 }, + { StateName: 'Minnesota', Abbreviation: 'MN', Capital: 'St. Paul', StatehoodYear: 1858, StatehoodOrder: 32 }, + { StateName: 'Mississippi', Abbreviation: 'MS', Capital: 'Jackson', StatehoodYear: 1817, StatehoodOrder: 20 }, + { StateName: 'Missouri', Abbreviation: 'MO', Capital: 'Jefferson City', StatehoodYear: 1821, StatehoodOrder: 24 }, + { StateName: 'Montana', Abbreviation: 'MT', Capital: 'Helena', StatehoodYear: 1889, StatehoodOrder: 41 }, + { StateName: 'Nebraska', Abbreviation: 'NE', Capital: 'Lincoln', StatehoodYear: 1867, StatehoodOrder: 37 }, + { StateName: 'Nevada', Abbreviation: 'NV', Capital: 'Carson City', StatehoodYear: 1864, StatehoodOrder: 36 }, + { StateName: 'New Hampshire', Abbreviation: 'NH', Capital: 'Concord', StatehoodYear: 1788, StatehoodOrder: 9 }, + { StateName: 'New Jersey', Abbreviation: 'NJ', Capital: 'Trenton', StatehoodYear: 1787, StatehoodOrder: 3 }, + { StateName: 'New Mexico', Abbreviation: 'NM', Capital: 'Santa Fe', StatehoodYear: 1912, StatehoodOrder: 47 }, + { StateName: 'New York', Abbreviation: 'NY', Capital: 'Albany', StatehoodYear: 1788, StatehoodOrder: 11 }, + { StateName: 'North Carolina', Abbreviation: 'NC', Capital: 'Raleigh', StatehoodYear: 1789, StatehoodOrder: 12 }, + { StateName: 'North Dakota', Abbreviation: 'ND', Capital: 'Bismarck', StatehoodYear: 1889, StatehoodOrder: 39 }, + { StateName: 'Ohio', Abbreviation: 'OH', Capital: 'Columbus', StatehoodYear: 1803, StatehoodOrder: 17 }, + { StateName: 'Oklahoma', Abbreviation: 'OK', Capital: 'Oklahoma City', StatehoodYear: 1907, StatehoodOrder: 46 }, + { StateName: 'Oregon', Abbreviation: 'OR', Capital: 'Salem', StatehoodYear: 1859, StatehoodOrder: 33 }, + { StateName: 'Pennsylvania', Abbreviation: 'PA', Capital: 'Harrisburg', StatehoodYear: 1787, StatehoodOrder: 2 }, + { StateName: 'Rhode Island', Abbreviation: 'RI', Capital: 'Providence', StatehoodYear: 1790, StatehoodOrder: 13 }, + { StateName: 'South Carolina', Abbreviation: 'SC', Capital: 'Columbia', StatehoodYear: 1788, StatehoodOrder: 8 }, + { StateName: 'South Dakota', Abbreviation: 'SD', Capital: 'Pierre', StatehoodYear: 1889, StatehoodOrder: 40 }, + { StateName: 'Tennessee', Abbreviation: 'TN', Capital: 'Nashville', StatehoodYear: 1796, StatehoodOrder: 16 }, + { StateName: 'Texas', Abbreviation: 'TX', Capital: 'Austin', StatehoodYear: 1845, StatehoodOrder: 28 }, + { StateName: 'Utah', Abbreviation: 'UT', Capital: 'Salt Lake City', StatehoodYear: 1896, StatehoodOrder: 45 }, + { StateName: 'Vermont', Abbreviation: 'VT', Capital: 'Montpelier', StatehoodYear: 1791, StatehoodOrder: 14 }, + { StateName: 'Virginia', Abbreviation: 'VA', Capital: 'Richmond', StatehoodYear: 1788, StatehoodOrder: 10 }, + { StateName: 'Washington', Abbreviation: 'WA', Capital: 'Olympia', StatehoodYear: 1889, StatehoodOrder: 42 }, + { StateName: 'West Virginia', Abbreviation: 'WV', Capital: 'Charleston', StatehoodYear: 1863, StatehoodOrder: 35 }, + { StateName: 'Wisconsin', Abbreviation: 'WI', Capital: 'Madison', StatehoodYear: 1848, StatehoodOrder: 30 }, + { StateName: 'Wyoming', Abbreviation: 'WY', Capital: 'Cheyenne', StatehoodYear: 1890, StatehoodOrder: 44 }, ]; -const states = { - START: `_START`, - QUIZ: `_QUIZ`, -}; - -const welcomeMessage = `Welcome to the United States Quiz Game! You can ask me about any of the fifty states and their capitals, or you can ask me to start a quiz. What would you like to do?`; -const startQuizMessage = `OK. I will ask you 10 questions about the United States. `; -const exitSkillMessage = `Thank you for playing the United States Quiz Game! Let's play again soon!`; -const repromptSpeech = `Which other state or capital would you like to know about?`; -const helpMessage = `I know lots of things about the United States. You can ask me about a state or a capital, and I'll tell you what I know. You can also test your knowledge by asking me to start a quiz. What would you like to do?`; -const useCardsFlag = true; - /* HELPER FUNCTIONS */ // returns true if the skill is running on a device with a display (show|spot) function supportsDisplay(handlerInput) { - var hasDisplay = + const 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 + handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display; return hasDisplay; } -function getBadAnswer(item) { - return `I'm sorry. ${item} is not something I know very much about in this skill. ${helpMessage}`; -} - -function getCurrentScore(score, counter) { - return `Your current score is ${score} out of ${counter}. `; -} - -function getFinalScore(score, counter) { - return `Your final score is ${score} out of ${counter}. `; -} - function getCardTitle(item) { return item.StateName; } @@ -399,42 +402,24 @@ function getLargeImage(item) { return `https://m.media-amazon.com/images/G/01/mobile-apps/dex/alexa/alexa-skills-kit/tutorials/quiz-game/state_flag/1200x800/${item.Abbreviation}._TTH_.png`; } -function getImage(height, width, label) { - return imagePath.replace("{0}", height) - .replace("{1}", width) - .replace("{2}", label); -} - function getBackgroundImage(label, height = 1024, width = 600) { - return backgroundImagePath.replace("{0}", height) - .replace("{1}", width) - .replace("{2}", label); -} - -function getSpeechDescription(item) { - return `${item.StateName} is the ${item.StatehoodOrder}th state, admitted to the Union in ${item.StatehoodYear}. The capital of ${item.StateName} is ${item.Capital}, and the abbreviation for ${item.StateName} is ${item.Abbreviation}. I've added ${item.StateName} to your Alexa app. Which other state or capital would you like to know about?`; + return backgroundImagePath.replace('{0}', height) + .replace('{1}', width) + .replace('{2}', label); } function formatCasing(key) { return key.split(/(?=[A-Z])/).join(' '); } -function getQuestion(counter, property, item) { - return `Here is your ${counter}th question. What is the ${formatCasing(property)} of ${item.StateName}?`; -} +function getAnswer(property, item, handlerInput) { + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); -// getQuestionWithoutOrdinal returns the question without the ordinal and is -// used for the echo show. -function getQuestionWithoutOrdinal(property, item) { - return "What is the " + formatCasing(property).toLowerCase() + " of " + item.StateName + "?"; -} - -function getAnswer(property, item) { switch (property) { case 'Abbreviation': - return `The ${formatCasing(property)} of ${item.StateName} is ${item[property]}. `; + return requestAttributes.t('GET_ANSWER_ABBREVIATION', formatCasing(property), item.StateName, item[property]); default: - return `The ${formatCasing(property)} of ${item.StateName} is ${item[property]}. `; + return requestAttributes.t('GET_ANSWER', formatCasing(property).toLowerCase(), item.StateName, item[property]); } } @@ -443,26 +428,27 @@ function getRandom(min, max) { } function askQuestion(handlerInput) { - console.log("I am in askQuestion()"); - //GENERATING THE RANDOM QUESTION FROM DATA + console.log('I am in askQuestion()'); + // GENERATING THE RANDOM QUESTION FROM DATA const random = getRandom(0, data.length - 1); const item = data[random]; const propertyArray = Object.getOwnPropertyNames(item); const property = propertyArray[getRandom(1, propertyArray.length - 1)]; - //GET SESSION ATTRIBUTES + // GET ATTRIBUTES const attributes = handlerInput.attributesManager.getSessionAttributes(); + const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); - //SET QUESTION DATA TO ATTRIBUTES + // SET QUESTION DATA TO ATTRIBUTES attributes.selectedItemIndex = random; attributes.quizItem = item; attributes.quizProperty = property; attributes.counter += 1; - //SAVE ATTRIBUTES + // SAVE ATTRIBUTES handlerInput.attributesManager.setSessionAttributes(attributes); - const question = getQuestion(attributes.counter, property, item); + const question = requestAttributes.t('GET_QUESTION', attributes.counter, formatCasing(property), item.StateName); return question; } @@ -504,7 +490,6 @@ function getSpeechCon(type) { return `${speechConsWrong[getRandom(0, speechConsWrong.length - 1)]} `; } - function getTextDescription(item) { let text = ''; @@ -523,7 +508,6 @@ function getAndShuffleMultipleChoiceAnswers(currentIndex, item, property) { // This function randomly chooses 3 answers 2 incorrect and 1 correct answer to // display on the screen using the ListTemplate. It ensures that the list is unique. function getMultipleChoiceAnswers(currentIndex, item, property) { - // insert the correct answer first let answerList = [item[property]]; @@ -534,24 +518,24 @@ function getMultipleChoiceAnswers(currentIndex, item, property) { // to prevent duplicates we need avoid index collisions and take a sample of // 8 + 4 + 1 = 13 answers (it's not 8+4+3 because later we take the unique // we only need the minimum.) - let count = 0 - let upperBound = 12 + let count = 0; + const upperBound = 12; - let seen = new Array(); + const seen = []; seen[currentIndex] = 1; while (count < upperBound) { - let random = getRandom(0, data.length - 1); + const random = getRandom(0, data.length - 1); // only add if we haven't seen this index - if ( seen[random] === undefined ) { + if (seen[random] === undefined) { answerList.push(data[random][property]); - count++; + count += 1; } } // remove duplicates from the list. - answerList = answerList.filter((v, i, a) => a.indexOf(v) === i) + answerList = answerList.filter((v, i, a) => a.indexOf(v) === i); // take the first three items from the list. answerList = answerList.slice(0, 3); return answerList; @@ -559,19 +543,53 @@ function getMultipleChoiceAnswers(currentIndex, item, property) { // This function takes the contents of an array and randomly shuffles it. function shuffle(array) { - let currentIndex = array.length, temporaryValue, randomIndex; + const newArray = array.slice(0); + let currentIndex = newArray.length; + let temporaryValue; + let randomIndex; - while ( 0 !== currentIndex ) { + while (currentIndex !== 0) { randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; - temporaryValue = array[currentIndex]; - array[currentIndex] = array[randomIndex]; - array[randomIndex] = temporaryValue; + currentIndex -= 1; + temporaryValue = newArray[currentIndex]; + newArray[currentIndex] = newArray[randomIndex]; + newArray[randomIndex] = temporaryValue; } - return array; + return newArray; } +const LocalizationInterceptor = { + process(handlerInput) { + const localizationClient = i18n.use(sprintf).init({ + lng: handlerInput.requestEnvelope.request.locale, + resources: languageStrings, + }); + localizationClient.localize = function localize() { + const args = arguments; + const values = []; + for (let i = 1; i < args.length; i += 1) { + values.push(args[i]); + } + const value = i18n.t(args[0], { + returnObjects: true, + postProcess: 'sprintf', + sprintf: values, + }); + if (Array.isArray(value)) { + return value[Math.floor(Math.random() * value.length)]; + } + return value; + }; + const attributes = handlerInput.attributesManager.getRequestAttributes(); + attributes.t = function translate(...args) { + return localizationClient.localize(...args); + }; + }, +}; + /* LAMBDA SETUP */ +const skillBuilder = Alexa.SkillBuilders.custom(); + exports.handler = skillBuilder .addRequestHandlers( LaunchRequestHandler, @@ -581,7 +599,9 @@ exports.handler = skillBuilder RepeatHandler, HelpHandler, ExitHandler, - SessionEndedRequestHandler + SessionEndedRequestHandler, ) + .addRequestInterceptors(LocalizationInterceptor) .addErrorHandlers(ErrorHandler) + .withCustomUserAgent('sample/quiz/v1') .lambda(); diff --git a/lambda/custom/languages/en.js b/lambda/custom/languages/en.js new file mode 100644 index 0000000..4c2ddc0 --- /dev/null +++ b/lambda/custom/languages/en.js @@ -0,0 +1,22 @@ +// Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Licensed under the Amazon Software License +// http://aws.amazon.com/asl/ + +module.exports = { + translation: { + WELCOME_MESSAGE: 'Welcome to the United States Quiz Game! You can ask me about any of the fifty states and their capitals, or you can ask me to start a quiz. What would you like to do?', + START_QUIZ_MESSAGE: 'OK. I will ask you 10 questions about the United States. ', + GET_DESCRIPTION_SPEECH: '%s is the %sth state, admitted to the Union in %s. The capital of %s is %s, and the abbreviation for %s is %s. I\'ve added %s to your Alexa app. ', + GET_DESCRIPTION_REPROMPT: ' Which other state or capital would you like to know about?', + GET_QUESTION: 'Here is your %sth question. What is the %s of %s?', + GET_QUESTION_WITHOUT_ORDINAL: 'What is the %s of %s?', + GET_BAD_ANSWER: 'I\'m sorry. %s is not something I know very much about in this skill.', + GET_ANSWER: 'The %s of %s is %s. ', + GET_ANSWER_ABBREVIATION: 'The %s of %s is %s.', + GET_CURRENT_SCORE: 'Your current score is %s out of %s.', + GET_FINAL_SCORE: 'Your final score is %s out of %s.', + CARD_TITLE: 'United States Quiz Game!', + EXIT_SKILL_MESSAGE: 'Thank you for playing the United States Quiz Game! Let\'s play again soon!', + HELP_MESSAGE: 'I know lots of things about the United States. You can ask me about a state or a capital, and I\'ll tell you what I know. You can also test your knowledge by asking me to start a quiz. What would you like to do?', + }, +}; diff --git a/lambda/custom/package.json b/lambda/custom/package.json index ac37d30..e426e6f 100755 --- a/lambda/custom/package.json +++ b/lambda/custom/package.json @@ -1,5 +1,5 @@ { - "name": "basic", + "name": "fact", "version": "1.0.0", "description": "", "main": "index.js", @@ -10,6 +10,8 @@ "license": "ISC", "dependencies": { "ask-sdk-core": "^2.0.0", - "ask-sdk-model": "^1.0.0" + "ask-sdk-model": "^1.0.0", + "i18next": "^11.8.0", + "i18next-sprintf-postprocessor": "^0.2.2" } } diff --git a/models/en-US.json b/models/en-US.json index 7dadf9c..8bf8595 100644 --- a/models/en-US.json +++ b/models/en-US.json @@ -19,10 +19,18 @@ "name": "AMAZON.StartOverIntent", "samples": [] }, + { + "name": "AMAZON.NavigateHomeIntent", + "samples": [] + }, { "name": "AMAZON.StopIntent", "samples": [] }, + { + "name": "AMAZON.RepeatIntent", + "samples": [] + }, { "name": "AnswerIntent", "samples": [ diff --git a/skill.json b/skill.json index 5a931a7..2085939 100644 --- a/skill.json +++ b/skill.json @@ -26,7 +26,12 @@ "custom": { "endpoint": { "sourceDir": "lambda/custom" - } + }, + "interfaces": [ + { + "type":"RENDER_TEMPLATE" + } + ] } }, "manifestVersion": "1.0"