An Alexa Skill in Javascript – Part 5

Finally, the last post in the series.  Here, we will show Alexa how to Roll Some Dice with a modifier so that we can get our skill working properly.

We’re going to do this in two steps:

  1. Create a function that will take the diceCount, plusMinus and modifier parameters and return the results as speech text.
  2. We’ll modify the RollSomeDiceIntentHandler to pull those parameters from the Request and call the function we created in the first step.

Create a Function that can Roll Some Dice

To contain our function, we’ll create a new JavaScript file in the lambda\custom folder of our project.  Right-click on the lambda\custom folder and select New File from the menu.  Enter rollSomeDiceFunction.js as the file name.

In the new file, let’s stub out our function.  We need to export the function to be able to access it from the other file, index.js.  It will accept three parameters.  Like so:

exports.rollSomeDice = function rollSomeDice(diceCountParm, 
plusMinusParm, modifierParm) {

}

Next, we need to verify that the diceCount Slot parameter (diceCountParm) has been provided (i.e. is not null):

if (diceCountParm == null) {
    speechOutput = "Sorry, I did not hear that.  Please say something like roll 2 dice.";
    return (speechOutput);
}

Then, we need to convert the string parameters into numbers.  Slot values are always provided as strings.  We need to verify that the diceCount and modifier contain numeric values and parse the strings to actual numbers.

var diceCount = (isNaN(diceCountParm)) ? null :
    parseInt(diceCountParm);
const diceSize = 6;
var modifier = (isNaN(modifierParm)) ? 0 :
    parseInt(modifierParm);

Now, we need to check again that diceCount is not null:

if (diceCount == null) {
    speechOutput = "Sorry, I did not hear that.  Please say something like roll 2 dice.";
    return (speechOutput);
}

Next, we’re going to set up an array of possible dice rolls so we can keep track of what we roll:

var buckets = [0, 0, 0, 0, 0, 0, 0];
var bucketSlots = ['zero', 'one', 'two', 'three', 'four', 'five', 'six'];

And then we can finally roll the dice:

for (var i = 0; i < diceCount; i++) {
    let roll = 0;
    roll = Math.floor(Math.random() * diceSize) + 1;

And if the user supplied a plusMinus Slot value (in plusMinusParm) and a modifier value, then we need to add or subtract the modifier to/from the dice roll:

if (plusMinusParm != null && modifier != 0) {
    // add or subtract the modifier to each roll
    if (plusMinusParm == 'plus') {
        roll += modifier;
    } else {
        roll -= modifier;
    }
}

Next, we need to put the roll in the correct bucket.  Note that because of the modifier, we could roll less than 1 or more than 6, so we need to handle those values too:

if (roll < 1) {     buckets[0]++; } else if (roll > 5) {
    buckets[6]++;
} else {
    buckets[roll]++;
}

Now, we’ve completed the for loop that loops over the diceCount and we can start putting together the speech phrases that we want Alexa to say in her response.  First, we’ll tackle the buckets so she can say “I rolled 3 ones, 2 twos, a four and 3 sixes.”  Note:  This code snippet uses apostrophes ( ‘ ‘ ) and backward tick marks ( ` ` ) to create the right strings.

var bucketsPhrase = 'I rolled ';
for (let i = 0; i < 7; i++) {     if (buckets[i] > 1) {
        if (i < 6) {
            bucketsPhrase += `${buckets[i]} ${bucketSlots[i]}s, `;
        } else {
            bucketsPhrase += `and ${buckets[i]} ${bucketSlots[i]}es `;
        }
    } else if (buckets[i] == 1) {
        if (i < 6) {
            bucketsPhrase += `a ${bucketSlots[i]}, `;
        } else {
            bucketsPhrase += `and a ${bucketSlots[i]} `;
        }
    }
}

The above code handles the fact that six is the only plural that ends in ‘es’.  And that six is the last number in the set, so put an ‘and’ in front of it.  It also differentiates between ‘a six’ and ‘two sixes’.

The next phrase to put together is the entire speech that we want Alexa to say.  We have two options:  with modifier and without, so the code looks like this:

if (modifier == 0 && target == null) {
    speechOutput = `You said roll ${diceCount} d ${diceSize}. ${bucketsPhrase}.`;
} else if (target == null) {
    speechOutput = `You said roll ${diceCount} d ${diceSize} ${plusMinusParm} ${modifier}. ${bucketsPhrase}.`;
}

Lastly, we return the speechOutput to Alexa:

return (speechOutput);

The Finished Function

Putting it all together, we get the following:

exports.rollSomeDice = function rollSomeDice(diceCountParm, plusMinusParm, modifierParm) {

    var speechOutput = "";
    if (diceCountParm == null) {
        speechOutput = "Sorry, I did not hear that.  Please say something like roll 2 dice.";
        return (speechOutput);
    }
    var diceCount = (isNaN(diceCountParm)) ? null :
        parseInt(diceCountParm);
    const diceSize = 6;
    var modifier = (isNaN(modifierParm)) ? 0 :
        parseInt(modifierParm);

    if (diceCount == null) {
        speechOutput = "Sorry, I did not hear the number of dice to roll.  Please say something like roll 2 dice.";
        return (speechOutput);
    }

    var buckets = [0, 0, 0, 0, 0, 0, 0];
    var bucketSlots = ['zero', 'one', 'two', 'three', 'four', 'five', 'six'];

    for (var i = 0; i < diceCount; i++) {
        let roll = 0;
        roll = Math.floor(Math.random() * diceSize) + 1;

        if (plusMinusParm != null && modifier != 0) {
            // add or subtract the modifier to each roll
            if (plusMinusParm == 'plus') {
                roll += modifier;
            } else {
                roll -= modifier;
            }
        }

        if (roll < 1) {             buckets[0]++;         } else if (roll > 5) {
            buckets[6]++;
        } else {
            buckets[roll]++;
        }
    }

    var bucketsPhrase = 'I rolled ';
    for (let i = 0; i < 7; i++) {         if (buckets[i] > 1) {
            if (i < 6) {
                bucketsPhrase += `${buckets[i]} ${bucketSlots[i]}s, `;
            } else {
                bucketsPhrase += `and ${buckets[i]} ${bucketSlots[i]}es `;
            }
        } else if (buckets[i] == 1) {
            if (i < 6) {
                bucketsPhrase += `a ${bucketSlots[i]}, `;
            } else {
                bucketsPhrase += `and a ${bucketSlots[i]} `;
            }
        }
    }

    if (modifier == 0) {
        speechOutput = `You said roll ${diceCount} dice. ${bucketsPhrase}.`;
    } else if (target == null) {
        speechOutput = `You said roll ${diceCount} dice ${plusMinusParm} ${modifier}. ${bucketsPhrase}.`;
    }

    return (speechOutput);
}

Modify the RollSomeDiceIntentHandler

Now that we have a function that can roll some dice, we need to wire it up to the RollSomeDiceIntentHandler we stubbed out in the basic template code in index.js.

Open index.js, right at the top we’re going to import the function we just created above, like so:

const Alexa = require('ask-sdk-core');
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');
const diceRoller = require("./rollSomeDiceFunction.js");

Next, find the RollSomeDiceIntentHandler.  We’re going to replace the red, bold text in this function:

const RollSomeDiceIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
            handlerInput.requestEnvelope.request.intent.name === 'RollSomeDiceIntent';
    },
    handle(handlerInput) {

        const speechText = 'Hello World!';

        return handlerInput.responseBuilder
            .speak(speechText)
            .withSimpleCard('Roll Some Dice', speechText)
            .getResponse();
    },
};

First, delete the line const speechText = ‘Hello World!’;

Next, we need to pull the Slot values out of the request:

var diceCount = handlerInput.requestEnvelope.request.intent.slots["diceCount"].value;
var plusMinusSlot = handlerInput.requestEnvelope.request.intent.slots["plusMinus"];
var plusMinus = '';
if (plusMinusSlot.resolutions) {
    plusMinus = plusMinusSlot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
}
var modifier = handlerInput.requestEnvelope.request.intent.slots["modifier"].value;

Next, we need to make sure the user supplied values for the Slots.  If not, we will assign a default value:

if (!(diceCount)) {
    diceCount = '1';
}
if (!(plusMinus)) {
    plusMinus = 'plus';
}
if (!(modifier)) {
    modifier = '0';
}

Next, we need to get the speech from the RollSomeDice() function we created earlier.

