ask-gib v0.12.6
ask-gib
This lib allows you to create Alexa Skills using TypeScript and an (IMO) easier approach to request/response architecture.
Use in combination with...
- ask-gib-api
- Broad coverage of Alexa AWS JSON reference, including type definitions and string literal constants.
- lex-gib
- i18n and synonym-like dynamic speech composition for more expressive Alexa speech.
- ssml-gib
- Factory functions for SSML tags supported by Alexa
- SpeechCons
- English (US)
- English (UK)
- German (DE)
- Use auto-completion for any and every SpeechCon currently supported!
- helper-gib
- Logging, other utils.
- Useful with Cloudwatch logs.
Installation
Install with npm:
npm install --save ask-gib
Import ES6 style:
import * as ask from 'ask-gib';
let h = new ask.Helper(); // If you want to use the helper
Usage Overview
- Implement
FuncyAlexaSkill
class. - Flesh out functions to handle requests.
- Hook up the skill in the handler in index.ts.
Usage
Inherit from
FuncyAlexaSkill
. In ctor, map each intent to an array of transforms that will be executed in order. Be sure to initialize the launch request handler as well.export interface FooStore extends ask.BaseSkillStore { foo: string; } export class FooSkill extends ask.FuncyAlexaSkill<FooStore> { constructor(appId: string, dynamoDbTableName: string) { super(appId, dynamoDbTableName, lexData); // see lex-gib for lexData info // Creates first-non-null-wins request handling pipelines // I personally name these `transform___` but not necessary. t.transformsByName = { "FooIntent": [t.transformFooBar, t.transformFooBaz, t.transformHelpDefault], "AMAZON.HelpIntent": [t.transformHelpBar, t.transformHelpDefault], "AMAZON.FallbackIntent": [t.transformHelpDefault], "AMAZON.RepeatIntent": [t.transformRepeat], "AMAZON.CancelIntent": [t.transformGoodbye], "AMAZON.StopIntent": [t.transformGoodbye] } // Launch is special. Define pipeline for it here. t.transformsByName[t.getLaunchRequestName()] = [t.transformWelcome]; } ... }
Inside each transform, check the state, most often in the
_store
property. Check to see if you want your transform to handle the incoming stimulus. If yes, return the nextSkillState
. If no (i.e. the transform doesn't apply), returnnull
.transformFooBar: ask.SkillTransform = ( stimulus: ask.Stimulus, history: ask.SkillState[] ): ask.SkillState => { // Can also use Promise<ask.SkillState> // Check to see if this transform applies, if not return null. if (!this._store || this._store.foo !== "bar") { return null; } // Create your speech, mixing plain text, ssml, with ssml helpers. // lex provides powerful i18n, 'hi' -> hello, howdy, Guten Tag, etc. // But here is also shown hard-coded text. let hi = this.lex._('hi'); let output = ask.SpeechBuilder.with() .ssml(hi.ssml) .text("Build some speech with plain text...") .ssml(`<p>${Ssml.emphasis("Emphasize", "strong")} with some ssml.</p>`) .text("Would you like to foo?") .outputSpeech(); let reprompt = ask.SpeechBuilder.with() .text("Maybe you didn't hear me...") .existing(output) // reuses existing speech output .outputSpeech(); // Create interaction object, which represents the entire request // & response. The JSON response object sent back to Alexa will // be built automatically from this. let interaction: ask.Interaction = { stimulus: stimulus, type: "ask", output: output, reprompt: reprompt, // If you include `cardTitle/Content`, it will automatically // incorporate this into your response with a simple card, // e.g. `TellWithCard`, `AskWithCard`. cardTitle: "My Title", cardContent: output.text, } // The final bit of plumbing let nextSkillState: ask.SkillState = { id: h.generateUUID(), interaction: interaction, location: "a1" } // Optionally store some global state for this user. // Anything that is stored in the _store object is persisted across // sessions, and you can extend `BaseStore` for type-safety. this._store.foo = "baz"; return nextSkillState; }
Put this in your handler in your
index.ts
file.// Create the handler that responds to the Alexa Request. export var handler = async function ( event: ask.RequestBody, context: ask.Context, callback: (error: any, response: ask.ResponseBody) => void ) { try { // Create an instance of the skill. var skill = new FooSkill(APP_ID, DB_NAME); // I await execute, which is not necessary WRT the callback. // Async execute is convenient to write, and the callback // actually is called just before the await will return. await skill.execute(event, context, callback); h.logFuncComplete(lc); } catch (errExec) { h.logError(`errExec`, errExec, lc); } }
Highlights
FuncyAlexaSkill
- Easier workflow for handling incoming requests without function decorators.
- Plumbing is handled for you.
SpeechBuilder
- Helper class that builds up
OutputSpeech
objects with a fluent manner. - See ssml-gib for details.
- Helper class that builds up
DynamoDBHelper
- Simplistic, promise-based helper class that saves and retrieves a user's
DynamoRecord
based on user id and db table name. - Assumes that you have separately created the table in DynamoDB with the given table name.
- Simplistic, promise-based helper class that saves and retrieves a user's
DevOps
I also wanted to mention that you can create a nice workflow for DevOps.
With a single keystroke chord ctrl+shift+B
, vscode executes the default build task, which is npm run build
.
This...
- Compiles the TypeScript with
tsc
. - Builds and copies my TS output to the
bin
folder. - Copies over the node_modules folder fresh.
- Deletes old zip and zips up bin into new zip. (Note: You need to create the first bin.zip file manually. Need to correct the npm bash task.)
- Uses aws cli to push the
bin.zip
to the Lambda function (which uses aliases for production so I can't harm any live skills).
I also update my models and check status via npm scripts as well,
which can get/update models and more. ctrl+~
brings up the vscode bash shell which
makes this a breeze.
I highly recommend using the npm scripts + aws and ask CLIs. (Needs updated to task version 2.0.0)
Here is the relevant code:
.vscode/tasks.json:
{
"version": "0.1.0",
"tasks": [
{
"taskName": "tsc",
"isBuildCommand": true,
"isShellCommand": true,
"command": "npm",
"args": [ "run", "build" ],
"showOutput": "silent",
"problemMatcher": "$tsc"
},
{
"taskName": "mochatest",
"isShellCommand": true,
"command": "npm",
"args": [ "run", "test" ],
"showOutput": "always"
}
]
}
package.json:
{
...
"scripts": {
"build": "npm run build:ts",
"build:ts": "tsc -p .",
"postbuild": "npm run do-node-modules && npm run zip:bin && npm run aws-push",
"zip:bin": "cd bin && pwd && rm bin.zip && zip -r bin.zip * && cd .. && pwd",
"do-node-modules": "rm -rf bin/node_modules && cp -r node_modules bin/node_modules",
"aws-push": "aws lambda update-function-code --function-name skillGibA --zip-file fileb://bin/bin.zip --profile alexa",
"aws-push-eu": "aws lambda update-function-code --function-name skillGibA_EU --zip-file fileb://bin/bin.zip --profile alexa-eu",
"ask-get-models": "ask api get-model -s $SKILL_SID -l en-US > models_tmp/en-US.json --profile alexa && ask api get-model -s $SKILL_SID -l en-IN > models_tmp/en-IN.json --profile alexa && ask api get-model -s $SKILL_SID -l en-CA > models_tmp/en-CA.json --profile alexa && ask api get-model -s $SKILL_SID -l en-GB > models_tmp/en-GB.json --profile alexa && ask api get-model -s $SKILL_SID -l en-AU > models_tmp/en-AU.json --profile alexa",
"ask-update-models": "ask api update-model -s $SKILL_SID -l en-US --file models/en-US.json --profile alexa && ask api update-model -s $SKILL_SID -l en-IN --file models/en-IN.json --profile alexa && ask api update-model -s $SKILL_SID -l en-CA --file models/en-CA.json --profile alexa && ask api update-model -s $SKILL_SID -l en-GB --file models/en-GB.json --profile alexa && ask api update-model -s $SKILL_SID -l en-AU --file models/en-AU.json --profile alexa",
"ask-get-model-statuses": "ask api get-model-status -s $SKILL_SID -l en-US --profile alexa && ask api get-model-status -s $SKILL_SID -l en-IN --profile alexa && ask api get-model-status -s $SKILL_SID -l en-CA --profile alexa && ask api get-model-status -s $SKILL_SID -l en-GB --profile alexa && ask api get-model-status -s $SKILL_SID -l en-AU --profile alexa"
},
...
}
Thanks
- Amazon for creating such good documentation and a good product.
- The official Alexa Skills Kit SDK for Node.js
- This is where I got the idea for the simple DynamoDB table persistence.
- Up.
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago