I have created a skill for Amazon Alexa using node.js, which plays an MP3 stream.
Now I have problems to play a jingle with a fixed URL before the stream starts.
How do I have to proceed to realize this project?
Below is the most important part of the code of the simple player:
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'
|| (Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'PlayStationIntent');
},
handle(handlerInput) {
const speakOutput = messages.welcome;
return handlerInput.responseBuilder
.speak(speakOutput)
.addAudioPlayerPlayDirective("REPLACE_ALL", url, token(), 0)
.getResponse();
}
};
There are multiple options for implementing this:
SSML if the jingle is very short and comply to some encodings, you may include it in the speakOutput by using SSML and the audio tag.
M3U Instead of the including the URL of the stream directly in the AudioPlayerPlayDirective, you can include there the URL to an M3U, which then includes a playlist of the Jingle URL and the stream URL.
PlayBackFinished Intent Just sent as first play directive the url of the Jingle and add support for the PlayBackFinished Intent, which will be invoked by the AudioPlayer itself when playing Jingle has been finished and then send inside this intent an audio player play directive (without a speak) but with the URL of the stream. But be aware if that gets finished, the same PlayBackFinished Intent will get called, so you need to identify that it already has been called to avoid making an infinity loop. Best way would to use token attribute on both play commands with (first with "Jingle" and second with "Stream" or token()) so if PlayBackFinished Intent is called, check token in the request and only send the second play command, if token is "Jingle" and so identifying the Jingle has ended.
The last option would change your code to something like:
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'
|| (Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'PlayStationIntent');
},
handle(handlerInput) {
const speakOutput = messages.welcome;
return handlerInput.responseBuilder
.speak(speakOutput)
.addAudioPlayerPlayDirective("REPLACE_ALL", url_jingle, "jingle", 0)
.getResponse();
}
};
const PlayBackFinishedHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'AudioPlayer.PlaybackFinished';
},
handle(handlerInput) {
if (handlerInput.requestEnvelope.request.token === 'jingle') {
return handlerInput.responseBuilder
.addAudioPlayerPlayDirective("REPLACE_ALL", url, token(), 0)
.getResponse();
}
}
};
Related
I built an app in Slack, that on interactions in Slack, will send an HTTP POST request to a URL. That URL is a Firebase Function that is triggered with an HTTP request.
The Firebase Function looks like this...
// process incoming shortcuts
exports.interactions = functions.https.onRequest(async (request, response) => {
response.send();
const payload = JSON.parse(request.body.payload);
functions.logger.log(payload);
if (payload.type === 'shortcut') {
functions.logger.log('Found a shortcut...');
const shortcuts = require('./shortcuts');
await shortcuts(payload);
} else if (payload.type === 'block_actions') {
functions.logger.log('Found a block action...');
const blockActions = require('./blockActions');
await blockActions(payload);
} else if (payload.type === 'view_submission') {
functions.logger.log('Found a view submission...');
const viewSubmissions = require('./viewSubmissions');
await viewSubmissions(payload);
}
functions.logger.log('Done with interactions.');
});
The problem is, is that Firebase is taking 5-10 seconds to respond, and Slack is expecting a response in 3 seconds.
So the app in Slack erroring out.
It turns out while I thought it would be useful to do a response.send() immediately when the function was called so that Slack had its instant response, I was then also inadvertently starting background activities in Firebase.
The line in the above Firebase docs that gave me the biggest clue was:
Background activity can often be detected in logs from individual invocations, by finding anything that is logged after the line saying that the invocation finished.
Which I found here... the function started, and completed, and then the code to open a modal began to be executed...
I then found in the Firebase docs
Terminate HTTP functions with res.redirect(), res.send(), or res.end().
So all I really had to do was move response.send() to the end of the function. Also I had to make sure that I had await statements before my async functions, so that async functions waited to be resolved before executing the final response.send()
// process incoming shortcuts
exports.interactions = functions.https.onRequest(async (request, response) => {
const payload = JSON.parse(request.body.payload);
functions.logger.log(payload);
if (payload.type === 'shortcut') {
functions.logger.log('Found a shortcut...');
const shortcuts = require('./shortcuts');
await shortcuts(payload);
} else if (payload.type === 'block_actions') {
functions.logger.log('Found a block action...');
const blockActions = require('./blockActions');
await blockActions(payload);
} else if (payload.type === 'view_submission') {
functions.logger.log('Found a view submission...');
const viewSubmissions = require('./viewSubmissions');
await viewSubmissions(payload);
}
functions.logger.log('Done with interactions.');
response.send();
});
The modal interaction response times in Slack are much quicker and usable now.
I'm trying to write an Alexa skill which reads from Firebase
I'm in a position where I have a NodeJS method that gets called when I use the Alexa test console, but if I add in code to retrieve data from Firebase the method hangs until the lambda times out
const HelloWorldIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'plantsIntent';
},
async handle(handlerInput) {
const snapshot = (await db.collection('plants').get()).data();
const names = snapshot.docs.map(doc => doc.data().name);
const speakOutput = 'Get yourself some cool plants like' + names.join(' and ');
console.log(speakOutput);
var response = handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
console.log(response);
return response;
}
};
When I run this code I get both the speakOutput string and response object output into the logs, so I know the code is managing to get that far
I'm suspicious it's something to do with Firebase as if I remove the db.collection('plants').get() snippet (and the associated variables) then the code runs to completion
I'm suspicious that it's to do with the method not returning rather than an exception happening, because the output for response is the same in the working version (without Firebase .get()), and the non-working version
Any help would be appreciated!
I am trying to pass messages between content script and the extension
Here is what I have in content-script
chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
console.log(response)
});
And in the background script I have
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.type == "getUrls"){
getUrls(request, sender, sendResponse)
}
});
function getUrls(request, sender, sendResponse){
var resp = sendResponse;
$.ajax({
url: "http://localhost:3000/urls",
method: 'GET',
success: function(d){
resp({urls: d})
}
});
}
Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.
From the documentation for chrome.runtime.onMessage.addListener:
This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).
So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.
The accepted answer is correct, I just wanted to add sample code that simplifies this.
The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not.
Consider this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
handleMethod1(sendResponse);
}
How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?
My solution is this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
var responseStatus = { bCalled: false };
function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async
try {
sendResponseParam(obj);
} catch (e) {
//error handling
}
responseStatus.bCalled= true;
}
if (request.method == "method1") {
handleMethod1(sendResponse);
}
else if (request.method == "method2") {
handleMethod2(sendResponse);
}
...
if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
return true;
}
});
This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.
You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.
Your code will look like:
Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;
$.ajax({
'url': "http://localhost:3000/urls",
'method': 'GET'
}).then( urls => { resolve({ urls }); });
}) );
I am trying to pass messages between content script and the extension
Here is what I have in content-script
chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
console.log(response)
});
And in the background script I have
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.type == "getUrls"){
getUrls(request, sender, sendResponse)
}
});
function getUrls(request, sender, sendResponse){
var resp = sendResponse;
$.ajax({
url: "http://localhost:3000/urls",
method: 'GET',
success: function(d){
resp({urls: d})
}
});
}
Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.
From the documentation for chrome.runtime.onMessage.addListener:
This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).
So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.
The accepted answer is correct, I just wanted to add sample code that simplifies this.
The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not.
Consider this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
handleMethod1(sendResponse);
}
How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?
My solution is this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
var responseStatus = { bCalled: false };
function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async
try {
sendResponseParam(obj);
} catch (e) {
//error handling
}
responseStatus.bCalled= true;
}
if (request.method == "method1") {
handleMethod1(sendResponse);
}
else if (request.method == "method2") {
handleMethod2(sendResponse);
}
...
if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
return true;
}
});
This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.
You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.
Your code will look like:
Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;
$.ajax({
'url': "http://localhost:3000/urls",
'method': 'GET'
}).then( urls => { resolve({ urls }); });
}) );
I'm using Zombie.js to resolve urls with multiple redirections. For some reason I want to be able to detect a certain type of redirection and then stop the processing (do not follow that redirection).
var browser = new Browser();
browser.on('redirect', function(request, response, redirectRequest){
var redirectUrl = response.url;
if (redirectUrl === 'http://www.example.com'){
//Do something and stop the processing
}
});
browser.visit(urlToLoad).then(function() {
//Do something
});
I tried to add a pipeline handler and throw an error with next() but that didn't work because I couldn't detect the redirection that way. Is there something I can do to stop the browser for loading the page if I detect my redirection?
Depends on what you mean by "stop". You can certainly detect the redirection on the pipeline with a helper variable. In my case, I have to detect if the redirect is to a login form, perform the login and then return to the original page. I've found a way mixing pipeline and event listener:
let redirectedFrom = null;
Browser.extend(function(browser) {
browser.on('redirect', function(request, response) {
if (response.status === 302 && response.url === 'login_page')) {
redirectedFrom = request.headers.get('referer');
}
});
});
const browser = new Browser();
browser.pipeline.addHandler(function(browser, request, response) {
if (redirectedFrom !== null) {
// do something interesting here
redirectedFrom = null;
}
return response;
});
Now when I browser.visit() and get redirected to the login page, I can login and the visit(redirectedFrom)