/
VoxaDocumentationRelease2.1.2RainAgencyJul06,2018 VoxaDocumentationRelease2.1.2RainAgencyJul06,2018

VoxaDocumentationRelease2.1.2RainAgencyJul06,2018 - PDF document

lucinda
lucinda . @lucinda
Follow
343 views
Uploaded On 2020-11-23

VoxaDocumentationRelease2.1.2RainAgencyJul06,2018 - PPT Presentation

Contents 1Summary12WhyVoxavsotherframeworks33Features54Installation75InitialCon2guration96Respondingtoalexaevents117Usingthedevelopmentserver138Respondingtoanintentevent159ProjectSamples1791Start ID: 821030

alexaevent 149 skill voxa 149 alexaevent voxa skill reply voxadocumentation release2 x0028fe00 150 callback atlast index json return model

Share:

Link:

Embed:

Download Presentation from below link

Download Pdf The PPT/PDF document "VoxaDocumentationRelease2.1.2RainAgencyJ..." 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.


Presentation Transcript

VoxaDocumentationRelease2.1.2RainAgencyJ
VoxaDocumentationRelease2.1.2RainAgencyJul06,2018Contents:1Summary12WhyVoxavsotherframeworks33Features54Installation75InitialConguration96Respondingtoalexaevents117Usingthedevelopmentserver138Respondingtoanintentevent159ProjectSamples179.1StarterKit................................................179.2MyFirstPodcast.............................................179.3AccountLinking.............................................1710Links1910.1VoxaDocumentation!..........................................1910.2NewAlexadeveloper...........................................2110.3VoxaarchitecturepatternMVC.....................................2210.4Models..................................................2210.5ViewsandVariables...........................................2310.6Controllers................................................2510.7Transition.................................................2610.8ThealexaEventObje

ct......................................
ct........................................2710.9ThereplyObject............................................2710.10Voxa...................................................2810.11RequestFlow...............................................3410.12I18N...................................................3510.13Plugins..................................................3710.14Debugging................................................4010.15StarterKit................................................4010.16MyFirstPodcast.............................................42i10.17AccountLinking.............................................46iiCHAPTER1SummaryVoxaisaframeworkthatprovidesawaytoorganizeaconversationalexperienceintoastatemachine.Eventhemostcomplexvoiceuserinterface(VUI)canberepresentedthroughthestatemachineanditprovidestheexibilityneededtobothberigidwhenneededinspecicstatesandexibletojumparoundstateswhenn

eeded.1VoxaDocumentation,Release2.1.2
eeded.1VoxaDocumentation,Release2.1.22Chapter1.SummaryCHAPTER2WhyVoxavsotherframeworksVoxaprovidesamorerobustframeworkforbuildingAlexaskills.Itprovidesadesignpatternthatwasn'tfoundinotherframeworks.CriticaltoVoxawasprovidingapluggableinterfaceandsupportingallofthelatestASKfeatures.3VoxaDocumentation,Release2.1.24Chapter2.WhyVoxavsotherframeworksCHAPTER3Features•MVCPattern•StateorIntenthandling(StateMachine)•EasyintegrationwithseveralAnalyticsproviders•Easytomodifyresponsele(theview)•CompatibilitywithallSSMLfeatures•Workswithcompanionappcards•Supportsi18nintheresponses•Cleancodestructurewithaunittestingframework•Easyerrorhandling•Accountlinkingsupport•SeveralPlugins5VoxaDocumentation,Release2.1.26Chapter3.FeaturesCHAPTER4InstallationVoxaisdistributedvianpm$npminstallvoxa--save7VoxaDocumentation,Release2.1.28Chapter4.InstallationC

HAPTER5InitialCongurationInstantia
HAPTER5InitialCongurationInstantiatingaStateMachineSkillrequiresacongurationspecifyingyourViewsandVariables.usestrict;constVoxa=require(voxa);constviews=require(./views):constvariables=require(./variables);constskill=newVoxa({variables,views});9VoxaDocumentation,Release2.1.210Chapter5.InitialCongurationCHAPTER6RespondingtoalexaeventsOnceyouhaveyourskillconguredrespondingtoeventsisassimpleascallingtheskill.lambdamethodconstskill=require(./MainStateMachine);exports.handler=skill.lambda();11VoxaDocumentation,Release2.1.212Chapter6.RespondingtoalexaeventsCHAPTER7UsingthedevelopmentserverTheframeworkprovidesasimplebuiltinserverthat'sconguredtoserveallPOSTrequeststoyourskill,thisworksgreatwhendeveloping,speciallywhenpairedwithngrok//thiswillstartanhttpserverlisteningonport3000skill.startServer(3000);13VoxaDocumentation,Release2

