Skip to content

Commit

Permalink
Merge pull request #128 from mediarain/dev
Browse files Browse the repository at this point in the history
Docs, Sample Projects, CanFullfillIntent
  • Loading branch information
omenocal committed Jul 6, 2018
2 parents 76f2221 + add7927 commit 01d965f
Show file tree
Hide file tree
Showing 102 changed files with 597 additions and 10,702 deletions.
12 changes: 5 additions & 7 deletions docs/account-linking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,12 @@ The serverless framework is a tool that helps you manage your lambda application
Running the project
---------------------

1. Clone the `Voxa <https://github.com/mediarain/voxa>`_ repository
1. Clone the `Account Linking Sample <https://github.com/mediarain/voxa-accountlinking-sample>`_ repository

2. Create a new skill project using the ``samples/starterKit`` directory as a basis
2. Make sure you're running node 6.10, this is easiest with `nvm <https://github.com/creationix/nvm>`_

3. Make sure you're running node 4.3, this is easiest with `nvm <https://github.com/creationix/nvm>`_
3. Create a ``config/local.json`` file using ``config/local.json.example`` as an example

4. Create a ``config/local.json`` file using ``config/local.json.example`` as an example
4. Run the project with ``gulp watch``

5. Run the project with ``gulp watch``

6. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
5. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
57 changes: 57 additions & 0 deletions docs/canFulfillIntentRequest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.. _canFulfillIntentRequest:

CanFulfillIntentRequest
=======================

Name-free interaction enables customers to interact with Alexa without invoking a specific skill by name, which helps facilitate greater interaction with Alexa because customers do not always know which skill is appropriate.

When Alexa receives a request from a customer without a skill name, such as "Alexa, play relaxing sounds with crickets," Alexa looks for skills that might fulfill the request. Alexa determines the best choice among eligible skills and hands the request to the skill.

To make your skill more discoverable for name-free interaction, you can implement the the `CanFulfillIntentRequest <https://developer.amazon.com/docs/custom-skills/quick-start-canfulfill-intent-request.html>`_ interface in your skill.

In Voxa, you can take advantage of this feature by following this example:

.. code-block:: javascript
skill.onCanFulfillIntentRequest((alexaEvent, reply) => {
if (alexaEvent.intent.name === 'InfluencerIntent') {
reply.fulfillIntent('YES');
_.each(alexaEvent.intent.params, (value, slotName) => {
reply.fulfillSlot(slotName, 'YES', 'YES');
});
}
return reply;
});
Voxa offers the function `onCanFulfillIntentRequest`_ so you can implement it in your code to validate wether you're going to fulfill the request or not.

Additionally, if you have several intents that you want to automatically fulfill, regardless of the slot values in the request, you can simply add an array of intents to the property: `defaultFulfillIntents`_ of the Voxa config file:

.. code-block:: javascript
const defaulFulfillIntents = [
'NameIntent',
'PhoneIntent',
];
const skill = new StateMachineSkill({ variables, views, defaultFulfillIntents });
If Alexa sends an intent that you didn't register with this function, then you should implement the `onCanFulfillIntentRequest`_ method to handle it. Important: If you implement this method in your skill, you should always return the `reply`_ object.

If a skill has implemented canFulfillIntent according to the interface specification, the skill should be aware that the skill is not yet being asked to take action on behalf of the customer, and should not modify any state outside its scope, or have any observable interaction with its own calling functions or the outside world besides returning a value. Thus, the skill should not, at this point, perform any actions such as playing sound, turning lights on or off, providing feedback to the customer, committing a transaction, or making a state change.


Testing using ASK-CLI
=====================

There are 2 options to test this feature manually. The first one is using the `Manual JSON` section of the `Test` tab in the developer portal. And the other one, is to use the `ASK CLI <https://developer.amazon.com/docs/custom-skills/implement-canfulfillintentrequest-for-name-free-interaction.html#test-the-skill-using-ask-cli>`_ from Amazon.

You can just trigger this command in the console, and you'll get the result in your terminal:

.. code-block:: bash
$ ask api invoke-skill --skill-id amzn1.ask.skill.[unique-value-here] --file /path/to/input/json --endpoint-region [endpoint-region-here]
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Voxa is a framework that provides a way to organize a conversational experience

