Contents1Summary12WhyVoxavsotherframeworks33Features54Installation75InitialCon2guration96Respondingtoalexaevents117Usingthedevelopmentserver138Respondingtoanintentevent159ProjectSamples1791StarterKit1 ID: 869688
Download Pdf The PPT/PDF document "VoxaDocumentationRelease212RainAgencySep..." is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
1 VoxaDocumentationRelease2.1.2RainAgencyS
VoxaDocumentationRelease2.1.2RainAgencySep03,2018 Contents: 1Summary12WhyVoxavsotherframeworks33Features54Installation75InitialConguration96Respondingtoalexaevents117Usingthedevelopmentserver138Respondingtoanintentevent159ProjectSamples179.1StarterKit................................................179.2MyFirstPodcast.............................................179.3AccountLinking.............................................1710Links1910.1VoxaDocumentation!..........................................1910.2NewAlexadeveloper...........................................2110.3VoxaarchitecturepatternMVC.....................................2210.4Models..................................................2210.5ViewsandVariables...........................................2310.6Controllers................................................2510.7Transition.................................................2610.8ThealexaEventObje
2 ct......................................
ct........................................2710.9ThereplyObject............................................2710.10Voxa...................................................2810.11RequestFlow...............................................3410.12I18N...................................................3510.13Plugins..................................................3710.14Debugging................................................4010.15StarterKit................................................4010.16MyFirstPodcast.............................................42 i 10.17AccountLinking.............................................46 ii CHAPTER1 Summary Voxaisaframeworkthatprovidesawaytoorganizeaconversationalexperienceintoastatemachine.Eventhemostcomplexvoiceuserinterface(VUI)canberepresentedthroughthestatemachineanditprovidestheexibilityneededtobothberigidwhenneededinspecicstatesandexibletojumparoundstateswhenn
3 eeded. 1 VoxaDocumentation,Release2.1.2
eeded. 1 VoxaDocumentation,Release2.1.2 2Chapter1.Summary CHAPTER2 WhyVoxavsotherframeworks VoxaprovidesamorerobustframeworkforbuildingAlexaskills.Itprovidesadesignpatternthatwasn'tfoundinotherframeworks.CriticaltoVoxawasprovidingapluggableinterfaceandsupportingallofthelatestASKfeatures. 3 VoxaDocumentation,Release2.1.2 4Chapter2.WhyVoxavsotherframeworks CHAPTER3 Features MVCPatternStateorIntenthandling(StateMachine)EasyintegrationwithseveralAnalyticsprovidersEasytomodifyresponsele(theview)CompatibilitywithallSSMLfeaturesWorkswithcompanionappcardsSupportsi18nintheresponsesCleancodestructurewithaunittestingframeworkEasyerrorhandlingAccountlinkingsupportSeveralPlugins 5 VoxaDocumentation,Release2.1.2 6Chapter3.Features CHAPTER4 Installation Voxaisdistributedvianpm $npminstallvoxa--save 7 VoxaDocumentation,Release2.1.2 8Chapter4.Installation C
4 HAPTER5 InitialConguration Instantia
HAPTER5 InitialConguration InstantiatingaStateMachineSkillrequiresacongurationspecifyingyourViewsandVariables. usestrict;constVoxa=require(voxa);constviews=require(./views):constvariables=require(./variables);constskill=newVoxa({variables,views}); 9 VoxaDocumentation,Release2.1.2 10Chapter5.InitialConguration CHAPTER6 Respondingtoalexaevents Onceyouhaveyourskillconguredrespondingtoeventsisassimpleascallingtheskill.lambdamethod constskill=require(./MainStateMachine);exports.handler=skill.lambda(); 11 VoxaDocumentation,Release2.1.2 12Chapter6.Respondingtoalexaevents CHAPTER7 Usingthedevelopmentserver Theframeworkprovidesasimplebuiltinserverthat'sconguredtoserveallPOSTrequeststoyourskill,thisworksgreatwhendeveloping,speciallywhenpairedwithngrok //thiswillstartanhttpserverlisteningonport3000skill.startServer(3000); 13 VoxaDocumentation,Release2
5 .1.2 14Chapter7.Usingthedevelopmentserve
.1.2 14Chapter7.Usingthedevelopmentserver CHAPTER8 Respondingtoanintentevent skill.onIntent(HelpIntent,(alexaEvent)={return{reply:HelpIntent.HelpAboutSkill};});skill.onIntent(ExitIntent,(alexaEvent)={return{reply:ExitIntent.Farewell};}); 15 VoxaDocumentation,Release2.1.2 16Chapter8.Respondingtoanintentevent CHAPTER9 ProjectSamples Tohelpyougetstartedthestatemachinehasanumberofexampleprojectsyoucanuse.9.1StarterKitThisisthesimplestproject,itdenesthedefaultdirectorystructurewerecommendusingwithvoxaprojectsandhasanexampleserverless.ymllethatcanbeusedtodeployyourskilltoalambdafunction.9.2MyFirstPodcastInthisexampleyouwillseehowtoimplementapodcastskillbyhavingalistofaudiosinale(podcast.js)withtitlesandurls.ItimplementsallaudiointentsallowedbytheaudiobackgroundfeatureandhandlesalltheplaybackrequestsdispatchedbyAlexaonceanaudiohasstarted,sto
6 pped,failed,nishedornearlytonish
pped,failed,nishedornearlytonish.Keepinmindtheaudiosmustbehostedinasecureserver.9.3AccountLinkingAmorecomplexprojectthatshowshowtoworkwithaccountlinkingandmakeresponsesusingthemodelstate.Itusesserverlesstodeployyouraccountlinkingserverandskilltolambda,createadynamodbtabletostoreyouraccountlinkingandcreateans3buckettostoreyourstaticassets.ItalsohasagulptasktouploadyourassetstoS3 17 VoxaDocumentation,Release2.1.2 18Chapter9.ProjectSamples CHAPTER10 Links search10.1VoxaDocumentation!10.1.1SummaryVoxaisanAlexaskillframeworkthatprovidesawaytoorganizeaskillintoastatemachine.Eventhemostcomplexvoiceuserinterface(VUI)canberepresentedthroughthestatemachineanditprovidestheexibilityneededtobothberigidwhenneededinspecicstatesandexibletojumparoundwhenallowingthatalsomakessense.10.1.2WhyVoxavsotherframeworksVoxaprovidesamorerobustframeworkforbuildingAlexaskills.Itprovidesadesignpatternthatwasn'tfou
7 ndinotherframeworks.CriticaltoVoxawaspro
ndinotherframeworks.CriticaltoVoxawasprovidingapluggableinterfaceandsupportingallofthelatestASKfeatures.10.1.3FeaturesMVCPatternStateorIntenthandling(StateMachine)EasyintegrationwithseveralAnalyticsprovidersEasytomodifyresponsele(theview)CompatibilitywithallSSMLfeaturesCompatiblewithAudiodirectivesWorkswithcompanionappcardsSupportsi18nintheresponses 19 VoxaDocumentation,Release2.1.2 CleancodestructurewithaunittestingframeworkEasyerrorhandlingAccountlinkingsupportPersistentsessionsupportusingDynamoDBSeveralPlugins10.1.4InstallationVoxaisdistributedvianpm $npminstallvoxa--save 10.1.5InitialCongurationInstantiatingaStateMachineSkillrequiresacongurationspecifyingyourViewsandVariables. usestrict;constVoxa=require(voxa);constviews=require(./views):constvariables=require(./variables);cons
8 tskill=newVoxa({variables,views}); 10.1.
tskill=newVoxa({variables,views}); 10.1.6RespondingtoalexaeventsOnceyouhaveyourskillconguredrespondingtoeventsisassimpleascallingtheskill.lambdamethod constskill=require(./MainStateMachine);exports.handler=skill.lambda(); 10.1.7UsingthedevelopmentserverTheframeworkprovidesasimplebuiltinserverthat'sconguredtoserveallPOSTrequeststoyourskill,thisworksgreatwhendeveloping,speciallywhenpairedwithngrok //thiswillstartanhttpserverlisteningonport3000skill.startServer(3000); 10.1.8Respondingtoanintentevent skill.onIntent(HelpIntent,(alexaEvent)={return{reply:HelpIntent.HelpAboutSkill};});skill.onIntent(ExitIntent,(alexaEvent)={return{reply:ExitIntent.Farewell};}); 20Chapter10.Links VoxaDocumentation,Release2.1.2 10.2NewAlexadeveloperIftheskillsdevelopmentforalexaisanewthingforyou,wehavesomesuggestiontogetyoudeepintothisworld.10.2.1Getting
9 StartedwiththeAlexaSkillsAlexaprovidesas
StartedwiththeAlexaSkillsAlexaprovidesasetofbuilt-incapabilities,referredtoasskills.Forexample,Alexa'sabilitiesincludeplayingmusicfrommultipleproviders,answeringquestions,providingweatherforecasts,andqueryingWikipedia.TheAlexaSkillsKitletsyouteachAlexanewskills.CustomerscanaccessthesenewabilitiesbyaskingAlexaquestionsormakingrequests.Youcanbuildskillsthatprovideuserswithmanydifferenttypesofabilities.Forexample,askillmightdoanyoneofthefollowing:Lookupanswerstospecicquestions(Alexa,asktidepoolerforthehightidetodayinSeattle.)Challengetheuserwithpuzzlesorgames(Alexa,playJeopardy.)Controllightsandotherdevicesinthehome(Alexa,turnonthelivingroomlights.)Provideaudioortextcontentforacustomer'sashbrieng(Alexa,givememyashbrieng)Youcanseethedifferenttypesofskillsheretogotmoredeepreference.HowusersinteractwithAlexa?WithInteractionM
10 odel.EndusersinteractwithallofAlexa'sabi
odel.EndusersinteractwithallofAlexa'sabilitiesinthesamewaybywakingthedevicewiththewakeword(orabuttonforadevicesuchastheAmazonTap)andaskingaquestionormakingarequest.Forexample,usersinteractwiththebuilt-inWeatherservicelikethis:User:Alexa,what'stheweather?Alexa:RightnowinSeattle,therearecloudyskies...InthecontextofAlexa,aninteractionmodelissomewhatanalogoustoagraphicaluserinterfaceinatraditionalapp.Insteadofclickingbuttonsandselectingoptionsfromdialogboxes,usersmaketheirrequestsandrespondtoquestionsbyvoice.Hereyoucanseehowtheinteractionmodelworks10.2.2AmazonDeveloperServiceAccountAmazonWebServicesprovidesasuiteofsolutionsthatenabledevelopersandtheirorganizationstoleverageAma-zon.com'srobusttechnologyinfrastructureandcontentviasimpleAPIcalls.TherstthingyouneedtodoiscreateyourownAmazonDeveloperAccount.10.2.3RegisteringanAlexaskillRegisteringanewskillorabilityontheAmazonDeveloperPortalcreatesacongura
11 tioncontainingtheinformationthattheAlexa
tioncontainingtheinformationthattheAlexaserviceneedstodothefollowing:RouterequeststotheAWSLambdafunctionorwebservicethatimplementstheskill,orfordevelopmentpurposeyoucanrunitlocallyusingngrok.DisplayinformationabouttheskillintheAmazonAlexaApp.Theappshowsallpublishedskills,aswellasallofyourownskillscurrentlyunderdevelopment. 10.2.NewAlexadeveloper21 VoxaDocumentation,Release2.1.2 YoumustregisteraskillbeforeyoucantestitwiththeServiceSimulatorinthedeveloperportaloranAlexa-enableddevice.FollowtheseinstructionstoregisterandmanagingyourAlexaskill.10.3VoxaarchitecturepatternMVC 10.4ModelsModelsarethedatastructurethatholdsthecurrentstateofyourapplication,theframeworkdoesn'tmakemanyassumptionsonitandonlyrequireshaveafromEventmethodthatshouldinitializeitbasedonthealexaEventsessionattributesandaserializemethodthatshouldreturnJSON.stringifyablestructuretothenstoreinthesessionattributes usestrict;cons
12 t_=require(lodash);classModel{
t_=require(lodash);classModel{constructor(data){_.assign(this,data);}staticfromEvent(alexaEvent){ (continuesonnextpage) 22Chapter10.Links VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) returnnewthis(alexaEvent.session.attributes.modelData);}serialize(){returnthis;}}module.exports=Model; 10.5ViewsandVariables10.5.1ViewsViewsaretheVoxawayofhandlingrepliestotheuser,they'retemplatesofresponsesusingasimplejavascriptDSL.Theycancontainssmlandincludecards.Thereare5responsesinthefollowingsnippet:LaunchIntent.OpenResponse,ExitIntent.Farewell,HelpIntent.HelpAboutSkill,Count.SayandCount.TellAlso,there'saspecialtypeofviewwhichcancontainarraysofoptions,whenVoxandsoneofthoseliketheLaunchIntent.OpenResponseitwillselectarandomsampleanduseitastheresponse. constviews={LaunchIntent:{OpenResponse:{tell:[Hello!rea;k-60;time="3s"/Good{time}.Isthereanythingicandotohelp ,!youtoday?
13 1;,Hithere!rea;k-60;ʏ
1;,Hithere!rea;k-60;time="3s"/Good{time}.Howmayibeofservice?,Good{time},Welcome!.Howcanihelpyou?,]},},ExitIntent:{Farewell:{tell:Ok.Formoreinfovisit{site}site.},},HelpIntent:{HelpAboutSkill:{tell:Formorehelpvisitexampledotcomcard:{type:Standard,text:Helpisavailableatishttp://example.com,},},},Count:{Say:{say:{count}},Tell:{tell:{count}},},}; Theycomein3forms:say,askandtell. 10.5.ViewsandVariables23 VoxaDocumentation,Release2.1.2 tellTellviewssendaresponsetoalexaandclosethesessioninmediately.They'reusedwhentheskillisdoneinteractingwiththeuser.TheExitIntent.Farewellisanexampleofthis.askAskviewsareusedtoprompttheuserforinformation,theysendaresponsetoalexabutkeepthesessionopensotheusercanrespond.TheLaunchIntent.OpenResponseisanaskview.sayWhilethetellandaskviewtypesareanexactrepresentationofthebasealexaprogrammingmod
14 el,thesayviewsaredifferent.They'reanabst
el,thesayviewsaredifferent.They'reanabstractioncreatedbyvoxatomakeitsimplertocomposeyourresponsesovermanystatetransitions.Theydon'tsendarespondtoalexabutinsteadmakeastatetransitioninternallyandcontinueexecutingyourskillcodeuntilthere'satelloraskresponse.10.5.2DirectivesNowyoucanincludedirectivesinyourviewsle.Hintdirectivecanbeeasilyspecifywithasimpleobjectcontainingahintkey. constviews={LaunchIntent:{OpenResponse:{tell:[Hello!rea;k-60;time="3s"/Good{time}.Isthereanythingicandotohelp ,!youtoday?,Hithere!rea;k-60;time="3s"/Good{time}.Howmayibeofservice?,Good{time},Welcome!.Howcanihelpyou?,],directives:[{hint:hint,},{type:Display.RenderTemplate,template:{type:"BodyTemplate1",textContent:{primaryText:{text:"Seemyfavoritecar",type:"PlainText"},secondaryText:{text:"Custom-painted",type:"PlainText"},tertiaryText:{text:"Byme!"
15 ,type:"PlainText"}}, (continuesonnextpag
,type:"PlainText"}}, (continuesonnextpage) 24Chapter10.Links VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) backButton:"VISIBLE"}}],},}; 10.5.3VariablesVariablesaretherenderingenginewayofaddinglogicintoyourviews.They'redessignedtobeverysimplesincemostofyourlogicshouldbeinyourmodelorcontrollers.Avariablesignatureis:variable(model,alexaEvent)ArgumentsmodelTheinstanceofyourmodelforthecurrentalexaevent.AlexaEventThecurrentalexaevent.ReturnsThevaluetoberenderedorapromiseresolvingtoavaluetoberenderedintheview. constvariables={site:functionsite(model){returnPromise.resolve(example.com);},count:functioncount(model){returnmodel.count;},locale:functionlocale(model,alexaEvent){returnalexaEvent.locale;}}; 10.6ControllersControllersinyourapplicationcontrolthelogicofyourskill,theyrespondtoalexaalexaEvents,externalresources,manipulatetheinputandgiveproperresponsesusingyourModel,
16 ViewsandVariables.Statescomeinoneoftwowa
ViewsandVariables.Statescomeinoneoftwoways,theycanbeanobjectofmappingsfromintentnametostate. skill.onState(entry,{LaunchIntent:launch,AMAZON.HelpIntent:help,}); OrtheycanbeafunctionthatgetsaalexaEventobject. skill.onState(launch,(alexaEvent)={return{reply:LaunchIntent.OpenResponse,to:die};}); 10.6.Controllers25 VoxaDocumentation,Release2.1.2 Yourstateshouldrespondwithatransition.Thetransitionisaplainobjectthatcantakedirectives,toandreplykeys.10.6.1TheentrycontrollerTheentrycontrollerisspecialinthatit'sthedefaultstatetogotoatthebeginningofyoursessionandifyourstatereturnsnoresponse.Forexampleinthenextsnippedthere'sawaitingstatethatexpectsanAMAZON.NextIntentoranAMAZON.PreviousIntent,inthecasetheuserssayssomethingunexpectedlikeanAMAZON.HelpIntentthestatereturnsundened,theStateMachineframeworkhandlesthissituationsbyredirectingtot
17 heentrystate skill.onState(waiting&
heentrystate skill.onState(waiting,(alexaEvent)={if(alexaEvent.intent.name===AMAZON.NextIntent){alexaEvent.model.index+=1;return{reply:Ingredients.Describe,to:waiting}}elseif(alexaEvent.intent.name===AMAZON.PreviousIntent){alexaEvent.model.index-=1;return{reply:Ingredients.Describe,to:waiting}}}); 10.6.2TheonIntenthelperForthesimplepatternofhavingacontrollerrespondtoanspecicintenttheframeworkprovidestheonIntenthelper skill.onIntent(LaunchIntent,(alexaEvent)={return{reply:LaunchIntent.OpenResponse,to:die};}); IfyoureceiveaDisplay.ElementSelectedtyperequest,youcouldusethesameapproachforintentsandstate.VoxareceivesthistypeofrequestandturnsitintoDisplayElementSelectedIntent skill.onIntent(DisplayElementSelected,(alexaEvent)={return{reply:DisplayElementSelected.
18 OpenResponse,to:die};});
OpenResponse,to:die};}); Underthehoodthiscreatesanewkeyintheentrycontrollerandanewstate10.7TransitionAtransitionistheresultofcontrollerexecution,it'sasimpleobjectwithkeysthatcontroltheowofexecutioninyourskill.10.7.1toThetokeyshouldbethenameofastateinyourstatemachine,whenpresentitindicatestotheframeworkthatitshouldmovetoanewstate.Ifabsentit'sassumedthattheframeworkshouldmovetothediestate. return{to:stateName}; 26Chapter10.Links VoxaDocumentation,Release2.1.2 10.7.2directivesDirectivesareusedpasseddirectlytothealexaresponse,theformatisdescribedinthealexadocumentation return{directives:[{type:AudioPlayer.Play,playBehavior:REPLACE_ALL,audioItem:{stream:{token:lesson.id,url:lesson.Url,offsetInMilliseconds:0,}}}],}; 10.7.3replyThereplykeycantake2forms,asimplestringpointingtooneofyourviewsoraReplyobject. return{reply:LaunchIntent.OpenResponse};constreply=new
19 Reply(alexaEvent,{tell:Hithere!
Reply(alexaEvent,{tell:Hithere!});return{reply}; 10.8ThealexaEventObjectclassAlexaEvent(event,lambdaContext)ThealexaEventobjectcontainsalltheinformationfromtheAlexaevent,it'sanobjectkeptfortheentirelifecycleofthestatemachinetransitionsandassuchisaperfectplaceformiddlewaretoputinformationthatshouldbeavailableoneveryrequest.AlexaEvent.AlexaEvent.modelThedefaultmiddlewareinstantiatesaModelandmakesitavailablethroughalexaEvent.modelAlexaEvent.AlexaEvent.intent.paramsThealexaEventobjectmakesintent.slotsavailablethroughintent.paramsafteraplyingasim-pletransformationso{slots:[{name:Dish,value:FriedChicken}]}becomes{Dish:FriedChicken}AlexaEvent.AlexaEvent.userAconveniencegettertoobtaintheuserfromsesssion.userorcontext.System.userAlexaEvent.AlexaEvent.tokenAconveniencegettertoobtainthetokenfromrequest.token.ThistokenisspeciedinDisplay.ElementSelectedrequest.10.9ThereplyObject
20 classReply(alexaEvent[,message])Thereply
classReply(alexaEvent[,message])ThereplyobjectisusedbytheframeworktorenderAlexaresponses,ittakesallofyourstatements, 10.8.ThealexaEventObject27 VoxaDocumentation,Release2.1.2 cardsanddirectivesandgeneratesaproperjsonresponseforAlexaArgumentsalexaEvent(AlexaEvent)messageAmessageobjectReply.Reply.append(message)AddsstatementstotheReplyArgumentsmessageAnobjectwithkeysask,tell,say,reprompt,card,ordirectiveskeys.OranotherreplyobjectReturnstheReplyobjectReply.Reply.toJSON()ReturnsAnobjectwiththeproperformattosendbacktoAlexa,withstatementswrappedinSSMLtags,cards,repromptsanddirectivesReply.Reply.fulfillIntent(canFulll)ArgumentscanFulfillAstringwithpossiblevalues:YES|NO|MAYBEtofulllrequestReply.Reply.fulfillSlot(slotName,canUnderstand,canFulll)ArgumentsslotNameAstringwiththeslottofulllcanUnderstandAstringwithpossiblevalues:YES|NO|
21 MAYBEthatindicatesslotunderstanding
MAYBEthatindicatesslotunderstandingcanFulfillAstringwithpossiblevalues:YES|NOthatindicatesslotfulllment10.10VoxaclassVoxa(cong)ArgumentsconfigCongurationforyourskill,itshouldincludeViewsandVariablesandoptionallyamodelandalistofappIds.IfappIdsispresentthentheframeworkwillcheckeveryalexaeventandenforcetheapplicationidtomatchoneofthespeciedapplicationids. constskill=newVoxa({Model,variables,views,appIds}); Voxa.lambda()ReturnsAlambdahandlerthatwillcallyourskill.executemethod exports.handler=skill.lambda(); Voxa.execute(event)ThemainentrypointfortheSkillexecutionArguments 28Chapter10.Links VoxaDocumentation,Release2.1.2 eventTheeventsentbyalexa.contextThecontextofthelambdafunctionReturnsPromiseAresponseresolvingtoajavascriptobjecttobesentasaresulttoAlexa. skill.execute(event,context).then(result=callback(null,result)).catch(callback); Vox
22 a.onState(stateName,handler)Mapsahandler
a.onState(stateName,handler)MapsahandlertoastateArgumentsstateName(string)Thenameofthestatehandler(function/object)ThecontrollertohandlethestateReturnsAnobjectorapromisethatresolvestoanobjectthatspeciesatransitiontoanotherstateand/oraviewtorender skill.onState(entry,{LaunchIntent:launch,AMAZON.HelpIntent:help,});skill.onState(launch,(alexaEvent)={return{reply:LaunchIntent.OpenResponse,to:die};}); Voxa.onIntent(intentName,handler)AshortcutfordeniningstatecontrollersthatmapdirectlytoanintentArgumentsintentName(string)Thenameoftheintenthandler(function/object)ThecontrollertohandlethestateReturnsAnobjectorapromisethatresolvestoanobjectthatspeciesatransitiontoanotherstateand/oraviewtorender skill.onIntent(HelpIntent,(alexaEvent)={return{reply:HelpI
23 ntent.HelpAboutSkill};}); Voxa.onIn
ntent.HelpAboutSkill};}); Voxa.onIntentRequest(callback[,atLast])ThisisexecutedforallIntentRequestevents,defaultbehavioristoexecutetheStateMachinemachinery,yougenerallydon'tneedtooverridethis.Argumentscallback(function)last(bool)ReturnsPromiseVoxa.onLaunchRequest(callback[,atLast])AddsacallbacktobeexecutedwhenprocessingaLaunchRequest,thedefaultbehavioristofakethealexaeventasanIntentRequestwithaLaunchIntentandjustdefertotheonIntentRequesthandlers.Yougenerallydon'tneedtooverridethis. 10.10.Voxa29 VoxaDocumentation,Release2.1.2 Voxa.onBeforeStateChanged(callback[,atLast])Thisisexecutedbeforeenteringeverystate,itcanbeusedtotrackstatechangesormakechangestothealexaeventobjectVoxa.onBeforeReplySent(callback[,atLast])Addsacallbacktobeexecutedjustbeforesendingthereply,internallythisisusedtoaddtheserializedmodelandnextstatetothesession.Itcanbeusedtoalterthereply,orforexampletotrackthena
24 lresponsesenttoauserinanalytics. skill.o
lresponsesenttoauserinanalytics. skill.onBeforeReplySent((alexaEvent,reply)={constrendered=reply.toJSON();analytics.track(alexaEvent,rendered)}); Voxa.onAfterStateChanged(callback[,atLast])Addscallbackstobeexecutedontheresultofastatetransition,thisarecalledaftereverytransitionandinternallyit'susedtorenderthetransitionreplyusingtheviewsandvariablesThecallbacksgetalexaEvent,replyandtransitionparams,itshouldreturnthetransitionobject skill.onAfterStateChanged((alexaEvent,reply,transition)={if(transition.reply===LaunchIntent.PlayTodayLesson){transition.reply=_.sample([LaunchIntent.PlayTodayLesson1,LaunchIntent.,!PlayTodayLesson2]);}returntransition;}); Voxa.onUnhandledState(callback[,atLast])Addsacallbacktobeexecutedwhenastatetransitionfailstogeneratearesult,thisusuallyhappenswhenredirectingtoamissingstateoranentrycallforanonconguredintent,thehandlersgetaal
25 exaeventparameterandshouldreturnatransit
exaeventparameterandshouldreturnatransitionthesameasastatecontrollerwould.Voxa.onSessionStarted(callback[,atLast])AddsacallbacktotheonSessinStartedevent,thisexecutesforalleventswherealexaEvent.session.new===trueThiscanbeusefultotrackanalytics skill.onSessionStarted((alexaEvent,reply)={analytics.trackSessionStarted(alexaEvent);}); Voxa.onRequestStarted(callback[,atLast])Addsacallbacktobeexecutedwheneverthere'saLaunchRequest,IntentRequestoraSessionEndedRequest,thiscanbeusedtoinitializeyouranalyticsorgetyouraccountlinkinguserdata.Internallyit'susedtoinitializethemodelbasedontheeventsession skill.onRequestStarted((alexaEvent,reply)={alexaEvent.model=this.config.Model.fromEvent(alexaEvent);}); Voxa.onSessionEnded(callback[,atLast])AddsacallbacktotheonSessionEndedevent,thisiscalledforeverySessionEndedRequestorwhentheskillreturnsatransitiontoastatewhereisTerminal===true,normallythisisatransit
26 iontothediestate.Youwouldnormallyusethis
iontothediestate.Youwouldnormallyusethistotrackanalytics 30Chapter10.Links VoxaDocumentation,Release2.1.2 Voxa.onSystem.ExceptionEncountered(callback[,atLast])ThishandlesSystem.ExceptionEncounteredeventthataresenttoyourskillwhenaresponsetoanAudioPlayereventcausesanerror returnPromise.reduce(errorHandlers,(result,errorHandler)={if(result){returnresult;}returnPromise.resolve(errorHandler(alexaEvent,error));},null); 10.10.1ErrorhandlersYoucanregistermanyerrorhandlerstobeusedforthedifferentkindoferrorstheapplicationcouldgenerate.Theyallfollowthesamelogicwhereifthersterrortypeisnothandledthenthedefaultistobedeferredtothemoregeneralerrorhandlerthatultimatelyjustreturnsadefaulterrorreply.They'reexecutedsequentiallyandwillstopwhenthersthandlerreturnsareply.Voxa.onStateMachineError(callback[,atLast])ThishandlerwillcatchallerrorsgeneratedwhentryingtomaketransitionsinthestateMachine,thiscouldincludee
27 rrorsinthestatemachinecontrollers,,theha
rrorsinthestatemachinecontrollers,,thehandlersget(alexaEvent,reply,error)param-eters skill.onStateMachineError((alexaEvent,reply,error)={//itgetsthecurrentreply,whichcouldbeincompleteduetoanerror.returnnewReply(alexaEvent,{tell:Anerrorinthecontrollerscode}).toJSON();}); Voxa.onError(callback[,atLast])Thisisthemoregeneralhandlerandwillcatchallunhandlederrorsintheframework,itgets(alexaEvent,error)parametersasarguments skill.onError((alexaEvent,error)={returnnewReply(alexaEvent,{tell:Anunrecoverableerroroccurred.}).toJSON();}); 10.10.2PlaybackControllerhandlersHandleeventsfromtheAudioPlayerinterfaceaudioPlayerCallback(alexaEvent,reply)AllaudioplayermiddlewarecallbacksgetaalexaeventandareplyobjectArgumentsalexaEvent(AlexaEvent)ThealexaeventsentbyAlexareply(object)AreplytobesentasaresponseReturnsobjectwriteYouralexaeventhandlershouldreturnanapprop
28 riateresponseaccordingtotheeventtype,thi
riateresponseaccordingtotheeventtype,thisgenerallymeansappendingtothereplyobjectInthefollowingexamplethealexaeventhandlerreturnsaREPLACE_ENQUEUEDdirectivetoaPlaybackNearlyFinished()event. 10.10.Voxa31 VoxaDocumentation,Release2.1.2 skill[onAudioPlayer.PlaybackNearlyFinished]((alexaEvent,reply)={constdirectives={type:AudioPlayer.Play,playBehavior:REPLACE_ENQUEUED,token:"",url:https://www.dl-sounds.com/wp-content/uploads/edd/2016/09/Classical-Bed3-,!preview.mp3,offsetInMilliseconds:0,};returnreply.append({directives});}); Voxa.onAudioPlayer.PlaybackStarted(callback[,atLast])Voxa.onAudioPlayer.PlaybackFinished(callback[,atLast])Voxa.onAudioPlayer.PlaybackStopped(callback[,atLast])Voxa.onAudioPlayer.PlaybackFailed(callback[,atLast])Voxa.onAudioPlayer.PlaybackNearlyFinished(callback[,atLast])Voxa.onPlaybackController.NextCommandIssued(callback[,atLast])Voxa.onPla
29 ybackController.PauseCommandIssued(callb
ybackController.PauseCommandIssued(callback[,atLast])Voxa.onPlaybackController.PlayCommandIssued(callback[,atLast])Voxa.onPlaybackController.PreviousCommandIssued(callback[,atLast])10.10.3AlexaSkillEventhandlersHandlerequestfortheAlexaSkillEventsalexaSkillEventCallback(alexaEvent)AllthealexaskilleventcallbacksgetaalexaeventandareplyobjectArgumentsalexaEvent(AlexaEvent)ThealexaeventsentbyAlexareply(object)AreplytobesentastheresponseReturnsobjectreplyAlexaonlyneedsanacknowledgementthatyoureceivedandprocessedtheeventsoitdoesn'tneedtoresendtheevent.JustreturningthereplyobjectisenoughThisisanexampleonhowyourskillcanprocessaSkillEnabled()event. skill[onAlexaSkillEvent.SkillEnabled]((alexaEvent,reply)={constuserId=alexaEvent.user.userId;console.log(skillwasenabledforuser:${userId});returnreply;}); Voxa.onAlexaSkillEvent.SkillAccountLinked(callback[,atLast])Voxa
30 .onAlexaSkillEvent.SkillEnabled(callback
.onAlexaSkillEvent.SkillEnabled(callback[,atLast])Voxa.onAlexaSkillEvent.SkillDisabled(callback[,atLast])Voxa.onAlexaSkillEvent.SkillPermissionAccepted(callback[,atLast]) 32Chapter10.Links VoxaDocumentation,Release2.1.2 Voxa.onAlexaSkillEvent.SkillPermissionChanged(callback[,atLast])10.10.4AlexaListEventhandlersHandlerequestfortheAlexaListEventsalexaListEventCallback(alexaEvent)AllthealexalisteventcallbacksgetaalexaeventandareplyobjectArgumentsalexaEvent(AlexaEvent)ThealexaeventsentbyAlexareply(object)AreplytobesentastheresponseReturnsobjectreplyAlexaonlyneedsanacknowledgementthatyoureceivedandprocessedtheeventsoitdoesn'tneedtoresendtheevent.JustreturningthereplyobjectisenoughThisisanexampleonhowyourskillcanprocessaItemsCreated()event. skill[onAlexaHouseholdListEvent.ItemsCreated]((alexaEvent,reply)={constlistId=alexaEvent.request.body.listId;constuserId=alexaEven
31 t.user.userId;console.log(Itemscrea
t.user.userId;console.log(Itemscreatedforlist:${listId}foruser${userId});returnreply;}); Voxa.onAlexaHouseholdListEvent.ItemsCreated(callback[,atLast])Voxa.onAlexaHouseholdListEvent.ItemsUpdated(callback[,atLast])Voxa.onAlexaHouseholdListEvent.ItemsDeleted(callback[,atLast]) 10.10.Voxa33 VoxaDocumentation,Release2.1.2 10.11RequestFlow 34Chapter10.Links VoxaDocumentation,Release2.1.2 10.12I18NInternationalizationsupportisdoneusingthei18nextlibrary,thesametheAmazonAlexaNodeSDKuses.10.12.1ConguringtheskillforI18NTouseityouneedtocongureyourskilltousetheI18NRendererclassinsteadofthedefaultrendererclass. constVoxa=require(voxa);constskill=newVoxa({Model,variables,views,RenderClass:Voxa.I18NRenderer}); TheframeworktakescareofselectingthecorrectlocaleoneveryalexaeventbylookingatthealexaEvent.request.localeproperty.10.12.2ChangesinyourviewsTheotherchangeyouwillneedistodeneyourviewsusing
32 thei18nexttranslateformat: usestric
thei18nexttranslateformat: usestrict;constviews=(functionviews(){return{en-us:{translation:{LaunchIntent:{OpenResponse:{tell:Hello!Good{time}},},Question:{Ask:{ask:Whattimeisit?},},ExitIntent:{Farewell:{tell:Ok.Formoreinfovisit{site}site.},},Number:{One:{tell:{numberOne}},},Say:{Say:{say:say},},Random:{tell:[Random1,Random2,Random3]},},},de-de:{translation:{LaunchIntent:{OpenResponse:{tell:Hallo!guten{time}},},Question:{Ask:{ask:wiespätistes?},},ExitIntent:{Farewell:{tell:OkfürweitereInfosbesuchen{site}Website},},Number:{ (continuesonnextpage) 10.12.I18N35 VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) One:{tell:{numberOne}},},Say:{Say:{say:sagen},},Random:{tell:[zufällig1,zufällig2,zufällig3]},},},};}());
33 module.exports=views; 10.12.3VariablesVa
module.exports=views; 10.12.3VariablesVariablesshouldworkmostlythesameaswiththeDefaultRenderer,withtheexceptionthatvariableswillnowgetalocalekey usestrict;/***Variablesfortests**Copyright(c)2016RainAgency.*LicensedundertheMITlicense.*/constPromise=require(bluebird);constvariables={exitDirectiveMessage:functionexitDirectiveMessage(){return({text:Thanksforplaying!,type:PlainText,});},exitCard:functionexitCard(){return{type:Standard,title:title,text:text,image:{smallImageUrl:smallImage.jpg,largeImageUrl:largeImage.jpg,},};},exitArray:functionexitArray(){return[{a:1},{b:2},{c:3}];},time:functiontime(){consttoday=newDate();constcurHr=today.getHours();if(curHr12){ (continuesonnextpage) 36Chapter10.Links VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) returnPromise.resolve(Morning);}if(curHr18){returnPromis
34 e.resolve(Afternoon);}returnPr
e.resolve(Afternoon);}returnPromise.resolve(Evening);},site:functionsite(){returnPromise.resolve(example.com);},count:functioncount(model){returnmodel.count;},numberOne:functionnumberOne(model,request){if(request.request.locale===en-us){returnone;}elseif(request.request.locale===de-de){returnein;}return1;},};module.exports=variables; 10.13PluginsPluginsallowyoutomodifyhowtheStateMachineSkillhandlesanalexaevent.Whenapluginisregistereditwillusethedifferenthooksinyourskilltoaddfunctionality.Ifyouhaveseveralskillswithsimilarbehaviorthenyouransweristocreateaplugin.10.13.1UsingapluginAfterinstantiatingaStateMachineSkillyoucanregisterpluginsonit.BuiltinpluginscanbeaccessedthroughVoxa.plugins usestrict;constVoxa=require(voxa);constModel=require(./model);constviews=require(./views):constvariables=require(
35 ./variables);constskill=newVoxa({Mo
./variables);constskill=newVoxa({Model,variables,views});Voxa.plugins.replaceIntent(skill); 10.13.Plugins37 VoxaDocumentation,Release2.1.2 10.13.2StateFlowpluginStoresthestatetransitionsforeveryalexaeventinanarray.stateFlow(skill)StateFlowattachescallbackstoonRequestStarted(),onBeforeStateChanged()andonBeforeReplySent()totrackstatetransitionsinaalexaEvent.flowarrayArgumentsskill(Voxa)TheskillobjectUsage constalexa=require(alexa-statemachine);alexa.plugins.stateFlow.register(skill)skill.onBeforeReplySent((alexaEvent)={console.log(alexaEvent.flow.join());//entryfirstStatesecondStatedie}); 10.13.3ReplaceIntentpluginItallowsyoutorenameanintentnamebasedonaregularexpression.Bydefaultitwillmatch/(.*)OnlyIntent$/andreplaceitwith$1Intent.replaceIntent(skill[,cong])ReplaceIntentpluginusesonIntentRequest()tomodifytheinco
36 mingrequestintentnameArgumentsskil
mingrequestintentnameArgumentsskill(Voxa)ThestateMachineSkillconfigAnobjectwiththeregextolookforandthereplacevalue.Usage constskill=newVoxa({Model,variables,views});Voxa.plugins.replaceIntent(skill,{regex:/(.*)OnlyIntent$/,replace:$1Intent});Voxa.plugins.replaceIntent(skill,{regex:/^VeryLong(.*)/,replace:Long$1}); WhyOnlyIntents?Agoodpracticeistoisolateanutteranceintoanotherintentifitcontainsasingleslot.BycreatingtheOnlyIntent,Alexawillprioritizethisintentiftheusersaysonlyavaluefromthatslot.Let'sexplainwiththefollowingscenario.Youneedtheusertoprovideazipcode.YouwouldhaveanintentcalledZipCodeIntent.Butyoustillhavetomanageiftheuseronlysaysazipcodewithoutanyotherwords.Sothat'swhenwecreateanOnlyIntent.Let'scallitZipCodeOnlyIntent.Ourutterancelewillbelikethis: 38Chapter10.Links VoxaDocumentation,Release2.1.2 ZipCodeIntenthereismy{ZipCodeSlot}ZipCodeIntentmyzipis{ZipC
37 odeSlot}...ZipCodeOnlyIntent{ZipCodeSlot
odeSlot}...ZipCodeOnlyIntent{ZipCodeSlot} Butnowwehavetwostateswhicharebasicallythesame.ReplaceIntentpluginwillrenameallincomingrequestsintentsfromZipCodeOnlyIntenttoZipCodeIntent.10.13.4CloudWatchpluginItlogsaCloudWatchmetricwhentheskillcatchesanerrororsuccessexecution.Paramscloudwatch(skill,cloudwatch[,eventMetric])CloudWatchpluginusesVoxa.onError(),Voxa.onStateMachineError()andVoxa.onBeforeReplySent()tologmetricsArgumentsskill(Voxa)ThestateMachineSkillcloudwatchAnewAWS.CloudWatchobject.putMetricDataParamsParamsforputMetricDataUsage constAWS=require(aws-sdk);constskill=newVoxa({Model,variables,views});constcloudWatch=newAWS.CloudWatch({});consteventMetric={MetricName:CaughtError,//NameofyourmetricNamespace:SkillName//Nameofyourskill};Voxa.plugins.cloudwatch(skill,cloudWatch,eventMetric); 10.13.5AutoloadpluginItacceptsanadaptertoautoloadinfoin
38 tothemodelobjectcomingineveryalexareques
tothemodelobjectcomingineveryalexarequest.ParamsautoLoad(skill[,cong])Autoloadpluginusesskill.onSessionStartedtoloaddatathersttimetheuseropensaskillArgumentsskill(Voxa)ThestateMachineSkill. 10.13.Plugins39 VoxaDocumentation,Release2.1.2 configAnobjectwithanadapterkeywithagetPromisemethodinwhichyoucanhandleyourdatabaseaccesstofetchinformationfromanyresource.Usage constskill=newVoxa({Model,variables,views});Voxa.plugins.autoLoad(skill,{adapter}); 10.14DebuggingVoxausesthedebugmoduleinternallytologanumberofdifferentinternalevents,ifyouwanthavealookatthoseeventsyouhavetodeclarethefollowingenvironmentvariableDEBUG=voxaThisisanexampleofthelogoutput voxaReceivednewevent:{"version":"1.0","session":{"new":true,"sessionId":,!"SessionId.09162f2a-cf8f-414f-92e6-1e3616ecaa05","application":{"applicationId":,!"amzn1.ask.skill.1fe77997-14db-409b-926c-0d8c161e5376"},"attributes":{},"user":{,!"
39 userId":"amzn1.ask.account.","accessToke
userId":"amzn1.ask.account.","accessToken":""}},"request":{"type":"LaunchRequest",,!"requestId":"EdwRequestId.0f7b488d-c198-4374-9fb5-6c2034a5c883","timestamp":"2017-,!01-25T23:01:15Z","locale":"en-US"}}+0msvoxaInitializedmodellike{}+8msvoxaStartingthestatemachinefromentrystate+2svoxaRunningsimpleTransitionforentry+1msvoxaRunningonAfterStateChangeCallbacks+0msvoxaentrytransitionresultedin{"to":"launch"}+0msvoxaRunninglaunchenterfunction+1msvoxaRunningonAfterStateChangeCallbacks+0msvoxalaunchtransitionresultedin{"reply":"Intent.Launch","to":"entry","message":{,!"tell":"Welcomemail@example.com!"},"session":{"data":{},"reply":null}}+7ms 10.15StarterKitThisprojectisdesignedtobeasimpletemplateforyournewskills.Withsomewellthoughtdefaultsthathaveprovenusefulwhendevelopingreallifeskills.10.15.1DirectoryStructureIthasthefollowingdirectorystructure . README.md config env.js index.js local.json.example production.json (
40 continuesonnextpage) 40Chapter10.Links V
continuesonnextpage) 40Chapter10.Links VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) staging.json gulpfile.js package.json serverless.yml services skill MainStateMachine.js index.js variables.js views.js speechAssets IntentSchema.json SampleUtterances.txt customSlotTypes test server.js congBydefaultyourskillwillhavethefollowingenvironments:localstagingproductionWhatenvironmentyou'reisdeterminedintheconfig/env.jsmoduleusingthefollowingcode:skillThisiswhereyourcodetohandlealexaeventsgoes,youwillusuallyhaveaStateMachinedenition,thiswillincludestates,middlewareandaModel,ViewsandVariablesspeechAssetsThisshouldbeaversioncontrolledcopyofyourintentschema,sampleutterrancesandcustomslots.server.jsAnhttpserverforyourskillconguredtolistenonport3000,thisshouldbeusedfordevelopmentonly.servicesJustacommonplacetoputmodelsandlibrariestestYouwritetestsright? 10.15.StarterKit41 VoxaDoc
41 umentation,Release2.1.2 gulpleAgulpr
umentation,Release2.1.2 gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneededsoyoucandeployyourskilltolambdawiththefollowingcommand: $slsdeploy 10.15.2Dependencies(installtheserst)1.Ifyouareusingnode.jsforthersttime,installnode:Installnvm,curl-o-https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh|bash,Installnode6.10,nvminstall6.102.Installgulpglobally,npminstall-ggulp3.Installngrok,andaddittoyourlocalbin10.15.3Runningtheproject1.ClonetheStarterKitrepository2.Makesureyou'rerunningnode6.10,thisiseasiestwithnvm3.Inyourskilldirectory,installnodedependenciesusingnpminstall4.Createaconfig/local.jsonleusingconfig/local.json.exampleasanexample
42 5.Runtheprojectwithgulpwatch6.Atthispoin
5.Runtheprojectwithgulpwatch6.Atthispointyoushouldstartngrokhttp3000andcongureyourskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint.10.16MyFirstPodcastThisprojectwillhelpyoubuildapodcastskillusingtheAudiodirectivestemplate.Youwillbeabletomanageloop,shuferequestsaswellasoffertheuserthepossibilitytostartanaudioover,pause,stopitorplaythenextorpreviousaudiofromapodcastlist.10.16.1DirectoryStructureIthasthefollowingdirectorystructure 42Chapter10.Links VoxaDocumentation,Release2.1.2 . README.md config env.js index.js local.json.example production.json staging.json gulpfile.js package.json serverless.yml services skill data podcast.js MainStateMachine.js index.js states.js variables.js views.js speechAssets IntentSchema.json SampleUtterances.txt customSlotTypes test server.js congBydefaultyourskillwillhavethefollowingenvironments:localstagingproductionWhatenvironmentyou'reisdeter
43 minedintheconfig/env.jsmoduleusingthefol
minedintheconfig/env.jsmoduleusingthefollowingcode:skill10.16.2index.jsFirstleinvokedbythelambdafunction,itinitializesthestatemachine.Youdon'tneedtomodifythisle.10.16.3MainStateMachine.jsStatemachineisinitializedwithyourmodel,viewsandvariables.Theclassstates.jswillbeinchargetohandleallintentsandeventscomingfromAlexa.Youdon'tneedtomodifythisle.10.16.4states.jsAlleventsandintentsdispatchedbytheAlexaVoiceServicetoyourskillarehandledhere.YoucanintegrateanyothermoduleorAPIcallstothirdpartyservices,calldatabaseresourcesorjustsimplyreplyaHelloorGoodbye 10.16.MyFirstPodcast43 VoxaDocumentation,Release2.1.2 responsetotheuser.Theaudiointentshandledinthisexampleare:AMAZON.CancelIntentAMAZON.LoopOffIntentAMAZON.LoopOnIntentAMAZON.NextIntentAMAZON.PauseIntentAMAZON.PreviousIntentAMAZON.RepeatIntentAMAZON.ResumeIntentAMAZON.ShufeOffIntentAMAZON.Shu
44 feOnIntentAMAZON.StartOverInte
feOnIntentAMAZON.StartOverIntentYoucantrackthevaluesforloop,shufeandcurrentURLplayinginthetokenpropertyoftheAlexaeventinthepathalexaEvent.context.AudioPlayer.token: skill.onState(loopOff,(alexaEvent)={if(alexaEvent.context){consttoken=JSON.parse(alexaEvent.context.AudioPlayer.token);constshuffle=token.shuffle;constloop=0;constoffsetInMilliseconds=alexaEvent.context.AudioPlayer.,!offsetInMilliseconds;letindex=token.index;if(index===podcast.length){index=0;}constdirectives=buildPlayDirective(podcast[index].url,index,shuffle, ,!loop,offsetInMilliseconds);return{reply:Intent.LoopDeactivated,to:die,directives};}return{reply:Intent.Exit,to:die};}); ForanyoftheseeventsyoucanmakeAlexatospeakafteruser'sactionwithareplyobject,optionallyyoucandenethediestateandpassthroughthedirectivesobjectwitheitheraAudioPlayer.PlayorAudioPlayer.Stopdirecti
45 vetype.Youcanalsohandledthefollowingplay
vetype.Youcanalsohandledthefollowingplaybackrequestevents:AudioPlayer.PlaybackStartedAudioPlayer.PlaybackFinishedAudioPlayer.PlaybackStoppedAudioPlayer.PlaybackNearlyFinishedAudioPlayer.PlaybackFailed 44Chapter10.Links VoxaDocumentation,Release2.1.2 You'renotallowedtorespondwithareplyobjectsinceit'sjustaneventmostfortrackignpurposes,soit'soptionaltoimplementandyoucandothefollowingsyntax: skill[onAudioPlayer.PlaybackStarted]((alexaEvent)={console.log(onAudioPlayer.PlaybackStarted,JSON.stringify(alexaEvent,null, ,!2));}); IncasetheuserhasactivatedtheloopmodebydispatchingtheAMAZON.LoopOnIntentintent,youcanimplementaqueuelistintheAudioPlayer.PlaybackNearlyFinishedthisway: skill[onAudioPlayer.PlaybackNearlyFinished]((alexaEvent,reply)={consttoken=JSON.parse(alexaEvent.context.AudioPlayer.token);if(token.loop===0){returnreply;}const
46 shuffle=token.shuffle;constloop=token.lo
shuffle=token.shuffle;constloop=token.loop;letindex=token.index+1;if(shuffle===1){index=randomIntInc(0,podcast.length-1);}elseif(index===podcast.length){index=0;}constdirectives=buildEnqueueDirective(podcast[index].url,index,shuffle, ,!loop);returnreply.append({directives});});functionbuildEnqueueDirective(url,index,shuffle,loop){constdirectives={};directives.type=AudioPlayer.Play;directives.playBehavior=REPLACE_ENQUEUED;directives.token=createToken(index,shuffle,loop);directives.url=podcast[index].url;directives.offsetInMilliseconds=0;returndirectives;} ThebuildEnqueueDirectivefunctionisinchargetobuildadirectiveobjectwithaqueuebehavior,whichwillallowtheskilltoplaythenextaudioassoonasthecurrentoneisnished.Thisiswhereyourcodetohandlealexaeventsgoes,youwillusuallyhaveaStateMachinedenition,thiswillincludestates,middlewareandaModel,ViewsandVariables.10.16.5data/podcast.jsAJSONvariablew
47 ithtitlesandurlsfor5audioexampleshostedi
ithtitlesandurlsfor5audioexampleshostedinasecureserver,allalongplayapodcastwhichtheusercanshufeorloop.Youcanmodifythislewithwhateverotheraudiotoaddtoyourplaylist.Keepinmindthattheymustbehostedinasecureserver.ThesupportedformatsfortheaudioleincludeAAC/MP4,MP3,HLS,PLSandM3U.Bitrates:16kbpsto384kbps. 10.16.MyFirstPodcast45 VoxaDocumentation,Release2.1.2 speechAssetsThisshouldbeaversioncontrolledcopyofyourintentschema,sampleutterrancesandcustomslots.server.jsAnhttpserverforyourskillconguredtolistenonport3000,thisshouldbeusedfordevelopmentonly.servicesJustacommonplacetoputmodelsandlibrariestestYouwritetestsright?gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneeded
48 soyoucandeployyourskilltolambdawiththefo
soyoucandeployyourskilltolambdawiththefollowingcommand: $slsdeploy 10.16.6Runningtheproject1.ClonetheAudioPodcastSamplerepository2.Makesureyou'rerunningnode6.10,thisiseasiestwithnvm3.Createaconfig/local.jsonleusingconfig/local.json.exampleasanexample4.Runtheprojectwithgulpwatch5.CreateaskillinyourAmazonDeveloperPortalaccountundertheALEXAmenu.6.GototheinteractionmodeltabandcopytheintentschemaandutterancesfromthethespeechAssetsfolder.7.Atthispointyoushouldstartngrokhttp3000andcongureyourskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint.10.17AccountLinkingThisprojectisdesignedtobeasimpletemplateforyournewskillswithaccountlinking.User'sinformationisstoredinaDynamoDBtablesoyoucanfetchitfromtheskillonceusersareauthenticated. 46Chapter10.Links VoxaDocumentation,Release2.1.2 10.17.1DirectoryStructureIthasthefollowingdirectorystructure . README.md config env.js index.js local.json.example production.jso
49 n staging.json gulpfile.js package.json
n staging.json gulpfile.js package.json serverless.yml services model.js userStorage.js skill MainStateMachine.js index.js states.js variables.js views.js speechAssets IntentSchema.json SampleUtterances.txt customSlotTypes test www infrastructure mount.js routes index.js skill.js server.js congBydefaultyourskillwillhavethefollowingenvironments:localstagingproductionWhatenvironmentyou'reisdeterminedintheconfig/env.jsmoduleusingthefollowingcode:skill10.17.2index.jsFirstleinvokedbythelambdafunction,itinitializesthestatemachine.Youdon'tneedtomodifythisle. 10.17.AccountLinking47 VoxaDocumentation,Release2.1.2 10.17.3MainStateMachine.jsStatemachineisinitializedwithyourmodel,viewsandvariables.Theclassstates.jswillbeinchargetohandleallintentsandeventscomingfromAlexa.Youdon'tneedtomodifythisle.10.17.4states.jsAlleventsandintentsdispatchedbytheAlexaVoiceServicetoyourskillarehandledhere
50 .YoucanintegrateanyothermoduleorAPIcalls
.YoucanintegrateanyothermoduleorAPIcallstothirdpartyservices,calldatabaseresourcesorjustsimplyreplyaHelloorGoodbyeresponsetotheuser.Beforetheverybeginningofthelesson,youcanimplementthemethodonRequestStartedtofetchuser'sdatafromDynamoDBbasedontheaccessTokencomingfromAlexa skill.onRequestStarted((alexaEvent)={if(!alexaEvent.session.user.accessToken){returnalexaEvent;}conststorage=newUserStorage();returnstorage.get(alexaEvent.session.user.accessToken).then((user)={alexaEvent.model.user=user;returnalexaEvent;});}); IftheuserisnotauthenticatedyoucanalsosendaLinkingAccountcardtotheAlexaappsousersknowthatbeforeusingyourskill,theymustgetauthenticated.speechAssetsThisshouldbeaversioncontrolledcopyofyourintentschema,sampleutterrancesandcustomslots.wwwAstandardexpressprojectconguredtoserveyourskillinthe/skillroute.Combinedwithngrokthisisagreattoolwhendevelopingordebugging.10.17.5routes/index.
51 jsYoucanhandleallGETandPOSTrequestsforyo
jsYoucanhandleallGETandPOSTrequestsforyouraccountlinkingprojectshere.ThemostcommononewillbethePOSTcalloftheformafterusershitthesubmitbutton.Inthisexample,wegatheruser'sinformationandcreatearowinDynamoDBfortheirinformation.ForexampleyoucangenerateanUUIDtoidentifytheusersastheprimarykeyandsenditbacktoAlexaastheaccessTokensoyoucaneasilyfetchuser'sinformationlateron. router.post(/,(req,res,next)={constmd=newMobileDetect(req.headers[user-agent]);constdb=newStorage();constemail=req.body.email;constcode=uuidV4().replace(/-/g,);constparams={id:code, (continuesonnextpage) 48Chapter10.Links VoxaDocumentation,Release2.1.2 (continuedfrompreviouspage) email,};returndb.put(params).then(()={constredirect=${req.query.redirect_uri}#state=${req.query.state}&access_,!token=${code}&token_type=Bearer;if(md.is(AndroidOS)){console.log(redirectingandroidt
52 o:${redirect});res.redirect(redirec
o:${redirect});res.redirect(redirect);}else{console.log(redirectingwebto:${redirect});res.render(auth/success,{page:success,title:Success,redirectUrl:redirect,});}}).catch(next);}); Tonishtheauthenticationprocessyouhavetomakearedirectiontotheredirect_uriAmazonsendstoourservice.Sincetherecouldbe2originstoredirectto,wecreatethisURLdynamically;theseendpointscouldlooklikethis:https://pitangui.amazon.com/spa/skill/account-linking-status.html?vendorId=xxx-ForUnitedStatesstorehttps://layla.amazon.com/spa/skill/account-linking-status.html?vendorId=xxxxxx-ForUKandGermanystoreTheotherparameterstosendare:access_token=YOUR-TOKENtoken_type=BearerservicesJustacommonplacetoputmodelsandlibraries10.17.6userStorage.jsUsethisleasanexampletohandledatabaselogic.SinceweuseDynamoDBforthisexample,weincluded2methods,aputandaget,souser
53 'sinformationgetstoredfromtheaccountlink
'sinformationgetstoredfromtheaccountlinkingprojectandgetfetchedfromthealexaskillside.ForreachingoutDynamoDByouneedsomepermissionsforyourlambdafunction.MakesuretograntyourlambdafunctionwitharolewithDynamoDBaccess.testYouwritetestsright? 10.17.AccountLinking49 VoxaDocumentation,Release2.1.2 gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneededsoyoucandeployyourskilltolambdawiththefollowingcommand: $slsdeploy 10.17.7Runningtheproject1.ClonetheAccountLinkingSamplerepository2.Makesureyou'rerunningnode6.10,thisiseasiestwithnvm3.Createaconfig/local.jsonleusingconfig/local.json.exampleasanexample4.Runtheprojectwithgulpwatch5.Atthispointyoushouldstartngrokhttp3000andcongureyou
54 rskillintheAmazonDeveloperpaneltousethen
rskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint. 50Chapter10.Links Index AAlexaEvent()(class),27AlexaEvent.AlexaEvent.intent.params(Alex-aEvent.AlexaEvent.intentattribute),27AlexaEvent.AlexaEvent.model(AlexaEvent.AlexaEventattribute),27AlexaEvent.AlexaEvent.token(AlexaEvent.AlexaEventattribute),27AlexaEvent.AlexaEvent.user(AlexaEvent.AlexaEventat-tribute),27alexaListEventCallback()(built-infunction),33alexaSkillEventCallback()(built-infunction),32audioPlayerCallback()(built-infunction),31autoLoad()(built-infunction),39Ccloudwatch()(built-infunction),39RreplaceIntent()(built-infunction),38Reply()(class),27Reply.Reply.append()(Reply.Replymethod),28Reply.Reply.fulllIntent()(Reply.Replymethod),28Reply.Reply.fulllSlot()(Reply.Replymethod),28Reply.Reply.toJSON()(Reply.Replymethod),28SstateFlow()(built-infunction),38Vvariable()(built-infunction),25Voxa()(class),28Voxa.execute()(Voxamethod),28Voxa.l
55 ambda()(Voxamethod),28Voxa.onAfterStateC
ambda()(Voxamethod),28Voxa.onAfterStateChanged()(Voxamethod),30Voxa.onAlexaHouseholdListEvent.ItemsCreated()(Voxa.onAlexaHouseholdListEventmethod),33Voxa.onAlexaHouseholdListEvent.ItemsDeleted()(Voxa.onAlexaHouseholdListEventmethod),33Voxa.onAlexaHouseholdListEvent.ItemsUpdated()(Voxa.onAlexaHouseholdListEventmethod),33Voxa.onAlexaSkillEvent.SkillAccountLinked()(Voxa.onAlexaSkillEventmethod),32Voxa.onAlexaSkillEvent.SkillDisabled()(Voxa.onAlexaSkillEventmethod),32Voxa.onAlexaSkillEvent.SkillEnabled()(Voxa.onAlexaSkillEventmethod),32Voxa.onAlexaSkillEvent.SkillPermissionAccepted()(Voxa.onAlexaSkillEventmethod),32Voxa.onAlexaSkillEvent.SkillPermissionChanged()(Voxa.onAlexaSkillEventmethod),32Voxa.onAudioPlayer.PlaybackFailed()(Voxa.onAudioPlayermethod),32Voxa.onAudioPlayer.PlaybackFinished()(Voxa.onAudioPlayermethod),32Voxa.onAudioPlayer.PlaybackNearlyFinished()(Voxa.onAudioPlayermethod),32Voxa.onAudioPlayer.Pl
56 aybackStarted()(Voxa.onAudioPlayermethod
aybackStarted()(Voxa.onAudioPlayermethod),32Voxa.onAudioPlayer.PlaybackStopped()(Voxa.onAudioPlayermethod),32Voxa.onBeforeReplySent()(Voxamethod),30Voxa.onBeforeStateChanged()(Voxamethod),29Voxa.onError()(Voxamethod),31Voxa.onIntent()(Voxamethod),29Voxa.onIntentRequest()(Voxamethod),29Voxa.onLaunchRequest()(Voxamethod),29Voxa.onPlaybackController.NextCommandIssued()(Voxa.onPlaybackControllermethod),32Voxa.onPlaybackController.PauseCommandIssued()(Voxa.onPlaybackControllermethod),32Voxa.onPlaybackController.PlayCommandIssued()(Voxa.onPlaybackControllermethod),32Voxa.onPlaybackController.PreviousCommandIssued()(Voxa.onPlaybackControllermethod),32 51 VoxaDocumentation,Release2.1.2 Voxa.onRequestStarted()(Voxamethod),30Voxa.onSessionEnded()(Voxamethod),30Voxa.onSessionStarted()(Voxamethod),30Voxa.onState()(Voxamethod),29Voxa.onStateMachineError()(Voxamethod),31Voxa.onSystem.ExceptionEncountered()(Voxa.onSystemmet