.1.214Chapter7.Usingthedevelopmentserve
.1.214Chapter7.UsingthedevelopmentserverCHAPTER8Respondingtoanintenteventskill.onIntent(HelpIntent,(alexaEvent)�={return{reply:HelpIntent.HelpAboutSkill};});skill.onIntent(ExitIntent,(alexaEvent)�={return{reply:ExitIntent.Farewell};});15VoxaDocumentation,Release2.1.216Chapter8.RespondingtoanintenteventCHAPTER9ProjectSamplesTohelpyougetstartedthestatemachinehasanumberofexampleprojectsyoucanuse.9.1StarterKitThisisthesimplestproject,itdenesthedefaultdirectorystructurewerecommendusingwithvoxaprojectsandhasanexampleserverless.ymllethatcanbeusedtodeployyourskilltoalambdafunction.9.2MyFirstPodcastInthisexampleyouwillseehowtoimplementapodcastskillbyhavingalistofaudiosinale(podcast.js)withtitlesandurls.ItimplementsallaudiointentsallowedbytheaudiobackgroundfeatureandhandlesalltheplaybackrequestsdispatchedbyAlexaonceanaudiohasstarted,sto

pped,failed,nishedornearlytonish
pped,failed,nishedornearlytonish.Keepinmindtheaudiosmustbehostedinasecureserver.9.3AccountLinkingAmorecomplexprojectthatshowshowtoworkwithaccountlinkingandmakeresponsesusingthemodelstate.Itusesserverlesstodeployyouraccountlinkingserverandskilltolambda,createadynamodbtabletostoreyouraccountlinkingandcreateans3buckettostoreyourstaticassets.ItalsohasagulptasktouploadyourassetstoS317VoxaDocumentation,Release2.1.218Chapter9.ProjectSamplesCHAPTER10Links•search10.1VoxaDocumentation!10.1.1SummaryVoxaisanAlexaskillframeworkthatprovidesawaytoorganizeaskillintoastatemachine.Eventhemostcomplexvoiceuserinterface(VUI)canberepresentedthroughthestatemachineanditprovidestheexibilityneededtobothberigidwhenneededinspecicstatesandexibletojumparoundwhenallowingthatalsomakessense.10.1.2WhyVoxavsotherframeworksVoxaprovidesamorerobustframeworkforbuildingAlexaskills.Itprovidesadesignpatternthatwasn'tfou

ndinotherframeworks.CriticaltoVoxawaspro
ndinotherframeworks.CriticaltoVoxawasprovidingapluggableinterfaceandsupportingallofthelatestASKfeatures.10.1.3Features•MVCPattern•StateorIntenthandling(StateMachine)•EasyintegrationwithseveralAnalyticsproviders•Easytomodifyresponsele(theview)•CompatibilitywithallSSMLfeatures•CompatiblewithAudiodirectives•Workswithcompanionappcards•Supportsi18nintheresponses19VoxaDocumentation,Release2.1.2•Cleancodestructurewithaunittestingframework•Easyerrorhandling•Accountlinkingsupport•PersistentsessionsupportusingDynamoDB•SeveralPlugins10.1.4InstallationVoxaisdistributedvianpm$npminstallvoxa--save10.1.5InitialCongurationInstantiatingaStateMachineSkillrequiresacongurationspecifyingyourViewsandVariables.usestrict;constVoxa=require(voxa);constviews=require(./views):constvariables=require(./variables);cons

tskill=newVoxa({variables,views});10.1.
tskill=newVoxa({variables,views});10.1.6RespondingtoalexaeventsOnceyouhaveyourskillconguredrespondingtoeventsisassimpleascallingtheskill.lambdamethodconstskill=require(./MainStateMachine);exports.handler=skill.lambda();10.1.7UsingthedevelopmentserverTheframeworkprovidesasimplebuiltinserverthat'sconguredtoserveallPOSTrequeststoyourskill,thisworksgreatwhendeveloping,speciallywhenpairedwithngrok//thiswillstartanhttpserverlisteningonport3000skill.startServer(3000);10.1.8Respondingtoanintenteventskill.onIntent(HelpIntent,(alexaEvent)�={return{reply:HelpIntent.HelpAboutSkill};});skill.onIntent(ExitIntent,(alexaEvent)�={return{reply:ExitIntent.Farewell};});20Chapter10.LinksVoxaDocumentation,Release2.1.210.2NewAlexadeveloperIftheskillsdevelopmentforalexaisanewthingforyou,wehavesomesuggestiontogetyoudeepintothisworld.10.2.1Getting

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

odel.EndusersinteractwithallofAlexa'sabi
odel.EndusersinteractwithallofAlexa'sabilitiesinthesameway–bywakingthedevicewiththewakeword(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

tioncontainingtheinformationthattheAlexa
tioncontainingtheinformationthattheAlexaserviceneedstodothefollowing:•RouterequeststotheAWSLambdafunctionorwebservicethatimplementstheskill,orfordevelopmentpurposeyoucanrunitlocallyusingngrok.•DisplayinformationabouttheskillintheAmazonAlexaApp.Theappshowsallpublishedskills,aswellasallofyourownskillscurrentlyunderdevelopment.10.2.NewAlexadeveloper21VoxaDocumentation,Release2.1.2YoumustregisteraskillbeforeyoucantestitwiththeServiceSimulatorinthedeveloperportaloranAlexa-enableddevice.FollowtheseinstructionstoregisterandmanagingyourAlexaskill.10.3VoxaarchitecturepatternMVC10.4ModelsModelsarethedatastructurethatholdsthecurrentstateofyourapplication,theframeworkdoesn'tmakemanyassumptionsonitandonlyrequireshaveafromEventmethodthatshouldinitializeitbasedonthealexaEventsessionattributesandaserializemethodthatshouldreturnJSON.stringifyablestructuretothenstoreinthesessionattributesusestrict;cons

