Billionaire Mark Cuban has been telling everyone they need to learn how to make Alexa Skills and so here I cover Amazon Alexa Development in Massive Detail!!!
I’ll cover how to Make a Skill that Pulls Stock Prices from an External Web Service, another that Multiplies / Saves User Data, an Alexa Quiz App, Create Skills with Displays, Work with JSON, Cloudwatch and so much more.
If you like my videos on YouTube, get the best course I have ever made on Udemy for just $10.
►► Get my Python Programming Bootcamp Series for $9.99 ( Expires June 2nd ) : https://bit.ly/SavePython11
►► Highest Rated Python Udemy Course + 25 Hrs + 108 Videos + New Videos Every Week
Step-by-Step Directions & Code
Amazon Alexa Skill Development
Interaction with an Alexa Skill
User says Alexa (Wake Word) which tells Alexa to listen
User says Alexa open skill name and Alexa opens that skill (Launch Words : tell, ask, open, launch, and use)
User says Alexa ask Zoo Bee to multiply 3 times 5 (Zoo Bee is the Invocation Name) (Utterance : Users request) (Intent : Multiply is an action that will be handled)
Alexa takes the information (Slots) 3 & 5 and passes them to be handled by a function called multiply in the Zoo Bee Skill using JSON (JavaScript Object Notation)
Zoo Bee has a lambda function that contains functions (Intent Handlers) that except data (slots) and return JSON to be read by Alexa
Skills can also pass text or images to be displayed on Alexa devices with screens
Steps to Build a Skill
Write a script that details the phrases users may use when interacting with your skill
Define the phrases you expect to receive from the user (Utterances)
Define the data that you expect from the user and its data types (Slots)
Define the commands you want to handle (Intents) Multiply is an example of an Intent
You define your Utterances, Slots and Intents in the Alexa Developer Console
You define how to handle the above and produce text to be read in your Lambda function
Installation
Go to developer.amazon.com and click on Amazon Alexa
Login using your Amazon password
Fill out required registration info
Click Amazon Alexa -> Create Alexa Skills -> Console -> Create Skill [We'll be Back]
Create an AWS Free account (Amazon Web Service)
Click All services -> Amazon S3 -> You can store files and other data in Buckets
It is always a good idea to create a backup of your data
To upload files click on the Bucket named derekbanasdata -> Upload
Click All services -> IAM
IAM Identity and Access Management
Controls access to AWS resources, so you can define who and how resources can be used. To access IAM go to Services -> IAM
Click on Roles on the left side of the screen. Click Create Role. Select AWS service -> Click Lambda - > Click button Next: Permissions
Now you have to grant policies. We'll add AmazonS3FullAccess and AWSLambdaFullAccess. Click button Next: Tags
I'm not going to add a tag -> Click Next: Review
Give the role a name S3AndLambdaRole -> Click Create role
Build Zoo Bee
Go to https://developer.amazon.com/alexa/console/ask
Click Create Skill
Name it zoo bee, select Custom Skill, select Alexa-Hosted (Node.js)
Click Create skill
Select Hello World Skill
Click Code tab
const Alexa = require('ask-sdk-core');
// Handler called when the app launches
const LaunchRequestHandler = {
// Define requests the handler responds to
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
// Creates the response sent back to the user
handle(handlerInput) {
// Message spoken
const speakOutput = "Hi I am Zoo Bee. What multiplication problem should I solve?";
// This text is used to guide the user to answer in a certain way
const repromptText = "If you asked what is 3 times 5 I will respond with 15."
// Define card title for screens
const cardTitle = "I solve multiplication problems";
// Define text to show on screen
const cardContent = "If you asked what is 3 times 5 I will respond with 15.";
// Alexa speaks text in speakOutput
// If you have reprompt then you wait for a response from the user for 5 seconds
// You output to screens using withSimpleCard
// getResponse converts the responseBuilders work into the response the skill returns
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(cardTitle, cardContent)
.reprompt(repromptText)
.getResponse();
}
};
7. Click Save & Deploy
8. Click the Build tab
9. Click + Add next to Intents
10. Create custom intent named MultiplyIntent
11. Add custom Utterances
12. Create slots numone and numtwo of type AMAZON.NUMBER
13. You don't need to require confirmation. That is used for things like making purchases, sending emails, etc.
14. Delete HelloWorldIntent
15. Save & Build Model
16. Make the following changes to HelloWorldIntentHandler
const MultiplyIntentHandler = {
canHandle(handlerInput) {
// Define this function is called when a MultiplyIntent request comes through
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'MultiplyIntent';
},
handle(handlerInput) {
// Get the 2 numbers spoke by the user and convert to floats
const numone = parseFloat(handlerInput.requestEnvelope.request.intent.slots.numone.value);
const numtwo = parseFloat(handlerInput.requestEnvelope.request.intent.slots.numtwo.value);
const answer = numone * numtwo;
const speakOutput = `${numone} times ${numtwo} is equal to ${answer}, Bye!`;
const cardTitle = "The answer is";
const cardContent = `${numone} times ${numtwo} is equal to ${answer}`;
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(cardTitle, cardContent)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'You can ask me to multiply 2 numbers. Want to give it a try?';
const cardTitle = 'Ask me to multiply';
const cardContent = 'You can ask me to multiply 2 numbers. Want to give it a try?';
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(cardTitle, cardContent)
.reprompt(speakOutput)
.getResponse();
}
};
17. Replace HelloWorldIntentHandler with MultiplyIntentHandler in addRequestHandlers
18. Test your skill -> Save & Deploy -> Test Tab -> Switch dropdown to Development
Add the Ability to Save Data
Click Code Tab -> Open package.json
Change name to zoo-bee and add a dependency for the persistence adapter and Save
{
"name": "zoo-bee",
"version": "1.1.0",
"description": "alexa utility for quickly building skills",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Amazon Alexa",
"license": "ISC",
"dependencies": {
"ask-sdk-core": "^2.6.0",
"ask-sdk-model": "^1.18.0",
"aws-sdk": "^2.326.0",
"ask-sdk-s3-persistence-adapter": "^2.0.0"
}
}
3. Add following to index.js
const Alexa = require('ask-sdk-core');
// Allows you to save and read user data
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
4. Add persistence adapter to exports.handler
exports.handler = Alexa.SkillBuilders.custom()
.withPersistenceAdapter(
new persistenceAdapter.S3PersistenceAdapter({bucketName:process.env.S3_PERSISTENCE_BUCKET})
)
5. Update MultiplyIntentHandler and Save
const MultiplyIntentHandler = {
canHandle(handlerInput) {
// Define this function is called when a MultiplyIntent request comes through
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'MultiplyIntent';
},
// We need to save user data without slowing down the execution, so I'll add
// the ability to execute code asynchronously by putting async infront of
// this function
async handle(handlerInput) {
// The attributes manager is needed to save data
const attributesManager = handlerInput.attributesManager;
// Get the 2 numbers spoke by the user and convert to floats
const numone = parseFloat(handlerInput.requestEnvelope.request.intent.slots.numone.value);
const numtwo = parseFloat(handlerInput.requestEnvelope.request.intent.slots.numtwo.value);
const answer = numone * numtwo;
// Tell Amazon S3 to save the above values
const calcAttributes = {
"numone": numone,
"numtwo": numtwo,
"answer": answer
};
// Cause the function to pause until data is saved
attributesManager.setPersistentAttributes(calcAttributes);
await attributesManager.savePersistentAttributes();
const speakOutput = `${numone} times ${numtwo} is equal to ${answer}, Bye!`;
const cardTitle = "The answer is";
const cardContent = `${numone} times ${numtwo} is equal to ${answer}`;
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(cardTitle, cardContent)
.getResponse();
}
};
6. Create a new Intent Handler that retrieves saved calculations
a. Click Build Tab
b. Click + Add to create a new Intent
c. Name it GetCalcIntent
d. Add Utterances : Get previous calculation, Get last calculation, What was my last calculation, Last calculation, Previous calculation
e. Save and Build
7. Add GetCalcIntentHandler
a. Click Code Tab
const GetCalcIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GetCalcIntent';
},
async handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
const sessionAttributes = await attributesManager.getPersistentAttributes() || {};
const numone = sessionAttributes.hasOwnProperty('numone') ? sessionAttributes.numone : 0;
const numtwo = sessionAttributes.hasOwnProperty('numtwo') ? sessionAttributes.numtwo : 0;
const answer = sessionAttributes.hasOwnProperty('answer') ? sessionAttributes.answer : 0;
let speakOutput = `Sorry I couldn't retrieve your last calculation`;
let cardTitle = "No Calculation Found";
let cardContent = `Sorry I couldn't retrieve your last calculation`;
if (numone && numtwo && answer){
speakOutput = `${numone} times ${numtwo} is equal to ${answer}, was your last calculation`;
cardTitle = "Previous Calculation";
cardContent = `${numone} times ${numtwo} is equal to ${answer}`;
}
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(cardTitle, cardContent)
.getResponse();
}
};
8. Add GetCalcIntentHandler to addRequestHandlers, Save & Deploy
9. HelpIntentHandler defines what is said if the user asks for help
10. CancelAndStopIntentHandler defines what is said if the user asks to stop the skill
11. SessionEndedRequestHandler is used to save data and perform other clean up operations after a skill ends
12. If you try to trigger an intent, but forgot to add it to addRequestHandlers then IntentReflectorHandler will say You just triggered "The Name of the Intent"
13. ErrorHandler is the default error handler for your skill if something goes wrong
Alexa Quiz Skill
Go to developer.amazon.com
Click Amazon Alexa
Create Alexa Skills
Click Console
Click Create Skill
Name it quiz bee -> Custom Skill -> Alexa Hosted Node.js
Click Create skill
Quiz Game Skill
Click Choose button
Click Code tab
Do a Find on Alabama -> Here is where the quiz information is. You can customize this to make your own type of quiz
Test Tab -> Select Development
Click microphone and say "Open Quiz Bee"
Alexa Stock App
Go to https://www.alphavantage.co/support/#api-key
Get your API key
https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=IBM&apikey= UZXXNQA2AROXA3SB
Go to developer.amazon.com/alexa
Give name stock bee, Custom, Node.js, Create Skill
Hello World -> Choose
Click Build Tab
Click Add next to Slot Types -> Create custom slot type -> Name tickers -> Enter MSFT, AMZN, AAPL, GOOGL
+ Add Intent -> GetStockIntent -> Create custom intent
Add utterances with the slot ticker of type tickers -> Save & Build
Click Code tab
Stock Prices
Go to developer.amazon.com
Create Skill
stock prices -> Custom -> Provision your own -> Create Skill
Start from Scratch -> Choose
Click Build Tab
Click Add next to Slot Types -> Create custom slot type -> Name tickers -> Enter MSFT, IBM, AMZN, AAPL, GOOGL
+ Add Intent -> GetStockIntent -> Create custom intent
Add Utterance -> Get the price for {ticker} -> Create Slot ticker and give Slot Type of tickers
Add Utterance Get {ticker} price
Save Model + Build Model
Go to aws.amazon.com
Create Function
Browse Serverless app Repository -> alexa-skills-kit-nodejs-factskill
Application name stocks-two -> IdentityNameParameter index.handler -> Deploy
Test App Button
Click hello-world at the bottom of the screen
Set Trigger to Alexa Skills Kit
Go to Build Tab Endpoint in Alexa Developers Console and copy and paste your Skill ID
There is no destination -> Save
Create new file functions.js and update index.js
functions.js
// Allows you to establish an HTTPS connection
const https = require("https");
const myfunctions = {
// Make an HTTP request and retrieve data using options
// which include the host and path
getData: function(options){
console.log("Entered getData");
return new Promise(function(resolve, reject){
var request = https.request(options, function(response) {
// Reject if status code is bad
console.log(`Status Code ${response.statusCode}`);
if (response.statusCode < 200 || response.statusCode >= 300){
return reject(new Error("Status Code" + response.statusCode));
}
// Get data sent by Alpha Vantage
var body = [];
response.on("data", function(chunk) {
body.push(chunk);
});
// Processing ends
response.on("end", function(){
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch (error) {
reject(error);
}
resolve(body);
});
});
// Check for other errors
request.on("error", function(error) {
reject(error);
});
// End the request
request.end();
});
}
};
// Export myfunctions object so it can be used in index.js
module.exports = myfunctions;
index.js
const Alexa = require('ask-sdk-core');
// Provide access to the myfunctions object which retrieves data
const actions = require('./functions');
// Will hole the stock ticker symbol
var stock_ticker = "MSFT";
// API Key for Alpha Vantage
var av_api_key = "UZXXNQA2AROXA3SB";
// Will store the retrieved stock price
var stock_price = 0.00;
// Parts of the Alpha Vantage URL
// "/query?function=TIME_SERIES_DAILY&symbol=" + stock_ticker + "&apikey=" + av_api_key
var av_api_host = "www.alphavantage.co";
var av_api_path_1 = "/query?function=TIME_SERIES_DAILY&symbol=";
var av_api_path_2 = "&apikey=" + av_api_key;
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
let speakOutput = `I'm stock bee, say a ticker for todays high price?`;
let repromptOutput = `You can say something like get the price for M. S. F. T. `;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(repromptOutput)
.getResponse();
}
};
const GetStockIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GetStockIntent';
},
// We'll be calling the API so we must mark this as asynchronous
async handle(handlerInput) {
console.log("Called GetStockIntentHandler");
// Get the ticker the user requested
let stock_ticker = handlerInput.requestEnvelope.request.intent.slots.ticker.value;
// Complete the path now that we have the ticker
let final_api_path = av_api_path_1 + stock_ticker + av_api_path_2;
// Set HTTPS options to pass to getData()
let options = {
host: av_api_host,
path: final_api_path,
method: "GET"
};
// Log URL passed
console.log(av_api_host + final_api_path);
let speakOutput = "Something went wrong";
let cardTitle = "Stock Price";
let cardContent = "Get the days high stock price."
// Because of the use of Promise in getData we can use await
try {
// Get JSON data requested
let jsondata = await actions.getData(options);
console.log(jsondata);
// Check if useable data was received
let status = jsondata.status;
console.log(`Status : ${status}`);
// if(status === "OK"){
// Get the Daily High Stock Price
// Get the 2nd JSON object in the JSON data
var data_key_obj = jsondata[Object.keys(jsondata)[1]];
// Get the first key in the object which is the current date
let date_key = Object.keys(data_key_obj)[0];
// Use the daily price data + the current date + high price & convert to float
let stock_price = parseFloat(jsondata["Time Series (Daily)"][date_key]["2. high"]).toFixed(2);
console.log(`Stock Price : ${stock_price}`);
// Create the final speakOutput on success
speakOutput = `The current high price for ${stock_ticker} is ${stock_price}`;
// }
} catch (error){
cardTitle = "API Error";
cardContent = av_api_host + final_api_path;
return handlerInput.responseBuilder
.speak(`There was an API error.`)
.withSimpleCard(cardTitle, cardContent)
.getResponse();
}
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speakOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
if(null!== handlerInput.requestEnvelope.request.error) {
console.log(JSON.stringify(handlerInput.requestEnvelope.request.error));
}
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
},
handle(handlerInput) {
const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
const speakOutput = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.stack}`);
const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
// The SkillBuilder acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
GetStockIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addErrorHandlers(
ErrorHandler,
)
.lambda();
Drop down next to text -> Configure Events
Create new test event -> Event Template : Alexa Intent Get New Fact -> Event Name : GetPrice
In Request ->
"request": {
"type": "IntentRequest",
"requestId": "amzn1.echo-api.request.1234",
"timestamp": "2016-10-27T21:06:28Z",
"locale": "en-US",
"intent": {
"name": "GetStockIntent",
"slots": {
"ticker": {
"name": "ticker",
"value": "IBM"
}
}
}
}
24. Create Button
25. Click Monitoring tab to see log files