Building Cross-Platform Voice Apps With VOXA — Part 3
By: Octavio Menocal
Tags: Voxa, Amazon Echo, Alexa Skills, Google Assistant, Google Actions

Building Cross-Platform Voice Apps With VOXA — Part 3

Third part of a series on creating a cross-platform voice app in VOXA

Photo by Rodion Kutsaev on Unsplash

In the previous article, we created Alexa skill and Google Assistant apps. We also created the VUI spreadsheets where anyone in your team can collaborate, by adding intents, utterances, and responses for your voice apps. Additionally, we learned that we can download all this info to our VOXA project with VOXA-CLI, as well as create a project from scratch. Finally, we added Account Linking to the Alexa skill and Google Sign-In to the Google action. In this article, we’re going to create the basic logic of our game, get the user’s information, and persist the progress in AWS DynamoDB.

So, let’s get started!

Image via www.pixabay.com

As we learned in the first part of this series, VOXA leverages the MVC principle to separate three different layers: model, views, and controllers. We’re going to try to follow that pattern in our app by leaving:

The Model in the /src/app/model.js file

file All the Views in the /src/languageResources folder.

folder. All the Controllers in the /src/app/states folder.

You can download the code from the quick maths template I have pushed to my GitHub repository in the branch part3.

Let’s check the model.js file. The file is too large to show here, so follow this link to check it out in the repository.

There are two important methods to generate the main logic of the game:

selectOperation(voxaEvent)

and

isAnswerRight(result)

In selectOperation , we randomly select the numbers and the operator to make the user calculate the results. We will leave it simple for now, just two numbers and one operator at a time. We also make sure the result is never negative. We don’t want the users to blow their mind trying to calculate 51–78 for example, which is still a simple operation, but we want users to engage with our app. If they get this kind of operation when they start using the app, they could stop using it!

if (this.user.isFirstTime) {

this.level = _.head(levels);

} else if (this.user.sessionCount <= 3) {

this.level = _.nth(levels, this.user.sessionCount);

} else {

this.level = _.sample(levels);

}

With this bit of code, we make sure the difficulty level is according to the number of times the user has opened the skill.

Also, you might find this piece of code interesting:

const wordsNumber1 = ntw.toWords(this.number1);

const wordsNumber2 = ntw.toWords(this.number2);

const params = {

operatorName: this.operatorName,

wordsNumber1,

wordsNumber2,

};



const speech = voxaEvent.t('Operation.Expression.calculation', params);

const durationTime = readingTime(speech);



console.log('speech', speech);

console.log('durationTime', durationTime);



this.speechTimeDiff += durationTime.time;

Remember, we’re giving points to the user based upon the time they took to calculate the result, but we have to account for the time Alexa lasted saying:

How much is 55 * 2?

How much is 105 + 203?

How much is 3512–512?

The time cannot be fixed to a single number, so we use the number-to-words and reading-time NPM packages to calculate it. This time is not 100% accurate — it will vary depending on the internet connection between Alexa and the server the skill is hosted on. With the current values you’ll find it’s not 100% accurate when testing your skill locally, but I found it to be pretty accurate when deploying the code to an AWS Lambda function.

In the isAnswerRight method, we get the number of seconds the user took to calculate the operation and give them points accordingly:

if (timeDiff <= 3000) {

pointsEarned += 10;

} else if (timeDiff <= 6000) {

pointsEarned += 8;

} else if (timeDiff <= 9000) {

pointsEarned += 4;

} else {

pointsEarned += 2;

}

In the /src/app/states/main.states.js file you can see the main states we use to control this flow:

VOXA states handlers

The conversation goes like this:

User: Alexa, open quick maths

Alexa: Welcome back to Quick Maths! Should we start with the game?

User: Yes

Alexa: How much is 995–213?

User: 782

Alexa: Excellent! You get 2 points. Should we try again?

User: Yes

Alexa: How much is 7 * 9?

User: 73

Alexa: No! It’s 63. Should we try again?

User: No

Alexa: Ok. Goodbye.

In VOXA, we control the views we want to return and the state we expect the next interaction. For example:

return {

reply: [‘RightAnswerExpression’, ‘RightAnswer’],

to: ‘begin’,

};

RightAnswerExpression is a view with a unique directive: say . In VOXA this means the content of the view will be attached to the next view we use in the same state or in the next state until we set the point to return the response to Alexa, or Google.

RightAnswer is a view with three directives:

ask : the main response Alexa or Google will give. This indicates the response should be returned to the voice platform and not to another VOXA state.

: the main response Alexa or Google will give. This indicates the response should be returned to the voice platform and not to another VOXA state. reprompt : the reprompt Alexa, or Google will give if the user remains in silence after the main speech.

: the reprompt Alexa, or Google will give if the user remains in silence after the main speech. dialogflowSuggestions: suggestions shown at the bottom of the screen in Google Assistant.

VOXA has a several directives that can be used to render platform-specific features. For example:

alexaCard : renders a linking, simple, or basic card for Alexa

: renders a linking, simple, or basic card for Alexa alexaAPLTemplate : renders an APL template directive

: renders an APL template directive alexaRenderTemplate : renders a DisplayTemplate directive

: renders a DisplayTemplate directive dialogflowSuggestions : renders suggestion chips for Google Assistant

: renders suggestion chips for Google Assistant dialogflowTable : renders a Table for Google Assistant

: renders a Table for Google Assistant googleCompletePurchase: renders a DigitalGoods directive for Google

Follow these links to check out other Alexa, and Google Assistant directives:

Alexa Directives

Google Assistant Directives