t_=require(lodash);classModel{
t_=require(lodash);classModel{constructor(data){_.assign(this,data);}staticfromEvent(alexaEvent){(continuesonnextpage)22Chapter10.LinksVoxaDocumentation,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;&#xk-60;�time="3s"/Good{time}.Isthereanythingicandotohelp,!youtoday?

1;,Hithere! rea;&#xk-60;ʏ
1;,Hithere! rea;&#xk-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.ViewsandVariables23VoxaDocumentation,Release2.1.2tellTellviewssendaresponsetoalexaandclosethesessioninmediately.They'reusedwhentheskillisdoneinteractingwiththeuser.TheExitIntent.Farewellisanexampleofthis.askAskviewsareusedtoprompttheuserforinformation,theysendaresponsetoalexabutkeepthesessionopensotheusercanrespond.TheLaunchIntent.OpenResponseisanaskview.sayWhilethetellandaskviewtypesareanexactrepresentationofthebasealexaprogrammingmod

el,thesayviewsaredifferent.They'reanabst
el,thesayviewsaredifferent.They'reanabstractioncreatedbyvoxatomakeitsimplertocomposeyourresponsesovermanystatetransitions.Theydon'tsendarespondtoalexabutinsteadmakeastatetransitioninternallyandcontinueexecutingyourskillcodeuntilthere'satelloraskresponse.10.5.2DirectivesNowyoucanincludedirectivesinyourviewsle.Hintdirectivecanbeeasilyspecifywithasimpleobjectcontainingahintkey.constviews={LaunchIntent:{OpenResponse:{tell:[Hello! rea;&#xk-60;�time="3s"/Good{time}.Isthereanythingicandotohelp,!youtoday?,Hithere! rea;&#xk-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!"

,type:"PlainText"}},(continuesonnextpag
,type:"PlainText"}},(continuesonnextpage)24Chapter10.LinksVoxaDocumentation,Release2.1.2(continuedfrompreviouspage)backButton:"VISIBLE"}}],},};10.5.3VariablesVariablesaretherenderingenginewayofaddinglogicintoyourviews.They'redessignedtobeverysimplesincemostofyourlogicshouldbeinyourmodelorcontrollers.Avariablesignatureis:variable(model,alexaEvent)Arguments•model–Theinstanceofyourmodelforthecurrentalexaevent.•AlexaEvent–Thecurrentalexaevent.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,

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.Controllers25VoxaDocumentation,Release2.1.2Yourstateshouldrespondwithatransition.Thetransitionisaplainobjectthatcantakedirectives,toandreplykeys.10.6.1TheentrycontrollerTheentrycontrollerisspecialinthatit'sthedefaultstatetogotoatthebeginningofyoursessionandifyourstatereturnsnoresponse.Forexampleinthenextsnippedthere'sawaitingstatethatexpectsanAMAZON.NextIntentoranAMAZON.PreviousIntent,inthecasetheuserssayssomethingunexpectedlikeanAMAZON.HelpIntentthestatereturnsundened,theStateMachineframeworkhandlesthissituationsbyredirectingtot

heentrystateskill.onState(waiting&
heentrystateskill.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.2TheonIntenthelperForthesimplepatternofhavingacontrollerrespondtoanspecicintenttheframeworkprovidestheonIntenthelperskill.onIntent(LaunchIntent,(alexaEvent)�={return{reply:LaunchIntent.OpenResponse,to:die};});IfyoureceiveaDisplay.ElementSelectedtyperequest,youcouldusethesameapproachforintentsandstate.VoxareceivesthistypeofrequestandturnsitintoDisplayElementSelectedIntentskill.onIntent(DisplayElementSelected,(alexaEvent)�={return{reply:DisplayElementSelected.

OpenResponse,to:die};});
OpenResponse,to:die};});Underthehoodthiscreatesanewkeyintheentrycontrollerandanewstate10.7TransitionAtransitionistheresultofcontrollerexecution,it'sasimpleobjectwithkeysthatcontroltheowofexecutioninyourskill.10.7.1toThetokeyshouldbethenameofastateinyourstatemachine,whenpresentitindicatestotheframeworkthatitshouldmovetoanewstate.Ifabsentit'sassumedthattheframeworkshouldmovetothediestate.return{to:stateName};26Chapter10.LinksVoxaDocumentation,Release2.1.210.7.2directivesDirectivesareusedpasseddirectlytothealexaresponse,theformatisdescribedinthealexadocumentationreturn{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

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

classReply(alexaEvent[,message])Thereply
classReply(alexaEvent[,message])ThereplyobjectisusedbytheframeworktorenderAlexaresponses,ittakesallofyourstatements,10.8.ThealexaEventObject27VoxaDocumentation,Release2.1.2cardsanddirectivesandgeneratesaproperjsonresponseforAlexaArguments•alexaEvent(AlexaEvent)–•message–AmessageobjectReply.Reply.append(message)AddsstatementstotheReplyArguments•message–Anobjectwithkeysask,tell,say,reprompt,card,ordirectiveskeys.OranotherreplyobjectReturnstheReplyobjectReply.Reply.toJSON()ReturnsAnobjectwiththeproperformattosendbacktoAlexa,withstatementswrappedinSSMLtags,cards,repromptsanddirectivesReply.Reply.fulfillIntent(canFulll)Arguments•canFulfill–Astringwithpossiblevalues:YES|NO|MAYBEtofulllrequestReply.Reply.fulfillSlot(slotName,canUnderstand,canFulll)Arguments•slotName–Astringwiththeslottofulll•canUnderstand–Astringwithpossiblevalues:YES|NO|

MAYBEthatindicatesslotunderstanding•
MAYBEthatindicatesslotunderstanding•canFulfill–Astringwithpossiblevalues:YES|NOthatindicatesslotfulllment10.10VoxaclassVoxa(cong)Arguments•config–Congurationforyourskill,itshouldincludeViewsandVariablesandoptionallyamodelandalistofappIds.IfappIdsispresentthentheframeworkwillcheckeveryalexaeventandenforcetheapplicationidtomatchoneofthespeciedapplicationids.constskill=newVoxa({Model,variables,views,appIds});Voxa.lambda()ReturnsAlambdahandlerthatwillcallyourskill.executemethodexports.handler=skill.lambda();Voxa.execute(event)ThemainentrypointfortheSkillexecutionArguments28Chapter10.LinksVoxaDocumentation,Release2.1.2•event–Theeventsentbyalexa.•context–ThecontextofthelambdafunctionReturnsPromiseAresponseresolvingtoajavascriptobjecttobesentasaresulttoAlexa.skill.execute(event,context).then(result�=callback(null,result)).catch(callback);Vox

a.onState(stateName,handler)Mapsahandler
a.onState(stateName,handler)MapsahandlertoastateArguments•stateName(string)–Thenameofthestate•handler(function/object)–ThecontrollertohandlethestateReturnsAnobjectorapromisethatresolvestoanobjectthatspeciesatransitiontoanotherstateand/oraviewtorenderskill.onState(entry,{LaunchIntent:launch,AMAZON.HelpIntent:help,});skill.onState(launch,(alexaEvent)�={return{reply:LaunchIntent.OpenResponse,to:die};});Voxa.onIntent(intentName,handler)AshortcutfordeniningstatecontrollersthatmapdirectlytoanintentArguments•intentName(string)–Thenameoftheintent•handler(function/object)–ThecontrollertohandlethestateReturnsAnobjectorapromisethatresolvestoanobjectthatspeciesatransitiontoanotherstateand/oraviewtorenderskill.onIntent(HelpIntent,(alexaEvent)�={return{reply:HelpI

ntent.HelpAboutSkill};});Voxa.onIn
ntent.HelpAboutSkill};});Voxa.onIntentRequest(callback[,atLast])ThisisexecutedforallIntentRequestevents,defaultbehavioristoexecutetheStateMachinemachinery,yougenerallydon'tneedtooverridethis.Arguments•callback(function)–•last(bool)–ReturnsPromiseVoxa.onLaunchRequest(callback[,atLast])AddsacallbacktobeexecutedwhenprocessingaLaunchRequest,thedefaultbehavioristofakethealexaeventasanIntentRequestwithaLaunchIntentandjustdefertotheonIntentRequesthandlers.Yougenerallydon'tneedtooverridethis.10.10.Voxa29VoxaDocumentation,Release2.1.2Voxa.onBeforeStateChanged(callback[,atLast])Thisisexecutedbeforeenteringeverystate,itcanbeusedtotrackstatechangesormakechangestothealexaeventobjectVoxa.onBeforeReplySent(callback[,atLast])Addsacallbacktobeexecutedjustbeforesendingthereply,internallythisisusedtoaddtheserializedmodelandnextstatetothesession.Itcanbeusedtoalterthereply,orforexampletotrackthena

lresponsesenttoauserinanalytics.skill.o
lresponsesenttoauserinanalytics.skill.onBeforeReplySent((alexaEvent,reply)�={constrendered=reply.toJSON();analytics.track(alexaEvent,rendered)});Voxa.onAfterStateChanged(callback[,atLast])Addscallbackstobeexecutedontheresultofastatetransition,thisarecalledaftereverytransitionandinternallyit'susedtorenderthetransitionreplyusingtheviewsandvariablesThecallbacksgetalexaEvent,replyandtransitionparams,itshouldreturnthetransitionobjectskill.onAfterStateChanged((alexaEvent,reply,transition)�={if(transition.reply===LaunchIntent.PlayTodayLesson){transition.reply=_.sample([LaunchIntent.PlayTodayLesson1,LaunchIntent.,!PlayTodayLesson2]);}returntransition;});Voxa.onUnhandledState(callback[,atLast])Addsacallbacktobeexecutedwhenastatetransitionfailstogeneratearesult,thisusuallyhappenswhenredirectingtoamissingstateoranentrycallforanonconguredintent,thehandlersgetaal

