The First Attempt
After some playing around, I settled on three core intents:
FooIntent foo {MyNumber}
BarIntent bar {MyNumber}
FooIntent foo {MyNumber}
BarIntent bar {MyNumber}
NumberIntent {MyNumber}
This allowed me to catch the user filling in the numeric slot while saying foo or bar. It also allowed me to ask the user to provide a number if they did not when saying foo or bar. I thought this was fairly solid and updated the intent implementations to handle the user leaving out a number:
exports.handleFooAction = function(intent, session) {
var myNumber = parseMyNumber(intent);
if (typeof myNumber === 'undefined') {
setPriorAction(session, 'FOO');
return { 'view': 'Error_FooToUnspecifiedNumberView', 'data': data };
}
...
}
I added code for the new intent:
exports.handleNumberAction = function(intent, session) {
var priorAction = getPriorAction(session);
if (priorAction === 'FOO') {
clearPriorAction(session);
return exports.handleFooAction(intent, session);
} else if (priorAction === 'BAR') {
clearPriorAction(session);
return exports.handleBarAction(intent, session);
} else {
return { 'view': 'OK_HelpView', 'data': data};
}
}
In the case of no prior action, the code sends help text to the user, so they can understand how to use the application (we'll come back to this shortly). After running through a number of tests, I was confident that it behaved as expected and submitted the skill for certification.
Getting Feedback
I did not pass certification ("Alexa, Sad Trombone") due to the NumberIntent triggering the help text when invoked directly.I'll admit, my first thought was, "Well, yes, if the user does not understand how to give the skill commands, it tries to give them help."
My second thought was, "Forcing the user to say things in an arbitrary order is not good UX, I should fix this."
Back to the Drawing Board
I reworked the code so that, if there is no possibility the user meant bar, the skill assumes the user meant foo. Otherwise, the user is asked if they would like foo or bar:
exports.handleNumberAction = function(intent, session) {
...
} else {
if (!isBarAllowed(session)) {
return exports.handleFooAction(intent, session);
} else {
var myNumber = parseMyNumber(intent);
...
setPriorNumber(session, myNumber);
return { 'view': 'Error_FooOrBarRequired', 'data': data };
}
}
}
Both the foo and bar action handlers were updated to account for prior numbers having been provided:
// Check if a prior number was given.
var myNumber = getPriorNumber(session);
if (typeof myNumber !== 'undefined') {
clearPriorNumber(session);
} else {
myNumber = parseMyNumber(intent);
}
After some play testing and debugging, the skill appears stable and the interactions feel more natural for having made this change.