Left-facing arrow
back to blog

Making a Message-Replacing Slash Command Slack Bot using AWS Lambda + API Gateway

Slack is a powerful tool. An organization can use it to interact with leads in real time from a chat box on their website, track Continuous Integration and Continuous Deployment status by having the build server post success/fail messages into a DevOps channel, or even for teammates to send MoCkInG MesSaGEs to each other 😎

There are a few nice guides on the internet describing how to create a Slash Command using serverless technologies.

The Slack docs themselves even tell you how to do it! Here's another thorough walkthrough.

So why am I writing this?

My requirements were very specific. I was trying to create a Slash Command which replaced the original message (the one that triggered the bot), vs simply appending a new message to the chat log.

The guides linked to above, as much as they helped get 90% of it all working, didn't dig into the specifics of how to make that happen.

Therefore, here we are.

If you're trying to write your own Slash Command, then go ahead and follow one of the above guides, preferably the second one, then come back here.

Update API Gateway

After setting up your API Gateway POST endpoint as per the original guide, we'll need to update the Integration Request to proxy the full request to Lambda.

In Amazon's API Gateway, select your API, then Resources. Choose your POST endpoint, then click on Integration Request.

Check the box next to Use Lambda Proxy integration, then re-deploy that API!

Edit the Lambda Function

Checking that Use Lambda Proxy integration box means that the full original request will be proxied from API Gateway to Lambda. Without it, API Gateway grabs the original request, handles it, and sends a different request (with stripped down data) over to Lambda.

That's not going to work for our use case. We need all the request data in order to correctly craft our responses back to Slack.

Slack asks us to perform a specific workflow during the request/response lifecycle in order to support the functionality we're looking for.

Specifically, we want to send a "delayed response":

As with immediate response messages, you can include the response_type field. However, when you use a value of in_channel with this delayed method, the original text that invoked the command is not included.

This isn't exactly intuitive, considering we just want to replace the original message, but it's just how Slack works ¯\_(ツ)_/¯

Here's what your Lambda code should look like:

-- CODE language-shell -- const https = require('https'); const querystring = require('querystring'); const url = require('url'); const spongebob = text => { const og = [...text.toLowerCase()]; for (let i = 0; i < og.length; i++) { if (Math.random() > 0.5) og[i] = og[i].toUpperCase(); } return og.join(''); }; exports.handler = function(event, context, callback) { // 1 callback(null, { statusCode: 200, body: '' }); // 2 const query = querystring.parse(event.body); const originalMessage = query.text; const responseInfo = url.parse(query.response_url) // 3 const delayedSpongebob = JSON.stringify({ text: spongebob(originalMessage), response_type: "in_channel" }); // 4 const req = https.request({ hostname: responseInfo.host, port: 443, path: responseInfo.path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': delayedSpongebob.length } }); req.write(delayedSpongebob); req.end(); };

view raw
spongebob-handler.js hosted with ❤ by GitHub

This example code takes the user's message and applies the MOcKinG SpOnGeBOB MeME to the text. Character-casing logic happens in the spongebob function.

  1. The first line in the handler is our "immediate response" of an empty 200 response. Slack asks us to provide this to acknowledge that we'll be handling this request eventually (in a new request, which we'll build up manually).
  2. Pull out all of the data we'll need to create the new request back to Slack. This includes the original message (which we'll manipulate), and information about the endpoint we need to hit. Building up the new request requires parsing the response_url that Slack gives us from within the original request. We use the querystring and url libraries for parsing that data.
  3. We create a POST body of some data (delayedSpongebob) including the spongebob text and a response_type (being "in_channel").
  4. Finally, we'll use the https library to generate the new request, which is just a simple POST with some data.


This guide is meant as a compliment to existing guides which go over the meat of setting up your serverless infrastructure, but don't go deep enough into the specifics of covering the use case of replacing the original message.

If you'd like to make a serverless Slack bot that responds to a slash command which replaces the original message text, it requires knowledge of some specific "gotchas" in the API Gateway, Lambda, and Slack stacks, which this guide covers.

Go forth and troll your teammates.

Adam Gall

Build with Us

We are always interested in connecting with people who want to fund, innovate
or work in the open financial system.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.