I started by running through the "Custom Skill" tutorial on the Amazon developer site.
https://developer.amazon.com/docs/custom-skills/steps-to-build-a-custom-skill.html
I have not made my new skill publicly available, it was more for me to get a feel for what was needed, and I just wanted it to run on specific devices.
When I followed the instructions at the above link, some of the pages/fields had changed from what was explained. I imagine that they will again change from what I describe below, but hopefully it will still guide you through what you need to do.
I wanted my new skill to store a list of employees and their job titles and for Alexa to randomly pick one as a response to the above question. It should also be possible to ask
Who’s the best developer?
or
Who’s the best account manager?
and for Alexa to respond with someone with that occupation. As we have a range of job titles (e.g. “Technical Developer”, “Front End Developer”), it also needed to group and associate them together.
In order to create a new Alexa skill, you need to do two things.
To create the skill, head over to the Amazon developer website and sign in using your normal Amazon login (the one you use to buy stuff), or create one if you don’t already have it. It’s a good idea to use the same account as your Alexa device is signed up to, as you can then use that device to test without needing to publish the skill.
https://developer.amazon.com/home.html
Once in the developer console select the “Alexa” tab at the top of the screen and click on “Get Started” for the Alexa Skills Kit. Here you’ll see a list of skills that you have already created (if any) and you can click on “Add a New Skill” to get started.
Under “Skill Information” set the values as follows.
When I developed my first skill the Interaction Model Builder was still undergoing Beta testing, but hopefully it won’t have changed much since then. It shows a dedicated page on which you define what phrases Alexa should accept for this skill. Some Intents should already have been created and you will need to add a new one, I called mine “GetBestIntent”.
Enter all of the variations of the phrases that you wish to handle, where part of the phrase can change you should use a “slot” (or a variable) which can be picked up in the code later on. I used {Role} to substitute the occupation that was being asked for. My phrases were:
I didn’t need a separate “Who’s the best” for the everybody case, as the last two cover that – the {Role} will just be blank.
You will notice that as you add slots to your phrases, each unique Intent Slot will be shown on the right-hand side of the page. Each will require a “slot type”, which indicates what kind of information is expected for this part of the phrase. You will see that there are some standard types in the drop-down list (e.g. city, number, date), but we can also define our own.
Under the “Intents” on the left-hand side of the page you can add a new slot type and set the possible values allowed. In my case they were things like “Coder”, “Developer”, “Front End Developer”, “Senior Account Manager”. You can then return to the intent and set the Role to use the new slot type, to indicate that these are the possible phrases to expect in that position.
Save and build the model (the latter will take a short time) and then return to the main skill definition.
Eventually we’ll set up an endpoint, which links to the actual implementation of the skill, but first we need to create the code and host it somewhere.
The actual logic of a skill is some code that you have hosted somewhere. I decided to stick with the Amazon recommendation and hosted my code on the AWS. To do this I logged into my AWS management console and created a “Lambda” function. Lots more information can be found in the following location, which will likely to be more up-to-date than this blog.
https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-an-aws-lambda-function.html
Here’s my code that implemented “Who’s the Best?”. The app isn’t available publicly, but I was able to set it up so that it worked on our work Echo Plus.
/* eslint-disable func-names */ /* eslint quote-props: ["error", "consistent"]*/ /** * This sample demonstrates a simple skill built with the Amazon Alexa Skills * nodejs skill development kit. * This sample supports multiple lauguages. (en-US, en-GB, de-DE). * The Intent Schema, Custom Slots and Sample Utterances for this skill, as well * as testing instructions are located at https://github.com/alexa/skill-sample-nodejs-fact **/ 'use strict'; const Alexa = require('alexa-sdk'); //========================================================================================================================================= //TODO: The items below this comment need your attention. //========================================================================================================================================= //Replace with your app ID (OPTIONAL). You can find this value at the top of your skill's page on http://developer.amazon.com. //Make sure to enclose your value in quotes, like this: const APP_ID = 'amzn1.ask.skill.bb4045e6-b3e8-4133-b650-72923c5980f1'; const APP_ID = undefined; const SKILL_NAME = 'The Best'; const IS_THE_BEST = " is the best "; const HELP_MESSAGE = 'You can ask me who is the best. Go on, ask me.'; const HELP_REPROMPT = 'What can I help you with?'; const STOP_MESSAGE = 'Goodbye!'; //========================================================================================================================================= //TODO: Replace this data with your own. You can find translations of this data at http://github.com/alexa/skill-sample-node-js-fact/lambda/data //========================================================================================================================================= const data = [ ['<sub alias="laowra">laura</sub>','senior account manager','f'], ['david','managing director','m'], ['kirsty','client services director','f'], ['mark','senior designer','m'], ['scott walker','design director','m'], ['douglas','production manager','m'], ['jonathan','front end developer','m'], ['ali','front end developer','f'], ['alysha','account manager','f'], ['charles','technical director','m'], ['stuart','senior technical developer','m'], ['scott millar','art director','m'], ['gillian','account director','f'], ['nikki','account director','f'], ['garry','front end developer','m'], ['dale','junior designer','m'], ['kari','senior designer','f'], ['barry','finance director','m'], ['colette','group services manager','f'], ['<sub alias="owen">owain</sub>','technical developer','m'], ['lauren','digital marketing assistant','f'], ['zara','account manager','f'], ['nicola','management accountant','f'], ['martin','technical developer','m'], ['laura','senior account manager','f'], ['lily','project manager','f'] ]; const alternateRoles = [ ['designer','senior designer','design director','art director','junior designer'], ['creative','senior designer','design director','art director','junior designer'], ['developer','front end developer','technical director','senior technical developer','technical developer'], ['frontend developer','front end developer'], ['back end developer','senior technical developer','technical developer'], ['backend developer','senior technical developer','technical developer'], ['coder','senior technical developer','technical developer','front end developer'], ['account manager','senior account manager','client services director','account director'] ]; //========================================================================================================================================= //Editing anything below this line might break your skill. //========================================================================================================================================= exports.handler = function(event, context, callback) { var alexa = Alexa.handler(event, context); alexa.appId = APP_ID; alexa.registerHandlers(handlers); alexa.execute(); }; const handlers = { 'LaunchRequest': function () { this.emit('GetBestIntent'); }, 'GetBestIntent': function () { var role = ""; if (this.event.request.intent.slots.Role) { role = this.event.request.intent.slots.Role.value; if (!role || role == null) { role = ""; } } var factArr = []; var lastPersonSex = "f"; for (var person of data) { if (role.length > 0 && person[1] == role) { factArr.push(person[0]); lastPersonSex = person[2]; } else if (role.length > 0) { for (var alternateRole of alternateRoles) { if (alternateRole[0] == role && alternateRole.includes(person[1],1)) { factArr.push(person[0]); lastPersonSex = person[2]; } } } else { // No role factArr.push(person[0]); lastPersonSex = person[2]; } } var speechOutput = "I don't understand: " + role; var personName = "No one"; if (factArr.length > 0) { const factIndex = Math.floor(Math.random() * factArr.length); personName = factArr[factIndex]; speechOutput = personName + IS_THE_BEST; if (role.length > 0) { speechOutput += role; } if (factArr.length == 1) { if (lastPersonSex == "f") { speechOutput += ", but then she's the <emphasis level='strong'>only</emphasis> one."; } else { speechOutput += ", but then he's the <emphasis level='strong'>only</emphasis> one."; } } } else { speechOutput = "I can't find anyone with the role: " + role; } this.response.cardRenderer(SKILL_NAME, personName); this.response.speak(speechOutput); this.emit(':responseReady'); }, 'AMAZON.HelpIntent': function () { const speechOutput = HELP_MESSAGE; const reprompt = HELP_REPROMPT; this.response.speak(speechOutput).listen(reprompt); this.emit(':responseReady'); }, 'AMAZON.CancelIntent': function () { this.response.speak(STOP_MESSAGE); this.emit(':responseReady'); }, 'AMAZON.StopIntent': function () { this.response.speak(STOP_MESSAGE); this.emit(':responseReady'); }, };