This package has been deprecated

Author message:

Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

ask-gib
TypeScript icon, indicating that this package has built-in type declarations

0.12.6 • Public • Published

ask-gib

npm

This lib allows you to create Alexa Skills using TypeScript and an (IMO) easier approach to request/response architecture.

Use in combination with...

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

  1. Implement FuncyAlexaSkill class.
  2. Flesh out functions to handle requests.
  3. Hook up the skill in the handler in index.ts.

Usage

  1. 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];
        }
        ...
    }
  2. 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 next SkillState. If no (i.e. the transform doesn't apply), return null.

    transformFooBarask.SkillTransform = (
        stimulusask.Stimulus, 
        historyask.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;
    }
  3. 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.
  • 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.

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...

  1. Compiles the TypeScript with tsc.
  2. Builds and copies my TS output to the bin folder.
  3. Copies over the node_modules folder fresh.
  4. 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.)
  5. 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.

Package Sidebar

Install

npm i ask-gib

Weekly Downloads

8

Version

0.12.6

License

MIT

Unpacked Size

119 kB

Total Files

18

Last publish

Collaborators

  • wraiford