Home

Awesome

Alexa Parrot

Travis CircleCI

A simple parroting skill for Alexa to grasp basics of creating alexa skills with Alexa-App library and deploying to AWS Lambda Functions. Requires Use NodeJS v8.x or higher.

Prerequisite files

Package JSON

Require alexa-app.

{
  "name": "parrot",
  "version": "1.0.0",
  "description": "Alexa parroting skill",
  "main": "parrot.js",
  "dependencies": {
    "alexa-app": "^4.2.2"
  },
  "author": "Daniel Doubrovkine (db@artsy.net)",
  "license": "MIT",
  "engines": {
    "node": ">=8.0.0"
  }
}

.gitignore

node_modules

Install

yarn install

Coding the skill

Basic Alexa App

const alexa = require('alexa-app');

const app = new alexa.app('parrot');

app.launch((req, res) => {
  res.say('I am a parrot.');
});

module.exports = app;

Writing basic MochaJS tests

Add express and mocha to package.json and support for yarn test.

"devDependencies": {
  "chai": "4.1.2",
  "eslint": "^4.19.1",
  "express": "^4.14.0",
  "mocha": "5.2.0",
  "supertest": "3.1.0"
},
"scripts": {
  "test": "mocha test"
}

A test in test/test_parrot.js.

/* eslint-disable no-undef, no-unused-vars, sort-vars, no-mixed-requires, global-require*/
const express = require('express');
const request = require('supertest');
const {expect} = require('chai');

describe('Parrot', () => {
  let server = null;

  beforeEach(() => {
    const app = express();
    const parrot = require('../parrot');

    parrot.express({
      expressApp: app,
      debug: true,
      checkCert: false
    });
    server = app.listen(3000);
  });

  afterEach(() => {
    server.close();
  });
});

Respond to Invalid Data

it('responds to invalid data', () => request(server)
  .post('/parrot')
  .send({})
  .expect(200)
  .then(response => expect(response.body).to.eql({
    version: '1.0',
    response: {
      directives: [],
      shouldEndSession: true,
      outputSpeech: {
        type: 'SSML',
        ssml: '<speak>Error: not a valid request</speak>'
      }
    },
    sessionAttributes: {}
  })));

Responds to a Launch Request

it('responds to a launch event', () => request(server)
  .post('/parrot')
  .send({request: {type: 'LaunchRequest'}})
  .expect(200)
  .then((response) => {
    const {ssml} = response.body.response.outputSpeech;

    console.log(ssml);

    return expect(ssml).to.eql('<speak>I am a parrot.</speak>');
  }));

Deployment to AWS Lambda

For the deployment we will use Apex, which requires files to be in a functions directory so lets move some files around:

mkdir -p functions/parrot
mv parrot.js functions/parrot
mv package.json functions/parrot
mv test functions/parrot

New folder structure is:

.
| functions/
|─── parrot/
|   | parrot.js
|   | package.json
|   |──test/
|   |  | test_parrot.js
└

Create functions/parrot/index.js.

const parrot = require('parrot');

exports.handle = parrot.lambda();

Create project.json.

{
  "name": "alexa",
  "description": "I am a parrot.",
  "memory": 128,
  "timeout": 5,
  "role": ""
}

For the role value go to AWS → IAM → Roles → Open the role you created while creating the function → Copy the ARN

To deploy run this command:

apex deploy

Creating a replying function

Add this to functions/parrot/parrot.js.

app.intent('RepeatIntent', {
  slots: {VALUE: 'AMAZON.NUMBER'},
  utterances: ['repeat {-|VALUE}', 'to repeat {-|VALUE}']
}, (req, res) => {
  const value = req.slot('VALUE') || 2;

  res.say(`You said ${value}.`);
  for (let i = 0; i < value; i++) {
    res.say(`I repeat, you said ${value}.`);
  }
});

And a test to functions/parrot/test/test_parrot.js.

it('responds to a repeat event', () => request(server)
.post('/parrot')
.send({
  request: {
    type: 'IntentRequest',
    intent: {
      name: 'RepeatIntent',
      slots: {
        VALUE: {
          name: 'VALUE',
          value: '2'
        }
      }
    }
  }
})
.expect(200)
.then((response) => {
  const { ssml } = response.body.response.outputSpeech;

  return expect(ssml).to.eql('<speak>You said 2. I repeat, you said 2. I repeat, you said 2.</speak>');
}));
});

Create a new Alexa Skill

Intent data can be generated with functions/parrot/skill.js. Format the output of this file as proper JSON then paste it in the JSON Editor.

const parrot = require('./parrot');

console.log('Intent Schema:');
console.log();
console.log(parrot.schemas.skillBuilder());
console.log('Utterances');
console.log(parrot.utterances());

The skill is now available in http://alexa.amazon.com under Skills → Your Skills → Dev Skills

Try It

Setting up a CI service

CI (Continuous Integration) services will run your tests every time you commit some code and integrate flawlessly with major Git players such as GitHub, BitBucket and GitLab. Having a CI service set up will motivate you to always ensure the code you push works lest you'll be seeing the "tests failed" everywhere you have notifiers for the service set up.

For this project we have provided examples for setting up CI on Travis and on CircleCI. You can find the Travis setup by clicking here and the CircleCI setup here

Setting up TravisCI

  1. Go to TravisCI.org and create an account by signing in with your GitHub
  2. Once logged in click the + next to My repositories or navigate to your profile by clicking your name at the top right of the window
  3. Enable the repo you want to build for
  4. Push some code to the repo to start building

Note: for private repositories please go to TravisCI.com instead

Setting up CircleCI

  1. Go to CircleCI and create your account by signing in with GitHub or BitBucket
  2. Once on the dashboard go to Add Projects on the left and add your Alexa Skill project
  3. If you haven't committed your config.yml (in a folder .circleci in root) yet do so now
  4. Press the button start building to start testing your repo from this point forward