Creating Custom Food Fact Skill Using Fact Skill Template

Introduction

 
Here is another article on custom Alexa skills. In this article, I am going to demonstrate how to create a custom Alexa skill on Alexa Hosted node.js server. For that, I will be creating a custom Alexa skill named Food Fact. With the help of this skill, users can ask Alexa to fetch a random fact about food, and Alexa will fetch facts about food at random and prompt a message giving information about any particular food.
 
Each time Alexa asks if the user wants to know any facts about different food items. The users can give a response by saying for example “give me a food fact”. Alexa will fetch facts about food at random and prompt a message giving information about any particular food. The backend code required for the Alexa skill to work will be stored in a lambda function inside the Amazon developer console.
 

Creating an Alexa Skill

 
To create a new skill, first we need to login to the Alexa developer console. Then, 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 lambda 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 the Create Skill button. Now that a skill has been created, we need to make adjustments to the skill’s frontend. I will be creating intents, slots, and custom slot types to create the 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.
 
 
Now we have to create intents:
 
 
Here, I have added a new intent named FoodFacts along with some sample utterances such as give me a food fact, tell me a food fact. No slots and custom slot types are defined for this Alexa skill.
 
After creating a model for a particular skill, we can save and build the model by clicking on the save model and build model button on the top.
 
Json code for the above frontend is as follows:
  1. {  
  2.     "interactionModel": {  
  3.         "languageModel": {  
  4.             "invocationName""food facts",  
  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""FoodFacts",  
  24.                     "slots": [],  
  25.                     "samples": [  
  26.                         "give me a food fact",  
  27.                         "amazing food facts",  
  28.                         "a food fact",  
  29.                         "food facts",  
  30.                         "tell me a food fact"  
  31.                     ]  
  32.                 }  
  33.             ],  
  34.             "types": []  
  35.         }  
  36.     }  
  37. }  

Creating the backend resource for the Alexa skill