exaeventparameterandshouldreturnatransit
exaeventparameterandshouldreturnatransitionthesameasastatecontrollerwould.Voxa.onSessionStarted(callback[,atLast])AddsacallbacktotheonSessinStartedevent,thisexecutesforalleventswherealexaEvent.session.new===trueThiscanbeusefultotrackanalyticsskill.onSessionStarted((alexaEvent,reply)�={analytics.trackSessionStarted(alexaEvent);});Voxa.onRequestStarted(callback[,atLast])Addsacallbacktobeexecutedwheneverthere'saLaunchRequest,IntentRequestoraSessionEndedRequest,thiscanbeusedtoinitializeyouranalyticsorgetyouraccountlinkinguserdata.Internallyit'susedtoinitializethemodelbasedontheeventsessionskill.onRequestStarted((alexaEvent,reply)�={alexaEvent.model=this.config.Model.fromEvent(alexaEvent);});Voxa.onSessionEnded(callback[,atLast])AddsacallbacktotheonSessionEndedevent,thisiscalledforeverySessionEndedRequestorwhentheskillreturnsatransitiontoastatewhereisTerminal===true,normallythisisatransit

iontothediestate.Youwouldnormallyusethis
iontothediestate.Youwouldnormallyusethistotrackanalytics30Chapter10.LinksVoxaDocumentation,Release2.1.2Voxa.onSystem.ExceptionEncountered(callback[,atLast])ThishandlesSystem.ExceptionEncounteredeventthataresenttoyourskillwhenaresponsetoanAudioPlayereventcausesanerrorreturnPromise.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

rrorsinthestatemachinecontrollers,,theha
rrorsinthestatemachinecontrollers,,thehandlersget(alexaEvent,reply,error)param-etersskill.onStateMachineError((alexaEvent,reply,error)�={//itgetsthecurrentreply,whichcouldbeincompleteduetoanerror.returnnewReply(alexaEvent,{tell:Anerrorinthecontrollerscode}).toJSON();});Voxa.onError(callback[,atLast])Thisisthemoregeneralhandlerandwillcatchallunhandlederrorsintheframework,itgets(alexaEvent,error)parametersasargumentsskill.onError((alexaEvent,error)�={returnnewReply(alexaEvent,{tell:Anunrecoverableerroroccurred.}).toJSON();});10.10.2PlaybackControllerhandlersHandleeventsfromtheAudioPlayerinterfaceaudioPlayerCallback(alexaEvent,reply)AllaudioplayermiddlewarecallbacksgetaalexaeventandareplyobjectArguments•alexaEvent(AlexaEvent)–ThealexaeventsentbyAlexa•reply(object)–AreplytobesentasaresponseReturnsobjectwriteYouralexaeventhandlershouldreturnanapprop

riateresponseaccordingtotheeventtype,thi
riateresponseaccordingtotheeventtype,thisgenerallymeansappendingtothereplyobjectInthefollowingexamplethealexaeventhandlerreturnsaREPLACE_ENQUEUEDdirectivetoaPlaybackNearlyFinished()event.10.10.Voxa31VoxaDocumentation,Release2.1.2skill[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

ybackController.PauseCommandIssued(callb
ybackController.PauseCommandIssued(callback[,atLast])Voxa.onPlaybackController.PlayCommandIssued(callback[,atLast])Voxa.onPlaybackController.PreviousCommandIssued(callback[,atLast])10.10.3AlexaSkillEventhandlersHandlerequestfortheAlexaSkillEventsalexaSkillEventCallback(alexaEvent)AllthealexaskilleventcallbacksgetaalexaeventandareplyobjectArguments•alexaEvent(AlexaEvent)–ThealexaeventsentbyAlexa•reply(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

.onAlexaSkillEvent.SkillEnabled(callback
.onAlexaSkillEvent.SkillEnabled(callback[,atLast])Voxa.onAlexaSkillEvent.SkillDisabled(callback[,atLast])Voxa.onAlexaSkillEvent.SkillPermissionAccepted(callback[,atLast])32Chapter10.LinksVoxaDocumentation,Release2.1.2Voxa.onAlexaSkillEvent.SkillPermissionChanged(callback[,atLast])10.10.4AlexaListEventhandlersHandlerequestfortheAlexaListEventsalexaListEventCallback(alexaEvent)AllthealexalisteventcallbacksgetaalexaeventandareplyobjectArguments•alexaEvent(AlexaEvent)–ThealexaeventsentbyAlexa•reply(object)–AreplytobesentastheresponseReturnsobjectreplyAlexaonlyneedsanacknowledgementthatyoureceivedandprocessedtheeventsoitdoesn'tneedtoresendtheevent.JustreturningthereplyobjectisenoughThisisanexampleonhowyourskillcanprocessaItemsCreated()event.skill[onAlexaHouseholdListEvent.ItemsCreated]((alexaEvent,reply)�={constlistId=alexaEvent.request.body.listId;constuserId=alexaEven

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.Voxa33VoxaDocumentation,Release2.1.210.11RequestFlow34Chapter10.LinksVoxaDocumentation,Release2.1.210.12I18NInternationalizationsupportisdoneusingthei18nextlibrary,thesametheAmazonAlexaNodeSDKuses.10.12.1ConguringtheskillforI18NTouseityouneedtocongureyourskilltousetheI18NRendererclassinsteadofthedefaultrendererclass.constVoxa=require(voxa);constskill=newVoxa({Model,variables,views,RenderClass:Voxa.I18NRenderer});TheframeworktakescareofselectingthecorrectlocaleoneveryalexaeventbylookingatthealexaEvent.request.localeproperty.10.12.2ChangesinyourviewsTheotherchangeyouwillneedistodeneyourviewsusing

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.I18N35VoxaDocumentation,Release2.1.2(continuedfrompreviouspage)One:{tell:{numberOne}},},Say:{Say:{say:sagen},},Random:{tell:[zufällig1,zufällig2,zufällig3]},},},};}());