Why Voxa vs other frameworks
----------------------------
Voxa provides a more robust framework for building Alexa skills, Cortana skills, or Google Assistant Actions. It provides a design pattern that wasnt found in other frameworks. Critical to Voxa was providing a pluggable interface and supporting all of the latest platform features.
Voxa provides a more robust framework for building Alexa skills. It provides a design pattern that wasn't found in other frameworks. Critical to Voxa was providing a pluggable interface and supporting all of the latest ASK features.

Features
--------
Expand Down
18 changes: 8 additions & 10 deletions docs/my-first-podcast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,24 +205,22 @@ serverless.yml
The serverless framework is a tool that helps you manage your lambda applications, assuming you have your AWS credentials setup properly this starter kit defines the very minimum needed so you can deploy your skill to lambda with the following command:

.. code-block:: bash
$ sls deploy
Running the project
---------------------

1. Clone the `Voxa <https://github.com/mediarain/voxa>`_ repository

2. Create a new skill project using the ``samples/my-first-podcast`` directory as a basis
1. Clone the `Audio Podcast Sample <https://github.com/mediarain/voxa-audiopodcast-sample>`_ repository

3. Make sure you're running node 4.3, this is easiest with `nvm <https://github.com/creationix/nvm>`_
2. Make sure you're running node 6.10, this is easiest with `nvm <https://github.com/creationix/nvm>`_

4. Create a ``config/local.json`` file using ``config/local.json.example`` as an example
3. Create a ``config/local.json`` file using ``config/local.json.example`` as an example

5. Run the project with ``gulp watch``
4. Run the project with ``gulp watch``

6. Create a skill in your Amazon Developer Portal account under the ALEXA menu.
5. Create a skill in your Amazon Developer Portal account under the ALEXA menu.

7. Go to the interaction model tab and copy the intent schema and utterances from the the speechAssets folder.
6. Go to the interaction model tab and copy the intent schema and utterances from the the speechAssets folder.

8. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
7. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
18 changes: 14 additions & 4 deletions docs/reply.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ The ``reply`` Object
:param message: A message object

.. js:function:: Reply.append(message)

Adds statements to the ``Reply``
:param message: An object with keys ``ask``, ``tell``, ``say``, ``reprompt``, ``card`` or ``directives`` keys. Or another ``reply`` object

:param message: An object with keys ``ask``, ``tell``, ``say``, ``reprompt``, ``card``, or ``directives`` keys. Or another ``reply`` object
:returns: the ``Reply`` object

.. js:function:: Reply.toJSON()

:returns: An object with the proper format to send back to Alexa, with statements wrapped in SSML tags, cards, reprompts and directives

.. js:function:: Reply.fulfillIntent(canFulfill)

:param canFulfill: A string with possible values: YES | NO | MAYBE to fulfill request

.. js:function:: Reply.fulfillSlot(slotName, canUnderstand, canFulfill)

:param slotName: A string with the slot to fulfill
:param canUnderstand: A string with possible values: YES | NO | MAYBE that indicates slot understanding
:param canFulfill: A string with possible values: YES | NO that indicates slot fulfillment
22 changes: 10 additions & 12 deletions docs/starter-kit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ serverless.yml
The serverless framework is a tool that helps you manage your lambda applications, assuming you have your AWS credentials setup properly this starter kit defines the very minimum needed so you can deploy your skill to lambda with the following command:

.. code-block:: bash
$ sls deploy
Dependencies (install these first)
----------------------------------
1. If you are using node.js for the first time, install node:
* Install `nvm <http://nvm.sh/>`_, ``curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash``,
* Install node 4.3, ``nvm install 4.3``
* Install `nvm <http://nvm.sh/>`_, ``curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash``,
* Install node 6.10, ``nvm install 6.10``

2. Install gulp globally, ``npm install -g gulp``

Expand All @@ -103,16 +103,14 @@ Dependencies (install these first)
Running the project
---------------------

1. Clone the `voxa <https://github.com/mediarain/voxa>`_ repository

2. Create a new skill project using the ``samples/starterKit`` directory as a basis
1. Clone the `Starter Kit <https://github.com/mediarain/voxa-starterkit>`_ repository

3. Make sure you're running node 4.3, this is easiest with `nvm <https://github.com/creationix/nvm>`_
2. Make sure you're running node 6.10, this is easiest with `nvm <https://github.com/creationix/nvm>`_

4. In your skill directory, install node dependencies using ``npm install``
3. In your skill directory, install node dependencies using ``npm install``

5. Create a ``config/local.json`` file using ``config/local.json.example`` as an example
4. Create a ``config/local.json`` file using ``config/local.json.example`` as an example

