Getting A Username In An Alexa Skill

Introduction

 
In this article on custom Alexa skills, I am going to demonstrate how to get a user’s name for use in a custom Alexa skill on Alexa Hosted node.js server. For that, I will be creating a custom Alexa skill named user information. With the help of this skill, Users can ask Alexa for the user’s full name to be used in custom Alexa skill. Alexa fetches the user’s full name from the Amazon account linked with that particular Alexa skill and returns the username as part of the information about the user.
 
The users can ask Alexa for the username by saying some sample utterances such as “what is my name”, Alexa will respond by fetching and prompting the user’s full name accordingly. Alexa can also respond by prompting please enable profile permissions from the Amazon Alexa app. 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 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, 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 AskMeIntent along with some sample utterances such as name, what’s my name, and what is my name. 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""contact info",  
  5.             "intents": [  
  6.                 {  
  7.                     "name""AMAZON.NavigateHomeIntent",  
  8.                     "samples": []  
  9.                 },  
  10.                 {  
  11.                     "name""AMAZON.CancelIntent",  
  12.                     "samples": []  
  13.                 },  
  14.                 {  
  15.                     "name""AMAZON.HelpIntent",  
  16.                     "samples": []  
  17.                 },  
  18.                 {  
  19.                     "name""AMAZON.StopIntent",  
  20.                     "samples": []  
  21.                 },  
  22.                 {  
  23.                     "name""AskMeIntent",  
  24.                     "slots": [],  
  25.                     "samples": [  
  26.                         "name",  
  27.                         "what's my name",  
  28.                         "what is my name"  
  29.                     ]  
  30.                 }  
  31.                   
  32.               ],  
  33.             "types": []  
  34.         }  
  35.     }  
  36. }  
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.   
  3. const messages = {  
  4.   NOTIFY_MISSING_PERMISSIONS: 'Please enable profile permissions in the Amazon Alexa app.',  
  5.   ERROR: 'Uh Oh. Looks like something went wrong.'  
  6. };  
  7.   
  8. const FULL_NAME_PERMISSION = "alexa::profile:name:read";  
  9.   
  10. const LaunchRequestHandler = {  
  11.   canHandle(handlerInput) {  
  12.     return handlerInput.requestEnvelope.request.type === 'LaunchRequest';  
  13.   },  
  14.   handle(handlerInput) {  
  15.      
  16.     const speechText = `Hello. You can say: what's my name.`;  
  17.       
  18.     const reprompt = `say: what's my name.`;  
  19.       
  20.     return handlerInput.responseBuilder  
  21.       .speak(speechText)  
  22.       .reprompt(reprompt)  
  23.       .getResponse();  
  24.   },  
  25. };  
  26.   
  27. const AskMeIntentHandler = {  
  28.   canHandle(handlerInput) {  
  29.     return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  30.       && handlerInput.requestEnvelope.request.intent.name === 'AskMeIntent';  
  31.   },  
  32.   async handle(handlerInput) {  
  33.     const { serviceClientFactory, responseBuilder } = handlerInput;  
  34.     try {  
  35.       const upsServiceClient = serviceClientFactory.getUpsServiceClient();  
  36.       const profileName = await upsServiceClient.getProfileName();  
  37.       const speechResponse = `Your name is, ${profileName}`;  
  38.       return responseBuilder  
  39.                       .speak(speechResponse)  
  40.                       .getResponse();  
  41.     } catch (error) {  
  42.       console.log(JSON.stringify(error));  
  43.       if (error.statusCode === 403) {  
  44.         return responseBuilder  
  45.         .speak(messages.NOTIFY_MISSING_PERMISSIONS)  
  46.         .withAskForPermissionsConsentCard([FULL_NAME_PERMISSION])  
  47.         .getResponse();  
  48.       }  
  49.       console.log(JSON.stringify(error));  
  50.       const response = responseBuilder.speak(messages.ERROR).getResponse();  
  51.       return response;  
  52.     }  
  53.   },  
  54. }  
  55.   
  56.   
  57. const HelpIntentHandler = {  
  58.   canHandle(handlerInput) {  
  59.     return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  60.       && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';  
  61.   },  
  62.   handle(handlerInput) {  
  63.     const speechText = 'You can say hello to me!';  
  64.   
  65.     return handlerInput.responseBuilder  
  66.       .speak(speechText)  
  67.       .reprompt(speechText)  
  68.       .withSimpleCard('Hello World', speechText)  
  69.       .getResponse();  
  70.   },  
  71. };  
  72.   
  73. const CancelAndStopIntentHandler = {  
  74.   canHandle(handlerInput) {  
  75.     return handlerInput.requestEnvelope.request.type === 'IntentRequest'  
  76.       && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'  
  77.         || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');  
  78.   },  
  79.   handle(handlerInput) {  
  80.     const speechText = 'Goodbye!';  
  81.   
  82.     return handlerInput.responseBuilder  
  83.       .speak(speechText)  
  84.       .getResponse();  
  85.   },  
  86. };  
  87.   
  88. const SessionEndedRequestHandler = {  
  89.   canHandle(handlerInput) {  
  90.     return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';  
  91.   },  
  92.   handle(handlerInput) {  
  93.     console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);  
  94.   
  95.     return handlerInput.responseBuilder.getResponse();  
  96.   },  
  97. };  
  98.   
  99. const ErrorHandler = {  
  100.   canHandle() {  
  101.     return true;  
  102.   },  
  103.   handle(handlerInput, error) {  
  104.     console.log(`Error handled: ${error.message}`);  
  105.   
  106.     return handlerInput.responseBuilder  
  107.       .speak('Sorry, I can\'t understand the command. Please say again.')  
  108.       .reprompt('Sorry, I can\'t understand the command. Please say again.')  
  109.       .getResponse();  
  110.   },  
  111. };  
  112.   
  113.   
  114. const skillBuilder = Alexa.SkillBuilders.custom();  
  115.   
  116. exports.handler = skillBuilder  
  117.   .addRequestHandlers(  
  118.     LaunchRequestHandler,  
  119.     AskMeIntentHandler,  
  120.     HelpIntentHandler,  
  121.     CancelAndStopIntentHandler,  
  122.     SessionEndedRequestHandler  
  123.   )  
  124.   .addErrorHandlers(ErrorHandler)  
  125.   .withApiClient(new Alexa.DefaultApiClient())  
  126.   .lambda();  