const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
sessionAttributes.speakOutput = diceRoller.rollSomeDice(diceCount, plusMinus, modifier) + " ... Now, what shall I roll?";
sessionAttributes.repromptSpeech = 'What shall I roll?';

Lastly, we need to return the speech to Alexa:

return handlerInput.responseBuilder
    .speak(sessionAttributes.speakOutput)
    .reprompt(sessionAttributes.repromptSpeech)
    .withSimpleCard('Roll Some Dice', sessionAttributes.speakOutput)
    .getResponse();

The Completed RollSomeDiceIntentHandler

Putting it all together, we get:

const RollSomeDiceIntentHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
            handlerInput.requestEnvelope.request.intent.name === 'RollSomeDiceIntent';
    },
    handle(handlerInput) {

        var diceCount = handlerInput.requestEnvelope.request.intent.slots["diceCount"].value;
        var plusMinusSlot = handlerInput.requestEnvelope.request.intent.slots["plusMinus"];
        var plusMinus = '';
        if (plusMinusSlot.resolutions) {
            plusMinus = plusMinusSlot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
        }
        var modifier = handlerInput.requestEnvelope.request.intent.slots["modifier"].value;

        if (!(diceCount)) {
            diceCount = '1';
        }
        if (!(plusMinus)) {
            plusMinus = 'plus';
        }
        if (!(modifier)) {
            modifier = '0';
        }

        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        sessionAttributes.speakOutput = diceRoller.rollSomeDice(diceCount, plusMinus, modifier) + " ... Now, what shall I roll?";
        sessionAttributes.repromptSpeech = 'What shall I roll?';

        return handlerInput.responseBuilder
            .speak(sessionAttributes.speakOutput)
            .reprompt(sessionAttributes.repromptSpeech)
            .withSimpleCard('Roll Some Dice', sessionAttributes.speakOutput)
            .getResponse();
    },
};

Publish the Code to Alexa and AWS Lambda

Now that the RollSomeDice() function and the RollSomeDiceIntentHandler are written, we can publish the skill to Alexa and AWS Lambda.

In VS Code, open the Command Palette (View | Command Palette…).  Type ASK.  Select Deploy the skill.

snip_20181106045852

Wait a moment while the ASK initializes, then select the default profile:

snip_20181106045953

Then select “All”:

snip_20181106050011

Now, it will try, and probably fail, to deploy. If it succeeds, great.  If it fails, you probably need to change the working directory in the Terminal window at the bottom right of VS Code.  This is done with the CD command, like so:

snip_20181106050219

Once you’ve changed the directory to the one with your skill in it, press the up arrow on the keyboard a couple of times to get the “ask deploy” command back.  Then press enter.

If all goes well, a few moments later, your skill will be deployed:

snip_20181106050943

Now that your skill is deployed, we can switch over to the Alexa Developer Console to test it.  If you still have your template project open from earlier, click “Your Skills” in the top left corner.  Then you will see your deployed skill at the top of the list:

snip_20181106051315

Click the Edit link on the far right.  On the Build tab, you should see a little popup saying your build was successful:

snip_20181106051456

Click on the Test tab.  If it’s not enabled, enable testing for this skill:

snip_20181106051713

In the Alexa Simulator, type in “start Roll Some Dice and roll 2 dice”.  Press enter.

snip_20181106051938

Alexa should say that she rolled some dice and list the results:

snip_20181109075156

I just realized, there’s one last step:  Submitting Your Skill for Amazon Approval.  This will be covered in my next blog entry.

An Alexa Skill in Javascript – Part 4

So far we’ve defined, gathered and installed the requisite tools in Part 1.  We’ve defined the Skill we’re creating in Part 2.  And, we’ve created the Intents for our skill in Part 3.

Now, we will code the Lambda function that is the brains of our skill in this entry, Part 4.

What is a Lambda Function?

Amazon Web Services (AWS) calls the functions you create in their cloud “Lambda Functions.”  For the purposes of Alexa skills, these functions are the brains behind the skill.  Alexa takes care of the speech part of the skill, and figures out how to interpret the Alexa User’s spoken words and which intent to choose and slots to fill based on those words, but you then have to make your Alexa skill actually do something to fulfill the intent you created on the Alexa side of your Skill development process.  In my case, that means writing a function that can roll some dice and add or subtract and specified modifier to the rolls.

Lambda functions can be written in a variety of languages.  I’ve blogged before about using C# to do this.  (Note:  Using C# is now SOOO much easier as the Lambda Function tools in AWS have been updated to use .Net Core as a standard platform.)  Today, I am using JavaScript (node.js).  Other options include Python, Java and Go.

A Little More Cleanup First

In our VS Code project, in the \lambda\custom folder is a file named package.json.  We need to correct the name of our package here.  My skill is called “Roll Some Dice” so I’ll have to name my code package “roll-some-dice”.  Note the lower case and replacing spaces with dashes.

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",

I’m going to simply replace “hello-world” with “roll-some-dice” and save and close the file.

{
  "name": "roll-some-dice",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",

In that same folder, is a file named package-lock.json.  We need to make the same edit to that file.  So:

{
  "name": "hello-world",
  "version": "1.0.0",
  "lockfileVersion": 1,

Becomes:

{
  "name": "roll-some-dice",
  "version": "1.0.0",
  "lockfileVersion": 1,

One More Thing Before We Can Code

In order to roll a dice, we need a random number generator.  This can be found in the node.js module named i18next.  We will also need the module named i18next-sprintf-postprocessor.  To get these, we will use the npm package manager.

On the left side of the VS Code screen, right click on the “custom” folder in the basic skill template and choose “Open in Terminal”:

snip_20181105201412

In the bottom right of the VS Code screen is the Terminal panel.  When you get to the command prompt:

snip_20181105202241

Type in the commands “npm install i18next” and then “npm install i18next-sprintf-postprocessor“:

snip_20181105203203

Once completed, you should have a couple of new folders in the “custom\node_modules” folder of your project:

snip_20181105203632

Finally, the Brains of the Operation (index.js)

Since we just added the modules above to the project, let’s also add them to index.js, then I’ll discuss what’s in there and what we need to do.

Include the i18next Modules in index.js

At the top of the index.js file, we need to include the i18next modules.  This is done by adding new constants for them below the Alexa constant, like so:

const Alexa = require('ask-sdk-core');
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');

A Brief Tour of index.js

After the constants declared at the top of the file to include the required modules, there are several “Handler” functions.  The first is the LaunchRequestHandler:

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'Welcome to the Alexa Skills Kit, you can say hello!';
    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Hello World', speechText)
      .getResponse();
  },
};

Each of the Handler functions handles one specific request from the user to Alexa.  In this case, it the LaunchRequestHandler can handle requests with a type of “LaunchRequest”.  When this Handler is fired, it builds a response that is sent back to the user.  This response includes some speech (what Alexa will say), a reprompt (what she will say to remind the user what to do, and a Card (to be displayed on the Echo Show and other devices with screens).

The other Handler functions are set up to handle the different intents of the skill:

  • HelloWorldIntentHandler  –  The main intent handler
  • HelpIntentHandler  –  Handles the case where the user asks Alexa for help
  • CancelAndStopIntentHandler  –  Handles Alexa, Stop and Alexa, Cancel\
  • SessionEndedRequestHandler  –  Handles the final request sent to the skill when the session ends
  • ErrorHandler  –  Handles any error situations

Next, we get to the lines that export the various Handlers so that the Alexa SkillBuilder knows which functions to call:

const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    HelloWorldIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

What We Need to Change

First, we will change the LaunchRequestHandler, as shown in bold, blue text below:

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'Welcome to Roll Some Dice.  You can say, Roll 2 Dice, or Roll 4 Dice plus 1!';
    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Roll Some Dice', speechText)
      .getResponse();
  },
};

Next, we will fix the Card lines in all the other intents so that this:

.withSimpleCard('Hello World', speechText)

becomes:

.withSimpleCard('Roll Some Dice', speechText)

Next, we will change the Help Speech Text in the HelpIntentHandler to:

const speechText = 'You can say roll 2 dice, roll 3 dice plus 1, roll 6 dice minus 1 or any number of dice with any modifier.';

Next, we will completely change the HelloWorldIntentHandler.  We will do this in stages.  First, we need to change the name of the Handler and its corresponding entry in the SkillBuilder at the bottom of the file, like so:

const RollSomeDiceIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'RollSomeDiceIntent';
  },
  handle(handlerInput) {
    var speechText = 'Hello World!';
    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Roll Some Dice', speechText)
      .getResponse();
  },
};

...

const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    RollSomeDiceIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

Now, we have a Handler for our Intent that doesn’t actually do what it needs to do.
But, we do have a Handler.  At this point, we can deploy and test our Skill.

Deploying Your Skill to Alexa Developer Console and Amazon Web Services

It turns out, that with the ASK in VS Code, deploying our Skill to Alexa and Amazon is quite easy.  Open the Command Palette (View | Command Palette…).  Type ASK.  Select Deploy the skill.

snip_20181106045852

Wait a moment while the ASK initializes, then select the default profile:

snip_20181106045953

Then select “All”:

snip_20181106050011

Now, it will try, and probably fail, to deploy. If it succeeds, great.  If it fails, you probably need to change the working directory in the Terminal window at the bottom right of VS Code.  This is done with the CD command, like so:

snip_20181106050219

Once you’ve changed the directory to the one with your skill in it, press the up arrow on the keyboard a couple of times to get the “ask deploy” command back.  Then press enter.

If all goes well, a few moments later, your skill will be deployed:

snip_20181106050943

Now  that your skill is deployed, we can switch over to the Alexa Developer Console to test it.  If you still have your template project open from earlier, click “Your Skills” in the top left corner.  Then you will see your newly deployed skill at the top of the list:

snip_20181106051315

Click the Edit link on the far right.  On the Build tab, you should see a little popup saying your build was successful:

snip_20181106051456

Click on the Test tab.  If it’s not enabled, enable testing for this skill:

snip_20181106051713

In the Alexa Simulator, type in “start Roll Some Dice and roll 2 dice”.  Press enter.

snip_20181106051938

Alexa should say, “Hello World”.  (Remember, we haven’t actually told her how to roll some dice yet.)

snip_20181106052130

Notice the JSON Output from your Skill code in index.js:

snip_20181106052146

And scroll down to see the Card visual that would be shown on the Echo Show:

snip_20181106052221

That’s it for now.  In the final post in this series, I will teach Alexa to roll dice with a modifier and report the results.

An Alexa Skill in Javascript – Part 3

The Skill Model (en-US.json)

Open the en-US.json file in the models folder.  Notice the areas I’ve highlighted in bold, red text.  These are things we will change.

{
  "interactionModel": {
    "languageModel": {
      "invocationName": "greeter",
      "types": [
      ],
      "intents": [
        {
          "name": "AMAZON.CancelIntent",
          "samples": [
          ]
        },
        {
          "name": "AMAZON.HelpIntent",
          "samples": [
          ]
        },
        {
          "name": "AMAZON.StopIntent",
          "samples": [
          ]
        },
        {
          "name": "HelloWorldIntent",
          "slots": [
          ],
          "samples": [
            "hello",
            "say hello",
            "say hello world"
          ]
        }
      ]
    }
  }
}

First, the invocation name.  This is the name by which users will invoke your skill and it should be the same as the name of the skill you put in the skill manifest.  In my case, in Part 2 I named the skill “Roll some dice”.  The invocation name has to follow these rules:

  1. Your invocation name should be two or more words
  2. Can contain only lower-case alphabetic characters, spaces between words, possessive apostrophes (for example, “sam’s science trivia”), or periods used in abbreviations (for example, “a. b. c.”).
  3. Other characters like numbers must be spelled out. For example, “twenty one”.
  4. Invocation names cannot contain any of the Alexa skill launch phrases.

I will enter the invocation name as “roll some dice”.

"invocationName": "roll some dice",

There are three AMAZON intents in the basic skill template.  Those are fine the way they are and should not be modified.

The meat of the skill model is your intent.  In the basic skill template, this is the HelloWorldIntent.  We will change this.

Using the Alexa Developer Console to build the JSON for an Intent

The easiest way to get the correct syntax for the Skill Intent is to use the Alexa Developer Console.

1.  Log in and click Create Skill.  Enter the name “My Template”. Pick Custom as the type.  On the next page, click Start from Scratch as the template.  You should end up here:

snip_20181103080102

