Creating Custom Birthdate Alexa Skill Using Alexa Hosted NodeJS

Introduction

 
In this article, I am going to demonstrate how to create a custom Alexa skill on an Alexa-hosted node.js server. For that I will be creating a custom Alexa skill named birthday night which asks users for their respective birth dates and respond accordingly. The backend code required for the Alexa skill to work will be stored in a lambda function inside an Amazon developer console. To know the basics on what components are needed to create an Alexa skill you can visit my previous article.
 

Creating Alexa Skill

 
To create a new skill, first we need to login into the Alexa developer console, we need to mention the unique skill name and select the default language according to our location.
 
 
After that we can choose a model to add to our skill. To create a custom skill, we can select custom model.
 
 
We can also choose a method or a template to host the skill’s backend code inside a lamda function.
 
 
We can choose alexa hosted node.js or Python template. We can also mention our own endpoint or server to store backend resources for the required Alexa skills.
 
The next step is to choose a template to add to our skill, which we customize later according to our need and click on create skill button.
 
Now as the skill has been created, we need to make adjustments to skill’s front end. Now I will be creating intents, slots and custom slot types to create skill’s frontend.
 
First we need to mention the invocation name. Users say a skill's invocation name to begin an interaction with a particular custom skill.
 
 
 
Here I have added a new intent named ShowBirthdayIntent along with a sample utterance. Inside the utterance “my birthday is on {month} {day} {year}”, month, day and year are slots defined for the skill. Slots are defined within curly brackets.
 
The slot type of each slot is defined as follows,
 
 
Day and year have been assigned pre-defined slot types whereas month is assigned a custom slot type.
 
Custom slot type can be created by clicking on Add Slot Type button under slot types menu. To create a custom slot type, type the name of the custom slot also fill in the required slot values along with Id and synonyms.
 
 
We can also create validation rules for each and every slots being created. To create a validation rule we can choose a validator such as Accept only slot type’s values and synonyms. We can also specify what alexa will say to ask the user for an acceptable value.
 
 
The json code for the above frontend is as follows,
  1. {  
  2.     "interactionModel": {  
  3.         "languageModel": {  
  4.             "invocationName""birthday night",  
  5.             "intents": [  
  6.                 {  
  7.                     "name""AMAZON.CancelIntent",  
  8.                     "samples": []  
  9.                 },  
  10.                 {  
  11.                     "name""AMAZON.HelpIntent",  
  12.                     "samples": []  
  13.                 },  
  14.                 {  
  15.                     "name""AMAZON.StopIntent",  
  16.                     "samples": []  
  17.                 },  
  18.                 {  
  19.                     "name""AMAZON.NavigateHomeIntent",  
  20.                     "samples": []  
  21.                 },  
  22.                 {  
  23.                     "name""ShowBirthdayIntent",  
  24.                     "slots": [  
  25.                         {  
  26.                             "name""month",  
  27.                             "type""MonthType"  
  28.                         },  
  29.                         {  
  30.                             "name""day",  
  31.                             "type""AMAZON.Ordinal"  
  32.                         },  
  33.                         {  
  34.                             "name""year",  
  35.                             "type""AMAZON.FOUR_DIGIT_NUMBER"  
  36.                         }  
  37.                     ],  
  38.                     "samples": [  
  39.                         "my birthday is on {month} {day} {year}"  
  40.                     ]  
  41.                 }  
  42.             ],  
  43.             "types": [  
  44.                 {  
  45.                     "name""MonthType",  
  46.                     "values": [  
  47.                         {  
  48.                             "name": {  
  49.                                 "value""december"  
  50.                             }  
  51.                         },  
  52.                         {  
  53.                             "name": {  
  54.                                 "value""november"  
  55.                             }  
  56.                         },  
  57.                         {  
  58.                             "name": {  
  59.                                 "value""october"  
  60.                             }  
  61.                         },  
  62.                         {  
  63.                             "name": {  
  64.                                 "value""september"  
  65.                             }  
  66.                         },  
  67.                         {  
  68.                             "name": {  
  69.                                 "value""august"  
  70.                             }  
  71.                         },  
  72.                         {  
  73.                             "name": {  
  74.                                 "value""july"  
  75.                             }  
  76.                         },  
  77.                         {  
  78.                             "name": {  
  79.                                 "value""june"  
  80.                             }  
  81.                         },  
  82.                         {  
  83.                             "name": {  
  84.                                 "value""may"  
  85.                             }  
  86.                         },  
  87.                         {  
  88.                             "name": {  
  89.                                 "value""april"  
  90.                             }  
  91.                         },  
  92.                         {  
  93.                             "name": {  
  94.                                 "value""march"  
  95.                             }  
  96.                         },  
  97.                         {  
  98.                             "name": {  
  99.                                 "value""february"  
  100.                             }  
  101.                         },  
  102.                         {  
  103.                             "name": {  
  104.                                 "value""january"  
  105.                             }  
  106.                         }  
  107.                     ]  
  108.                 }  
  109.             ]  
  110.         },  
  111.         "dialog": {  
  112.             "intents": [  
  113.                 {  
  114.                     "name""ShowBirthdayIntent",  
  115.                     "confirmationRequired"false,  
  116.                     "prompts": {},  
  117.                     "slots": [  
  118.                         {  
  119.                             "name""month",  
  120.                             "type""MonthType",  
  121.                             "confirmationRequired"false,  
  122.                             "elicitationRequired"false,  
  123.                             "prompts": {},  
  124.                             "validations": [  
  125.                                 {  
  126.                                     "type""hasEntityResolutionMatch",  
  127.                                     "prompt""Slot.Validation.477931298699.1338914932885.605682662604"  
  128.                                 }  
  129.                             ]  
  130.                         },  
  131.                         {  
  132.                             "name""day",  
  133.                             "type""AMAZON.Ordinal",  
  134.                             "confirmationRequired"false,  
  135.                             "elicitationRequired"false,  
  136.                             "prompts": {}  
  137.                         },  
  138.                         {  
  139.                             "name""year",  
  140.                             "type""AMAZON.FOUR_DIGIT_NUMBER",  
  141.                             "confirmationRequired"false,  
  142.                             "elicitationRequired"false,  
  143.                             "prompts": {}  
  144.                         }  
  145.                     ]  
  146.                 }  
  147.             ],  
  148.             "delegationStrategy""ALWAYS"  
  149.         },  
  150.         "prompts": [  
  151.             {  
  152.                 "id""Slot.Validation.477931298699.1338914932885.605682662604",  
  153.                 "variations": [  
  154.                     {  
  155.                         "type""PlainText",  
  156.                         "value""please mention correct month name"  
  157.                     }  
  158.                 ]  
  159.             }  
  160.         ]  
  161.     }  
  162. }  
 After creating a model for a particular skill, we can save and build the model by clicking on save model and build model button on the top.
 

