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.

Unit Testing an Alexa Skill in C# – Step 3a

In Step 3, we created the Lambda function that will provide the functionality of the Alexa Skill we defined in Step 1, we wired everything up and then we tested our Skill.  So far so good.  However, that’s an awful long way round to test the Skill Request and Response.  It would be nice if we could set up a unit test project and unit test our Lambda function without having to deploy it.

It turns out, we can.

When we created the Lambda function project in Step 3, we selected the AWS Lambda Project with Tests template.  So we already have a Tests project and a stubbed out xunit test.  (Note: Because we are using the .Net Core for our Lambda function, the normal Microsoft Unit Testing Framework doesn’t work (because it uses the full .Net platform) and so the AWS template uses xunit.)

The tricky part of Unit Testing our Lambda function is that we need to test them FunctionHandler method, which takes a SkillRequest and a Context as parameters:

public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext ctx)

In order to Unit Test our simple GetTodaysDateIntent, we will need to set up a SkillRequest that names that Intent.  Like so:

using AlexaAPI;
using AlexaAPI.Request;
using AlexaAPI.Response;
using Amazon.Lambda.TestUtilities;
using System.Collections.Generic;
using Xunit;
var intent = new Intent();
intent.Name = "GetTodaysDateIntent";
intent.ConfirmationStatus = "NONE";
var request = new Request();
request.Intent = intent;
request.Locale = "en-US";
request.Type = AlexaConstants.IntentRequest;
var interfaces = new Dictionary<string, object>();
var device = new Device();
device.SupportedInterfaces = interfaces;
var sysObj = new SystemObject();
sysObj.Device = device;
var skillContext = new Context();
skillContext.System = sysObj;
var session = new Session();
var skillRequest = new SkillRequest();
skillRequest.Request = request;
skillRequest.Context = skillContext;
skillRequest.Session = session;

With that code to create the skillRequest, this line will create the Lambda Context:

var context = new TestLambdaContext();

Now we can create the Function instance:

var function = new MyAlexaSkill.Lambda.Function();

And then we can call the FunctionHandler method to test the Intent:

var result = function.FunctionHandler(skillRequest, context);

A few Assertions will make sure we got the response we are looking for:

Assert.NotNull(result);
Assert.NotNull(result.Response);
Assert.NotNull(result.Response.OutputSpeech);
 
Assert.Equal("<speak>Today's date is 5/15/2018 12:00:00 AM</speak>", 
    (result.Response.OutputSpeech as SsmlOutputSpeech).Ssml);

Putting it all together, we get this Unit Test:

[Fact]
public void TestGetTodaysDateIntent()
{
 
    var intent = new Intent();
    intent.Name = "GetTodaysDateIntent";
    intent.ConfirmationStatus = "NONE";
    var request = new Request();
    request.Intent = intent;
    request.Locale = "en-US";
    request.Type = AlexaConstants.IntentRequest;
    var interfaces = new Dictionary<string, object>();
    var device = new Device();
    device.SupportedInterfaces = interfaces;
    var sysObj = new SystemObject();
    sysObj.Device = device;
    var skillContext = new Context();
    skillContext.System = sysObj;
    var session = new Session();
    var skillRequest = new SkillRequest();
    skillRequest.Request = request;
    skillRequest.Context = skillContext;
    skillRequest.Session = session;
            
    var context = new TestLambdaContext();
 
    var function = new MyAlexaSkill.Lambda.Function();
 
    var result = function.FunctionHandler(skillRequest, context);
 
    Assert.NotNull(result);
    Assert.NotNull(result.Response);
    Assert.NotNull(result.Response.OutputSpeech);
 
    Assert.Equal("<speak>Today's date is 5/15/2018 12:00:00 AM</speak>", 
        (result.Response.OutputSpeech as SsmlOutputSpeech).Ssml);
}

And there you have it, a Unit Test to test a simple Alexa Skill Intent.

 

 

 

 

 

 

 

Creating an Alexa Skill in C# – Step 3

Now that you’ve defined your Alexa Skill in Step 1, and you’ve configured the security and downloaded the sample in Step 2, we’re ready to take a look at the sample and see how to wire it up to the skill definition.

Open your copy of the sample solution in Visual Studio 2017.  I left my copy in this folder:

C:\_GitHub\Alexa\MyAlexaSkill\sampleFactCsharp.sln