2.  Next to Intents, click Add.  Intent name can only contain case-insensitive alphabetical characters and underscores, and it must begin and end with an alphabetic character. Numbers, spaces, or other special characters are not allowed.  I will name my intent “RollSomeDiceIntent”:

snip_20181105212919

3.  Next, you enter the sample utterances.  This is what the Alexa User needs to say to make your skill work.  In my case, I have entered four utterances:

snip_20181105212943

Notice how I used braces { } to create “slots” (or variables).  These slots are placeholders for where the user can say a matching word.  For example, “Roll 10 dice plus 1” matches the second utterance “roll {diceCount} dice {plusMinus} {modifier}“.

4.  Now we need to define the slot types for the slots we named in the utterances.  This is done at the bottom of the sample utterances page.  Scroll down a bit and you will see this:

snip_20181103081409

For the Slot Type, the first and third slot (diceCount and modifier) are both numeric, so pick AMAZON.Number from the drop down.  You can type AMAZON.N and it will show AMAZON.Number as the only choice if you want to save on scrolling.

For the other slot, plusMinus, we will want to make a custom slot type where it only responds to “plus” and “minus”.  Do this by clicking the Add button next to the Slot Type menu on the left side of the screen:

snip_20181103081624

Give your new slot a name.  Note:  You cannot use a period in your name.  Amazon can, but you can’t.  So use an underscore.  I will name my slot: OMWTM_PlusMinus:

snip_20181103082100

Next, enter the words that are valid in your slot.  For me, that is plus and minus.  I will also add the synonyms add and subtract, just in case the user gets creative.  Note: Make sure you click the plus sign next to the synonym so it actually gets saved into the slot.  The slot ends up looking like this:

snip_20181103082135

Finally, click on a the {plusMinus} slot in the left panel and select your custom slot type as the slot type:

snip_20181105213145

  1. Now we’ve defined the intent, with sample utterances and slots.  Next, we need to get the JSON for the intent and copy it over to the en-US.json model file in VS Code.  The JSON for your intent is in the Alexa console under the JSON Editor menu option on the left side:

snip_20181105213232

We want ONLY the custom intent and the slots, so scroll down past the AMAZON intents and find the section that defines your intent:

snip_20181105213354

Copy the highlighted section over to the model file in VS Code.  Note:  When you paste it in, you will be replacing the last braces of the intents section.  If you don’t, your JSON file won’t have the right number of braces.  I.e.  Delete the HelloWorldIntent in its entirety and delete the extra square brace below it that ends the set of intents:

snip_20181103084258

Also, the basic skill template inserts a blank types section at the top.  Delete this too:

snip_20181103084343

Now, back at the bottom of the file, paste in the copied text from the Alexa console:

snip_20181103085025

The finished intent model should look like this:

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "roll some dice",
            "intents": [{
                    "name": "AMAZON.CancelIntent",
                    "samples": [
                    ]
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": [

                    ]
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": [

                    ]
                },
                {
                    "name": "RollSomeDiceIntent",
                    "slots": [{
                            "name": "diceCount",
                            "type": "AMAZON.NUMBER"
                        },
                        {
                            "name": "plusMinus",
                            "type": "OMWTM_PlusMinus"
                        },
                        {
                            "name": "modifier",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "roll a die {plusMinus} {modifier}",
                        "roll {diceCount} dice {plusMinus} {modifier}",
                        "roll {diceCount} dice",
                        "roll a die"
                    ]
                }
            ],
            "types": [{
                "name": "OMWTM_PlusMinus",
                "values": [{
                        "name": {
                            "value": "minus",
                            "synonyms": [
                                "subtract"
                            ]
                        }
                    },
                    {
                        "name": {
                            "value": "plus",
                            "synonyms": [
                                "add"
                            ]
                        }
                    }
                ]
            }]
        }
    }
}

That’s it for now.  In the next post, we’ll start looking at the Lambda function files and what needs to be changed there to get this skill working.

Bye for now.

 

An Alexa Skill in JavaScript – Part 2

Now that we have the tools and a basic project, let’s see what the Alexa Skills Kit (ASK) has created for us.

snip_20181102043136What’s in the Basic Project?

The ASK Config file contains the info the ASK needs to publish your skill.

