I am using the ask-sdk-core of the Alexa skill kit. I have the following LaunchRequestHandler and AudioIntentHandler. I face the issue that when I trigger the skill by just saying: "Alexa, speak to skillName" or "Alexa, start skillName" that somehow the request is sending AudioIntent and not LaunchRequest to the lambda function, thus, the audio stream starts playing. I was able to trigger both normal and the faulty behaviour via the developer console.
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'This message should be replied if the user launches the skill';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.addAudioPlayerStopDirective()
.getResponse();
}
};
And several other IntentHandlers. One of those IntentHandlers starts an audio stream.
const AudioIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AudioIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.NextIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.PreviousIntent');
},
handle(handlerInput) {
// this code should only be triggered if an utterance of the intent has been said and
// not on launch
const streamId = crypto.randomBytes(16).toString("hex");
return handlerInput.responseBuilder
.addAudioPlayerPlayDirective('REPLACE_ALL', mediaPath, streamId, 0, null, null)
.withShouldEndSession(true)
.getResponse();
}
};
The audio intent handler is supported by a ResumeIntentHandler
const AudioResumeIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.ResumeIntent';
},
handle(handlerInput) {
// this code should only be triggered if an utterance of the intent has been said and
// not on launch
const streamId = crypto.randomBytes(16).toString("hex");
return handlerInput.responseBuilder
.addAudioPlayerPlayDirective('REPLACE_ALL', mediaPath, streamId, 0, null, null)
.withShouldEndSession(true)
.getResponse();
}
};
And the following AudioPlayer handler
const AudioRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope).includes('AudioPlayer.');
},
handle(handlerInput) {
console.log(`AudioRequestHandler: ${handlerInput.requestEnvelope}`)
if(Alexa.getRequestType(handlerInput.requestEnvelope) === 'AudioPlayer.PlaybackFailed') {
console.log("Playback Failed : " + JSON.stringify(handlerInput.requestEnvelope.request.error, null, 2));
return handlerInput.responseBuilder
.speak('Something went wrong')
.getResponse();
}
return handlerInput.responseBuilder
.getResponse();
}
};
The skill acts as intended with one-shots e.g. Aelxa, ask skillName for help. I am able to reach all my non-audio intents. Also help intent, cancel, stop, etc work as intended. Why is the audio intent triggered on launch?
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
AudioRequestHandler,
AudioPauseIntentHandler,
AudioResumeIntentHandler,
NormalReplyIntentHandler,
AudioIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
FallbackIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler)
.addErrorHandlers(
ErrorHandler)
.lambda();
I added better logging. The faulty behaviour seems to happen after the second time the user starts the skill. First skill interaction works fine. After that, "start skillName" triggers "AudioIntentHandler". The request looks like follows:
type: IntentRequest
intent: {
name: AudioIntent
confirmationStatus: NONE
}
So it seems to me that my addRequestHandlers are not the issue here since its Alexa that sends the faulty intent. The AudioIntentHandler is triggered correctly based on the faulty request.
The faulty request includes AudioPlayer information. Probably from the last (first interaction) with the skill. I believe this could be the source of the bug?
AudioPlayer {
offsetInMilliseconds: 3239,
playerActivity: STOPPED,
}
Could it be that Alexa continues with the Audio Intent if the user starts the skill and Alexa detects that there exists a stopped Audio? Am I not clearing the audio player correctly on pause or stop?
const AudioPauseIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.PauseIntent';
},
handle(handlerInput) {
console.log('AudioPauseIntentHandler')
const speakOutput = 'Audio stopped.';
return handlerInput.responseBuilder
.speak(speakOutput)
.addAudioPlayerStopDirective()
.addAudioPlayerClearQueueDirective('CLEAR_ALL')
.getResponse();
}
};
I found the issue. As far as I know, it has nothing to do with my skill.json or lambda code. It's an Alexa thing and I was able to reproduce the same weird behaviour in other Alexa skills.
I am mainly testing in German. In English I usually starts skills using the following wake up words:
And that's the issue. In German, only "start" aka. "starte" works. "Talk to" aka. "spreche mit" seems to randomly trigger an intent within the skill instead of LaunchRequest.
I tested this with third-party skills and was able to reproduce the weird behaviour.
=> Fix: In German, start skill with "Alexa, starte skillname" instead of "Alexa, spreche mit skillname".