At this point, I had a decision to make:  Do I start a new project and copy the stuff from the sample project, or do I just mess with the sample project?  I think I’ll actually create a new project, and leave the sample in pristine condition for comparison.

Creating the Alexa Skill Project from the Sample

First, create a new project in the solution, choosing AWS Lambda Project with Tests as the template:

snip_20180511111204

We want an Empty Function:

snip_20180511111641

Now that we have our own project, let’s check out the dependencies:

snip_20180511111536

As of this writing, the NuGet package for Amazon.Lambda.Serialization.Json needs updating.  So I did that.  Also, the sample is for .NetCore 1.0.  My project has defaulted to using .NetCore 2.0.  I don’t expect this to be a problem.

Next, I might as well copy the code (both classes) and the AlexaAPI folder from the sample to my project.  This will at least give me a starting point.

Coding for the Alexa Intents

Now that the project is set up, we can start coding for the Intents in the Alexa Skill we created in Step 1.   To start off, we will crack open the Function class in our new project and locate the ProcessIntentRequest() function.  There’s a switch statement there that will handle our simplest Intent Request, which was GetTodaysDateIntent, like so:

switch (intentRequest.Intent.Name)
{
    case "GetTodaysDateIntent":
        innerResponse = new SsmlOutputSpeech();
        (innerResponse as SsmlOutputSpeech).Ssml = $"Today's date is {DateTime.Today.ToString()}";
        break;
 
    case "GetNewFactIntent":
        innerResponse = new SsmlOutputSpeech();
        (innerResponse as SsmlOutputSpeech).Ssml = GetNewFact(factdata, true);
        break;
   ...

At this point, you’ll probably want to do a build and deployment to AWS.  First, make sure your project builds.

Creating an AWS Profile

Next, you need to set up a Publish Profile in AWS.  This is done by viewing the AWS Explorer on the left side of your VS screen and clicking the New Account Profile icon.

snip_20180514110823

That will bring up the New Account Profile dialog.

snip_20180514110903

Click the link near the bottom of the dialog to go to the documentation on how to add a user.  Then click on one of the links that open the Console to IAM.  Or just click on the link here.

Next, select the Users category on the left.  Then create a Deployment User that has programmatic access and is in the Developers group we created in Step 2.

snip_20180514115025

When you click Create User, you will see the Success screen.  Be sure to click the Download .csv button (middle-left) to get the credentials file.  You can never get this file again, you will have to create a new Access Key and get a new credentials file.

snip_20180514115244

Now, back in Visual Studio, click the Import from csv file button:

snip_20180514110903

Give your profile a name, I called mine Deployment.  Account Number is optional and can be left blank.  Then click OK.  You will see the AWS Explorer switch to the Deployment profile.  You should then pick the default profile and delete it.

snip_20180514115955

Publishing Your AWS Lambda Project

You are now ready to publish your AWS Lambda project.  Right-click on the project and select Publish to AWS Lambda….

snip_20180514110347

If you don’t see this menu option, make sure you’re in the Lambda project, not the Test project.  Then, make sure you have installed the AWS Toolkit as described in Step 2.

When you see the publish dialog, give your function a name:

snip_20180514120525

Then click next and pick the Role we defined in Step 2.

snip_20180514121222

Then click Upload.  After a few moments, your Lambda function will be uploaded.

Uh-Oh.  Duplicate Compile Items

If you get the Duplicate Compile items error:

Duplicate Compile items were included. The .NET SDK includes Compile items from your project directory by default. You can either remove these items from your project file, or set the ‘EnableDefaultCompileItems’ property to ‘false’ if you want to explicitly include them in your project file.

The recommendation is to remove the Compile items from your project (*.csproj) file.  Right-click the project and choose to Edit your .csproj file:

snip_20180514124040

Then delete all the *.cs files from the Compile section:

snip_20180514124202
I also deleted the ItemGroups above that because they had Compile items in them too.  Now my project builds and deploys properly, and my .csproj file is much cleaner:

snip_20180514124350

Now publish the fixed project to AWS.

Back on Track:  Wiring up the Skill and the Lambda function.

Alright, we have a Skill defined.  We have a Lambda function uploaded.  Now we need to tell them each about the other.  First, we’ll tell the Skill about the Lambda function.

Go to the Lambda functions list in the Lambda Management Console.  Click on your new Lambda function, which I named MyAlexaSkillsLambdaFunction.  In the top right corner of the screen you will see the ARN:

snip_20180515111333

Copy the ARN to the clipboard and paste it into Notepad.  Now, switch over to the Alexa Skill Console.  Select your skill.  Click on the Build tab.  Click on the big “4. Endpoint >” button in the checklist on the right.

snip_20180515111539

Paste the ARN from the Lambda Function into the Default Region box on the right.

snip_20180515112156

Then, copy the Skill ID above it to the clipboard and paste that into Notepad too.

Switch back to the Lambda Management Console.  Under the Add Triggers list on the left, find the Alexa Skills Kit and drag it over to the Triggers area of your Lambda function:

snip_20180515112342

Then scroll down to configure the trigger and paste in the Skill ID you copied from the Skill and click the Add button:

snip_20180515112653

Lastly, in the top right corner of the Lambda screen, click the Save button:

snip_20180515112823

And that’s that.  The Alexa Skill is defined, the Lambda function is defined.  The two know about each other.  You are ready to test your skill.

Testing the Alexa Skill

To test your Alexa Skill and make sure everything is wired up properly, click on the Test tab in the Alexa Console.   Then enable testing for this skill.

snip_20180515113053

In the Alexa Simulator panel, in the text entry box, run your Skill:

snip_20180515113732

Alexa should respond with the Launch Message from your skill (which we will modify in a later Step in this series):

snip_20180515113938

Now, test your Skill’s Intent:

snip_20180515114036

Alexa should tell you the date:

snip_20180515114114

Tada!  That’s it for Step 3.

Check out Step 3a to see how to Unit Test your Alexa Skill Intent without publishing anything to AWS.

In Step 4, we will look at setting up a Web Service project to feed data from an Azure SQL database to our Alexa Skill.

 

Creating an Alexa Skill in C# – Step 2

If you haven’t done so already, check out Step 1 to define your Alexa Skill.

After defining the Alexa Skill in Step 1, you are ready to set up Visual Studio in Step 2.

AWS Account and AWS Toolkit Extension

Before you can set up an Alexa project, you need to create an AWS Lambda project.  Lambda functions are just class libraries that are hosted in the AWS Lambda cloud service.   To create one, you need two things:  the Amazon AWS Toolkit Extension and an AWS Developer Account.  You can install the Extension in VS 2017.

snip_20180511104603

You can create the AWS Developer Account at https://aws.amazon.com/.

With the AWS NuGet package installed and the Developer Account set up, you are ready to set up the AWS Security.  You do that here:  https://console.aws.amazon.com/iam.

Securing Your AWS Account and Lambda Function

In IAM Management Console, click on Groups on the left.  Create a group for your Developers that has Admin Access.  Create a group for your Apps that has AWSLambdaFullAccess

Then, click on Users on the left and create two accounts.  One for yourself, of type AWS Management Console access, assigned to the Developers group.  And, one for your app, of type Programmatic access, assigned to the Apps group:

snip_20180508155233

snip_20180508155436

Next, create an AWS Lambda Role by selecting Roles on the left side.  Then make the following selections:

snip_20180508155822

snip_20180508160005

snip_20180508160125

With the security Groups, Users and Role configured, you are ready to create the Solution for your Alexa Skill.  The easiest way to do this is to use one of the sample projects in the Alexa Git repository.

Getting the Alexa Skill Sample Solution

Navigate to the Alexa GitHub repository in your browser to see what’s available.

I used the alexa/skill-sample-csharp-fact sample as my starting point.  It has a ton of code in it that is ready to go, I just had to add something specific to my skill and I was off to the races.  You can get the sample from the command line (Start | Run | Cmd).  Make the directory/folder you want to host the project in with the md command:

C:>  md \_GitHub
C:>  md \_GitHub\Alexa
C:>  cd \_GitHub\Alexa

And then type:

git clone https://github.com/alexa/skill-sample-csharp-fact.git

You will find the C# Solution file here:

C:\_GitHub\Alexa\skill-sample-csharp-fact\lambda\custom\sampleFactCsharp.sln

Copy the sample solution to your own folder:

C:\_GitHub\Alexa>  md MyAlexaSkill
C:\_GitHub\Alexa>  cd skill-sample-csharp-fact\lambda\custom
C:\_GitHub\Alexa\skill-sample-csharp-fact\lambda\custom>  
    xcopy *.* \_GitHub\Alexa\MyAlexaSkill\*.* /S

Open the solution in VS 2017.  (Be sure to update to the latest version of VS — 15.7 as of this writing — as it has some cool new features!)

In Step 3, we will look at the sample solution and start to modify it to work with the skill we defined in Step 1.

 

Creating an Alexa Skill in C# – Step 1

Recently, I decided to make an Alexa skill that I could play on my boss’s Alexa.  At first, I was doing it as a gag, but I figured that wouldn’t work as he has to actively install my skill onto his Alexa.  Now it reads some stats from one of our Azure databases and publishes those in a conversation.  Here’s how I built it.

Step 1:  Creating the Alexa Skill Model

I don’t know any of the other languages that you can write a skill in, so I chose to write it in C#.  That means getting a bunch of bits and loading them into Visual Studio.  But first, you’ll need to start the process of creating a skill.  At the time of this writing, these are the steps I took.

  1. Start Here.
  2. Click the Start a Skill button.capture20180507114817423
  3. If you don’t have an Amazon Developer account, create one.
  4. Eventually, you’ll get to the Alexa Skills Developers Console where you can click the Create Skill button.  capture20180507115228773
  5. Give your skill a name.  I’m calling mine:  Simon’s Example Skill.
  6. On the Choose a Model screen, select Custom. capture20180507115624173
  7. Then click Create Skill.
  8. You should now be on the Build tab for your Skill.  Notice the tree view on the left and the checklist on the right.capture20180507115839651

Invocation Name

The Invocation Name is the phrase that an Alexa user will use to launch/run/start your Alexa Skill.  It could be “joe’s hot dog recipes” or some such.  It needs to be lower case, there are some restrictions on the characters you can use, and any abbreviations you use need periods to separate the letters so Alexa knows to read the letters and not pronounce the word.  Read the Invocation Name Requirements for more details.

Click the Invocation Name link in the tree view on the left or the first option in the checklist on the right.  Then give your skill an Invocation Name.  I named mine:  “simon’s example”.

Intents

The Intents are the various functions your skill can perform.  For example, stating today’s date, looking up some data, figuring something out, etc.  Let’s do all three.

First, my skill is going to provide today’s date, so I’m going to name my first Intent, “GetTodaysDateIntent”.  My skill is also going to look up some data in an Azure SQL Database, so I’m going to name my second Intent, “DataLookupIntent”.  Lastly, I want to figure something out, like the average temperature in a major US city.

Utterances

The Utterances are the phrases an Alexa user might say to trigger your Intent (function).  You should put in several Utterances using synonyms and different phrasing so Alexa has a better chance of triggering your Intent instead of responding with “I don’t know how to do that.”

For the GetTodaysDateIntent, I added the following utterances:

snip_20180511131124

Slots

Within an Utterance, you can have Slots (or placeholders), that represent the multiple options for that slot.  For example, if my table has the count of animals per household per neighborhood per county in it, I might want to create slots for Animal Type, Household Size, Neighborhood Name, and County Name.  You can do this by typing a left brace { in the Utterance box.

capture20180507123258765

Here are three of my sample utterances for the DataLookupIntent:

capture20180507123605384

Once you have created a slot, you need to populate it with options.  You do this in the bottom half of the Utterance screen.

capture20180507123836958

You can easily select one of the pre-defined Slot Types in the drop-down.  In my case, Amazon has a list of Animals, so I’ll pick AMAZON.Animal in the first slot.

I need to manually add a few Counties for the second slot though.  And at this time, you don’t want to click Edit Dialog (though it’s tempting).  Instead, you want to define your own Slot Type by clicking the Add (Plus) button next to Slot Type in the tree view on the left:

capture20180507124646880

For example, here is the custom Slot Type for County Name:

capture20180507130009338

Notice the synonyms column.  This is important if there are synonyms, for example, a 1 person household and a single person household are synonymous.  So be certain to add any synonyms you can think of.  Here is my custom Slot Type for Household Size, notice the synonyms off to the right:

capture20180507130107411

Now that you’ve defined some custom Slot Types, you can click on the Slot names under the Intent in the tree view on the left and select the newly created Slot Type for each Slot.

capture20180507130205126

For the GetAverageTemperatureIntent, I added one Utterance:

snip_20180511131952

And configured the {City} slot as follows:

snip_20180511133037

Finally, you can Save your Model and Build it by clicking the buttons at the top of the screen:

capture20180507130345077

Hopefully, the model will build and we are ready to move on to Step 2.  If the model doesn’t build, check the bottom right of the screen for a list of the errors:

snip_20180511132317

Fix the errors until the model builds:

snip_20180511133106

Then go to Step 2.