The lambda folder contains your JavaScript files that are the code behind your skill.  The node_modules folder contains the downloaded node modules you use.  index.js is the main code file for your skill.  package.json and package-lock.json are files that describe some of the technical aspects of your code, such as dependencies.

The models folder contains the various language versions of your Json skill description, including the intents and slots.

The skill.json file is the skill manifest and is where you describe the skill for your end users and the Amazon publishing process.

Where to Start?

In the image above, we’ll start at the bottom with the skill manifest, then the model, then a couple of minor changes to the package files, then we’ll write a couple of lines of JavaScript code and finally check the .ask\config.

The Skill Manifest (skill.json)

Open the skill.json file.  Notice the areas I’ve highlighted in bold, red text.  These are things we will change.

{
  "manifest": {
    "publishingInformation": {
      "locales": {
        "en-US": {
          "summary": "Sample Short Description",
          "examplePhrases": [
            "Alexa open hello world",
            "Alexa tell hello world hello",
            "Alexa ask hello world say hello"
          ],
          "name": "omwtm-roll-a-dice",
          "description": "Sample Full Description"
        }
      },
      "isAvailableWorldwide": true,
      "testingInstructions": "Sample Testing Instructions.",
      "category": "EDUCATION_AND_REFERENCE",
      "distributionCountries": []
    },
    "apis": {
      "custom": {
        "endpoint": {
          "sourceDir": "lambda/custom"
        }
      }
    },
    "manifestVersion": "1.0"
  }
}

The “summary” entry should be a short description of your skill, for example:

"summary": "Roll some dice, optionally adding a modifier to each die",

The “examplePhrases”: section contains 3, and only 3, sample phrases.  I’ll have two main intents:  “Roll {diceCount} dice” and “Roll {diceCount} dice {plusMinus} {modifier}”, so I want to show those in the example phrases.  For example:

"examplePhrases": [
    "Alexa open roll some dice",
    "Alexa tell roll some dice to roll 2 dice",
    "Alexa ask roll some dice to roll 3 dice plus 1"
],

The “name” entry should be the plain, human-readable, name of your skill that people see in the Alexa Skills Catalog.  Note:  This does not have to be unique in the Catalog.  For example:

"name": "Roll Some Dice",

The “description” entry should describe your skill.  This can be a paragraph or two and will also display in the Alexa Skills Catalog.  Notice the description is all on one line.  In VS Code, you can turn on Word Wrap (View | Toggle Word Wrap) to see the entire description on screen.  For example:

"description": "Roll Some Dice will roll any number of standard dice (d 6's) and optionally add or subtract a modifier from each dice.  Alexa will then tell you the total of the rolls plus or minus the modifiers.  For example, you might say 'Roll 3 dice plus 1' and Alexa will roll 3 d 6, getting, say, 3, 4, and 6, then add 1 to each roll, getting 4, 5, and 7, and will tell you the total, 16.",

“testingInstructions” is something we’ll come back to later.  This is the instructions for the Alexa Skill evaluators to follow to test your skill intents thoroughly.  For now, I’ll leave it as is.

Lastly, the “category” entry is used to classify your skill in the Alexa Skills Catalog.  For this skill, I will pick:

"category": "GAME_INFO_AND_ACCESSORY",

There are a couple of additional settings you will want to add to your skill, if appropriate.  After the “manifest” setting, add this code:

"manifestVersion": "1.0",
"permissions": [],
"privacyAndCompliance": {
    "allowsPurchases": false,
    "isExportCompliant": true,
    "containsAds": false,
    "isChildDirected": false,
    "usesPersonalInfo": false
}

So, the completed file looks like this for now:

{
    "manifest": {
        "publishingInformation": {
            "locales": {
                "en-US": {
                    "summary": "Roll some dice, optionally adding a modifier to each die",
                    "examplePhrases": [
                        "Alexa open roll some dice",
                        "Alexa tell roll some dice to roll 2 dice",
                        "Alexa ask roll some dice to roll 3 dice plus 1"
                    ],
                    "name": "Roll Some Dice",
                    "description": "Roll Some Dice will roll any number of standard dice (d 6's) and optionally add or subtract a modifier from each dice.  Alexa will then tell you the total of the rolls plus or minus the modifiers.  For example, you might say 'Roll 3 dice plus 1' and Alexa will roll 3 d 6, getting, say, 3, 4, and 6, then add 1 to each roll, getting 4, 5, and 7, and will tell you the total, 16."
                }
            },
            "isAvailableWorldwide": true,
            "testingInstructions": "Sample Testing Instructions.",
            "category": "GAME_INFO_AND_ACCESSORY",
            "distributionCountries": []
        },
        "apis": {
            "custom": {
                "endpoint": {
                    "sourceDir": "lambda/custom"
                }
            }
        },
        "manifestVersion": "1.0",
        "permissions": [],
        "privacyAndCompliance": {
            "allowsPurchases": false,
            "isExportCompliant": true,
            "containsAds": false,
            "isChildDirected": false,
            "usesPersonalInfo": false
        }
    }
}

