1. RICK WARGO - MAR 2017
BUILD AN ALEXA SKILL
STEP-BY-STEP
1
2. BUILD AN ALEXA SKILL STEP-BY-STEP
Introduction
▸ This tutorial will guide you step-by-step to create a new Alexa skill
▸ This example skill will interface with Twitter and read recent tweets from a query
▸ The aim is to be able to extend this workflow for your own use
▸ This skill will be called Twitter News and will be private
▸ This tutorial will not cover publishing a new skill - there is plenty of available
documentation on how to publish
▸ Please let me know if you use this to build a new skill — I welcome feedback!
2
3. BUILD AN ALEXA SKILL STEP-BY-STEP
Sample Interactions — Define Prior to Building New Skill
▸ The critical path to creating a great skill is pre-visualization of the skill
▸ Try to define the interactions as much as possible and maintain in a file, for example, Examples.txt
▸ Q: What's the latest news?
▸ A: The last 1 tweet was: Rick Wargo tweeted: Join me this Sat at 11:30am - we'll build an Alexa skill that interfaces with Twitter at #PhillyCode @PhillyDotNet.
▸ Q: What's the latest popular tweet?
▸ A: There were no matching tweets.
▸ Q: What is the last 5 tweets?
▸ A: The last 5 tweets were: Rick Wargo tweeted: Join me this Sat at 11:30am - we'll build an Alexa skill that interfaces with Twitter at #PhillyCode @PhillyDotNet. Alina retweeted
Stratisplatform's tweet: #Stratis are gold sponsors @PhillyDotNet on 24th-25th February explaining our #blockchain t…
▸ Q: What is the last tweet from Rick?
▸ A: The last 1 tweet was: Rick Wargo tweeted: Join me this Sat at 11:30am - we'll build an Alexa skill that interfaces with Twitter at #PhillyCode @PhillyDotNet.
▸ Q: Who tweeted?
▸ A: Recent tweets were from: Rick Wargo, Kucilo Oro, Alina, XTexplorer, and Mohammad Khalid.
▸ Q: Who shared?
▸ A: Recent tweets were from: Rick Wargo, Kucilo Oro, Alina, XTexplorer, and Mohammad Khalid.
3
4. BUILD AN ALEXA SKILL STEP-BY-STEP
Assumptions
▸ Implementing on OSX 10.12.3
▸ Using AWS Lambda and Node.js 4.3 to host code
▸ Linux-style commands used throughout
# Commands to be executed at the command line are in this style
▸ Input from interactive commands is in bold text
▸ References to files point to the completed version of the twitter-news-skill in Github
▸ Depending on copy/paste, some files may require retouching, mostly white-space, for lint to succeed
▸ All commands to be executed from skill directory unless otherwise noted
▸ Should work with slight modifications on Windows
4
5. BUILD AN ALEXA SKILL STEP-BY-STEP
User Interaction Flow
5
Twitter API
User
Voice Request Audio Stream
Twitter-News
Skill
Lambda
JSON Request
Node.js
JSON Response
(SSML+Card)
Response (Audio)
Response (Text/Graphics)
Audio Response
App/Web
JSON Response
(statuses)
GET search/tweets
7. BUILD AN ALEXA SKILL STEP-BY-STEP
AWS Credentials
‣ For command line access to AWS, store credentials in ~/.aws/credentials
$ cat ~/.aws/credentials
[default]
aws_access_key_id = OATLIM5W7KKX1V6PQ792
aws_secret_access_key = I2WiUyTkPI7b36PQnin11oUqgZVX575tSUAB1FOm
‣ This may already be done when setting up the AWS CLI
‣ Development on Windows will require a different approach to storing this file
Not my real credentials :)
7
8. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the Directory Structure
▸ Create a directory where both the Alexa skills and the Alexa App Server will live. All new
skills will live under here.
▸ For this tutorial, we’ll use ~/Code/alexa-js
▸ In that directory:
cd ~/Code/alexa-js
git clone https://github.com/rickwargo/alexa-app-root
mkdir alexa-js-apps†
▸ Install the node modules for alexa-app-root
cd alexa-app-root
npm install
8
†If you choose to call this directory something else, you’ll need
to update filesPath.server in gulpfile.js. You’ll
also need to update server.js and change app_dir to
point to the directory.
9. BUILD AN ALEXA SKILL STEP-BY-STEP
Create Certificate for HTTPS (if using)
▸ Install a self-signed certificate for HTTPS
gulp make-cert
[18:06:51] Using gulpfile ~/Code/alexa-js/test/alexa-app-root/gulpfile.js
[18:06:51] Starting 'make-cert'...
Generating RSA private key, 1024 bit long modulus
...++++++
.............++++++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Pennsylvania
Locality Name (eg, city) []:Blue Bell
Organization Name (eg, company) [Internet Widgits Pty Ltd]:epicminds
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:Rick Wargo
Email Address []:cert@epicminds.com
[18:07:21] Finished 'make-cert' after 30 s
9
10. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Web Server is Running
▸ Browse to http://localhost:8003/test
▸ Also test https://localhost:8003/test
10
11. BUILD AN ALEXA SKILL STEP-BY-STEP
Start a New Skill
▸ Fork the alexa-app starter template, if desired
https://github.com/rickwargo/alexa-app-template
▸ Clone into a directory under alexa-js-apps
cd ~/Code/alexa-js/alexa-js-apps
git clone https://github.com/rickwargo/alexa-app-template twitter-news
▸ Alternately, all code is available from Github (don’t do this if you want to code by
hand)
cd ~/Code/alexa-js/alexa-js-apps
git clone https://github.com/rickwargo/twitter-news
11
12. BUILD AN ALEXA SKILL STEP-BY-STEP
Install Node Modules
▸ Use npm install to download and install all modules in package.json
cd twitter-news
npm install
12
13. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Fresh Installation
▸ Run the default tests using gulp
▸ test-mock runs the test runner framework (mocha, chai is the test framework) — only a few tests are shown below
gulp test-mock
[12:14:16] Using gulpfile ~/Code/alexa-js/alexa-js-apps/twitter-news/gulpfile.js
[12:14:16] Starting 'test-mock'...
App Starter Tests
starting up
✓ should fail if an unknown application id is provided
✓ should fail if a missing application is provided
...
My Intents
the story intent
✓ tells you the whole story
✓ tells a partial story when asked
Text
for exception message
✓ returns exception message if supplied
✓ returns message if supplied
21 passing (23ms)
[12:14:16] Finished 'test-mock' after 569 ms
13
14. BUILD AN ALEXA SKILL STEP-BY-STEP
Create an AWS IAM Role for Lambda Code Execution
▸ Name should be function name (later we’ll name it twitter-news-skill) followed by “-role”
aws iam create-role --role-name twitter-news-skill-role --assume-role-policy-document file://assets/json/aws/trust-policy.json
{
"Role": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": {
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
},
"RoleId": "ZG7523QZS8J7R951Y3C0J",
"CreateDate": "2017-02-27T17:59:56.785Z",
"RoleName": "twitter-news-skill-role",
"Path": "/",
"Arn": "arn:aws:iam::123456789012:role/twitter-news-skill-role"
}
}
▸ From the create-role output, copy Arn JSON value and update the role in config/aws-config.js
14
15. BUILD AN ALEXA SKILL STEP-BY-STEP
Attach Policy to Newly Created Role
▸ Attach an Amazon-managed policy to the twitter-news-skill-role
aws iam attach-role-policy --role-name twitter-news-skill-role --policy-arn
arn:aws:iam::aws:policy/AWSLambdaFullAccess
▸ Access can be changed by choosing a different policy below or roll your own
✦ AWSLambdaFullAccess
✦ AWSLambdaDynamoDBExecutionRole
✦ AWSLambdaBasicExecutionRole
15
16. BUILD AN ALEXA SKILL STEP-BY-STEP
Give the Alexa Service Access
▸ This is the same as adding the Alexa Skills Kit as a trigger to the Lambda
function
aws lambda add-permission --function-name twitter-news-skill --statement-id 1 --action
lambda:invokeFunction --principal alexa-appkit.amazon.com --region us-east-1
{
"Statement": "{"Sid":"1","Resource":"arn:aws:lambda:us-
east-1:100866613345:function:twitter-news-skill","Effect":"Allow","Principal":
{"Service":"alexa-appkit.amazon.com"},"Action":["lambda:invokeFunction"]}"
}
▸ More information can be found in the AWS Lambda Developer Guide
16
17. BUILD AN ALEXA SKILL STEP-BY-STEP
Edit config/aws-config.js prior to Pushing Lambda Code to AWS
▸ Ensure role is updated per Create an AWS IAM Role for Lambda Code Execution
▸ The region defaults to us-east-1 — update as necessary
▸ If you use a specific AWS credentials profile, update the profile value — defaults
to “default”.
▸ Update timeout and memorySize, if needed. The defaults of 3 seconds and
128MB are usually sufficient.
▸ Runtime should be nodejs4.3, unless a newer version was released after this
documentation
17
18. BUILD AN ALEXA SKILL STEP-BY-STEP
Update config/app-config.js prior to Pushing Lambda Code to AWS
▸ The applicationId can be left as-is until the skill is created on the Amazon
Alexa Developer Portal
▸ Update the applicationName to “twitter-news”
▸ Update the functionName to be the applicationName followed by
“-skill”, so it would be “twitter-news-skill”
▸ Update the description to reflect the skill’s intent, i.e. “Grabs the latest or
popular tweets about a specific topic”
18
19. BUILD AN ALEXA SKILL STEP-BY-STEP
Update Config Tests in test/test_config.js
▸ Due to changes to some default settings, tests need to be updated to reflect the change(s)
▸ Update AWS Config —> Property —> region test if changed from us-east-1
▸ Update AWS Config —> Property —> runtime test if changed from nodejs4.3
▸ Update AWS Config —> Property —> applicationName test to reflect the new application
name
▸ For example, change test/test_config.js (around line 47) such that it reads:
it('applicationName is twitter-news', function () {
var result = config.applicationName;
return result.should.equal('twitter-news');
});
19
20. BUILD AN ALEXA SKILL STEP-BY-STEP
Push Code to AWS Lambda
▸ Upload the code to AWS Lambda using “gulp push”
gulp push
▸ Upon successful completion, the test-lambda gulp task will pass all tests
20
21. BUILD AN ALEXA SKILL STEP-BY-STEP
Connect to the Alexa Developer Portal
▸ Browse to the Alexa Developer Portal
▸ Press Get Started > on the Alexa Skills Kit button
21
22. BUILD AN ALEXA SKILL STEP-BY-STEP
Create The Skill on the Alexa Developer Portal
▸ Press Add a New Skill in the top-right of the following page
22
23. BUILD AN ALEXA SKILL STEP-BY-STEP
Fill Out the Skill Information
▸ Keep the Skill Type as Custom
Interaction Model
▸ Set the Name to the application name
(defined previously), Twitter News
▸ Set the invocation name to the
application name (in lower case),
twitter news
▸ Leave Audio Player defaulted to No
▸ Press the Save button
23
24. BUILD AN ALEXA SKILL STEP-BY-STEP
Get the AWS Lambda ARN for the Lambda Function
▸ In the terminal window, get the function definition
aws lambda get-function --function-name twitter-news-skill
{
"Code": {
"RepositoryType": "S3",
"Location": “https://prod-04-2014-tasks.s3.amazonaws.com/snapshots/123456789012/twitter-news-
skill-00000000-0000-0000-0000-000000000000?X-Amz-Security-Token=..."
},
"Configuration": {
"Version": "$LATEST",
"CodeSha256": "JZKRixkiCQ5NQwHt5/tUna61fqDDfnWGKfgsWnyUJKA=",
"FunctionName": "twitter-news-skill",
"MemorySize": 128,
"CodeSize": 6575734,
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:twitter-news-skill",
"Handler": "index.handler",
"Role": "arn:aws:iam::123456789012:role/twitter-news-skill-role",
"Timeout": 3,
"LastModified": "2017-02-27T18:40:44.411+0000",
"Runtime": "nodejs4.3",
"Description": "Grabs the latest or popular tweets about a specific topic"
}
}
24
25. BUILD AN ALEXA SKILL STEP-BY-STEP
Configure the New Skill
▸ Click Configuration to set the skill’s endpoint
▸ We’ll skip the Interaction Model for now until we
develop the code
▸ Select AWS Lambda ARN for the Service
Endpoint Type and select the North America
region
▸ Copy the FunctionARN value from the previous
step into the text box under North America
▸ Keep the default of No in Account Linking
▸ Press Save
25
26. BUILD AN ALEXA SKILL STEP-BY-STEP
Update the Skill’s Application Id in config/applicationid.json
▸ From the previous step, copy the ID in the top-left of the page (under the
application name Twitter News) and paste it into the value for applicationId
in config/applicationid.json
▸ application.json is not stored in Github to keep the Id private
{
"applicationId": "amzn1.ask.skill.00000000-0000-0000-0000-000000000000"
}
26
27. BUILD AN ALEXA SKILL STEP-BY-STEP
Update package.json
▸ Update package.json to set alexa.applicationId to the value Id found in
Configure the New Skill
▸ Update name to “twitter-news” (the name of the application specified in config/
app-config.js)
▸ Update the version, description, author, license, and other URLs as necessary
27
28. BUILD AN ALEXA SKILL STEP-BY-STEP
Remove Sample Code
▸ Remove the sample StoryIntent and supporting dictionary and custom slot type
in index.js:
▸ Delete the block beginning with: Object.assign(app.dictionary, {
▸ Delete the block beginning with: app.customSlotType('STORY_TYPE',
▸ Delete the block beginning with: app.intent('StoryIntent', {
▸ Remove all the tests in test/test_intents.js — these were for the sample code
28
29. BUILD AN ALEXA SKILL STEP-BY-STEP
Create a New Twitter Application
▸ A new Twitter application is needed to gain keys
and secrets for API access
▸ Browse to https://apps.twitter.com/
▸ Press Create New App and fill out details
▸ Name: My-Twitter-News
▸ Note the name may be taken by another
application. Try to prefix with your organization
or initials. This name is not important for the skill
- only the access tokens and keys are necessary.
▸ Description: Grabs the latest or popular tweets
about a specific topic
▸ Website: http://example.com/twitternews
29
30. BUILD AN ALEXA SKILL STEP-BY-STEP
Grant Permissions to Twitter Application
▸ For this skill, we’ll only need read permission
▸ On the Permissions tab, select Read only
and press Update Settings
30
31. BUILD AN ALEXA SKILL STEP-BY-STEP
Twitter Application Keys and Access Tokens
▸ You’ll need both consumer and access token
▸ Click on the Keys and Access Tokens tab
▸ Near the bottom of the page press Create my access token. This will
provide the Access Token and Access Token Secret necessary to
connect to the Twitter API.
▸ Save the values and add to your environment under the following
environment variable names:
▸ TWITTER_CONSUMER_KEY
▸ TWITTER_CONSUMER_SECRET
▸ TWITTER_ACCESS_TOKEN_KEY
▸ TWITTER_ACCESS_TOKEN_SECRET
31
Not my real keys :)
Don’t skip the step! You’ll need to take appropriate actions
depending on your OS. For example, on Linux and OS X:
# Keys for twitter-news-skill
export TWITTER_CONSUMER_KEY='consumer key'
export TWITTER_CONSUMER_SECRET='consumer secret'
export TWITTER_ACCESS_TOKEN_KEY='token key'
export TWITTER_ACCESS_TOKEN_SECRET='token secret'
32. BUILD AN ALEXA SKILL STEP-BY-STEP
Configure Your Environment for Access to the Twitter
‣ Configure environment for keys for access to the Twitter API
‣ For OS X and Linux, add to ~/.bash_profile (or ~/.bashrc)
# Keys for twitter-news-skill
export TWITTER_CONSUMER_KEY=hSYvOtda3Ri74PkyuTOHrLMdf
export TWITTER_CONSUMER_SECRET=LXHvTRShADq0s8lOlHCPodPw1smNeJ5p6E8GyOlY9cM0Ti5QSX
export TWITTER_ACCESS_TOKEN_KEY=WVUXxMzZPrszP2AO3eQIogJNFIA2vqkO5bmRkCiO1zCdr7KSt6
export TWITTER_ACCESS_TOKEN_SECRET=tMOuVnb3nrlwtNgUtr9qCZTdEqj6SpqbJrt7uysGuf5oU
‣ Log out and log in again to set up environment
Not my real keys :)
32
33. BUILD AN ALEXA SKILL STEP-BY-STEP
Set Lambda Environment Variables for Access to Twitter
▸ After configuring a New
Twitter App, two pairs of
Keys/Secrets are available
for use
▸ Copy them and paste into
the Environment variables
section
▸ Enable encryption helpers
for more security
33
Not my real keys :)
34. BUILD AN ALEXA SKILL STEP-BY-STEP
Create Twitter Module to Gain Access to Twitter API
▸ Create lib/twitter.js and add the following code:
var Twitter = require('twitter');
/**
* Connect to the Twitter API. Gain access via environment variables.
* Optionally replace with actual strings (not recommended).
*/
var client = new Twitter({
consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
});
module.exports = client;
34
35. BUILD AN ALEXA SKILL STEP-BY-STEP
Add Tests for Twitter Module
▸ Create test/test_twitter.js and add the following code:
/*global describe, it, beforeEach */
'use strict';
var chai = require('chai'),
chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
////////////// Tests Twitter //////////////
describe('Twitter', function () {
describe('New Client', function () {
var twitter;
beforeEach(function () {
// here for code coverage
twitter = require('../lib/twitter');
});
it('should return an object', function () {
return twitter.should.be.an('object');
});
it('should have a version of at least 1.7.0', function () {
var versionCompare = require('./../vendor/version_compare');
var comparison = versionCompare(twitter.VERSION, '1.7.0');
return comparison.should.be.at.least(0); // same or higher version
});
});
});
35
36. BUILD AN ALEXA SKILL STEP-BY-STEP
Run Twitter Tests
▸ Run tests again, testing recently added Twitter tests
gulp test-mock # (or simply use the alias test, gulp test)
...
Twitter
New Client
✓ should return an object
✓ should have a version of at least 1.7.0
21 passing (32ms)
[10:59:20] Finished 'test-mock' after 338 ms
36
37. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the Intent — WhosTweeted
▸ This intent is designed to return the last N unique twitterers from tweets matching our search criteria
▸ This is a simple intent: it doesn’t take any slot values
▸ It is invoked when the Echo understands one of the following utterances while in the skill:
▸ Who tweeted recently
▸ Who shared recently
▸ Who recently tweeted
▸ Who recently shared
▸ It depends on being able to grab the latest tweets
▸ It depends on a function to take the latest tweets and find the last N unique twitterers (uniqueTwitterers)
▸ It depends on a function to take the last N unique twitterers and format them for output (recentTweetsFrom)
37
38. BUILD AN ALEXA SKILL STEP-BY-STEP
Create Unique Twitterers Function and Associated Tests
▸ Create the following files using the code available in https://github.com/
rickwargo/twitter-news
▸ lib/helpers/unique_twitterers.js
▸ lib/constants.js
▸ test/test_unique_twitterers.js
38
39. BUILD AN ALEXA SKILL STEP-BY-STEP
Run Twitter Tests for Unique Twitterers Function
▸ Run tests again, testing recently added Twitter tests
gulp test
...
Unique Twitterers
Users
of no length
✓ should be an empty list
of one unique user
✓ should be list of length one
✓ should be list of length one if duplicates
of multiple unique users
✓ should be a list of the unique users
✓ should be list of the unique users
26 passing (34ms)
39
40. BUILD AN ALEXA SKILL STEP-BY-STEP
Create Recent Tweets From Function and Associated Tests
▸ Create the following functions using the code available in https://github.com/rickwargo/twitter-news/blob/master/lib/text.js
▸ recentTweetsFrom
▸ noRecentTweets
▸ Add tests for recentTweetsFrom in test/test_text.js
describe('for recent tweets from', function () {
it('returns no one if no recent tweets', function () {
var result = Text.recentTweetsFrom([]);
return result.should.equal(Text.noRecentTweets);
});
it('returns one user if only one user', function () {
var result = Text.recentTweetsFrom(['alpha']);
return result.should.equal('Recent tweets were from: alpha.');
});
it('returns one user if only one user', function () {
var result = Text.recentTweetsFrom(['alpha', 'beta', 'gamma']);
return result.should.equal('Recent tweets were from: alpha, beta, and gamma.');
});
});
▸ Run tests using “gulp test” — there should now be 29 passing tests
40
41. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the WhoTweeted Intent Handler in index.js
▸ Add the following code into index.js
app.intent('WhoTweeted', {
utterances: [
'Who {tweeted|shared} {recently|}',
'Who recently {tweeted|shared}'
]
}, function (ignore, response) {
return Twitter.get('search/tweets', TwitterParams)
.then(function (tweets) {
var users = uniqueTwitterers(tweets.statuses, Constants.MAX_USERS);
var msg = Text.recentTweetsFrom(users);
response.say(msg);
});
});
▸ Add the following requires to index.js
Twitter = require('./lib/twitter'),
Text = require('./lib/text'),
Constants = require('./lib/constants'),
uniqueTwitterers = require('./lib/helpers/unique_twitterers');
41
42. BUILD AN ALEXA SKILL STEP-BY-STEP
alexa-utterances
▸ Utterances are kept with the skill definition in the code
▸ Utterances are condensed using alternation, being able to expand the
utterance with each word in the curly braces using the alexa-utterances node
module
▸ This module is installed by default with the starter template
▸ For more information refer to the project’s README
42
43. BUILD AN ALEXA SKILL STEP-BY-STEP
Add Tests for WhosTweeted in test/test_intents.js
▸ For testing, stub the Twitter.get() function with “sinon” returning a promise as the stub result instead of a call to the Twitter API
▸ To stub Twitter.get(), Add the following code before the tests
var sinon = require('sinon');
var sinonStubPromise = require('sinon-stub-promise');
sinonStubPromise(sinon);
▸ Add the following require’s
Text = require('./lib/text'),
Twitter = require(‘./lib/twitter');
▸ Add the following tests
▸ Include code block from around line 41 starting with describe('#WhoTweeted', function () {
▸ Wrap the preceding code block with:
describe('using a mock client', function () {
var get;
beforeEach(function () {
get = sinon.stub(Twitter, 'get').returnsPromise();
});
afterEach(function () {
get.restore();
});
// #WhoTweeted tests go here
});
▸ Run tests using “gulp test” — there should now be 32 passing tests
43
44. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the Intent — LatestTweets
▸ This intent is designed to return the last N tweets matching our search criteria
▸ This intent takes three slot values, two standard Amazon slot types and one custom slot type
▸ It is invoked when the Echo understands one of the following utterances while in the skill:
▸ What’s the most recent tweet
▸ For the news
▸ What are the last three tweets
▸ The last tweet from Rick
▸ It depends on being able to grab the latest tweets
▸ It depends on a function to take the last N tweets and format them for output (tweet2say)
44
45. BUILD AN ALEXA SKILL STEP-BY-STEP
Create Text Functions and Associated Tests
▸ Create the following functions using the code available in https://github.com/rickwargo/twitter-news/blob/master/lib/text.js
▸ lastTweets
▸ noMatchingTweets
▸ Add tests for lastTweets in test/test_text.js
describe('for LastTweet', function () {
describe('of a single item', function () {
it('is singular in its response', function () {
var result = Text.lastTweets(1);
return result.should.equal('The last 1 tweet was: ');
});
it('is plural in its response', function () {
var result = Text.lastTweets(2);
return result.should.equal('The last 2 tweets were: ');
});
});
});
▸ Run tests using “gulp test” — there should now be 34 passing tests
45
46. BUILD AN ALEXA SKILL STEP-BY-STEP
Create tweet2say Function and Associated Tests
▸ Create the tweet2say function using the code available in https://github.com/
rickwargo/twitter-news/blob/master/lib/helpers/tweet2say.js
▸ Create tests for the tweet2say function using the code available in https://
github.com/rickwargo/twitter-news/blob/master/test/test_tweet2say.js
▸ Run tests using “gulp test” — there should now be 41 passing tests
46
47. BUILD AN ALEXA SKILL STEP-BY-STEP
Prepare the LatestTweets Intent Handler in index.js
▸ Add the require for tweet2say to index.js
tweet2say = require('./lib/helpers/tweet2say'),
▸ Add dictionary near the top of index.js
Object.assign(app.dictionary, {
tweet: ['tweet', 'tweets', 'item', 'items', 'story', 'stories', 'news', 'news item',
'news items', 'news story', 'news stories', 'post', 'posts', 'update', 'updates',
'message', 'messages'],
whats: ['what is', 'what are', 'what's', 'about', 'give me', 'tell me'],
the: ['the', 'a', 'an']
});
▸ Add the custom slot for the intent handler to index.js
app.customSlotType('TWEET_CATEGORIES', ['popular', 'latest', 'recent', 'last']);
47
48. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the LatestTweets Intent Handler in index.js
▸ Add the following code to index.js
var TwitterParams = {
q: '#PhillyCode OR #PhillyCC OR @PhillyDotNet',
result_type: 'recent', // options: 'recent', 'mixed', or 'popular'
since_id: '',
include_entities: false
};
app.intent('LatestTweets', {
slots: {
Count: 'AMAZON.NUMBER',
User: 'AMAZON.US_FIRST_NAME',
TweetCategory: 'TWEET_CATEGORIES'
},
utterances: [
'{whats|} {the} {tweet}',
'{whats|} {-|Count} {tweet}',
'{whats|} {the} {most|} {-|TweetCategory} {tweet}',
'{whats|} {the} {most|} {-|TweetCategory} {-|Count} {tweet}',
'{whats|} {the} {most|} {-|TweetCategory} {tweet} from {-|User}'
]
}, function (request, response) {
48
49. BUILD AN ALEXA SKILL STEP-BY-STEP
Create the LatestTweets Intent Handler in index.js (cont’d)
▸ Add the following code to the end of the intent handler for LatestTweets in index.js
}, function (request, response) {
var max_tweets = Math.min(request.slot('Count') || 1, Constants.MAX_TWEETS);
var user = request.slot('User') || null;
var tweetCategory = request.slot('TweetCategory') || 'recent';
TwitterParams.result_type = tweetCategory.toLowerCase() === 'popular'
? 'popular'
: 'recent';
return Twitter.get('search/tweets', TwitterParams)
.then(function (tweets) { // Filter
var counter = 0;
return tweets.statuses.filter(function (tweet) {
// select if within max tweets and user matches, if specified
var select = (counter < max_tweets) && (!user || tweet.user.name.toLowerCase().indexOf(user.toLowerCase()) > -1);
if (select) {
counter += 1;
}
return select;
});
})
.then(function (tweets) { // Say matching tweets
var msg;
if (tweets.length > 0) {
msg = Text.lastTweets(tweets.length) + tweet2say(tweets);
} else {
msg = Text.noMatchingTweets;
}
response.say(msg);
});
});
49
50. BUILD AN ALEXA SKILL STEP-BY-STEP
Add Tests for LatestTweets in test/test_intents.js
▸ Add the following tests
▸ Include code block from around line 79 starting with describe('#LatestTweets', function () {
▸ Run tests using “gulp test” — there should now be 52 passing tests
▸ Add a test against the Twitter API instead of a mock
describe('against the Twitter API', function () {
describe('#LatestTweets', function () {
describe('response', function () {
it('contains the latest tweets', function () {
var result = request.intentRequest({name: 'LatestTweets'});
return result.should.eventually.match(/<speak>The last 1 tweet was:/);
});
});
});
});
▸ Run tests again using “gulp test” — there should now be 53 passing tests
50
51. BUILD AN ALEXA SKILL STEP-BY-STEP
Start Web Server for Testing Using the Web-based Interface
▸ Start the server with “npm start”
npm start
> alexa-app-root@1.0.0 start /Users/rick/Code/alexa-js/alexa-app-root
> node server.js --start
serving static content from: /Users/rick/Code/alexa-js/alexa-app-root/public_html
loading server-side modules from: /Users/rick/Code/alexa-js/alexa-app-root/server
loaded /Users/rick/Code/alexa-js/test/alexa-app-root/server/.gitkeep
loading apps from: /Users/rick/Code/alexa-js/alexa-js-apps/
loaded app [twitter-news] at endpoint: /alexa/twitter-news
enabling https
listening on https port 8443
listening on http port 8003
51
52. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Using the Web-based Interface
▸ Browse to http://localhost:8003/test/
▸ There should only be one app loaded:
twitter-news
▸ Click on Launch Request and press
submit; the welcome message should
appear. You can update the welcome
message in lib/text.js at
onLaunchPrompt.
▸ Experiment with the various intents to see
if they work as expected
52
53. BUILD AN ALEXA SKILL STEP-BY-STEP
Finalize Alexa Skill Configuration — Intent Schema
▸ Populate Interaction Model
▸ Using the test web page, copy the Schema (click on Schema in top-right and
then clock Copy to Clipboard) and paste into Intent Schema in Alexa Skill
page
53
54. BUILD AN ALEXA SKILL STEP-BY-STEP
Finalize Alexa Skill Configuration — Custom Slot Types
▸ Add a Custom Slot Type by pressing Add Slot Type and enter
TWEET_CATEGORIES for the type and the list of slot types from the test web
page under Values
54
55. BUILD AN ALEXA SKILL STEP-BY-STEP
Finalize Alexa Skill Configuration — Sample Utterances
▸ Add to Sample Utterances the list of utterances generated from the test web
page. Note how only a few lines of utterances in the code expanded to over
2,600 different utterances!
55
56. BUILD AN ALEXA SKILL STEP-BY-STEP
Build the Alexa Skill Interaction Model
▸ When all three areas are populated, press Save to build the interaction model
▸ You’ll see a message while it is building. It may take up to a minute to build the
model.
56
57. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill from Lambda - Create Test Event
▸ Select Configure test event from the Actions menu
57
58. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill from Lambda - Input Test Event
▸ Do not select a Sample event template -
just paste over what is there
▸ Copy the JSON from the Request tab on
the web test page and paste into the
code area
▸ Press Save
58
59. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill from Lambda - Perform the Test
▸ Press Test
▸ The Execution result should
show succeeded
59
60. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill from Alexa Skill Portal - Select Test Pane
▸ Press Test in the left panel to bring up the testing pane
60
61. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill from Alexa Skill Portal - Perform Test
▸ In the Service Simulator, enter what
is the latest news under Enter
Utterance and press Ask Twitter
News
▸ A valid Lambda Response should
appear
61
62. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill using echosim.io
▸ Go to https://echosim.io and sign in
to Echosim.io using your Amazon
account
▸ Don’t worry, it’s safe as it uses
OAUTH
62
63. BUILD AN ALEXA SKILL STEP-BY-STEP
Test Skill using echosim.io by Speaking
▸ Press the record button and say Ask
Twitter News for the latest
▸ You should hear the latest tweet
▸ Try testing other utterances
63
64. BUILD AN ALEXA SKILL STEP-BY-STEP
Test on Your Own Device
▸ If your Echo is linked to the same account as your Amazon developer account,
the skill is automatically enabled on your Echo and any other devices linked to
the same account
▸ Repeat the same questions to your Amazon Echo
▸ The skill is now ready for your enjoyment and tweaking!
64