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 Indian Desserts. With the help of this skill, Users can ask Alexa to fetch an Indian dessert, and Alexa will fetch a dessert at random and give information about that particular dessert.
Each time, Alexa asks if the user wants to fetch anymore dessert. The users can give a response by saying yes or no accordingly. If a user responds by saying no, then Alexa will prompt a goodbye message. 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 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 a 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 create a skill button.
Now as a skill has been created, we need to make adjustments to the skill’s frontend. 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.
Now we have to create intents:
Here, I have added a new intent named GetDessert along with some sample utterances such as give me a dessert, please fetch me a dessert. 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 the model button on the top.
Json code for the above frontend is as follows:
- {
- "interactionModel": {
- "languageModel": {
- "invocationName": "indian desserts",
- "modelConfiguration": {
- "fallbackIntentSensitivity": {
- "level": "LOW"
- }
- },
- "intents": [
- {
- "name": "AMAZON.CancelIntent",
- "samples": []
- },
- {
- "name": "AMAZON.HelpIntent",
- "samples": [
- "help"
- ]
- },
- {
- "name": "AMAZON.StopIntent",
- "samples": []
- },
- {
- "name": "AMAZON.NavigateHomeIntent",
- "samples": []
- },
- {
- "name": "AMAZON.FallbackIntent",
- "samples": []
- },
- {
- "name": "GetDessert",
- "slots": [],
- "samples": [
- "give me a dessert",
- "please fetch me a dessert"
- ]
- },
- {
- "name": "AMAZON.NoIntent",
- "samples": [
- "no"
- ]
- },
- {
- "name": "AMAZON.YesIntent",
- "samples": [
- "yes"
- ]
- }
- ],
- "types": []
- }
- }
- }
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:
- const Alexa = require('ask-sdk-core');
-
- const GET_DESSERT_MESSAGE = "Here's your dessert: ";
- const HELP_MESSAGE = 'You can say please fetch me a dessert, or, you can say exit... What can I help you with?';
- const HELP_REPROMPT = 'What can I help you with?';
- const STOP_MESSAGE = 'Enjoy the day...Goodbye!';
- const MORE_MESSAGE = 'Do you want more?'
-
-
-
- const data = [
- 'black forest cake ',
- 'chocolate ice cream ',
- 'chocolate brownie',
- 'chocolate doughnut ',
- 'choco lava cake ',
- 'choc chip muffins',
- 'Hot Chocolate ',
- 'chocolate pudding ',
- 'Dalgona Coffee',
- ];
-
- const LaunchRequestHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
- },
- handle(handlerInput) {
- const speechObj = getADessert();
- return handlerInput.responseBuilder
- .speak(speechObj.speach)
- .reprompt(speechObj.reprompt)
- .getResponse();
- }
- };
- const GetDessertHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'GetDessert';
- },
- handle(handlerInput) {
- const speechObj = getADessert();
- return handlerInput.responseBuilder
- .speak(speechObj.speach)
- .reprompt(speechObj.reprompt)
- .getResponse();
- }
- };
-
- const YesIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.YesIntent';
- },
- handle(handlerInput) {
- const speechObj = getADessert();
- return handlerInput.responseBuilder
- .speak(speechObj.speach)
- .reprompt(speechObj.reprompt)
- .getResponse();
- }
- };
-
- const NoIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.NoIntent';
- },
- handle(handlerInput) {
- const speechText = STOP_MESSAGE;
- return handlerInput.responseBuilder
- .speak(speechText)
- .getResponse();
- }
- };
- const HelpIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
- },
- handle(handlerInput) {
- const speechText = HELP_MESSAGE;
- const speechprompt = HELP_REPROMPT;
- return handlerInput.responseBuilder
- .speak(speechText)
- .reprompt(speechprompt)
- .getResponse();
- }
- };
- const CancelAndStopIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
- || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
- },
- handle(handlerInput) {
- const speechText = STOP_MESSAGE;
- return handlerInput.responseBuilder
- .speak(speechText)
- .getResponse();
- }
- };
- const SessionEndedRequestHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
- },
- handle(handlerInput) {
-
- return handlerInput.responseBuilder.getResponse();
- }
- };
-
-
- const IntentReflectorHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest';
- },
- handle(handlerInput) {
- const intentName = handlerInput.requestEnvelope.request.intent.name;
- const speechText = `You just triggered ${intentName}`;
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .getResponse();
- }
- };
-
-
- const ErrorHandler = {
- canHandle() {
- return true;
- },
- handle(handlerInput, error) {
- console.log(`~~~~ Error handled: ${error.message}`);
- const speechText = `Sorry, I couldn't understand what you said. Please try again.`;
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .reprompt(speechText)
- .getResponse();
- }
- };
-
- function getADessert() {
-
- const dessertArr = data;
- const dessertIndex = Math.floor(Math.random() * dessertArr.length);
- const randomDessert = dessertArr[dessertIndex];
- const tempOutput = GET_DESSERT_MESSAGE + randomDessert;
- const speechOutput = tempOutput + MORE_MESSAGE
- const more = MORE_MESSAGE
- return {speach: speechOutput, reprompt:more };
- }
-
- exports.handler = Alexa.SkillBuilders.custom()
- .addRequestHandlers(
- LaunchRequestHandler,
- GetDessertHandler,
- YesIntentHandler,
- NoIntentHandler,
- HelpIntentHandler,
- CancelAndStopIntentHandler,
- SessionEndedRequestHandler,
- IntentReflectorHandler)
- .addErrorHandlers(
- ErrorHandler)
- .lambda();
To receive a request 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 tells the user a random dessert name and also asks the user if he/she wants to know about any more desserts.
After that GetDessertHandler is defined to handle each and every user’s fetching dessert request. This handler will enable Alexa to tell users a random dessert name and also asks the user if he/she wants to know about any more desserts.
As we can see from the above code that a function getADessert() is defined. Inside the function, an array name containing the name of all the desserts is defined. With the help of random function, a dessert name is extracted 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 Alexa followed by invocation name and sample utterance. Here Indian desserts are an invocation name to invoke this skill and get me a dessert is a sample utterance defined for the skill. Once the skill is invoked, Alexa tells the user a random dessert name and also asks the user if he/she wants to know about any more desserts.
If a user responds by saying yes, then Alexa will again tell the user a random dessert name and also asks the user if he/she wants to know about any more desserts.
If a user responds by saying no, then Alexa will prompt a goodbye message.
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 a random dessert name each time a user asks for it. Proper coding snippets along with the output for the backend of skill is also provided.