Slack is a virtual office. Businesses, teams, and companies rely on it for communications and for managing daily activities. Activities such as placing orders for lunch, or contacting the front desk team or placing any form of request on the company's website can be carried out via the Slack workspace using Slack integrations via the Slack API.
In this article, I will be showing how you can build an integration for sending requests from the Slack workspace to a given website. We will build a simple Nodejs server to test the integration.
Requirements
- Knowledge of Node.js/Express
Slack workspace: If you don't have a Slack workspace, you can create one for test purposes.
Ngrok: Since we will be working with a local development server, we will need ngrok to provision a public URL from the API in order to be able to communicate with Slack. Visit the link to install it or you can install it globally via npm here
With those requirements above met, we can proceed to get the stuff done following the steps below:
Create Slack App
In order to interact with the Slack API, you need a Slack app. To create a Slack app, you need a Slack account (a workspace). Click here to create a Slack app.
Next up is to generate an access token for authorizing requests between the website and the Slack workspace. You will get the token by clicking OAuth & Permissions
Next up, we need to create a slash command (/frontdesk). The slash command would pop up a modal when a user invokes it anywhere on the Slack workspace. But before creating the slash command, let's spin up an Express server. If you already have a server running you can skip to Creating Slash Command. The reason for having a running server at this point is that the slash command requires the endpoint for sending payloads to the website when invoked.
Clone the project used in this article here to follow along
In server/index.js
, I set up a simple Express server as shown in the snippet below:
const express = require('express');
const bodyParser = require('body-parser');
const routes = require('../routes/');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.json({ message: 'The app is running...'})
})
app.use('/api', routes);
module.exports = app;
Note: You must use the bodyParser.urlencoded()
middle. This is needed to parse the url-encoded payload from Slack.
Slash command
Slash command is a means to trigger an action from the Slack workspace. In our case, we want a user to be able to contact the front desk by invoking the /frontdesk
command on Slack. When a user types the command /frontdesk
and hits enter; an action is triggered and Slack will notify your website through an endpoint you provided while creating the slash command. In our case, the endpoint simply returns a modal with a form for the user to submit his/her request.
Creating the slash command endpoint
The code for the slash command endpoint is as shown below:
router.post('/slack/frontdesk', (req, res) => {
const { trigger_id: triggerId } = req.body;
res.status(200).send('');
(async () => {
// Open a modal.
await web.views.open({
trigger_id: triggerId,
view: {
type: 'modal',
title: {
type: 'plain_text',
text: 'Contact Front Desk',
},
submit: {
type: 'plain_text',
text: 'Submit',
},
callback_id: 'frontdesk',
blocks: [
{
type: 'section',
text: {
type: 'plain_text',
text: ':wave: We will get back to you as soon as possible',
emoji: true,
},
},
{
type: 'divider',
},
{
type: 'input',
block_id: 'title',
label: {
type: 'plain_text',
text: 'Title',
emoji: true,
},
element: {
type: 'plain_text_input',
multiline: false,
action_id: 'title',
},
},
{
type: 'input',
block_id: 'description',
label: {
type: 'plain_text',
text: 'Description',
emoji: true,
},
element: {
type: 'plain_text_input',
multiline: true,
action_id: 'description',
},
optional: true,
},
],
},
});
})();
});
Things to note from the slash command endpoint
- The
req.body
contains among other payloads thetrigger_id
, thetrigger_id
is an identifier for the source of the command. In order to send the modal form to the user that invoked the command, we need thetrigger_id
so that the modal pops up at the exact channel. - I used the @slack/web-api which makes sending HTTP request to the Slack API seamless.
- I used the Slack modal, read more about it.
- I used the Block Kit to generate the modal form, read more about it.
- Note that each block has
block_id
andaction_id
, you'll find the reason shortly. - The line
res.status(200).send('');
is a way to tell slack that the trigger was received successfully. Failure to return a response of 200 would cause Slack trigger an error.
Handling Interactions
When the user submits the form, Slack sends the payload to an endpoint that you specified for it. This means that we need another endpoint for this purpose. The code for the interactions endpoint is as shown below:
router.post('/slack/interactions', (req, res) => {
res.status(200).send('');
const payload = JSON.parse(req.body.payload);
// view the payload on console
console.log(payload);
if (
payload.type === 'view_submission' &&
payload.view.callback_id === 'frontdesk'
) {
const { values } = payload.view.state;
const title = values.title.title.value;
const description = values.description.description.value;
console.log(`title ----->${title}`, `description---->${description}`);
// Save the title and description to the database or handle it as you may deem fit.
}
});
The form payload is located at the payload
object of the req.body
. The form values are located in the payload.view.state
object.
Notice the long chain in order to extract the form input value, that is due to the block_id
and action_id
used in building the form, without it, the form values would not be deterministic as the block_id
and action_id
would be random values.
Lastly, we need to create the actual slash command on the slack app we created earlier and also specify the interactions endpoint. To do so, we need to start the server and provision a public URL using ngrok
.
npm start
Next up, navigate to the folder where the ngrok was unzipped and run the command below:
./ngrok http 3000
If you installed ngrok globally, you can run the command below anywhere on the terminal
ngrok http 3000
Note that 3000
is the port the server is listening, you can change it to the port yours is running.
The screenshot below shows the commands above in action:
Now that we have provisioned a public URL from the local server, the slash command and the interactions endpoints would be https://38a73f75.ngrok.io/api/slack/frontdesk
and https://38a73f75.ngrok.io/api/slack/interactions
respectively. Let's proceed to create the slash command.
Head to the Slack app you created earlier as shown below:
Fill the form as shown below:
Next up, click the Interactive Components to add the interactions endpoint as shown below:
It is time to test it! Head over to the Slack workspace and invoke the /frontdesk
command, you'll see a modal as shown below:
The result of submitting the form is shown below as logged on the console:
Conclusion
I tried to make this article as simple as possible, hence, the snippets may not have followed best practices, feel free to refactor it to suit your needs.
If you have some suggestions that would further make the article easier to the readers, you are welcome to do so. Gracias!
Originally published in my blog