I am very excited to be the newest member of the Bespoken team.
My first assignment – to build a voice app, of course! I’m an experienced full-stack developer, but this is my first voice-first project. And as part of this, I got interested in using the Jovo Framework, which I learned about from my colleagues.
Jovo allows for building cross-platform voice apps for Alexa and Google Assistant – we have been doing a lot of work to integrate our software with it (read more here). And so I was excited to try it out and learn more about the framework as well as Alexa skills and Google Actions. It seemed like a great way to come up to speed on both platforms simultaneously!
Background
First, I had to figure out what I was going to build. When I was a kid I was a big fan of Pokémon, so I thought it would be cool to do something related to that. I decided to create a voice app that requests information on Pokémon from the “Pokédex” (the catalog of Pokémon – there are over 800 now! When did this happen?). I thought it would be a fun first voice experience.
I integrated with a convenient public API of Pokémon to do this, which also has a handy JS/Node client library.
Getting Started With Jovo To Build a Cross-Platform Voice App
Incorporating Jovo into my project was easy – it was as simple as:
npm install jovo-framework-nodejs --save
I also installed the jovo-cli globally, which provides nice helper commands from the console:
npm install jovo-cli -g
Then I followed this helpful guide:
https://www.jovo.tech/get-started.
Jovo leverages states and state handlers, an intuitive way to work with skills.
Here is where I got started, with creating a simple Launch Intent handler:
const repromptMessage = 'Ask me which number of pokemon you want to know about'; | |
const errorMessage = 'There was an error with your request, please try again'; | |
const goodbyeMessage = 'Thank you for using my pokedex! Remember to catch them all!'; | |
const pikachuAudio = 'https://s3.us-east-2.amazonaws.com/diego-bst-generalbucket/pikachu.mp3'; | |
const handlers = { | |
'LAUNCH': function () { | |
var welcome = 'Welcome to my pokedex. ' + repromptMessage; | |
app.ask(welcome, repromptMessage); | |
}, | |
... | |
} |
When the user requests a Pokédex, I switch their state to “Description State”:
app.followUpState('DescriptionState') | |
.showImageCard(response.name, description, response.sprites.front_default) | |
.ask(speech, reprompt); |
And to provide back info from the Pokédex, that is managed like so:
'DescriptionState': { | |
'YesIntent': function () { | |
var P = new Pokedex(); | |
var number = app.getSessionAttribute('pokemonNo'); | |
var img = app.getSessionAttribute('pokemonImg'); | |
P.getPokemonSpeciesByName(number) // with Promise | |
.then(function (response) { | |
var dexEntry = response.flavor_text_entries.filter(function (entry) { | |
return (entry.language.name === 'en'); | |
}); | |
var description = dexEntry[getRandomInt(0, dexEntry.length - 1)].flavor_text; | |
app.showImageCard(response.name, description, img).tell(response.name + ': ' + description + '. ' + goodbyeMessage); | |
}) | |
.catch(function (error) { | |
console.log(error); | |
app.tell(errorMessage); | |
}); | |
}, | |
'NoIntent': function () { | |
app.tell(goodbyeMessage); | |
}, | |
}, |
The code needs little explanation, I hope – all the method signatures from Jovo are self-explanatory and intuitive. In the routine above, we are calling the Pokédex API, getting back info for the selected Pokémon, and then formatting an Alexa reply with a text and a card. Easy!
Testing My Skill for Alexa
To test, I used the new Jovo/Bespoken integration.
By calling: jovo run --bst-proxy
, I got this output:
I added the URL displayed to the configuration page in the Alexa developer console (step-by-step walkthrough here).
With that set, I was able to debug and test my app right on my machine, sending requests from Alexa and Google Assistant right to my laptop. I went to the Alexa service simulator to try it out with this phrase: what is the pokemon at 10
I saw this response come back:
Very cool, right?
Getting Setup As A Google Action
Now that I had a working Alexa skill, I turned my attention to Google. Lucky for me, Jovo had a helpful guide for using my voice app there as well – and with the same codebase, no less!
Following these instructions, I was able to setup my app with DialogFlow (previously known as API.AI).
Once everything was configured, I tested it out in the Actions on Google Simulator:
Looks great!
Adding Unit Tests
Now that my app was running well, I decided to add some unit tests. For this, I used our Virtual Alexa project. It’s a very helpful tool that allows for the emulation of the real Alexa behavior.
The unit tests ensure the app handles launch intents and returning Pokédex values correctly – here is a sample:
test("Launches and asks for voltorb, then cancels ", (done) => { | |
alexa.launch().then((payload) => { | |
expect(payload.response.outputSpeech.ssml).toContain("Ask me which number of pokemon you want to know about"); | |
return alexa.utter("100"); | |
}).then((payload) => { | |
expect(payload.response.outputSpeech.ssml).toContain("voltorb"); | |
return alexa.utter("no"); | |
}).then((payload) => { | |
expect(payload.response.outputSpeech.ssml).toContain("Thank you"); | |
expect(payload.response.shouldEndSession).toBe(true); | |
done(); | |
}); | |
}); |
With this, I can make changes to my Alexa skill with the confidence that I am not going to break anything.
I also added CI and Code Coverage – all part of building an “industrial-strength” voice app. Does that seem excessive for Pokémon? Well, they may seem silly, but they are serious business :-). You can take a look at all of it here, including the source code and app itself.
Wrap Up
Overall, it was a great experience using the Jovo Framework. I was able to easily build a skill and action simultaneously, with a clean and simple API. And now that I know how to develop for both Google Assistant and Amazon Alexa – I look forward to learning more about both, and helping developers like myself build cool voice apps for them.
nice very good news ,thanks