2.0.1 • Published 6 years ago

story-engine v2.0.1

Weekly downloads
3
License
MIT
Repository
github
Last release
6 years ago

Build Status NPM version Known Vulnerabilities Coverage Status

story-engine

An engine to support user decision driven stories using Alexa skills

This code was initially part of a personal Alexa Skill. I believe there's a good opportunity to make it a component, but it requires some polishing. I'll keep it under version 0.x until I consider it ready for shipping.

Example

You can find an example alexa skill running on story-engine on story-engine-skill-example. This project is under ask-cli format already, making it easy to be deployed and tested on real echo devices.

Title: Lost in a romm

# home

You woke up in an empty room. There's only a locked door and an open window ahead of you. Would you like to knock the door or jump out of the window?

:Knock -> door
:Check the door -> door
:Jump out -> window
:Escape through the window -> window

# door

You knock on the door several times, but you can hear nothing but silence. You move then to the window.

--> window

# window

You get close to the window and you can feel a cold gust. It is very dark but today you are feeling specially brave. For some reason, you think it is a good idea to jump through the window <audio src='https://s3.amazonaws.com/ask-soundlibrary/magic/amzn_sfx_magic_blast_1x_01.mp3'/> and then you find yourself back in your bed. It was just a dream.

##

This text above is following story-engines's own custom grammar. Click here to see the grammar rail road diagram

You can also build a story as if it was a series of states and its possible transitions.

{
    "title": "Lost in a room",
    "states": [
        {
            "name": "home",
            "text": "You woke up in an empty room. There's only a locked door and an open window ahead of you. Would you like to knock the door or jump out of the window?",
            "actions": {
                "KnockDoorhIntent": "door",
                "JumpWindowIntent": "window"
            },
            "start": true
        }, {
            "name": "door",
            "text": "You knock on the door several times, but you can hear nothing but silence. You move then to the window.",
            "nextAction": "window"
        }, {
            "name": "window",
            "text": "You get close to the window and you can feel a cold gust. It is very dark but today you are feeling specially brave. For some reason, you think it is a good idea to jump through the window <audio src='https://s3.amazonaws.com/ask-soundlibrary/magic/amzn_sfx_magic_blast_1x_01.mp3'/> and then you find yourself back in your bed. It was just a dream.",
            "final": true
        }
    ]
}

State's properties

Every state has to have a name and a text. And optionally it can also have:

  • prompt: If the user simply stays quiet after a question, the text on "prompt" will kick in. This can be a better a experience than just replaying all the state text.
  • nextActiont: for automatic transitions. No user input required. This is the same as a state concatenation.
  • actions: a map between the user intent and the respective state. This is really where the possible transitions are mapped. If the user inputed an intent not mapped for the current state, the skill will just reply with an error and request another user input.
  • defaultAction: This is actioned when the user did provide some input, i.e. an intent was identified, but it is not mapped on the current state.
  • start: Every story should have one and only one startting point.
  • final: true/false. Your story can have several different endings. At least one is mandatory.

Publishing the story

story-engine uses alexa-app under the hood, and it will return an instance of app when the stories are loaded. If you are exposing your backend using lambdas, you could do something like this

exports.handler = require('story-engine')([{"states":[]}]).lambda();

Of course stories will be quite big objects, so it might be a good idea putting them into separate files and then just using require to pass them to story-engine

const storyEngine = require('story-engine');
const stories = [
                    require('./my-story')
                ];
exports.handler = storyEngine(stories).lambda();

Serving several stories

story-engine has support for multiple stories. In the current implementation you can load as many different stories as you like and the engine will randomly pick one whenever a session starts and stick to it until the session terminates.

const storyEngine = require('story-engine');
const stories = [
                    require('./storyA'),
                    require('./storyB')
                ];
exports.handler = storyEngine(stories).lambda();

Adherance to Amazon standards

Following Amzon's guidelines (missing reference), some intents will be implemented by default.

AMAZON.StopIntent and AMAZON.CancelIntent

Simply stop the skill immediately with a short goodbye message.

AMAZON.HelpIntent

Required by Amazon. The name is self-explanatory; it provides a short explanation of the app dynamic and start a new story right away.

AMAZON.RepeatIntent

This is also required by Amazon and it has to repeat the previous response entirely. I tried to just play only the values defined on the prompt property and that got rejected as part of the certification process.

Testing your story

story-engine also helps to ensure your story is consistent and enjoyable. As long as you have mocha and chai in your test dependencies scope, you can pull in some automatic tests. If you don't, just run the following:

npm i mocha --save-dev
npm i chai --save-dev

And then simply create a file in your test folder with something like this:

let app = require('story-engine')([require('../walk-on-the-beach-story')]);
app.testStories();
app.testModel(require("../../../models/en-US"));

This will automatically check your story and your model against the following items:

  story structure
    ✓ has no dead ends
    ✓ has no unreachable states
    ✓ has no duplicate states
    ✓ has one and only one start
    ✓ has no super long repeats in non-final states

  Models
    ✓ should not have undeclared intents
    ✓ model should not have duplicate utterance samples

The idea is that the test names should be meaningful enough to highlight what's wrong. If not, let's make them better together.

2.0.1

6 years ago

2.0.0

6 years ago

1.0.0

6 years ago

0.6.1

6 years ago

0.6.0

6 years ago

0.5.5

6 years ago

0.5.4

6 years ago

0.5.3

6 years ago

0.5.2

6 years ago

0.5.0

6 years ago