To receive 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.
 
LaunchRequestHandler
 
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 the handle() function inside LaunchRequestHandler, Alexa greets a user by saying “Hello” and tells the user to say “what is my name” to fetch and prompt the user’s full name.
 
AskMeIntentHandler
 
After that AskMeIntentHandler is defined to handle each and every user's request to ask Alexa to fetch and provide the user’s full name whosoever has to enable this particular skill on their Amazon Alex app.
 
This handler will configure the skill to ask a user for permission to read his/her full name so that Alexa can fetch and provide the user’s full name without having to ask for permission to access the user’s name.
 
As we can see from the above code, inside the handle() function of AskMeIntentHandler, the ServiceClientFactory is available to the handlers via the HandlerInput container object. It takes care of creating individual service client and configuring the ApiAccessToken and ApiEndpoint.
 
UpsServiceClient
 
UpsServiceClient can be used to query Alexa Customer Profile API for customer contact information and Alexa Customer Settings API for customer preferences of time zone, distance measuring, and temperature measurement unit.
The getProfileName() method inside UpsServiceClient class returns string containing user’s full name.
 
Request Customer Contact Information for Use in Your Skill
 
When a customer enables your Alexa skill, your skill can request the customer's permission to their contact information, which includes name, email address, and phone number, if the customer has consented. You can then use this data to support personalized intents to enhance the customer experience without account linking.
 
To enable this skill capability to request the customer's permission to their contact information following steps needs to be taken.
 
If you are using the developer console to manage your skill, configure your skill as follows:
  • Edit your skill in the developer console.
  • Navigate to the Build -> Permissions page in the console.
  • Select one or more of the following depending on which customer resources you require for your skill:
  • Customer Name -> Full Name
Sample response with permissions card
 
To query the Alexa Customer Profile API for customer contact information permissions card for requesting customer consent is required to define. Particular skills can display a special permissions card to ask customers for consent dynamically.
 
An in-session interaction can return a response that includes the new AskForPermissionsConsent card. The permissions values can be as follows:
 
Full Name -> alexa::profile:name:read
 
The permissions value always matches the scope that you declared for the skill on the Build -> Permissions page in the developer console.
 
Output
 
 
As we can see from the output above, to invoke a skill, the user can say open followed by invocation name. Here contact info is an invocation name to invoke this skill. Once the skill is invoked, Alexa greets the user by saying hello and tells the user to say what my name, so that Alexa can fetch and provide the user’s full name.
 
If a user responds through a sample utterance that is by saying “what is my name”, then Alexa will fetch and provide the user’s full name.
 
If this skill has permission to access the user’s full name then Alexa will return a string containing the user’s full name. If this skill does not have any permission to access the user’s full name then Alexa will prompt a message saying “Please enable profile permissions in the Amazon Alexa app”.
 

Summary

 
In this article, I created a custom Alexa skill. I also defined invocation name, intents, and sample utterances. I demonstrated how to get a user’s name for use in a custom Alexa skill and how Alexa fetches the user’s full name from the amazon account linked with that particular Alexa skill and returns the username as part of information about the user.
 
Proper coding snippets along with the output for the backend of skill are also provided.