Example of how to use web-hook node for response which takes more than 20 seconds

Hello Users,

Platform provides Service Nodes for calling external APIs. But it has a maximum timeout of 20 seconds.
For some APIs however it may take more than 20 seconds to respond. Though, as per industry standards, APIs are expected to have a average response times of about 6 seconds.

I want to walk you through a way you can use web-hook node and BotKit to resolve a response which could take more than 20 seconds.

Note: Web-Hook node supports a timeout of up to 10 minutes. But if you use it in synchronous way (example - FindFlight.js on our BotKit GitHub repository) Platform will still timeout in 20 seconds on Kore cloud platform.

You will need to use asynchronous way (as explained in BookACab.js on our BotKit GitHub repository)

Below is another simple working code which you can use for test.
Note - We are using setTimeout function along with service call to a delay API. setTimeout will add bulk of the time. In below example, setTimeOut waits for 30 seconds while delay API adds 5 seconds delay.

Firstly you need to have redis for this. Ensure redis is running (default port 6379)

Then in botkit’s config.json have the following configuration:

"redis": {
    "options": {
        "host": "localhost",
        "port": 6379
    },
    "available": true
},

Then you will need to have a js file like the below registered in app.js.
sdk.registerBot(require('./CustomBotkitWebHookNodeDemo.js'));

It is assumed that the bot will have a webhook node with name hookCallAPIFromBotkit.
Note the usage of

callback(null, new sdk.AsyncResponse());

AsyncResponse() tells the platform to wait for the response to be sent asynchronously.
and within
sdk.saveData(requestId, data)
the respond to hook will send the data back.
sdk.respondToHook(data);

In bot, based on the following code, the data from web-hook node will be available in
context.endPointResp

var botId = "st-XXXXXXX-XXX-XX";
var botName = "webhookDemoBot";
var sdk            = require("./lib/sdk");
var Promise        = sdk.Promise;
var request        = require("request");
var config         = require("./config");

//Make request to service app
    
function callAPIEndPoint(api) {
var serviceUrl = undefined;

if(api){
	serviceUrl = api;
} else {
	serviceUrl = 'https://httpbin.org/delay/5';
}
console.log("callAPIEndPoint is calling URL " + serviceUrl);

return new Promise(function(resolve, reject) {
    request({
        url: serviceUrl,
        method: 'get',
    }, function(err, res) {
        if (err) {
            return reject(err);
        }
        resolve(JSON.parse(res.body));			
	});
});


}



module.exports = {
	botId   : botId,
	botName : botName,
	 on_user_message : function(requestId, data, callback) {
		sdk.sendBotMessage(data, callback);
		console.log("###request id: " + requestId);
		console.log("on_user_message ==> " + data.message);
	},
	on_bot_message  : function(requestId, data, callback) {
		sdk.sendUserMessage(data, callback);
		console.log("###request id: " + requestId);
		console.log("on_bot_message ==> " + data.message);
	},
	on_webhook      : function(requestId, data, componentName, callback) {
		console.log("###request id: " + requestId);
		var context = data.context;
		if (componentName === 'hookCallAPIFromBotkit') {
		  var api = null;
		  console.log("on_webhook ==> calling API for hookCallAPIFromBotkit");
		console.log("wait on... " + new Date());
		sdk.saveData(requestId, data)
				.then(function() {
					setTimeout(function(){

						callAPIEndPoint(api).then(function(endPointResp) {
							data.context.endPointResp = endPointResp;
							sdk.respondToHook(data);
							console.log("wait done... " + new Date());
						});
					},30000);
					callback(null, new sdk.AsyncResponse());
				});

		}        
	},
	on_agent_transfer : function(requestId, data, callback){
		return callback(null, data);
	}
};

Output of botkit console:

Bot output

Message Node settings:

1 Like

Upadate -
In certain case you may have redis server with password enabled.

You may check if you are starting redis with command like
$>redis-server /etc/redis.conf

Usually a start like
$>redis-server
does not need password.

You may get errors like NOAUTH Authentication required or ERR operation not permitted while connecting to Redis through botkit.

You will need to use the following configuration in config.json.

"redis": {
    "options": {
        "host": "<HOST>",
        "port": <PORT>,
		"password" : "<PASSWORD like MyPa$$word1 in redis.conf file against requirepass key in the file>"
    },
    "available": true
},
1 Like

how can we achieve below scenario?

API call-1 -> takes 3 mins
API call-2 -> takes 0.5 mins

Both the request should process in parallel and response from call-2 should return first as it’s only taking 0.5 seconds. I tried multiple options but no luck.

cc: @shantanu.ghorai

Will it work for you if you split it into 2 wekbhook node calls?
How to process the calls in parallel is nodejs concept.

Is there an update for June 2021 to accomplish this in more ease way without using code?

we are trying to connect Kore with Automation Anywhere, we already accomplish this by using a service node with POST Method, the thing here is that POST return some response that is the answer to the end user, but this service delays a little bit longer. This delay is because the AA Bot is doing it thing so at the moment it ends it bring the return value.

So is there a guide (For Dummys) that can we use, to increase the timeout value for this service node?

@daniel
The industry standard for synchronous API calls is 6 seconds. Our platform allows up to 20 seconds. If we allow changing this further, that may potentially lead to severe performance issues on our platform. So, for the service node, 20 seconds is the max we can allow.
We recommend fine-tuning the end-API service to respond faster. It will always be a better and more scalable solution.

@swagata.sengupta
I recently had an issue with node npm redis version 4.5.1 that is mentioned by default in botkit repository.
When I downgraded it to 3.0.0 it connected with redis server v 3.0,503.

Can we access other bots’ dialog tasks without using webhook?
As the bots are in the same workspace can’t we directly access using Bot names?

@bhoitekishori7
This is not possible currently. If you use a universal bot, you may use another child bot.

@chandrashekar.mekala Just an update.

The module ‘node-redis’ has been upgraded from 3.0.0 to 4.1.0 as part of security updates. Even in the latest version it still has some compatibility issues.
Please note that this is essentially node/npm vs redis compatibility issue and not a code issue at Kore.ai product/library.

There is a workaround: There is a provision to run the module in legacyMode to keep it backward compatible.

Making below-mentioned changes would help to resolve the issue.

File path: lib/RedisClient.js

Replace code block

var client = opts.redis.createClient(
            opts.options.port,
            opts.options.host,
            opts.options);

with

  const client = opts.redis.createClient({
                    host: opts.options.host,
                    port: opts.options.port,
                    legacyMode: opts.options.legacyMode ?? true
                });

This should help address the issue.

Please note that Node version 12 or above must be used to run the latest BotKit.

Another workaround could be to change the usage or redis - npm to ioredis - npm
This is being contemplated at Kore but the related R&D including QA validations and security testing is not done. So, if anyone is trying to attempt this, they will need to do this on their own. Kore does not have any example/ready code available yet.