Creating the backend resource for the Alexa skill

 
To create backend functionality, we can write source code inside index.js node.js file under lambda. The code for the custom Alexa skill is as follows,
  1. // This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).  
  2. // Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,  
  3. // session persistence, api calls, and more.  
  4. const Alexa = require('ask-sdk-core');  
  5.   
  6. const LaunchRequestHandler = {  
  7.     canHandle(handlerInput) {  
  8.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';  
  9.     },  
  10.     handle(handlerInput) {  
  11.         const speakOutput = 'Hello! Welcome to birthday night. When is your birthday?';  
  12.         const repromptText = 'I was born Nov. 6th, 2014. When were you born?';  
  13.         return handlerInput.responseBuilder  
  14.             .speak(speakOutput)  
  15.             .reprompt(repromptText)  
  16.             .getResponse();  
  17.     }  
  18. };  
  19. const ShowBirthdayIntentHandler = {  
  20.     canHandle(handlerInput) {  
  21.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  22.             && Alexa.getIntentName(handlerInput.requestEnvelope) === 'ShowBirthdayIntent';  
  23.     },  
  24.     handle(handlerInput) {  
  25.           
  26.         const year = handlerInput.requestEnvelope.request.intent.slots.year.value;  
  27.   
  28.         const month = handlerInput.requestEnvelope.request.intent.slots.month.value;  
  29.   
  30.         const day = handlerInput.requestEnvelope.request.intent.slots.day.value;  
  31.           
  32.         const speakOutput = `Thanks, I'll remember that you were born ${month} ${day} ${year}.`;  
  33.         return handlerInput.responseBuilder  
  34.             .speak(speakOutput)  
  35.             //.reprompt('add a reprompt if you want to keep the session open for the user to respond')  
  36.             .getResponse();  
  37.     }  
  38. };  
  39. const HelpIntentHandler = {  
  40.     canHandle(handlerInput) {  
  41.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  42.             && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';  
  43.     },  
  44.     handle(handlerInput) {  
  45.         const speakOutput = 'You can say hello to me! How can I help?';  
  46.   
  47.         return handlerInput.responseBuilder  
  48.             .speak(speakOutput)  
  49.             .reprompt(speakOutput)  
  50.             .getResponse();  
  51.     }  
  52. };  
  53. const CancelAndStopIntentHandler = {  
  54.     canHandle(handlerInput) {  
  55.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'  
  56.             && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'  
  57.                 || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');  
  58.     },  
  59.     handle(handlerInput) {  
  60.         const speakOutput = 'Goodbye!';  
  61.         return handlerInput.responseBuilder  
  62.             .speak(speakOutput)  
  63.             .getResponse();  
  64.     }  
  65. };  
  66. const SessionEndedRequestHandler = {  
  67.     canHandle(handlerInput) {  
  68.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';  
  69.     },  
  70.     handle(handlerInput) {  
  71.         // Any cleanup logic goes here.  
  72.         return handlerInput.responseBuilder.getResponse();  
  73.     }  
  74. };  
  75.   
  76. // The intent reflector is used for interaction model testing and debugging.  
  77. // It will simply repeat the intent the user said. You can create custom handlers  
  78. // for your intents by defining them above, then also adding them to the request  
  79. // handler chain below.  
  80. const IntentReflectorHandler = {  
  81.     canHandle(handlerInput) {  
  82.         return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';  
  83.     },  
  84.     handle(handlerInput) {  
  85.         const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);  
  86.         const speakOutput = `You just triggered ${intentName}`;  
  87.   
  88.         return handlerInput.responseBuilder  
  89.             .speak(speakOutput)  
  90.             //.reprompt('add a reprompt if you want to keep the session open for the user to respond')  
  91.             .getResponse();  
  92.     }  
  93. };  
  94.   
  95. // Generic error handling to capture any syntax or routing errors. If you receive an error  
  96. // stating the request handler chain is not found, you have not implemented a handler for  
  97. // the intent being invoked or included it in the skill builder below.  
  98. const ErrorHandler = {  
  99.     canHandle() {  
  100.         return true;  
  101.     },  
  102.     handle(handlerInput, error) {  
  103.         console.log(`~~~~ Error handled: ${error.stack}`);  
  104.         const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;  
  105.   
  106.         return handlerInput.responseBuilder  
  107.             .speak(speakOutput)  
  108.             .reprompt(speakOutput)  
  109.             .getResponse();  
  110.     }  
  111. };  
  112.   
  113. // The SkillBuilder acts as the entry point for your skill, routing all request and response  
  114. // payloads to the handlers above. Make sure any new handlers or interceptors you've  
  115. // defined are included below. The order matters - they're processed top to bottom.  
  116. exports.handler = Alexa.SkillBuilders.custom()  
  117.     .addRequestHandlers(  
  118.         LaunchRequestHandler,  
  119.         ShowBirthdayIntentHandler,  
  120.         HelpIntentHandler,  
  121.         CancelAndStopIntentHandler,  
  122.         SessionEndedRequestHandler,  
  123.         IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers  
  124.     )  
  125.     .addErrorHandlers(  
  126.         ErrorHandler,  
  127.     )  
  128.     .lambda();  
To receive requests from the user, request handlers are created for each intent to handle. Inside each handlers, canHandle and handle functions are defined.
 
The canHandle() function is where you define what requests the handler responds to. The handle() function returns a response to the user. If your skill receives a request, the canHandle() function within each handler determines whether or not that handler can service the request.
 
In this case, the user wants to launch the skill, which is a LaunchRequest. Therefore, the canHandle() function within the LaunchRequestHandler will let the SDK know it can fulfill the request. In computer terms, the canHandle returns true to confirm it can do the work.
 
After that ShowBirthdayIntentHandler is defined to handle each and every user’s birthday request.
 
This handler will receive day, month and year of user’s birthday as request parameter and give response accordingly.
 
Output 
 
 
If user fails to mention valid month name while making a request to Alexa to store a user’s birthdate then Alexa will throw an error message and ask the user to provide a valid month name.
 
 

Summary

 
In this article, I created a custom Alexa skill. I also defined intents, slots and custom slot types for each slot. I demonstrated the method to create custom slot types and slot type values. Proper coding snippets along with output for backend of skill is also provided.