A Real World Alexa Skill

Building a Real World Alexa Skill

Shakeeb

5 minute read

Building a Real World Alexa Skill

Motivation

We all know amazon provides a very good platform to develop skills for its voice assistant Alexa. Getting started on the platform is pretty simple and straight forward following amazon’s getting started tutorials.

Now I wanted to build a real world skill.

Requirements

Requirements that I wanted to achieve

  1. Get prevailing 22 carat gold prices in India and in some of the major Indian cities
  2. Acquire the prices from a dependable source

So there were two main parts to the implementation..

Building the voice model on alexa

Here we will walk through the interaction model in json form, which can be easily pasted in the json eidtor window on alexa skill console.

  1. Set up the invocation word, in my case it was “gold rate today”. It goes something like this when launching your skill -

“Alexa ask gold rate today”

On launching the skill, I wanted it to respond with the gold rate on that day in India and follow up with an indication on how to query for gold rate in a specific city. We will come to the response building while going through the skill service implementation.


2. Next I wanted to create an intent to query for a gold rate in a specific Indian city. This means I had to let user speak out a name of Indian city and identify that "slot" in the *alexa* skill service. To do that you have to define something called slots, which are like placeholders in your query sentence. For example, if you define a sample utterance -

>"how is the weather in {city}"

City inside curly braces here acts like a placeholder. When user invokes this intent, *alexa* skill would receive this place holder in a identifier which you can use in the service. It will be clear when we look at the skill service implementation.

Another thing here was Amazon hasn't yet supported a slot for Indian city. So I had to implement a custom slot - which takes value from a custom list.

This is the intent that I defined

{ “name”: “GetGoldRateIntentCity”, “slots”: [ { “name”: “place”, “type”: “INDIA_CITY” } ], “samples”: [ “in {place}”, “for {place}”, “price in {place} “ ] }

And the custom slot of **INDIA_CITY** is defined as

“types”: [ { “name”: “INDIA_CITY”, “values”: [ { “name”: { “value”: “india” } }, { “name”: { “value”: “kolkata” } }, { “name”: { “value”: “chennai” } }, { “name”: { “value”: “bangalore” } } ] } ]

With this interaction model, my skills lambda service would get invoked with two intents - *LaunchRequest* and *GetGoldRateIntentCity*

>After that, following the getting started guide from [**amazon**](https://developer.amazon.com/alexa-skills-kit/tutorials/fact-skill-2), you create a skill service as lambda function and link it with your alexa skill.

#### Skill Lambda Service
Now lets look at the skill service implementation for this project.

1. First thing was to get gold rates from the website - https://www.goodreturns.in/gold-rates/.
    1. Used web scrapping method with a node modules *request-promise* - for loading the website and *cheerio* - for parsing the http response and getting it in a jqeury API form to traverse from nodejs.

      You define the following options which the request promise module would use for the http request. Here cheerio is used in the transform callback to parse the response body.
    ```
    const options = {
          uri: `https://www.goodreturns.in/gold-rates/`,
          transform: function (body) {
            return cheerio.load(body);
          }
    };
    ```

          Then the actual request is sent as
    ```
    rp(options)
      .then(($) => {
    	// handle the response
    }
    ```
    >If this structure is not familiar, I would suggest to go through node async implementations

    Then to actually extract the required information - first I had to go through the DOM response of the website by selecting the inspect option on required data from chrome browser.
    <div class="polaroid01">
      <img src="/img/2018/04/fig1.png" alt="LOGIN IMG"">
    </div>

    Then figure out the element I am interested in.
    <div class="polaroid01">
      <img src="/img/2018/04/fig2.png" alt="LOGIN IMG"">
    </div>

    Next get the same elements just like a jquery implementation

    ```
        rate =  $('.gold_silver_table').eq(0).find('table').find('tr').eq(3).find('td').eq(1).text().split(' ')[1].replace(',', '');
        cities = $('.gold_silver_table').eq(2).find('table').find('tr');
        cities.splice(0, 1);

        for (var i = 0; i < cities.length; i++) {
            // console.log(cities.eq(i).find('td').eq(0).find('a').text());
            // console.log(cities.eq(i).find('td').eq(1).text().split(' ')[2].replace(',', ''));
            var name = cities.eq(i).find('td').eq(0).find('a').text();
            var price = cities.eq(i).find('td').eq(1).text().split(' ')[2].replace(',', '');
            prices[name] = price;
        };
  1. Finally coming to the alexa skill intents and its response

    LaunchRequest would be invoked whenever a user calls out invocation word - “Alexa ask gold rate today”. On launch, in my implementation, I redirect to another intent GetGoldRateIntentCity - since I wanted the same handling for launch word as well as specific city query.

    'LaunchRequest': function () {
        this.emit('GetGoldRateIntentCity');
    },
    

    Then in GetGoldRateIntentCity, the default response is built as

    var speechOutput = "22 carat Gold rate in India today is rupees " + rate +
                        ". If you want for a particular city, please ask with the city name";
    

    This response is sent whenever the city slot value is empty.

    Let us look at how the custom slot for INDIA_CITY that we defined, is used in the service. Since the request promise is an asynchronus call and callback would be in another object’s context, we have to cache the intent object

    const intentObj = this.event.request.intent;
    

    And the slot is found with intentObj.slots.place.value

    In the request callback, we just figure out the cities available in the webpage, and if the requested city is present in the webpage we would return the rate for that city else return default rate for India.

    var speechOutput = "22 carat Gold rate in India today is rupees " + rate +
                         ". If you want for a particular city, please ask with the city name";
    try {
        if(intentObj.slots.place.value.toLowerCase() != "india") {
            speechOutput = "22 carat Gold rate in "+intentObj.slots.place.value+" today is rupees " +
                                    prices[intentObj.slots.place.value.toLowerCase()];
        }
    }
    catch (err) {
        console.log(err);
    }
    

Here we go, with this simple integration of available tools we were able to get ‘gold rate today’ alexa skill up and running live in no time, along the way learning a new tech - hacking our way through it.

Code for this project is shared here.

Alexa skill live on the Amazon Market Place.

Feel free try it and hack it!

Bye for now till the next hacking session..

comments powered by Disqus