Managing Skill Content with Alexa.NET & Contentful

So you have your skill, everything’s deployed and sounds fantastic and your users are happy. How often does a blog article start with that? But that’s where we need to start in this article.

Your skill is beautifully crafted, and when a user comes back it picks one of two phrases to say hello to them. Two is…fine…I guess…well actually, there’s the holidays coming up, and really wouldn’t it feel more conversational if you occassionally mentioned that as part of the set you picked from? A quick happy holiday added to the list and re-deploy and you’re good.

Okay…but now the holiday season has passed and your skill is still saying happy holidays…hmm…remove it and redeploy…and two is sounding really repetitve, so you thought of a couple of really good ones to add to the welcome messages the other day, so add and redeploy again…

and that’s just saying hello! And of course you’re absolutely sure that your tests pass every time you made that two minute change…right. But that’s not really the point, because your tests will still pass even if one of the combinations you stuck together doesn’t sound quite right and have to re-deploy to fix it, but how many times before a typo or deleting one line too many gets missed and you have a side effect in your code too?

So hopefully you get the idea. Updating your skill because of updating your content? Less than ideal. Ideally what you want is a way of managing content seperately, so your skill isn’t being touched when the content needs a change. And very few of us have time to mess around creating a functional CMS as well as all our other tasks – so in this article we’re going to look at how you can take advantage of a CMS API within your Alexa skill to keep content feeling fresh, and your fingers off the code!

Disclaimer

  • Yes there are other CMS systems out there. No, I don’t want to know why they’re better than the one I picked.
  • Yes, you could look at caching or API Gateway proxy to improve latency issues.
  • Yes there are more efficient ways of creating the content, but as you’ll see I really wanted the rich formatting.
  • Yes I’m sure you never do anything wrong in your code and will continue to hack skill code every time you want to add punctuation to a sentence, well done.

The CMS – Contentful

So for this article the CMS of choice for me is Contentful. It allows you to define types of content, specify the field names and type of data in each field, and then create documents of that type. The API is really straight forward and their documentation is easy to read. The big wins for me in regards to this article:

  • Nice interface when you’re taking screenshots for an article
  • They’ve recently restructured their pricing so their basic package is free
  • .NET Core SDK in NuGet

Defining the content type

So the first thing to do is to create the structure of our content. This not only defines the schema of your content but also the way the content is going to appear in the editor.

There are lots of ways you could structure it, but for now I’m going to write the content this way:

  • requestType: Specify the type of request you’re handling
  • context: A secondary value for things like intent name (keeping it generic)
  • text: the actual content itself

And here’s how it looks on screen as you create it

Create a new content type

Create the content

Field creation screen

Specify the fields and their types

Content creation screen

Create a new piece of content of type Alexa

Erm…Markdown? Alexa isn’t for text

So you might notice that the text field in my screenshot is a Markdown field. And yes, I’m aware that the rich text format of Markdown isn’t going to be of much use when you’re listening to Alexa talk to you.

But! It was annoying me that there was no great way of harnessing the richness of SSML within in Alexa skill without your content providers really understanding the nuances of the markup language, so I wrote Alexa.NET.MD2SSML, a little library that takes markdown text and translate it into SSML that can be used within Alexa skills. So italic affects pitch, emphasis adds..well…emphasis. I find it super useful as your content providers will quickly get used to the nuances.

So…you end up with a few entries, each that say something different, each matched to the same kind of request (we’ll keep it simple, launch request in this case)

Launch response choices

The different choices of response

So…how do we harness this to make a more robust skill?

The Skill Code

So we’ll tackle this in two parts (remembering the disclaimer about caching etc.)

Setting up Contentful

So there’s a nice Contentful SDK for .NET Core available on NuGet. We’ll add this to our lambda and then go through the necessary steps.

First of all let’s get a POCO that represents the fields we set up in Contentful

 public class AlexaContent
        {
            public string RequestType { get; set; }
            public string Context { get; set; }
            public string Text { get; set; }
        }

Then we need to get a Contentful client set up. Best to do this in the constructor if you’re using a lambda. Keys are environmental variables because why would you risk putting them anywhere near source code versioning?

 private static Contentful.Core.ContentfulClient _contentful;

public Function(){
 var apiKey = System.Environment.GetEnvironmentVariable("api_key");
 var spaceId = System.Environment.GetEnvironmentVariable("space_id");

_contentful = new Contentful.Core.ContentfulClient(
                new HttpClient(), apiKey, null, spaceId);
 }

Running the skill code

I’m just going to make the call to Contentful each time the skill triggers, but that has a latency cost, so look at the options out there to cache or manage the calls (Amazon recently did an article about using API Gateway to reduce skill call latency)

public async Task<SkillResponse> Handler(SkillRequest request){
    var query = QueryBuilder<AlexaContent>.New
    .ContentTypeIs("alexa")
    .FieldEquals("requestType", "Launch");
    var entries = await _contentful.GetEntries(query);


  var badRandom = new Random(); //Yeah - do better random than this!
  var choice = badRandom.Next(entries.Total-1);
  var pickedText = entries.Skip(choice).First().Text;

    return ResponseBuilder.Tell(pickedText);
}

Now this is a purposely daft example, but you hopefully see the power of putting together something straight forward that can help add a new level of flexibility without having to constantly re-edit your code directly.

Leave a Reply

Your email address will not be published. Required fields are marked *