To create backend code inside the lambda function, we can write code inside the index.js node.js file. The code for the custom Alexa skill is as follows:
  1. const Alexa = require('ask-sdk-core');  
  2. const data = [  
  3.   'Green, yellow, and red bell peppers are not actually the same vegetable.',  
  4.       'white chocolate is not actually chocolate at all.',  
  5.       'Scientists can turn peanut butter into diamonds.',  
  6.       'Ripe cranberries will bounce like rubber balls.',  
  7.       'Chocolate was once used as currency.',  
  8.       'Fruit snacks and cars are coated in the same type of wax.',  
  9.       'Potatoes can absorb and reflect Wi-fi signals.',  
  10. ];  
  11.   
  12. const GET_FACT_MESSAGE = "This is your Fact " ;  
  13.   
  14. const LaunchRequestHandler = {  
  15.     canHandle(handlerInput) {  
  16.         return handlerInput.requestEnvelope.request.type === 'LaunchRequest';  
  17.     },  
  18.     handle(handlerInput) {  
  19.         const speechText = 'Welcome to food fact. you can ask me about a food fact';  
  20.         return handlerInput.responseBuilder  
  21.             .speak(speechText)  
  22.             .reprompt(speechText)  
  23.             .getResponse();  
  24.     }  
  25. };  
  26. const FoodFactsHandler = {  
  27.     canHandle(handlerInput) {  
  28.         return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  29.             && handlerInput.requestEnvelope.request.intent.name === 'FoodFacts';  
  30.     },  
  31.     handle(handlerInput) {  
  32.         const factArr = data;  
  33.         const factIndex = Math.floor(Math.random() * factArr.length);  
  34.         const randomFact = factArr[factIndex];  
  35.         const speechOutput = GET_FACT_MESSAGE + randomFact;  
  36.   
  37.   
  38.         const speechText = speechOutput;  
  39.         return handlerInput.responseBuilder  
  40.             .speak(speechText)  
  41.             .reprompt(speechText)  
  42.               
  43.             .getResponse();  
  44.     }  
  45. };  
  46. const HelpIntentHandler = {  
  47.     canHandle(handlerInput) {  
  48.         return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  49.             && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';  
  50.     },  
  51.     handle(handlerInput) {  
  52.         const speechText = 'You can ask me about a food fact';  
  53.   
  54.         return handlerInput.responseBuilder  
  55.             .speak(speechText)  
  56.             .reprompt(speechText)  
  57.             .getResponse();  
  58.     }  
  59. };  
  60. const CancelAndStopIntentHandler = {  
  61.     canHandle(handlerInput) {  
  62.         return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  63.             && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'  
  64.                 || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');  
  65.     },  
  66.     handle(handlerInput) {  
  67.         const speechText = 'Goodbye!';  
  68.         return handlerInput.responseBuilder  
  69.             .speak(speechText)  
  70.             .getResponse();  
  71.     }  
  72. };  
  73. const SessionEndedRequestHandler = {  
  74.     canHandle(handlerInput) {  
  75.         return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';  
  76.     },  
  77.     handle(handlerInput) {  
  78.         // Any cleanup logic goes here.  
  79.         return handlerInput.responseBuilder.getResponse();  
  80.     }  
  81. };  
  82.   
  83. // The intent reflector is used for interaction model testing and debugging.  
  84. // It will simply repeat the intent the user said. You can create custom handlers  
  85. // for your intents by defining them above, then also adding them to the request  
  86. // handler chain below.  
  87. const IntentReflectorHandler = {  
  88.     canHandle(handlerInput) {  
  89.         return handlerInput.requestEnvelope.request.type === 'IntentRequest';  
  90.     },  
  91.     handle(handlerInput) {  
  92.         const intentName = handlerInput.requestEnvelope.request.intent.name;  
  93.         const speechText = `You just triggered ${intentName}`;  
  94.   
  95.         return handlerInput.responseBuilder  
  96.             .speak(speechText)  
  97.             //.reprompt('add a reprompt if you want to keep the session open for the user to respond')  
  98.             .getResponse();  
  99.     }  
  100. };  
  101.   
  102. // Generic error handling to capture any syntax or routing errors. If you receive an error  
  103. // stating the request handler chain is not found, you have not implemented a handler for  
  104. // the intent being invoked or included it in the skill builder below.  
  105. const ErrorHandler = {  
  106.     canHandle() {  
  107.         return true;  
  108.     },  
  109.     handle(handlerInput, error) {  
  110.         console.log(`~~~~ Error handled: ${error.message}`);  
  111.         const speechText = `Sorry, I couldn't understand what you said. Please try again.`;  
  112.   
  113.         return handlerInput.responseBuilder  
  114.             .speak(speechText)  
  115.             .reprompt(speechText)  
  116.             .getResponse();  
  117.     }  
  118. };  
  119.   
  120. // This handler acts as the entry point for your skill, routing all request and response  
  121. // payloads to the handlers above. Make sure any new handlers or interceptors you've  
  122. // defined are included below. The order matters - they're processed top to bottom.  
  123. exports.handler = Alexa.SkillBuilders.custom()  
  124.     .addRequestHandlers(  
  125.         LaunchRequestHandler,  
  126.         FoodFactsHandler,  
  127.         HelpIntentHandler,  
  128.         CancelAndStopIntentHandler,  
  129.         SessionEndedRequestHandler,  
  130.         IntentReflectorHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers  
  131.     .addErrorHandlers(  
  132.         ErrorHandler)  
  133.     .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.
 
With the help of LaunchRequestHandler, Alexa greets the user by saying welcome to food fact and tells the user that he/she ask Alexa about random food facts. She also asks the user if he/she wants to know any random facts about food items.
 
After that, FoodFactsHandler is defined to handle each and every request of the user to know a random food fact. This handler will enable Alexa to tell users a random fact about any food item and also asks the user if he/she wants to know about any more facts.
 
As we can see from the above code, inside the handle() function an array name containing some random facts about food items is defined. With the help of math.random function, a fact is extracted at random from the array and then displayed to the user by Alexa.
 
Output
 
 
As we can see from the output above, to invoke a skill, the user can say open followed by invocation name. Here food facts is an invocation name to invoke this skill. Alexa then greets the user by saying welcome to food fact and tells the user that he/she ask Alexa about random food facts.
 
Users can ask Alexa for a random fact by saying get me a food fact, which is a sample utterance defined for the skill. In response, Alexa tells a random fact about any food item.
 

Summary

 
In this article, I created a custom Alexa skill. I also defined the invocation name, intents, and sample utterances. I demonstrated the method to generate random facts about food items each time a user asks for it. Proper code snippets along with the output for the backend of the skill are also provided.