4

I'm trying to use a hook.io microservice to make a slack slash command bot. According to the docs I should be able to send an immediate response then a seperate POST later. But I cant get the immediate response and the later POST to both work.

Here is my test code.

module['exports'] = function testbot(hook) {

var request = require('request');
// The parameters passed in via the slash command POST request.
var params = hook.params;

data = {
    "response_type": "ephemeral",
    "text": "Immediate Response"
}
hook.res.setHeader('Content-Type', 'application/json');
console.log("returning immediate response")
hook.res.write(JSON.stringify(data), 'utf8', delay(params));
//calling end() here sends the immediate response but the POST never happens.
// but if end() is called below instead slack gives a timeout error but the POST succeeds.
//hook.res.end()

//test with 3.5 second delay
function delay(params) {
    setTimeout(function () {post_response(params)},3500);
}

function post_response(params) {

    console.log("posting delayed response")
    // Set up the options for the HTTP request.
    var options = {
        // Use the Webhook URL from the Slack Incoming Webhooks integration.
        uri: params.response_url,
        method: 'POST',
        // Slack expects a JSON payload with a "text" property.
        json: {"response_type":"in_channel", "text":"Delayed response","parse":"full"}
    };


    // Make the POST request to the Slack incoming webhook.
    request(options, function (error, response, body) {
        // Pass error back to client if request endpoint can't be reached.
        if (error) {
            console.log(error);
            hook.res.end(error.message);
        } else {
            console.log("post OK");
        }
        // calling end() here sends the POST but the immediate response is lost to a slack timeout error.
        hook.res.end()
    })
};
}

As detailed in the comments calling res.end() early means the immediate response gets sent but the POST never happens whereas delaying the res.end() until after POST means the delayed response is sent but it generates a timeout error from slack in the meantime.

I'm a javascript newbie so hopefully there is a simple solution that I've overlooked.

Rob
  • 14,746
  • 28
  • 47
  • 65
rw950431
  • 115
  • 1
  • 9
  • Why start with a 3.5 second delay? According to the slack documentation, slack will throw a timeout error if you wait longer than 3 seconds to respond. – Marak Oct 05 '17 at 21:27
  • It looks like you are passing the `delay` function as an argument to `hook.res.write`. That isn't right as `hook.res.write` doesn't accept a callback parameter. – Marak Oct 05 '17 at 22:02
  • @Marak- 3.5 seconds deliberately exceeds the slack timeout. My aim is to find a way to immediately respond but then continue to process data in the background and post the results back as a separate transaction. The code shown above works as described (post_response completes successfully 3.5 seconds later) so I'm a little confused by your claim that hook.res.write() doesn't accept a callback. – rw950431 Oct 06 '17 at 11:08
  • `hook.res.write` does not accept callback, `delay` method will execute immediately because of how JavaScript works. There is no way to process results in the background or make a separate transaction. You must respond back to Slack within 3 seconds. If you must used the delay response API ( which you shouldn't ), you will probably have to store the response URL in datastore for later usage. Best to try and respond immediately instead. – Marak Oct 06 '17 at 14:08

2 Answers2

0

Once you call res.end() inside hook.io, the script will immediately abort and end processing. It's the equivalent of calling process.exit. If you fail to end the request, hook.io will eventually hit it's own Time-out Limit.

hook.io should be capable of responding back to Slack within the three seconds Slack requires.

Here is a guide which may help: Making a Custom Slack Slash Command with hook.io

Marak
  • 235
  • 1
  • 2
  • Thanks @Marak- thats what I suspected. It appears that hook.io, whilst an awesome product, isn't suitable for this application as it involves a database lookup that's sometimes taking longer than the slack timeout. Initial signs are that https://webtask.io/docs/model "Custom Programming Model" may be more suitable in this case. – rw950431 Oct 07 '17 at 00:08
  • The webtask API is going to work pretty much the exact same way. I don't think you'll be able to find a solution unless you respond immediately to slack and store the Slack URL in hook.io datastore or webtask storage. – Marak Oct 07 '17 at 01:27
  • hook.io is dead – Bharel Mar 15 '23 at 14:35
0

Bad form to answer one's own question I know but the following worked for me using webtask so I include it here in case others find it useful.

var express = require('express');
var Webtask    = require('webtask-tools');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

//visiting above url in browser is handled by get
app.get('/', function (req, res) {
    console.log(req.query)
    res.send('OK');
});

//post from slack handled here
app.post('/', function (req, res) {
    var params = req.body;
    console.log(params);
    var data = {
        "response_type": "ephemeral",
        "text": "Immediate Response"
    }
    res.setHeader('Content-Type', 'application/json');
    res.send(data);
    // deliberate delay longer than Slack timeout
    // in order to test delayed response.
    setTimeout(function () { post_response(params) }, 3500);
});

function post_response(params) {

    console.log("posting delayed response")
    // Set up the options for the HTTP request.
    var options = {
        // Use the Webhook URL supplied by the slack request.
        uri: params.response_url,
        method: 'POST',
        // Slack expects a JSON payload with a "text" property.
        json: { "response_type": "in_channel", "text": "Delayed response", "parse": "full" }
    };


    // Make the POST request to the Slack incoming webhook.
    request(options, function (error, response, body) {
        if (error) {
            console.log(error);
        } else {
            console.log("post OK");
        }
    })
};
module.exports = Webtask.fromExpress(app);
rw950431
  • 115
  • 1
  • 9
  • Thanks. Based on their documentation I can now see that webtask will keep the script running after the response has ended for up to 30 seconds. I'll investigate, but we should be able to do the same at hook.io to allow post processing after initial response. – Marak Oct 07 '17 at 20:51