6. Run the project with ``gulp watch``
5. Run the project with ``gulp watch``

7. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
6. At this point you should start ``ngrok http 3000`` and configure your skill in the Amazon Developer panel to use the ngrok https endpoint.
6 changes: 3 additions & 3 deletions docs/statemachine-skill.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Voxa
.. code-block:: javascript
skill.onBeforeReplySent((alexaEvent, reply) => {
const rendered = reply.write();
const rendered = reply.toJSON();
analytics.track(alexaEvent, rendered)
});
Expand Down Expand Up @@ -180,7 +180,7 @@ They're executed sequentially and will stop when the first handler returns a rep
skill.onStateMachineError((alexaEvent, reply, error) => {
// it gets the current reply, which could be incomplete due to an error.
return new Reply(alexaEvent, { tell: 'An error in the controllers code' })
.write();
.toJSON();
});
.. js:function:: Voxa.onError(callback, [atLast])
Expand All @@ -191,7 +191,7 @@ They're executed sequentially and will stop when the first handler returns a rep
skill.onError((alexaEvent, error) => {
return new Reply(alexaEvent, { tell: 'An unrecoverable error occurred.' })
.write();
.toJSON();
});
Expand Down
2 changes: 0 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const I18NRenderer = require('./lib/renderers/I18NRenderer');
/**
* Plugins
*/
const badResponseReprompt = require('./lib/plugins/reprompt-on-bad-response');
const replaceIntent = require('./lib/plugins/replace-intent');
const stateFlow = require('./lib/plugins/state-flow');
const cloudWatch = require('./lib/plugins/cloud-watch');
Expand All @@ -37,7 +36,6 @@ module.exports.I18NRenderer = I18NRenderer;
module.exports.DefaultRenderer = DefaultRenderer;

module.exports.plugins = {
badResponseReprompt,
replaceIntent,
stateFlow,
cloudWatch,
Expand Down
19 changes: 18 additions & 1 deletion lib/AlexaSkill.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ class AlexaSkill {
this.registerRequestHandlers();
this.registerEvents();

this.onCanFulfillIntentRequest((alexaEvent, reply) => {
if (_.includes(this.config.defaultFulfillIntents, alexaEvent.intent.name)) {
reply.fulfillIntent('YES');

_.each(alexaEvent.intent.params, (value, slotName) => {
reply.fulfillSlot(slotName, 'YES', 'YES');
});
}

return reply;
});

this.onSessionEnded(() => ({ version: '1.0' }), true);
this.onError((alexaEvent, error) => {
debug('onError %s', error);
Expand All @@ -44,6 +56,7 @@ class AlexaSkill {
return [
'LaunchRequest',
'IntentRequest',
'CanFulfillIntentRequest',
'SessionEndedRequest',
'AudioPlayer.PlaybackStarted',
'AudioPlayer.PlaybackFinished',
Expand Down Expand Up @@ -98,6 +111,7 @@ class AlexaSkill {
execute(event, context) {
debug('Received new event: %s', JSON.stringify(event));
const alexaEvent = new AlexaEvent(event, context);

return Promise.try(() => {
// Validate that this AlexaRequest originated from authorized source.
if (this.config.appIds) {
Expand All @@ -114,7 +128,6 @@ class AlexaSkill {
}
}


if (this.getOnLaunchRequestHandlers().length === 0) {
throw new Error('onLaunchRequest must be implemented');
}
Expand All @@ -128,6 +141,10 @@ class AlexaSkill {

switch (alexaEvent.request.type) {
case 'LaunchRequest':
case 'PlaybackController.PlayCommandIssued':
case 'PlaybackController.PauseCommandIssued':
case 'PlaybackController.NextCommandIssued':
case 'PlaybackController.PreviousCommandIssued':
case 'IntentRequest':
case 'Display.ElementSelected':
case 'SessionEndedRequest': {
Expand Down
3 changes: 3 additions & 0 deletions lib/Errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class UnkownRequestType extends Error {

class OnSessionEndedError extends Error {
constructor(errorOnSession) {
if (errorOnSession instanceof Object && errorOnSession.constructor === Object) {
errorOnSession = JSON.stringify(errorOnSession, null, 2);
}
const message = `Session ended with an error: ${errorOnSession}`;
super(message);
this.name = this.constructor.name;
Expand Down
Loading

0 comments on commit 01d965f

Please sign in to comment.