How To Make An Alexa Skill
Create an Alexa Skill
Personal assistants are the new trend. I though it was time to build something for Alexa.
During the Amazon Prime Days, I bought an Amazon Dot with a 50% discount!
I thought information technology was fourth dimension for me to create my first Skill (i.e. vocal app) for Alexa.
Notation: In this article, I will non share all my source code, for security reasons, equally I'm using some personal API/servers
My goal was to create an song version of my Android app, ParisGo, which helps parisiens to get the real time schedules of subways, buses, tramways, trains... in a very simple app.
This app is in French, just I will explicate it in English :)
To create your skill, create a programmer account on the Alexa console website.
The Alexa documentation is quite well written.
Build the Skill
An Alexa Skill similar an app, but for Alexa.
When you create a Skill, you volition associate some Intents to it.
Each Intent will react to user Utterances.
To each utterance, y'all can add Slots to get the user parameters to the request.
Each Slot will match to a Slot Type, which can exist a custom list, or an Amazon predefined type (date, number, pone, state, movie...) (encounter hither).
Example:
Utterances for SubwayNextStop Intent (translated in English language) :
When is the next metro station {{SubwayStop}}
What time is the next metro station {{SubwayStop}}
What are the next metro stations {{SubwayStop}}
{{SubwayStop}} volition be my Slot, and is linked to the SubwayStop slot type (I used the same name for simplicity reasons).
And my slot type contains all subway stops, with an unique key and a value.
I created some CSV files to hands import those stops.
For each slot, you can also specify if it's a mandatory slot (and how to ask the user), or if the value must be confirmed by the user (eastward.yard. for an order).
After that, y'all will take to specify an Endpoint, which will receive every requests and reply to the user.
Yous have 2 choices:
- Create a custom HTTPS endpoint (POST) with your own server / resources, which will receive and respond in JSON
- Use an AWS lambda, which is a picayune function hosted by Amazon in NodeJs or Python. Amazon provides a library to aid yous parse the query / respond to the user.
For simplicity, I chose to use the AWS lambda service (in NodeJs), to format a request to ane of my API and respond in French.
Build the Endpoint
Annotation: This is not an instance of cute code, it's simply to help yous create a simple lambda function
Alert: AWS accept a lot of datacenter in the whole world, merely some of them are not yet compatible with the Alexa service. Be conscientious to expect in the Alexa documentation and detect which regions are compatible with Alexa Skills (I chose Irland in Europe).
To build an AWS Lambda, create an AWS account and become to the Lambda service page.
I chose to base my lambda on the "alexa-skills-kit-nodejs-factskill" (GitHub) instance. To create your lambda based on the sample, please follow the wiki instructions.
The wiki is well written, and explains all the steps to make it work.
Withal, the following sections can help y'all...
Check the "Blueprint" configuration section
I had to add the "Alexa Skill Kit" as a lambda input. I advise you to add the skill ID authorized to call your lambda.
Update/Add together dependencies
The default NodeJs dependencies are quite old. Update them !
More over, to add dependencies, you lot cannot just update the package.json
file, yous have to update the node_js
folder with the dependencies.
Sadly, y'all cannot do that with the embedded editor, you will have to download your project every bit a ZIP file, edit your code and upload it again as a ZIP.
Once that'due south done, yous will non be able to use the embedded editor anymore.
You can also use the AWS CLI, but I didn't bother for this small project.
Monitor your Lambda
When you test your Skill, you can follow the calls and see the logs into the "Monitoring" tab (button "Meet logs in CloudWatch"). It's helping a lot to sympathise the behavior of your skill.
Use Dialog Directives if applicative
In some contexts, you NEED some informations from the user, or some explicit confirmations.
To practise that, you take to activate some 'Dialog' in the intent / skills / slots with the Alexa console. But your lambda must also be able to manage them.
You can read the Alexa documentation to empathise each type of Dialog Directive.
In the following instance, I built ii intent handler to manage the steps of a Consul Directive.
Example of source code
package.json
{ "name": "alexa-sample", "version": "ane.0.0", "description": "Adjacent schedules with Alexa", "main": "index.js", "scripts": { "examination": "echo \"Mistake: no test specified\" && exit i" }, "keywords": [ "alexa", "skill", "fact" ], "writer": "IoT-Experiments.com", "dependencies": { "enquire-sdk": "^2.0.0", "lodash": "^4.17.10", "asking": "^2.87.0" } }
index.js
/* eslint-disable func-names */ /* eslint-disable no-console */ const Alexa = crave('ask-sdk'); const request = require('asking'); const _ = crave('lodash'); // TODO: alter the base URL const BASE_URL = 'https://api.domain.com/v1/'; const intentTypes = [ 'SubwayNextStop', 'BusNextStop', 'NoctilienNextStop', 'TramwayNextStop' ]; const LaunchRequestHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.request; return req.type === 'LaunchRequest'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak("C'est parti pour une recherche d'horaire. De quelle station souhaitez-vous avoir les prochains passages ?") .reprompt("Dites-moi de quelle station souhaitez-vous avoir les prochains passages ?") .getResponse(); } }; const InProgressNextStopHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.request; return req.type === 'IntentRequest' && intentTypes.indexOf(req.intent.name) >= 0 && req.dialogState !== 'COMPLETED'; }, handle(handlerInput) { const req = handlerInput.requestEnvelope.request; return handlerInput.responseBuilder .addDelegateDirective(req.intent) .getResponse(); } }; const CompletedNextStopHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.asking; return req.type === 'LaunchRequest' || (req.blazon === 'IntentRequest' && intentTypes.indexOf(req.intent.proper name) >= 0); }, handle(handlerInput) { const req = handlerInput.requestEnvelope.request; // All slots values const filledSlots = req.intent.slots; // Parse slots values to create an array const slotValues = getSlotValues(filledSlots); // Get slot value (key and text, as in slot type definition) based on the intent name const stopValue = slotValues[req.intent.name.replace('Next', '')]; // Get the ID used by the webservice const originStopKey = stopValue.resolvedId; return new Promise((resolve, decline) => { request({ method: 'GET', baseUrl: BASE_URL, uri: `${originStopKey}/schedules`, json: true }, function (err, response, body) { if(err) { refuse(err); } resolve(body); }) }) .and then(body => { // TODO : utilize the trunk to generate the 'speechOutput' let speechOutput = 'TODO'; return handlerInput.responseBuilder .speak(speechOutput) .withSimpleCard('Schedules', speechOutput) .getResponse(); }); }, }; const HelpHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.request; return req.type === 'IntentRequest' && req.intent.name === 'AMAZON.HelpIntent'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak("Ask me to look for the subways or buses schedules") .reprompt('How can I aid yous?') .getResponse(); }, }; const ExitHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.asking; return req.blazon === 'IntentRequest' && (req.intent.proper noun === 'AMAZON.CancelIntent' || req.intent.name === 'AMAZON.StopIntent'); }, handle(handlerInput) { return handlerInput.responseBuilder .speak('A la prochaine !') .getResponse(); }, }; const SessionEndedRequestHandler = { canHandle(handlerInput) { const req = handlerInput.requestEnvelope.request; return req.type === 'SessionEndedRequest'; }, handle(handlerInput) { panel.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`); return handlerInput.responseBuilder.getResponse(); }, }; const ErrorHandler = { canHandle() { render truthful; }, handle(handlerInput, error) { console.log(`Fault handled: ${mistake.message}`); return handlerInput.responseBuilder .speak('Désolé, une erreur est survenue.') .reprompt('Désolé, une erreur est survenue.') .getResponse(); }, }; // From 'https://developer.amazon.com/fr/blogs/alexa/post/44dd83f4-4842-40c5-91f8-3868b9f4608c/using-dialog-management-to-capture-a-and-b-or-c-slots' function getSlotValues(filledSlots) { const slotValues = {}; console.log(`The filled slots: ${JSON.stringify(filledSlots)}`); Object.keys(filledSlots).forEach((item) => { const name = filledSlots[item].name; if (filledSlots[item] && filledSlots[detail].resolutions && filledSlots[item].resolutions.resolutionsPerAuthority[0] && filledSlots[particular].resolutions.resolutionsPerAuthority[0].status && filledSlots[detail].resolutions.resolutionsPerAuthority[0].status.code) { switch (filledSlots[item].resolutions.resolutionsPerAuthority[0].status.code) { case 'ER_SUCCESS_MATCH': slotValues[name] = { synonym: filledSlots[particular].value, resolved: filledSlots[particular].resolutions.resolutionsPerAuthority[0].values[0].value.name, resolvedId: filledSlots[item].resolutions.resolutionsPerAuthority[0].values[0].value.id, isValidated: truthful, }; break; case 'ER_SUCCESS_NO_MATCH': slotValues[name] = { synonym: filledSlots[item].value, resolved: filledSlots[item].value, isValidated: false, }; interruption; default: break; } } else { slotValues[name] = { synonym: filledSlots[item].value, resolved: filledSlots[item].value, isValidated: false, }; } }, this); return slotValues; } const skillBuilder = Alexa.SkillBuilders.custom(); // Each registered Handler will be tested in the society with canHandle() // if canHandle() returns 'true', the 'handle()' method of the respective handler // will be used to respond to the user request exports.handler = skillBuilder .addRequestHandlers( LaunchRequestHandler, InProgressNextStopHandler, CompletedNextStopHandler, HelpHandler, ExitHandler, SessionEndedRequestHandler ) .addErrorHandlers(ErrorHandler) .lambda();
Test your skill
In one case the endpoint hosted, and selected into Skill (Alexa console), you can build your Skill with the "Build" push.
Once built, you tin test information technology under the "Test" tab, by activating the "Test is enabled for this skill" toggle.
Deploy it!
To deploy information technology, you just have to fill all the forms in the 'Distribution', and inquire for a certification!
Information technology takes few days to Amazon to reject or approve your Skill.
Source: https://www.iot-experiments.com/create-an-alexa-skill/
Posted by: buttswillart.blogspot.com
0 Response to "How To Make An Alexa Skill"
Post a Comment