module.exports=views;10.12.3VariablesVa
module.exports=views;10.12.3VariablesVariablesshouldworkmostlythesameaswiththeDefaultRenderer,withtheexceptionthatvariableswillnowgetalocalekeyusestrict;/***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.LinksVoxaDocumentation,Release2.1.2(continuedfrompreviouspage)returnPromise.resolve(Morning);}if(curHr18){returnPromis

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.pluginsusestrict;constVoxa=require(voxa);constModel=require(./model);constviews=require(./views):constvariables=require(

./variables);constskill=newVoxa({Mo
./variables);constskill=newVoxa({Model,variables,views});Voxa.plugins.replaceIntent(skill);10.13.Plugins37VoxaDocumentation,Release2.1.210.13.2StateFlowpluginStoresthestatetransitionsforeveryalexaeventinanarray.stateFlow(skill)StateFlowattachescallbackstoonRequestStarted(),onBeforeStateChanged()andonBeforeReplySent()totrackstatetransitionsinaalexaEvent.flowarrayArguments•skill(Voxa)–TheskillobjectUsageconstalexa=require(alexa-statemachine);alexa.plugins.stateFlow.register(skill)skill.onBeforeReplySent((alexaEvent)�={console.log(alexaEvent.flow.join(�));//entry�firstState�secondState�die});10.13.3ReplaceIntentpluginItallowsyoutorenameanintentnamebasedonaregularexpression.Bydefaultitwillmatch/(.*)OnlyIntent$/andreplaceitwith$1Intent.replaceIntent(skill[,cong])ReplaceIntentpluginusesonIntentRequest()tomodifytheinco

mingrequestintentnameArguments•skil
mingrequestintentnameArguments•skill(Voxa)–ThestateMachineSkill•config–Anobjectwiththeregextolookforandthereplacevalue.Usageconstskill=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.LinksVoxaDocumentation,Release2.1.2ZipCodeIntenthereismy{ZipCodeSlot}ZipCodeIntentmyzipis{ZipC

odeSlot}...ZipCodeOnlyIntent{ZipCodeSlot
odeSlot}...ZipCodeOnlyIntent{ZipCodeSlot}Butnowwehavetwostateswhicharebasicallythesame.ReplaceIntentpluginwillrenameallincomingrequestsintentsfromZipCodeOnlyIntenttoZipCodeIntent.10.13.4CloudWatchpluginItlogsaCloudWatchmetricwhentheskillcatchesanerrororsuccessexecution.Paramscloudwatch(skill,cloudwatch[,eventMetric])CloudWatchpluginusesVoxa.onError(),Voxa.onStateMachineError()andVoxa.onBeforeReplySent()tologmetricsArguments•skill(Voxa)–ThestateMachineSkill•cloudwatch–AnewAWS.CloudWatchobject.•putMetricDataParams–ParamsforputMetricDataUsageconstAWS=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

tothemodelobjectcomingineveryalexareques
tothemodelobjectcomingineveryalexarequest.ParamsautoLoad(skill[,cong])Autoloadpluginusesskill.onSessionStartedtoloaddatathersttimetheuseropensaskillArguments•skill(Voxa)–ThestateMachineSkill.10.13.Plugins39VoxaDocumentation,Release2.1.2•config–AnobjectwithanadapterkeywithagetPromisemethodinwhichyoucanhandleyourdatabaseaccesstofetchinformationfromanyresource.Usageconstskill=newVoxa({Model,variables,views});Voxa.plugins.autoLoad(skill,{adapter});10.14DebuggingVoxausesthedebugmoduleinternallytologanumberofdifferentinternalevents,ifyouwanthavealookatthoseeventsyouhavetodeclarethefollowingenvironmentvariableDEBUG=voxaThisisanexampleofthelogoutputvoxaReceivednewevent:{"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":{,!"

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}}+7ms10.15StarterKitThisprojectisdesignedtobeasimpletemplateforyournewskills.Withsomewellthoughtdefaultsthathaveprovenusefulwhendevelopingreallifeskills.10.15.1DirectoryStructureIthasthefollowingdirectorystructure.README.mdconfigenv.jsindex.jslocal.json.exampleproduction.json(

continuesonnextpage)40Chapter10.LinksV
continuesonnextpage)40Chapter10.LinksVoxaDocumentation,Release2.1.2(continuedfrompreviouspage)staging.jsongulpfile.jspackage.jsonserverless.ymlservicesskillMainStateMachine.jsindex.jsvariables.jsviews.jsspeechAssetsIntentSchema.jsonSampleUtterances.txtcustomSlotTypestestserver.jscongBydefaultyourskillwillhavethefollowingenvironments:•local•staging•productionWhatenvironmentyou'reisdeterminedintheconfig/env.jsmoduleusingthefollowingcode:skillThisiswhereyourcodetohandlealexaeventsgoes,youwillusuallyhaveaStateMachinedenition,thiswillincludestates,middlewareandaModel,ViewsandVariablesspeechAssetsThisshouldbeaversioncontrolledcopyofyourintentschema,sampleutterrancesandcustomslots.server.jsAnhttpserverforyourskillconguredtolistenonport3000,thisshouldbeusedfordevelopmentonly.servicesJustacommonplacetoputmodelsandlibrariestestYouwritetestsright?10.15.StarterKit41VoxaDoc

umentation,Release2.1.2gulpleAgulpr
umentation,Release2.1.2gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneededsoyoucandeployyourskilltolambdawiththefollowingcommand:$slsdeploy10.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

5.Runtheprojectwithgulpwatch6.Atthispoin
5.Runtheprojectwithgulpwatch6.Atthispointyoushouldstartngrokhttp3000andcongureyourskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint.10.16MyFirstPodcastThisprojectwillhelpyoubuildapodcastskillusingtheAudiodirectivestemplate.Youwillbeabletomanageloop,shuferequestsaswellasoffertheuserthepossibilitytostartanaudioover,pause,stopitorplaythenextorpreviousaudiofromapodcastlist.10.16.1DirectoryStructureIthasthefollowingdirectorystructure42Chapter10.LinksVoxaDocumentation,Release2.1.2.README.mdconfigenv.jsindex.jslocal.json.exampleproduction.jsonstaging.jsongulpfile.jspackage.jsonserverless.ymlservicesskilldatapodcast.jsMainStateMachine.jsindex.jsstates.jsvariables.jsviews.jsspeechAssetsIntentSchema.jsonSampleUtterances.txtcustomSlotTypestestserver.jscongBydefaultyourskillwillhavethefollowingenvironments:•local•staging•productionWhatenvironmentyou'reisdeter

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,calldatabaseresourcesorjustsimplyreplyaHelloorGoodbye10.16.MyFirstPodcast43VoxaDocumentation,Release2.1.2responsetotheuser.Theaudiointentshandledinthisexampleare:•AMAZON.CancelIntent•AMAZON.LoopOffIntent•AMAZON.LoopOnIntent•AMAZON.NextIntent•AMAZON.PauseIntent•AMAZON.PreviousIntent•AMAZON.RepeatIntent•AMAZON.ResumeIntent•AMAZON.ShufeOffIntent•AMAZON.Shu

feOnIntent•AMAZON.StartOverInte
feOnIntent•AMAZON.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

vetype.Youcanalsohandledthefollowingplay
vetype.Youcanalsohandledthefollowingplaybackrequestevents:•AudioPlayer.PlaybackStarted•AudioPlayer.PlaybackFinished•AudioPlayer.PlaybackStopped•AudioPlayer.PlaybackNearlyFinished•AudioPlayer.PlaybackFailed44Chapter10.LinksVoxaDocumentation,Release2.1.2You'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

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

ithtitlesandurlsfor5audioexampleshostedi
ithtitlesandurlsfor5audioexampleshostedinasecureserver,allalongplayapodcastwhichtheusercanshufeorloop.Youcanmodifythislewithwhateverotheraudiotoaddtoyourplaylist.Keepinmindthattheymustbehostedinasecureserver.ThesupportedformatsfortheaudioleincludeAAC/MP4,MP3,HLS,PLSandM3U.Bitrates:16kbpsto384kbps.10.16.MyFirstPodcast45VoxaDocumentation,Release2.1.2speechAssetsThisshouldbeaversioncontrolledcopyofyourintentschema,sampleutterrancesandcustomslots.server.jsAnhttpserverforyourskillconguredtolistenonport3000,thisshouldbeusedfordevelopmentonly.servicesJustacommonplacetoputmodelsandlibrariestestYouwritetestsright?gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneeded

soyoucandeployyourskilltolambdawiththefo
soyoucandeployyourskilltolambdawiththefollowingcommand:$slsdeploy10.16.6Runningtheproject1.ClonetheAudioPodcastSamplerepository2.Makesureyou'rerunningnode6.10,thisiseasiestwithnvm3.Createaconfig/local.jsonleusingconfig/local.json.exampleasanexample4.Runtheprojectwithgulpwatch5.CreateaskillinyourAmazonDeveloperPortalaccountundertheALEXAmenu.6.GototheinteractionmodeltabandcopytheintentschemaandutterancesfromthethespeechAssetsfolder.7.Atthispointyoushouldstartngrokhttp3000andcongureyourskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint.10.17AccountLinkingThisprojectisdesignedtobeasimpletemplateforyournewskillswithaccountlinking.User'sinformationisstoredinaDynamoDBtablesoyoucanfetchitfromtheskillonceusersareauthenticated.46Chapter10.LinksVoxaDocumentation,Release2.1.210.17.1DirectoryStructureIthasthefollowingdirectorystructure.README.mdconfigenv.jsindex.jslocal.json.exampleproduction.jso

nstaging.jsongulpfile.jspackage.json
nstaging.jsongulpfile.jspackage.jsonserverless.ymlservicesmodel.jsuserStorage.jsskillMainStateMachine.jsindex.jsstates.jsvariables.jsviews.jsspeechAssetsIntentSchema.jsonSampleUtterances.txtcustomSlotTypestestwwwinfrastructuremount.jsroutesindex.jsskill.jsserver.jscongBydefaultyourskillwillhavethefollowingenvironments:•local•staging•productionWhatenvironmentyou'reisdeterminedintheconfig/env.jsmoduleusingthefollowingcode:skill10.17.2index.jsFirstleinvokedbythelambdafunction,itinitializesthestatemachine.Youdon'tneedtomodifythisle.10.17.AccountLinking47VoxaDocumentation,Release2.1.210.17.3MainStateMachine.jsStatemachineisinitializedwithyourmodel,viewsandvariables.Theclassstates.jswillbeinchargetohandleallintentsandeventscomingfromAlexa.Youdon'tneedtomodifythisle.10.17.4states.jsAlleventsandintentsdispatchedbytheAlexaVoiceServicetoyourskillarehandledhere

.YoucanintegrateanyothermoduleorAPIcalls
.YoucanintegrateanyothermoduleorAPIcallstothirdpartyservices,calldatabaseresourcesorjustsimplyreplyaHelloorGoodbyeresponsetotheuser.Beforetheverybeginningofthelesson,youcanimplementthemethodonRequestStartedtofetchuser'sdatafromDynamoDBbasedontheaccessTokencomingfromAlexaskill.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.

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.LinksVoxaDocumentation,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

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�-ForUnitedStatesstore•https://layla.amazon.com/spa/skill/account-linking-status.html?vendorId=xxxxxx�-ForUKandGermanystoreTheotherparameterstosendare:•access_token=YOUR-TOKEN•token_type=BearerservicesJustacommonplacetoputmodelsandlibraries10.17.6userStorage.jsUsethisleasanexampletohandledatabaselogic.SinceweuseDynamoDBforthisexample,weincluded2methods,aputandaget,souser

'sinformationgetstoredfromtheaccountlink
'sinformationgetstoredfromtheaccountlinkingprojectandgetfetchedfromthealexaskillside.ForreachingoutDynamoDByouneedsomepermissionsforyourlambdafunction.MakesuretograntyourlambdafunctionwitharolewithDynamoDBaccess.testYouwritetestsright?10.17.AccountLinking49VoxaDocumentation,Release2.1.2gulpleAgulprunnerconguredwithawatchtaskthatstartsyourexpressserverandlistensforchangestoreloadyourapplication.serverless.ymlTheserverlessframeworkisatoolthathelpsyoumanageyourlambdaapplications,assumingyouhaveyourAWScredentialssetupproperlythisstarterkitdenestheveryminimumneededsoyoucandeployyourskilltolambdawiththefollowingcommand:$slsdeploy10.17.7Runningtheproject1.ClonetheAccountLinkingSamplerepository2.Makesureyou'rerunningnode6.10,thisiseasiestwithnvm3.Createaconfig/local.jsonleusingconfig/local.json.exampleasanexample4.Runtheprojectwithgulpwatch5.Atthispointyoushouldstartngrokhttp3000andcongureyou

rskillintheAmazonDeveloperpaneltousethen
rskillintheAmazonDeveloperpaneltousethengrokhttpsendpoint.50Chapter10.LinksIndexAAlexaEvent()(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

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

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),3251VoxaDocumentation,Release2.1.2Voxa.onRequestStarted()(Voxamethod),30Voxa.onSessionEnded()(Voxamethod),30Voxa.onSessionStarted()(Voxamethod),30Voxa.onState()(Voxamethod),29Voxa.onStateMachineError()(Voxamethod),31Voxa.onSystem.ExceptionEncountered()(Voxa.onSystemmet