Next up, the skill model, where you define your intents and slots.

Bye for now.

 

 

 

 

An Alexa Skill in JavaScript – Part 1

So I finally decided to stop fighting it…  Building Alexa Skills in C# is just difficult.  With the newly released Visual Studio Code plugin for Alexa ( ) the language of choice is JavaScript.  Ugh.  But, it turns out that Google is still my friend (even though the Google Home is a horrible platform for voice skill development).  A quick search turned up all sorts of free JavaScript training.  A few hours of interaction later and I’m a pro beginning to be barely capable in JavaScript.  So, what’s a Microsoft Developer to do to code an Alexa Skill?

Getting the Tools

Step 1 – From the command line (Start | cmd in Windows), run npm to install the ASK CLI:

npm install -g ask-cli

snip_20180915064344

This command takes 2-5 minutes to run and doesn’t appear to be doing anything while it’s running.

TIP:  It’s a good idea to run that npm command every month or so to get the latest version of the ASK-CLI.  It is updated quite frequently.

Step 2 – Install Git, if you haven’t already.

Step 2 – Install Visual Studio Code if you haven’t already.  (I recommend the 64-bit User version for Windows developers.)

Step 3 – In VS Code, select the Help | Welcome menu.  On the right-hand side, under Customize, under Tools and languages, install support for JavaScript.

Step 4 – In VS Code, select the View | Extensions menu.  Search for Alexa Skills Kit (currently – 09/15/18 – in beta).  Install it.

I think that’s all I had to do to get the tools.

Configuring for Alexa Skill Development

AWS Lambda Credentials

Since we will be using an AWS Lambda function as the back-end for our skill, we need to set up the credentials in AWS IAM.

Add a User.  Give the user a name.  Select Programmatic access.  Click Next.

snip_20181101181244

For this example, I’m going to Attach existing policies directly and select Administrator Access, but this is NOT best practice.  (I should create a user group that has the specific policies this account needs, but that’s a different blog entry.)  Click Next.

snip_20181101181336

Click Create User.

snip_20181101181404

Download the .csv.  Do this.

IMPORTANT:  This is the ONLY time you can Download the .csv, so do it now.

snip_20181101181516

Click Close.

Initialize ASK CLI

In VS Code, view the Command Palette (Ctrl-Shift-P) or select the View | Command Palette menu.  Enter:  ASK in the palette and choose the “ASK: Initialize / Configure the ASK CLI” option.

snip_20180915060653

Accept the default profile name.  Sign in to Amazon in the browser provided USING THE ACCOUNT YOU JUST CREATED ABOVE.  It should say “Profile [default] initialized successfully.”

snip_20180915072056

If not, go here for assistance.

Starting the Alexa Skill

Create a folder for Alexa Skills.

View the Command Palette (Ctrl-Shift-P) or select the View | Command Palette menu.

Enter:  ASK in the palette and choose the “ASK: Create a basic skill package” option.

snip_20181101174552

It should come back with an option to select your AWS profile.  Choose the profile you created above.  This profile needs to be linked to an IAM user who has the right to publish Lambda functions.  (Which is why I cheated above and gave it AdministratorAccess.)

snip_20180915071206

Now it will ask you to enter a skill name.  I’ll be calling mine “OMWTM Roll A Dice”.

snip_20181101174716

 

ASK will then create and run a command in the Terminal window at the bottom right of VS Code:

snip_20181101175829

Once it’s finished, VS Code will open a folder with your basic skill defined:

snip_20181101180914

In the next blog entry, we’ll go through the basic skill definition and customize it.

Bye for now.