Slack limits the time to the first slash command response to 3 seconds, which is a problem for longer tasks. If your command needs to talk to external resources that might get blocked, or genuinely need more time to complete, implementing everything in a single response won’t work.
Claudia Bot Builder, since version 1.4.0, offers a simple solution for delayed responses. This tutorial demonstrates how to work around the timing issues by using two important Claudia.js features:
- Enabling recursive Lambda calls
- Intercepting and modifying requests in API Builder
Prerequisites
- This tutorial will only work with
claudia
1.4.4 or later. - We’re using ES6 code, so this tutorial will only work with Lambda Node.js 6
What we’ll implement
Slack Slash commands support delayed and multiple responses, allowing a bot to respond to a command up to 5 times in 30 minutes. We’ll create a simple timer, which you can tell how many seconds to wait. It will respond immediately with a confirmation about the timer, and then again after the specified period.
How we’ll implement it
Claudia Bot Builder simplifies messaging workflows, but it enables only one response per request. In this case, we’ll need to send two responses. This would typically need the primary request to call another Lambda process asynchronously.
Before Claudia Bot Builder 1.4.0, you had to create a separate Lambda function for this, and complicate deployment. From version 1.4.0 onwards, you can use the same Lambda function for both the primary and the delayed response. The key trick is to intercept the second request and not allow it to go through the normal Web API request pipeline.
Responding to the primary request
To provide Slack users an immediate response, the bot will reply to the primary request with a confirmation message. It will also trigger an asynchronous call to the same Lambda function, without waiting for the response, and pass the original message. For that, we use the AWS SDK. The Bot Builder works with Promises for asynchronous requests, so we’ll just wrap the AWS SDK Lambda call into a Promise
.
The bot builder request processor gets two arguments. The first is the message coming from the bot engine, and the second is the Claudia API Builder Request. We can use the .lambdaContext
object to get the name and the version of the currently executing function. This makes it easy to recursively call the correct version, in case you use different aliases for development, testing and production.
When invoking a Lambda function, you can specify the invocation type. Using the Event
invocation type is effectively fire-and-forget, which is perfect for the secondary request, because we want to complete the primary process without waiting on the timer to end.
The Payload
field of the Lambda call will turn into the event body for the recursive call. Because we want to prevent normal API routing and request processing, we need to mark it somehow so it’s easy to detect. In this case, we’re passing the original message in the slackEvent
field, and we’ll check for that later.
Finally, when the invocation succeeds, we just respond to the user that the timer is active. In this case, we’re using the in_channel
response type so that all the Slack users in that channel see the message.
Responding to the secondary request
When the recursive request comes in, we don’t want to let Claudia API Builder route it as if it came from the API Gateway. Instead, we’ll block the normal request, and take over. For that, we need to define an intercept
function. If it gets a normal web request, it just needs to return it back, which will continue with the normal process. If it gets the one we marked with slackEvent
, it needs to return false
(or a Promise
resolving to false
), to stop the request processing pipeline.
The Claudia Bot Builder has a helper function .slackDelayedReply
that handles the workflow of sending delayed messages to Slack. Just pass in the original message and the response.
Configuring the Lambda function
To make things run, we need to configure the Lambda function a bit. First of all, the default time AWS will let Lambda functions run is 3 seconds, so we need to extend that. We can use --timeout
when creating the function using claudia create
for that. The second thing we need to do is set up IAM privileges so the Lambda function can call itself. By default, AWS will not allow that. So use --allow-recursion
when deploying with Claudia, and it will create all the required IAM settings.
So the final create
command will looks similar to this:
Full example
Check out the Slack Delayed Response example project for the complete source code, which you can run immediately.