From 1ebe6d8b9958ee8233c4c84b953e7f0e3531b0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=B6nig?= Date: Tue, 12 Sep 2017 15:04:04 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=93=9D=20Created=20README.md=20in=20e?= =?UTF-8?q?xamples=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/README.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..7df17a1a80 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,72 @@ +# Examples + +This folder provides code examples for various Jovo features. These are mostly index.js files with enabled logging so you can start testing right away from your command line. + +## Overview + +* [Basics and Routing](#basics-and-routing) + * [Hello World](#hello-world) + * [followUpState](#followupstate) + * [Input (Slots and Entities)](#input-slots-and-entities) + * [Session Attributes](#session-attributes) +* [Advanced Jovo Features](#advanced-jovo-features) + * [i18n for Multilingual Apps](#i18n-for-multilingual-apps) + * [Speech Builder](#speech-builder) + * [User Object](#user-object) +* [Integrations](#integrations) + * [Analytics](#analytics) +* [Alexa-specific Features](#alexa-specific-features) + * [Cards](#cards) + * [Lists](#lists) + * [Device Address](#device-address) + * [Alexa Verifier](#alexa-verifier) + * [Render Templates](#render-templates) + +## Basics and Routing + +### Hello World +The file [indexHelloWorld.js](./examples/indexHelloWorld.js) provides a simple voice app with "Hello World" output, similar to the [Jovo Sample Voice App](https://github.com/jovotech/jovo-sample-voice-app-nodejs). + +### followUpState +The file [indexFollowUpState.js](./examples/indexFollowUpState.js) shows how to add a state to an ask-call. + +### Input (Slots and Entities) +The file [indexInputs.js](./examples/indexInputs.js) shows how easy it is to access user data (slots, entities, parameters). + +### Session Attributes +The file [indexSession.js](./examples/indexSession.js) shows how to save data in a session (be careful, for persistence across sessions take a look at the [User Object](#user-object)). + +## Advanced Jovo Features + +### i18n for Multilingual Apps +The file [indexi18n.js](./examples/indexi18n.js) shows how to use the npm package i18next for multilingual apps. + +### Speech Builder +The file [indexSpeechBuilder.js](./examples/indexSpeechBuilder.js) shows an introduction into the SpeechBuilder class, a powerful way to create speech output. + +### User Object +The file [indexUser.js](./examples/indexUser.js) shows examples of how the User Object can be used to access certain information and store user-specific data. + +## Integrations + +### Analytics +The file [indexAnalytics.js](./examples/indexAnalytics.js) shows how easy it is to add analytics integrations with just one line of code. + +## Alexa-specific Features + +### Cards +The file [indexAlexaCards.js](./examples/indexAlexaCards.js) provides examples for adding Home Cards as visual output to the Alexa companion app. + +### Lists +The file [indexAlexaLists.js](./examples/indexAlexaLists.js) shows how to access Alexa List features. + +### Device Address +The file [indexAlexaDeviceAddress.js](./examples/indexAlexaDeviceAddress.js) is an example of how to get information about the Alexa-enabled device's address. + +### Alexa Verifier +The file [indexAlexaVerifier.js](./examples/indexAlexaVerifier.js) is helpful for verification when you want to host your Alexa Skill with a webhook, not on Lambda. + +### Render Templates +The file [indexRenderTemplate.js](./examples/indexRenderTemplate.js) offers examples for specific features of the Amazon Echo Show. + + From 6dc80ced6d1a34c3a285be49124d0e6920468316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ko=CC=88nig?= Date: Tue, 12 Sep 2017 16:00:08 +0200 Subject: [PATCH 2/8] :bulb: Added comments to example files --- docs/README.md | 2 ++ examples/indexAlexaCards.js | 29 +++++++++++-------- examples/indexAlexaDeviceAddress.js | 19 +++++++++---- examples/indexAlexaLists.js | 41 ++++++++++++++++----------- examples/indexAlexaVerifier.js | 27 ++++++++++++------ examples/indexAnalyticsIntegration.js | 31 ++++++++++++-------- examples/indexFollowUpState.js | 19 +++++++++---- examples/indexHelloWorld.js | 26 +++++++++++------ examples/indexInputs.js | 22 ++++++++++---- examples/indexRenderTemplate.js | 19 +++++++++---- examples/indexSession.js | 19 +++++++++---- examples/indexSpeechBuilder.js | 19 +++++++++---- examples/indexUser.js | 19 +++++++++---- examples/indexi18n.js | 22 +++++++++----- 14 files changed, 214 insertions(+), 100 deletions(-) diff --git a/docs/README.md b/docs/README.md index 2b62e3675b..4da12d75e7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,6 @@ # Jovo Framework Documentation +[Take a look at the docs on our website, they're prettier there!](https://www.jovo.tech/framework/docs/) + diff --git a/examples/indexAlexaCards.js b/examples/indexAlexaCards.js index f54aa14eb4..f1cc65b473 100644 --- a/examples/indexAlexaCards.js +++ b/examples/indexAlexaCards.js @@ -1,45 +1,50 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); -/** - * Alexa specific cards - */ +// ================================================================================= +// App Logic: Displays Alexa-specific cards +// ================================================================================= let handlers = { 'LAUNCH': function() { - app.tell('App launched'); + app.tell('App launched.'); }, 'SimpleCardIntent': function() { app.alexaSkill().showSimpleCard('Title', 'Content'); - app.tell('This is a simple card'); + app.tell('This is a simple card.'); }, 'StandardCardIntent': function() { app.alexaSkill().showStandardCard('Title', 'Content', { smallImageUrl: 'https://via.placeholder.com/720x480', largeImageUrl: 'https://via.placeholder.com/1200x800', }); - app.tell('This is a standard card with an image'); + app.tell('This is a standard card with an image.'); }, 'AccountLinkingCardIntent': function() { app.alexaSkill().showAccountLinkingCard(); - app.tell('This is a card with an account linking CTA'); + app.tell('This is a card with an account linking call to action.'); }, 'AskForCountryAndPostalCodeCardIntent': function() { app.alexaSkill().showAskForCountryAndPostalCodeCard(); diff --git a/examples/indexAlexaDeviceAddress.js b/examples/indexAlexaDeviceAddress.js index 9422a10a27..32a61a338d 100644 --- a/examples/indexAlexaDeviceAddress.js +++ b/examples/indexAlexaDeviceAddress.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Get either full address or postal code +// ================================================================================= + let handlers = { 'LAUNCH': function() { diff --git a/examples/indexAlexaLists.js b/examples/indexAlexaLists.js index d5d0408616..f17bde9245 100644 --- a/examples/indexAlexaLists.js +++ b/examples/indexAlexaLists.js @@ -1,35 +1,44 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); + +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -// app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Get and Update Shopping and ToDo-List-Items +// ================================================================================= + let handlers = { 'LAUNCH': function() { // app.toIntent('GetShoppingListIntent'); - // app.toIntent('GetTodoListIntent'); + // app.toIntent('GetTodoListIntent'); // app.toIntent('AddItemToToDoListIntent'); - app.toIntent('UpdateItemIntent'); + app.toIntent('UpdateToDoListItemIntent'); }, 'GetShoppingListIntent': function() { - // active or completed + // Active or completed app.user().getShoppingList('active') .then((data) => { - // iterate items on list + // Iterate through items on list for (let obj of data.items) { app.speech.addSentence(obj.value); } @@ -44,10 +53,10 @@ let handlers = { }); }, 'GetTodoListIntent': function() { - // active or completed + // Active or completed app.user().getToDoList('active') .then((data) => { - // iterate items on list + // Iterate through items on list for (let obj of data.items) { app.speech.addSentence(obj.value); } @@ -57,28 +66,28 @@ let handlers = { console.log(error); }); }, - 'UpdateItemIntent': function() { + 'UpdateToDoListItemIntent': function() { app.user().updateToDoList('Pay bills', 'Go Shopping', 'active') .then((data) => { console.log(data); - app.tell('item changed'); + app.tell('Item updated.'); }) .catch((error) => { if (error.code === 'NO_USER_PERMISSION') { app .showAskForListPermissionCard(['read', 'write']) - .tell('Please grant the permission to access your lists'); + .tell('Please grant the permission to access your lists.'); } if (error.code === 'ITEM_NOT_FOUND') { app - .tell('Item not found'); + .tell('Item not found.'); } }); }, 'AddItemToToDoListIntent': function() { app.user().addToTodoList('Sleep') .then((data) => { - app.tell('Item added'); + app.tell('Item added.'); }) .catch((error) => { if (error.code === 'NO_USER_PERMISSION') { diff --git a/examples/indexAlexaVerifier.js b/examples/indexAlexaVerifier.js index e56bdd83e0..5a6898a1ed 100644 --- a/examples/indexAlexaVerifier.js +++ b/examples/indexAlexaVerifier.js @@ -1,29 +1,38 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook (with Alexa Verifier) + Enable Logging +// ================================================================================= + const webhook = require('../index').WebhookVerified; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); - -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Hello World +// ================================================================================= + let handlers = { 'LAUNCH': function() { - app.tell('App launched'); + app.toIntent('HelloWorldIntent'); }, - 'HelloWorld': function() { - app.tell('Hello World'); + + 'HelloWorldIntent': function() { + app.tell('Hello World!'); }, }; diff --git a/examples/indexAnalyticsIntegration.js b/examples/indexAnalyticsIntegration.js index cac1cc2a3c..f9933f183f 100644 --- a/examples/indexAnalyticsIntegration.js +++ b/examples/indexAnalyticsIntegration.js @@ -1,37 +1,44 @@ 'use strict'; -const webhook = require('../index').Webhook; - -webhook.listen(3000, function() { - console.log('Example server listening on port 3000!'); -}); +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging + Add Analytics +// ================================================================================= +const webhook = require('../index').Webhook; const app = require('../index').Jovo; + +// Enable Logging for Quick Testing app.enableRequestLogging(); app.enableResponseLogging(); + +// Add Analytics Integrations app.addVoiceLabsAlexa('Voicelabs Alexa Key'); app.addVoiceLabsGoogleAction('Voicelabs Google Action Key'); app.addDashbotGoogleAction('Dashbot Google Action Key'); app.addDashbotAlexa('Dashbot Alexa Key'); -app.setIntentMap({ - 'AMAZON.HelpIntent': 'HelpIntent', - +// Listen for post requests +webhook.listen(3000, function() { + console.log('Example server listening on port 3000!'); }); -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Hello World +// ================================================================================= + let handlers = { 'LAUNCH': function() { - app.toIntent('HelloWorld'); + app.toIntent('HelloWorldIntent'); }, - 'HelloWorld': function() { - app.tell('Hey'); + + 'HelloWorldIntent': function() { + app.tell('Hello World!'); }, }; diff --git a/examples/indexFollowUpState.js b/examples/indexFollowUpState.js index 9b9545f855..f346ee9ac7 100644 --- a/examples/indexFollowUpState.js +++ b/examples/indexFollowUpState.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Say hey when in OnboardingState +// ================================================================================= + let handlers = { 'LAUNCH': function() { diff --git a/examples/indexHelloWorld.js b/examples/indexHelloWorld.js index 07f94ae58b..362752e200 100644 --- a/examples/indexHelloWorld.js +++ b/examples/indexHelloWorld.js @@ -1,28 +1,38 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Hello World +// ================================================================================= + let handlers = { 'LAUNCH': function() { - app.tell('App launched'); + app.toIntent('HelloWorldIntent'); }, - 'HelloWorld': function() { - app.tell('Hello World'); + + 'HelloWorldIntent': function() { + app.tell('Hello World!'); }, }; diff --git a/examples/indexInputs.js b/examples/indexInputs.js index a8064481f6..71d7d1027d 100644 --- a/examples/indexInputs.js +++ b/examples/indexInputs.js @@ -1,27 +1,37 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Get name parameter and say hello +// ================================================================================= + let handlers = { 'LAUNCH': function() { - app.tell('App launched'); + app.tell('App launched.'); }, + 'NameIntent': function(name) { app.tell('Hello ' + name); }, diff --git a/examples/indexRenderTemplate.js b/examples/indexRenderTemplate.js index 8644fc55fa..a48e0cad74 100644 --- a/examples/indexRenderTemplate.js +++ b/examples/indexRenderTemplate.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Add (Echo Show) Render Templates +// ================================================================================= + let handlers = { 'LAUNCH': function() { diff --git a/examples/indexSession.js b/examples/indexSession.js index f5515074a6..08e4233897 100644 --- a/examples/indexSession.js +++ b/examples/indexSession.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Story session attribute name=John Doe +// ================================================================================= + let handlers = { 'LAUNCH': function() { diff --git a/examples/indexSpeechBuilder.js b/examples/indexSpeechBuilder.js index e9ac8477db..89c00bf660 100644 --- a/examples/indexSpeechBuilder.js +++ b/examples/indexSpeechBuilder.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Example of speechBuilder methods and conditions +// ================================================================================= + let handlers = { 'LAUNCH': function() { diff --git a/examples/indexUser.js b/examples/indexUser.js index cb5a36c8e8..fe5313572d 100644 --- a/examples/indexUser.js +++ b/examples/indexUser.js @@ -1,22 +1,31 @@ 'use strict'; +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging +// ================================================================================= + const webhook = require('../index').Webhook; +const app = require('../index').Jovo; + +// Enable Logging for Quick Testing +app.enableRequestLogging(); +app.enableResponseLogging(); +// Listen for post requests webhook.listen(3000, function() { console.log('Example server listening on port 3000!'); }); -const app = require('../index').Jovo; -app.enableRequestLogging(); -app.enableResponseLogging(); - -// listen for post requests webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); app.execute(); }); +// ================================================================================= +// App Logic: Using the User Object to store and get data +// ================================================================================= + const handlers = { 'LAUNCH': function() { diff --git a/examples/indexi18n.js b/examples/indexi18n.js index 0299558cfd..9ea5f9a243 100644 --- a/examples/indexi18n.js +++ b/examples/indexi18n.js @@ -1,12 +1,13 @@ 'use strict'; -const webhook = require('../index').Webhook; - -webhook.listen(3000, function() { - console.log('Example server listening on port 3000!'); -}); +// ================================================================================= +// App Configuration: Create Webhook + Enable Logging + Language Resources Object +// ================================================================================= +const webhook = require('../index').Webhook; const app = require('../index').Jovo; + +// Enable Logging for Quick Testing app.enableRequestLogging(); app.enableResponseLogging(); @@ -25,14 +26,21 @@ let languageResources = { }, }; -// listen for post requests +// Listen for post requests +webhook.listen(3000, function() { + console.log('Example server listening on port 3000!'); +}); + webhook.post('/webhook', function(req, res) { app.handleRequest(req, res, handlers); - app.setLanguageResources(languageResources); app.execute(); }); +// ================================================================================= +// App Logic: +// ================================================================================= + let handlers = { 'LAUNCH': function() { From bd7ebe9389e2a2f40e784e5075dc38083299af37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ko=CC=88nig?= Date: Sun, 17 Sep 2017 22:35:29 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=93=9D=20Added=20Docs=20as=20Markdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 28 +- docs/building-a-voice-app/README.md | 176 +++++++++ docs/building-a-voice-app/input.md | 241 +++++++++++++ docs/building-a-voice-app/intents-states.md | 374 ++++++++++++++++++++ docs/building-a-voice-app/output.md | 261 ++++++++++++++ docs/getting-started/README.md | 56 +++ docs/getting-started/tutorials.md | 23 ++ docs/getting-started/voice-app-basics.md | 91 +++++ docs/integrations/README.md | 274 ++++++++++++++ docs/platform-specifics/README.md | 34 ++ docs/support/README.md | 1 + examples/indexAlexaCards.js | 6 + examples/indexAlexaDeviceAddress.js | 2 + examples/indexAlexaLists.js | 4 + examples/indexFollowUpState.js | 2 + examples/indexRenderTemplate.js | 2 + examples/indexSession.js | 2 + examples/indexSpeechBuilder.js | 1 + examples/indexUser.js | 3 + examples/indexi18n.js | 1 + 20 files changed, 1580 insertions(+), 2 deletions(-) create mode 100644 docs/building-a-voice-app/README.md create mode 100644 docs/building-a-voice-app/input.md create mode 100644 docs/building-a-voice-app/intents-states.md create mode 100644 docs/building-a-voice-app/output.md create mode 100644 docs/getting-started/README.md create mode 100644 docs/getting-started/tutorials.md create mode 100644 docs/getting-started/voice-app-basics.md create mode 100644 docs/integrations/README.md create mode 100644 docs/platform-specifics/README.md create mode 100644 docs/support/README.md diff --git a/docs/README.md b/docs/README.md index 4da12d75e7..b9a709898f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,30 @@ -# Jovo Framework Documentation +# Support -[Take a look at the docs on our website, they're prettier there!](https://www.jovo.tech/framework/docs/) +Any questions with the framework? Just let us know, we're happy to help! +* How to Reach Us + * Gitter + * Email + * Other Channels +## How to Reach Us +We're trying to be as responsive as possible, so please feel free to reach out to us with any feedback or suggestion. Below, you can find a list of channels: + +### Gitter + +For ad hoc, immediate support of very specific questions, Gitter is a great messaging based community. We created a new chat group on Gitter: [jovo-framework-nodejs](https://gitter.im/jovotech/jovo-framework-nodejs). + +Please reach out to us and say hi, we're happy to help there. + +### Email + +For more detailed feedback and feature suggestions, we also appreciate email. You can reach the creators of the framework directly at team@jovo.tech. + +### Other Channels + +* Create an [Issue on GitHub](https://github.com/jovotech/jovo-framework-nodejs/issues) +* Fill out this [feedback form](https://jovo.typeform.com/to/ewt3Lw) +* Reach out in the following Slack channels (@jankoenig and @aswetlow): [Alexa Slack](http://www.alexaslack.com/), [Google Home Slack](http://googleslack.com/), [Microsoft Cortana Slack](https://bit.ly/2qRqHMw) +* Create a comment [here](https://www.jovo.tech/framework/docs/support#comments-and-questions) +* Tweet at us [@jovotech](https://twitter.com/jovotech) \ No newline at end of file diff --git a/docs/building-a-voice-app/README.md b/docs/building-a-voice-app/README.md new file mode 100644 index 0000000000..3181634e91 --- /dev/null +++ b/docs/building-a-voice-app/README.md @@ -0,0 +1,176 @@ +# Building a Voice App + +In this section, you will learn more about the essentials of building an app with the Jovo Framework. For more basic information, see [Getting Started](../getting-started) and [Voice App Basics](../voice-app-basics). + +## Jovo App Structure +A Jovo voice app ([`index.js`](https://github.com/jovotech/jovo-sample-voice-app-nodejs/blob/master/index.js)) is divided into two main building blocks: [Configuration](#app-configuration) and [Logic](#app-logic): + +![Jovo App Structure](https://www.jovo.tech/img/docs/jovo-architecture.jpg) + +The upper part is used for [server configuration](#server-configuration), adding [integrations](../integrations) like analytics or databases, or global variables that are used throughout your app. + +The below part (the `handlers` variable) is where you're routing through your app and managing how you're responding to your users. + + +## App Configuration + +This building block changes depending on where you want to host your voice app. Jovo currently supports a [webhook](#webhook) (which we recommend for local prototyping) and [AWS Lambda](#aws-lambda). The [sample repository](https://github.com/jovotech/jovo-sample-voice-app-nodejs) offers examples for both, which you can see in more detail below. + +To add configurations, you have two options: You can either add them to the main file (outside any function), or to the `webhook.post`/`exports.handler` so that they are loaded with any new request. This depends on the type of configuration. + +### Server Configuration + +Jovo comes with off-the-shelf [webhook](#webhook) and [AWS Lambda](#aws-lambda) support. There are just a few simple building blocks that make the difference between two types, as you can see in the image below. This makes it easy to switch from local development mode to publishing your voice app, if you prefer AWS Lambda for that. + +![Jovo Server Configuration](https://www.jovo.tech/img/docs/building-a-voice-app/webhook-lambda-differences.jpg) + + +### Webhook + +For voice apps in prototype stage, we recommend using a webhook and a tunnel service like [ngrok](#developing-locally-with-ngrok). This way, you can easily update your app without having to upload it to a server or AWS Lambda each time. + +Jovo uses the [`express`](https://expressjs.com/) framework for running a server. Here is what the beginning of a Jovo project with a webhook looks like ([see the sample repository index.js](https://github.com/jovotech/jovo-sample-voice-app-nodejs/blob/master/index.js)): + +``` +const app = require('jovo-framework').Jovo; +const webhook = require('jovo-framework').Webhook; + +// Other configurations go somewhere here + +// Listen for post requests +webhook.listen(3000, function() { + console.log('Local development server listening on port 3000.'); +}); + +webhook.post('/webhook', function(req, res) { + app.handleRequest(req, res, handlers); + app.execute(); +}); + +// App Logic below +``` + +You can either run your server locally, or deploy to a webhosting service. + + +#### Developing locally with ngrok + +Do the following steps when you’re ready to test your prototype. To run the server, use the node command in your command line. Make sure that, with every file update, you terminate it with `ctrl+c and run it again: + +``` +$ node index.js + +# It should return this: +Local development server listening on port 3000. +``` + +[Ngrok](https://ngrok.com/) is a tunneling service that makes your localhost accessible to outside APIs. This way, you can prototype locally without having to deal with servers or Lambda uploads all the time. You can download ngrok like so: + +``` +# Open a new tab in your command line tool, then: +$ npm install ngrok -g + +# Point ngrok to your localhost:3000 +$ ngrok http 3000 +``` + +It should display something similar to this: + +![ngrok window](https://www.jovo.tech/img/docs/building-a-voice-app/webhook-url.jpg) + +Now use the `https://xyz.ngrok.io` address provided by ngrok, add `/webhook and paste it as webhook link to the respective developer platform consoles. + +Find the following sections in our beginner tutorials to learn how to do so: + +* [Amazon Alexa: Add Webhook as HTTPS Endpoint](https://www.jovo.tech/blog/alexa-skill-tutorial-nodejs/#app-configuration) +* [Google Assistant: Add Webhook as API.AI Fulfillment](https://www.jovo.tech/blog/google-action-tutorial-nodejs/#endpoint) + +### AWS Lambda + +Here is what the beginning of a Jovo project for Lambda looks like ([see the sample repository index_lambda.js](https://github.com/jovotech/jovo-sample-voice-app-nodejs/blob/master/index_lambda.js)): + +``` +const app = require('jovo-framework').Jovo; + +// Other configurations go somewhere here + +exports.handler = function(event, context, callback) { + app.handleRequest(req, res, handlers); + app.execute(); +}; + +// App Logic below +``` + +While for Alexa, the process of hosting a Skill on Lambda is straightforward, for a Google Action there are additional steps that need to be taken to create an API Gateway. To learn more about how to run your voice app on Lambda, please take a look at our step-by-step tutorials: + +* [Run your Alexa Skill on Lambda](https://www.jovo.tech/blog/alexa-skill-tutorial-nodejs/#aws-lambda) +* [Run your Google Action on Lambda with an API Gateway](https://www.jovo.tech/blog/google-action-tutorial-nodejs/#aws-lambda) + + +### How to Add Configurations + +As described above, configurations will mostly be added outside the `webhook.post`/`exports.handler functions (will be loaded when the server/function is started). Sometimes, they can also be added between the `handleRequest` and `execute` method calls (will be loaded every new request). + +* Mapping + * intentMap + * inputMap +* Logging + * Log Requests + * Log Responses +* Database Integrations + * Database Configurations + * FilePersistence + * DynamoDB +* Analytics Integrations + * Analytics Configurations + * Add VoiceLabs Analytics + * Add Dashbot Analytics + +## App Logic + +The `handlers` variable is the main building block of your voice app. This is where the logic happens. + +``` +let handlers = { + 'LAUNCH' : function () { + // This intent is required + // Opened when people open the voice app without a specific query + app.tell('Hello World!'); + }, + + 'YourFirstIntent' : function () { + // do something here + + }, + +}; +``` + +### Intents & States + +You can learn more about how to route through intents and states in the section [Handling Intents and States](/intents-states.md). + + +### User Input + +You can learn more about how to deal with user input and data in the section [Handling User Input](/input.md). + + +### Output + +You can learn more about how to craft speech, audio, and visual responses in the section [Creating Output](/output.md). + + + +## Command Line Tools + +You can use the Jovo Command Line Tools ([see GitHub repository](https://github.com/jovotech/jovo-cli)) to create new Jovo projects. See [Installation](../getting-started/#installation) for getting started. + +### Create a New Project + +You can create a Jovo project into a new directory with the following command: + +``` +$ jovo new +``` \ No newline at end of file diff --git a/docs/building-a-voice-app/input.md b/docs/building-a-voice-app/input.md new file mode 100644 index 0000000000..2f59b96658 --- /dev/null +++ b/docs/building-a-voice-app/input.md @@ -0,0 +1,241 @@ +# [Building a Voice App](../) > User Input & Data + +In this section, you will learn how to deal with entities and slot values provided by your users, and also store and retrieve user specific data with the User class. + +* [Introduction to User Input](#introduction-to-user-input) +* [How to Access Input](#how-to-access-input) + * [Input as Parameter](#input-as-parameter) + * [getInput | getInputs](#getinput-getinputs) + * [inputMap](#inputmap) +* [User Object](#user-object) + * [User ID](#user-id) + * [Platform Type](#platform-type) +* [Logging](#logging) + * [Log Requests](#log-requests) + * [Log Responses](#log-responses) +* [Persisting Data](#persisting-data) + * [Session Attributes](#session-attributes) + * [Database Integrations](#database-integrations) + +## Introduction to User Input + +> If you're new to voice applications, you can learn more general info about principles like slots and entities here: [Voice App Basics/Natural Language Lingo](../getting-started/voice-app-basics.md). + +We call user input any additional information your user provides besides an `intent`. On Amazon Alexa, input is usually called a `slot`, on Google Assistant/API.AI an `entity`. + + +## How to Access Input + +There are two ways to get the inputs provided by a user: either by adding parameters to your handlers' intent functions, or by using the `getInput` method. + +### Input as Parameter +You can add parameters directly to your intent, like so: + +``` +let handlers = { + + // Other Intents and States + + 'SomeIntent': function(inputNameOne, inputNameTwo) { + // Do something + } + + // Other Intents and States +}; +``` + +The parameter names need to be the same as the slot/entity names on the respective developer consoles at Amazon and API.AI. For some built-in entities at API.AI that use hyphens, the parameters should be in camelCase (for example, `given-name` can be accessed with a `givenName` parameter). + + +### getInput | getInputs + +You can either access the values of all user inputs with the `getInputs` method, or get specific values directly with `getInput(inputName)`. + +``` +let handlers = { + + // Other Intents and States + + 'SomeIntent': function() { + // Get all inputs + let inputs = app.getInputs(); + + // Get input for a single slot or entity + let value = app.getInput(inputName); + } + + // Other Intents and States +}; +``` + +### inputMap + +Similar to `[intentMap](../intents-states.md/#intentmap)`, there are cases where it might be valuable (due to naming conventions on different platforms or built-in input types) to map different input entities to one defined Jovo inputName. You can add this to the [configuration section](../#jovo-app-structure) of your voice app: + +``` +// Create above webhook.post (webhook) or exports.handler (Lambda) +let inputMap = { + 'incomingInputName' : 'mappedInputName' +}; +app.setInputMap(inputMap); +``` + +Example: You want to ask your users for their name and created a slot called `name` on the Amazon Developer Platform. However, on API.AI, you decided to use the pre-defined entity `given-name`. You can now use an inputMap to match incoming inputs from Alexa and Google. + +``` +// Map API.AI standard parameter given-name with name +let inputMap = { + 'given-name' : 'name' +}; +``` + +## User Object + +There is also additional information that is not explicitly provided by a user, like which device they are using, or their ID. Learn more about different types of implicit user input in this section. + +For retrieving and storing this type of information, the Jovo `User Class`can be used. + + +### User ID + +Returns user ID on the particular platform, either Alexa Skill User ID or Google Actions User ID: + +``` +app.getUserId(); +``` + +This is going to return an ID that looks like this: + +``` +// For Amazon Alexa +amzn1.ask.account.AGJCMQPNU2XQWLNJXU2K23R3RWVTWCA6OX7YK7W57E7HVZJSLH4F5U2JOLYELR4PSDSFGSDSD32YHMRG36CUUAY3G5QI5QFNDZ44V5RG6SBN3GUCNTRHAVT5DSDSD334e34I37N3MP2GDCHO7LL2JL2LVN6UFJ6Q2GEVVKL5HNHOWBBD7ZQDQYWNHYR2BPPWJPTBPBXPIPBVFXA + +// For Google Assistant +ARke43GoJIqbF8g1vfyDdqL_Sffh +``` + +### Platform Type + +Want to see which platform your user is currently interacting with? With getType, you can get exactly this. + +``` +app.getType(); +``` + +This is going to return a type that looks like this: + +``` +// For Amazon Alexa +AlexaSkill + +// For Google Assistant +GoogleAction +``` + +## Logging + +When you’re using a webhook and ngrok, it’s easy to use logging for debugging, like this: + +``` +console.log('This is going to appear in the logs'); +``` + +For voice app specific debugging, Jovo offers some handy functions for logging incoming requests and outgoing responses. + + +### Log Requests + +You can log the incoming JSON requests by adding the following configuration: + +``` +// Place anywhere in your index.js +app.enableRequestLogging(); +``` + +The result looks like this (data changed): + +``` +{ + "version": "1.0", + "session": { + "new": true, + "sessionId": "amzn1.echo-api.session.c4551117-1708-446e-a2b2-bg12d2913e3a", + "application": { + "applicationId": "amzn1.ask.skill.f5c2b3f3-35e6-4c69-98e1-11e75ee6745b" + }, + "user": { + "userId": "amzn1.ask.account.AGJCMQPNU2XQWLNJXU2KXDSTRSDTRDSDSDW4SDVT5DLTZKUH2J25I37N3MP2GDCHO7LL2JL2LVN6UFJ6Q2GEVVKL5HNHOWBBD7ZQDQYWNHYR2BPPWJPTBPBXPIPBVFXA" + } + }, + "context": { + "AudioPlayer": { + "playerActivity": "IDLE" + }, + "System": { + "application": { + "applicationId": "amzn1.ask.skill.f5c2b3f3-35e6-4c69-98e1-bg12d2913e3a" + }, + "user": { + "userId": "amzn1.ask.account.AGJCMQPNU2XQWXDSTRSDTRDSDSDW4SDVT5DOX7YK7W57E7HVZJSLH4F5U2JOLYELR4PTQQJTRJECVPYHMRG36CUUAY3G5QI5QFNDZ44V5RGUCNTRHAVT5DLTZKUH2J25I37N3MP2GDCHO7LL2JL2LVN6UFJ6Q2GEVVKL5HNHOWBBD7ZQDQYWNHYR2BPPWJPTBPBXPIPBVFXA" + }, + "device": { + "deviceId": "amzn1.ask.device.AHFATCOCAYDNSDENR7YISGVX2DGRIR3HJHIR47IMLSKZ4TPDRTKBX6AHD2RAIGRMI3WIBMWSUMM3A7JMI5GABHMABUETZWISVZTDDUK3TMVWTGSWQ2BU5VHOIL7KFYFLC6C3YDEMBMHJQOCXRBA", + "supportedInterfaces": { + "AudioPlayer": {} + } + }, + "apiEndpoint": "https://api.amazonalexa.com" + } + }, + "request": { + "type": "IntentRequest", + "requestId": "amzn1.echo-api.request.5c96e32a-d803-4ba0-ba04-4293ce23ggf1", + "timestamp": "2017-07-03T09:56:44Z", + "locale": "en-US", + "intent": { + "name": "HelloWorldIntent", + "confirmationStatus": "NONE" + } + } +} +``` + +### Log Responses + +You can log the outgoing JSON responses by adding the following configuration: + +``` +// Place anywhere in your index.js +app.enableResponseLogging(); +``` + +The result looks like this: + +``` +{ + "version": "1.0", + "response": { + "shouldEndSession": true, + "outputSpeech": { + "type": "SSML", + "ssml": "Hello World!" + } + }, + "sessionAttributes": {} +} +``` + +## Persisting Data + +> Learn more about Sessions here: [Handling Intents and States/Introduction to User Sessions](./intents-states.md/#introduction-to-user-sessions) + +If you want to store user input to use later, there is an important distinction to be made: Should the information only be available during a session, or be persisted for use in later sessions? + +### Session Attributes + +For information that is only needed across multiple requests during one session, you can attach attributes to your responses. Learn more here: [Handling Intents and States/Session Attributes](./intents-states.md/#session-attributes). + +### Database Integrations + +For information that is needed across sessions, you can use our database integrations. Learn more here: [Integrations/Databases](../integrations/#databases). + diff --git a/docs/building-a-voice-app/intents-states.md b/docs/building-a-voice-app/intents-states.md new file mode 100644 index 0000000000..974f30bf23 --- /dev/null +++ b/docs/building-a-voice-app/intents-states.md @@ -0,0 +1,374 @@ +# [Building a Voice App](../) > Intents & States + +In this section, you will learn more about how to use intents and states to route your users through your voice app. + +* [Introduction to User Sessions](#introduction-to-user-sessions) +* [Intents](#intents) + * ['LAUNCH' intent](#launch-intent) + * ['END' intent](#end-intent) + * [intentMap](#intentmap) +* [States](#states) + * [followUpState](#followupstate) +* [toIntent | toStateIntent](#tointent-tostateintent) +* [Session Attributes](#session-attributes) + +## Introduction to User Sessions + +To build engaging voice apps, it's important to understand the concept of user sessions on voice platforms. + +A `session is an uninterrupted interaction between a user and your application. It consists of at least one `request, but can consist of multiple sequential inputs and outputs. The length of a session is dependent of the following factors: + +* The response includes `shouldEndSession`, which is true for `tell` and `endSession` method calls +* A user doesn't respond to an ask prompt and the session times out +* The user asks to end the session by saying "quit" or "exit" + +Sessions that contain only a single request with a `tell` response could look like this: + +![One Session](https://www.jovo.tech/img/docs/session-tell.jpg) + + +For more conversational experiences that require back and forth between your app and user, you need to use the `ask` method. Here is what a session with two requests could look like: + +![Two Sessions](https://www.jovo.tech/img/docs/session-ask.jpg) + +To save user data in form of attributes across requests during a session, take a look at the [Session Attributes](#session-attributes) section below. The platforms don't offer the ability to store user data across sessions. For this, Jovo offers a Persistence Layer. + + +## Intents + +If you're new to voice applications, you can learn more general info about principles like intents here: [Voice App Basics/Natural Language Lingo](../getting-started/voice-app-basics.md/#natural-language-lingo). + +Intents are defined and handled in the `handlers` variable. Besides the required [`'LAUNCH'`](#launch-intent) intent, you can add more intents that you defined at the respective developer platforms (see how to create an intent for [Amazon Alexa](https://www.jovo.tech/blog/alexa-skill-tutorial-nodejs/#helloworldintent) and [Google Assistant](https://www.jovo.tech/blog/google-action-tutorial-nodejs/#helloworldintent) in our beginner tutorial) like this: + +``` +let handlers = { + 'LAUNCH' : function () { + // This intent is required + // Opened when people open the voice app without a specific query + app.tell('Hello World!'); + }, + + 'YourFirstIntent' : function () { + // do something here + + }, + +}; +``` +Whenever your application gets a request from one of the voice platforms, this will either be accompanied with an intent (which you need to add), or the signal to start or end the session. + +For this, offers two standard, built-in intents, `'LAUNCH'` and `'END'`, to make cross-platform intent handling easier: + +``` +let handlers = { + + 'LAUNCH' : function() { + // This intent called when a user opens your app without a specific query + // Groups LaunchRequest (Alexa) and Default Welcome Intent (API.AI) + }, + + // Add more intents here + + 'END' : function() { + // This intent is called when the session ends + // Currently supporting AMAZON.StopIntent and reprompt timeouts + } +}; +``` + +You can learn more about the built-in intents in the following sections: + + +### 'LAUNCH' intent + +The `'LAUNCH'` intent is the first one your users will be directed to when they open your voice app without a specific question (no deep invocations, just “open skill” or “talk to app” on the respective platforms). This intent is necessary to run your voice app. + +Usually, you would need to map the requests from Alexa and Google (as they have different names) to handle both in one intent block, but Jovo helps you there with a standard intent. + +### 'END' intent + +A session could end due to various reasons. For example, a user could call “stop,” there could be an error, or a timeout could occur after you asked a question and the user didn’t respond. Jovo uses the standard intent `'END' to match those reasons for you to “clean up” (for example, to get the reason why the session ended, or save something to the database). + +If you want to end the session without saying anything, use the following: + +``` +app.endSession(); +``` + +### getEndReason + +It is helpful to find out why a session ended. Use getEndReason insinde the `'END'` intent to receive more information. This currently only works for Amazon Alexa. + +``` +let reason = app.getEndReason(); + +// For example, log +console.log(reason); +``` + + +### intentMap + +In cases where the names of certain intents differ across platforms, Jovo offers a simple mapping function for intents. You can add this to the [configuration section](../#app-configuration) of your voice app: + +``` +// Create above webhook.post (webhook) or exports.handler (Lambda) +let intentMap = { + 'incomingIntentName' : 'mappedIntentName' +}; +app.setIntentMap(intentMap); +``` + +This is useful especially for platform-specific, built-in intents. One example could be Amazon’s standard intent when users ask for help: `AMAZON.HelpIntent`. You could create a similar intent on API.AI called `HelpIntent` and then do the matching with the Jovo `intentMap`. + +``` +let intentMap = { + 'AMAZON.HelpIntent' : 'HelpIntent' +}; +``` + +This can also be used if you have different naming conventions on both platforms and want to match both intents to a new name. In the below example, the `AMAZON.HelpIntent` and an intent called `help-intent` on API.AI are matched to a Jovo intent called `HelpIntent`. + +``` +let intentMap = { + 'AMAZON.HelpIntent' : 'HelpIntent', + 'help-intent' : 'HelpIntent' +}; +``` + +#### Platform built-in intents + +As mentioned above, the platforms offer different types of built-in intents. + +* Amazon Alexa: [Standard built-in intents](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/built-in-intent-ref/standard-intents) +* Google Assistant: Currently no documentation of built-in intents on API.AI + + + +## States + +For simple voice apps, the structure to handle the logic is quite simple: + +``` +let handlers = { + + 'LAUNCH' : function() { + // do something + }, + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + 'END' : function() { + // do something + } +}; +``` + +This means, no matter how deep into the conversation with your voice app the user is, they will always end up at a specific `'YesIntent'` or `'NoIntent'`. This means that you as a developer need to figure out yourself which question they just answered with "Yes." + +This is where `states` can be helpful. For more complex voice apps that include multiple user flows, it is necessary to remember and route through some user states to understand at which position the conversation currently is. For example, especially “Yes” and “No” as answers might show up across your voice app for a various number of questions. For each question, a state would be very helpful to distinct between different Yes’s and No’s. + +With Jovo, you can include states like this: + +``` +let handlers = { + + 'LAUNCH' : function() { + // do something + }, + + // Example: behave differently for a 'yes' or 'no' answer inside order state + 'OrderState' : { + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + }, + + 'END' : function() { + // do something + } + +}; +``` + +By routing a user to a state (by using [`followUpState`](#followupstate)), this means you can react specifically to this certain situation in the process. + +When a user is in a certain state and calls an intent, Jovo will first look if that intent is available in the given state. If not, a fallback option needs to be provided outside any state: + +``` +let handlers = { + + 'LAUNCH' : function() { + // do something + }, + + // Example: behave differently for a 'yes' or 'no' answer inside order state + 'OrderState' : { + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + }, + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + 'END' : function() { + // do something + } + +}; +``` + +### followUpState + +If you want to route a user to a state after you asked a specific question, prepend a `followUpState` call to an `ask` call. + +``` +app.ask(speech, reprompt) + .followUpState(stateName); +``` + +This way, the voice app will first look if the response-intent is available in the given state. If not, it will go to the default called intent if it’s available outside a state. + +``` +let handlers = { + + 'LAUNCH' : function() { + // Ask for a yes-no-question and route to order state + let speech = 'Do you want to order something?'; + let reprompt = 'Please answer with yes or no.'; + app.followUpState('OrderState').ask(speech, reprompt); + }, + + // Example: behave differently for a 'yes' or 'no' answer inside order state + 'OrderState' : { + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + }, + + // Default intents without states below + + 'YesIntent' : function() { + // do something + }, + + 'NoIntent' : function() { + // do something + }, + + 'END' : function() { + // do something + } + +}; +``` + + +## toIntent | toStateIntent + +Use the `toIntent` or `toStateIntent` methods to jump into a new intent within the same request (similar, but different to the `followUpState` method that awaits the next request within the same session). For example, the sample voice app uses this to go from `'LaunchIntent'` to `'HelloWorldIntent'`: + +``` +let handlers = { + + 'LAUNCH': function() { + app.toIntent('HelloWorldIntent'); + }, + + 'HelloWorldIntent': function() { + app.tell('Hello World!'); + } +}; +``` + +If `toStateIntent` is used, the framework will look for an intent within the given state, and go there if available. If not, it will go to the fallback option outside your defined states. + +Sometimes, you may want to pass additional information (like user input) to another intent. You can use the `arg` parameter to do exactly this. Make sure to add `arg` as a parameter to that intent as well to be able to use it. + +``` +app.toIntent(intent[, arg]); +app.toStateIntent(state, intent[, arg]); + +// Go to PizzaIntent +app.toIntent('PizzaIntent'); + +// Go to PizzaIntent and pass more info +app.toIntent('PizzaIntent', moreData); + +// Go to PizzaIntent in state Onboarding +app.toStateIntent('OnboardingState', 'PizzaIntent'); + +// Go to PizzaIntent in state Onboarding and pass more info +app.toStateIntent('OnboardingState', 'PizzaIntent', moreData); +``` + +To make use of the passed data, add a parameter to your intent handler: + +``` +let handlers = { + + 'LAUNCH': function() { + let data = 'data'; + app.toIntent('HelloWorldIntent', data); + }, + + 'HelloWorldIntent': function(data) { + app.tell('Hello World' + data + '!'); + } +}; +``` + +## Session Attributes + +It might be helpful to save certain informations across requests during a session (find out more about [session management in the introduction above](#introduction-to-session-management)). This can be done with Session Attributes. + +The `addSessionAttribute` or `setSessionAttribute` methods can be used to store certain information that you can use later. It’s like a cookie that’s alive until the session ends (usually after calling the `tell` function or when the user requests to stop). + +``` +app.setSessionAttribute(key, value); + +// Set the current game score to 130 points +app.setSessionAttribute('score', 130); +``` + +You can either access all session attributes with `getSessionAttributes`, or call for a certain attribute with `getSessionAttribute(key)`. + +``` +let attributes = app.getSessionAttributes(); +let value = app.getSessionAttribute(key); + +// Save current session's game score to variable +let score = app.getSessionAttribute('score'); +``` + +Have a look at our [User Object](./input.md/#user-object) to learn more about how to persist data across sessions. \ No newline at end of file diff --git a/docs/building-a-voice-app/output.md b/docs/building-a-voice-app/output.md new file mode 100644 index 0000000000..b4f8e69b9f --- /dev/null +++ b/docs/building-a-voice-app/output.md @@ -0,0 +1,261 @@ +# [Building a Voice App](../) > Output + +In this section, you will learn how to use Jovo to craft a response to your users. + +* [Introduction to Output Types](#introduction-to-output-types) +* [Basic Output](#basic-output) + * [tell](#tell) + * [ask](#ask) + * [play](#play) +* [Advanced Output](#advanced-output) + * [SSML](#ssml) + * [speechBuilder](#speechbuilder) + * [Raw JSON Responses](#raw-json-responses) +* [Visual Output](#visual-output) + * [Cards](#cards) +* [No Speech Output](#no-speech-output) + +## Introduction to Output Types + +What do users expect from a voice assistant? Usually, it's either direct or indirect output in form of speech, audio, or visual information. In this section, you will learn more about basic output types like tell, ask, and play, but also how to use SSML or the Jovo speechBuilder to create more advanced output elements. + + +## Basic Output + +Jovo's basic output options offer simple methods for text-to-speech, but also for playing pre-recorded audio files. If you're interested in more, take a look at [Advanced Output](#advanced-output). + +### tell + +The tell method is used to have Alexa or Google Home say something to your users. You can either user plain text or [SSML](#ssml) (Speech Synthesis Markup Language). + +Important: The session ends after a `tell` method, this means the mic is off and there is no more interaction between the user and your app until the user invokes it again. [Learn more about sessions here](./intents-states.md/#introduction-to-user-sessions). + +``` +app.tell(speech); + +// Use plain text as speech output +app.tell('Hello World!'); + +// Use SSML as speech output +app.tell('Hello World'); +``` + + +### ask + +Whenever you want to make the experience more interactive and get some user input, the `ask` method is the way to go. + +This method keeps the mic open ([learn more about sessions here](./intents-states.md/#introduction-to-user-sessions)), meaning the speech element is used initially to ask the user for some input. If there is no response, the reprompt is used to ask again. + +``` +app.ask(speech, reprompt); + +app.ask('How old are you?', 'Please tell me your age'); +``` + +You can also use [SSML](#ssml) for your speech and reprompt elements. + +### play + +There are several ways to play pre-recorded audio files as an output. The platforms expect [SSML](#ssml), which to generate you can use the speechbuilder for. However, sometimes you only want to play one sound. + +For this, you can use play. This includes an optional parameter `fallbacktext`, which is used for Google Assistant when the audio file can’t be accessed (with Alexa, the fallback option doesn’t work). The text is also displayed in the Google Assistant app on your users’ smartphones, if they access your action there. + +``` +app.play(url[, fallbacktext]); + +// Play weird pizza sound +app.play('https://www.jovo.tech/downloads/pizza.mp3'); + +// Play weird pizza sound with fallback text for Google Actions +app.play('https://www.jovo.tech/downloads/pizza.mp3', 'Pizza, Pizza, Pizza!'); +``` + +Note: When you’re developing locally, make sure you have the audio file uploaded to a server that supports SSL, and that it meets the platform requirements ([Amazon Alexa](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference#audio) and [Google Assistant](https://www.w3.org/TR/speech-synthesis/#S3.3.1)). + +> You can use this free tool to convert and host audio files for 24 hours: [Audio Converter](https://www.jovo.tech/audio-converter). + +## Advanced Output + +Voice platforms offer a lot more than just converting a sentence or paragraph to speech output. In the following sections, you will learn more about advanced output elements. + +### SSML + +SSML is short for "Speech Synthesis Markup Language," and you can use it to can add more things like pronunciations, breaks, or audio files. For some more info, see the [SSML reference by Amazon](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference), and [by Google](https://developers.google.com/actions/reference/ssml). Here’s another valuable resource for [cross-platform SSML](http://ssml.green/). + +Here is an example how SSML-enriched output could look like: + +``` +let speech = 'Welcome to this Pizza Skill.' + + 'Don\'t we all want some pizza' + + 'in our life? Oh yes.' + + ''; + +app.tell(speech); +``` + +But isn’t that a little inconvenient? Let’s take a look at the Jovo [speechBuilder](#speechbuilder). + +### speechBuilder + +With the `speechBuilder`, you can assemble a speech element by adding different types of input: + +``` +let speech = app.speechBuilder() + .addText('Welcome to this Pizza Skill.') + .addBreak('300ms') + .addAudio('https://www.jovo.tech/downloads/pizza.mp3') + .build(); + +app.tell(speech); +``` + +Here is what’s currently possible with speechBuilder: + +``` +// Add plain text for text-to-speech +addText(text) + +// Add link to audio file,