Webhooks in Slack

In this article by Paul Asjes, the author of the book, Building Slack Bots, we'll have a look at webhooks in Slack.

(For more resources related to this topic, see here.)

Slack is a great way of communicating at your work environment—it's easy to use, intuitive, and highly extensible. Did you know that you can make Slack do even more for you and your team by developing your own bots? This article will teach you how to implement incoming and outgoing webhooks for Slack, supercharging your Slack team into even greater levels of productivity. The programming language we'll use here is JavaScript; however, webhooks can be programmed with any language capable of HTTP requests.

Webhooks

First let's talk basics: a webhook is a way of altering or augmenting a web application through HTTP methods. Webhooks allow us to post messages to and from Slack using regular HTTP requests with a JSON payloads. What makes a webhook a bot is its ability to post messages to Slack as if it were a bot user.

These webhooks can be divided into incoming and outgoing webhooks, each with their own purposes and uses.

Incoming webhooks

An example of an incoming webhook is a service that relays information from an external source to a Slack channel without being explicitly requested, such as GitHub Slack integration:

The GitHub integration posts messages about repositories we are interested in

In the preceding screenshot, we see how a message was sent to Slack after a new branch was made on a repository this team was watching. This data wasn't explicitly requested by a team member but automatically sent to the channel as a result of the incoming webhook.

Other popular examples include Jenkins integration, where infrastructure changes can be monitored in Slack (for example, if a server watched by Jenkins goes down, a warning message can be posted immediately to a relevant Slack channel).

Let's start with setting up an incoming webhook that sends a simple "Hello world" message:

  1. First, navigate to the Custom Integration Slack team page, as shown in the following screenshot (https://my.slack.com/apps/build/custom-integration):

    The various flavors of custom integrations

  2. Select Incoming WebHooks from the list and then select which channel you'd like your webhook app to post messages to:

    Webhook apps will post to a channel of your choosing

  3. Once you've clicked on the Add Incoming WebHooks integration button, you will be presented with this options page, which allows you to customize your integration a little further:

    Names, descriptions, and icons can be set from this menu

  4. Set a customized icon for your integration (for this example, the wave emoji was used) and copy down the webhook URL, which has the following format:
    https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

This generated URL is unique to your team, meaning that any JSON payloads sent via this URL will only appear in your team's Slack channels.

Now, let's throw together a quick test of our incoming webhook in Node. Start a new Node project (remember: you can use npm init to create your package.json file) and install the superagent AJAX library by running the following command in your terminal:

npm install superagent –save

Create a file named index.js and paste the following JavaScript code within it:

const WEBHOOK_URL = [YOUR_WEBHOOK_URL];

const request = require('superagent');

request
  .post(WEBHOOK_URL)
  .send({
    text: 'Hello! I am an incoming Webhook bot!'
  })
  .end((err, res) => {
    console.log(res);
  });

Remember to replace [YOUR_WEBHOOK_URL] with your newly generated URL, and then run the program by executing the following command:

nodemon index.js

Two things should happen now: firstly, a long response should be logged in your terminal, and secondly, you should see a message like the following in the Slack client:

The incoming webhook equivalent of "hello world"

The res object we logged in our terminal is the response from the AJAX request. Taking the form of a large JavaScript object, it displays information about the HTTP POST request we made to our webhook URL.

Looking at the message received in the Slack client, notice how the name and icon are the same ones we set in our integration setup on the team admin site. Remember that the default icon, name, and channel are used if none are provided, so let's see what happens when we change that. Replace your request AJAX call in index.js with the following:

request
  .post(WEBHOOK_URL)
  .send({
    username: "Incoming bot",
    channel: "#general",
    icon_emoji: ":+1:",
    text: 'Hello! I am different from the previous bot!'
  })
  .end((err, res) => {
    console.log(res);
  });

Save the file, and nodemon will automatically restart the program. Switch over to the Slack client and you should see a message like the following pop up in your #general channel:

New name, icon, and message

In place of icon_emoji, you could also use icon_url to link to a specific image of your choosing.

If you wish your message to be sent only to one user, you can supply a username as the value for the channel property:

channel: "@paul"

This will cause the message to be sent from within the Slackbot direct message. The message's icon and username will match either what you configured in the setup or set in the body of the POST request.

Finally, let's look at sending links in our integration. Replace the text property with the following and save index.js:

text: 'Hello! Here is a fun link: <http://www.github.com|Github is
great!>'

Slack will automatically parse any links it finds, whether it's in the http://www.example.com or www.example.com formats. By enclosing the URL in angled brackets and using the | character, we can specify what we would like the URL to be shown as:

Formatted links are easier to read than long URLs

For more information on message formatting, visit https://api.slack.com/docs/formatting.

Note that as this is a custom webhook integration, we can change the name, icon, and channel of the integration. If we were to package the integration as a Slack app (an app installable by other teams), then it is not possible to override the default channel, username, and icon set.

Incoming webhooks are triggered by external sources; an example would be when a new user signs up to your service or a product is sold. The goal of the incoming webhook is to provide information to your team that is easy to reach and comprehend. The opposite of this would be if you wanted users to get data out of Slack, which can be done via the medium of outgoing webhooks.

Outgoing webhooks

Outgoing webhooks differ from the incoming variety in that they send data out of Slack and to a service of your choosing, which in turn can respond with a message to the Slack channel.

To set up an outgoing webhook, visit the custom integration page of your Slack team's admin page again—https://my.slack.com/apps/build/custom-integration—and this time, select the Outgoing WebHooks option.

On the next screen, be sure to select a channel, name, and icon. Notice how there is a target URL field to be filled in; we will fill this out shortly.

When an outgoing webhook is triggered in Slack, an HTTP POST request is made to the URL (or URLs, as you can specify multiple ones) you provide. So first, we need to build a server that can accept our webhook.

In index.js, paste the following code:

'use strict';

const http = require('http');

// create a simple server with node's built in http module
http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});

  // get the data embedded in the POST request
  req.on('data', (chunk) => {
    // chunk is a buffer, so first convert it to
    // a string and split it to make it more legible as an array
     console.log('Body:', chunk.toString().split('&'));
  });

  // create a response
  let response = JSON.stringify({
    text: 'Outgoing webhook received!'
  });

  // send the response to Slack as a message
  res.end(response);
}).listen(8080, '0.0.0.0');

console.log('Server running at http://0.0.0.0:8080/');

Notice how we require the http module despite not installing it with NPM. This is because the http module is a core Node dependency and is automatically included with your installation of Node.

In this block of code, we start a simple server on port 8080 and listen for incoming requests.

In this example, we set our server to run at 0.0.0.0 rather than localhost. This is important as Slack is sending a request to our server, so it needs to be accessible from the Internet. Setting the IP of your server to 0.0.0.0 tells Node to use your computer's network-assigned IP address. Therefore, if you set the IP of your server to 0.0.0.0, Slack can reach your server by hitting your IP on port 8080 (for example, http://123.456.78.90:8080).

If you are having trouble with Slack reaching your server, it is most likely because you are behind a router or firewall. To circumvent this issue, you can use a service such as ngrok (https://ngrok.com/). Alternatively, look at port forwarding settings for your router or firewall.

Let's update our outgoing webhook settings accordingly:

The outgoing webhook settings, with a destination URL

Save your settings and run your Node app; test whether the outgoing webhook works by typing a message into the channel you specified in the webhook's settings. You should then see something like this in Slack:

We built a spam bot

Well, the good news is that our server is receiving requests and returning a message to send to Slack each time. The issue here is that we skipped over the Trigger Word(s) field in the webhook settings page. Without a trigger word, any message sent to the specified channel will trigger the outgoing webhook. This causes our webhook to be triggered by a message sent by the outgoing webhook in the first place, creating an infinite loop.

To fix this, we could do one of two things:

  1. Refrain from returning a message to the channel when listening to all the channel's messages.
  2. Specify one or more trigger words to ensure we don't spam the channel.

Returning a message is optional yet encouraged to ensure a better user experience. Even a confirmation message such as Message received! is better than no message as it confirms to the user that their message was received and is being processed.

Let's therefore presume we prefer the second option, and add a trigger word:

Trigger words keep our webhooks organized

Let's try that again, this time sending a message with the trigger word at the beginning of the message. Restart your Node app and send a new message:

Our outgoing webhook app now functions a lot like our bots from earlier

Great, now switch over to your terminal and see what that message logged:

Body: [ 'token=KJcfN8xakBegb5RReelRKJng',
  'team_id=T000001',
  'team_domain=buildingbots',
  'service_id=34210109492',
  'channel_id=C0J4E5SG6',
  'channel_name=bot-test',
  'timestamp=1460684994.000598',
  'user_id=U0HKKH1TR',
  'user_name=paul',
  'text=webhook+hi+bot%21',
  'trigger_word=webhook' ]

This array contains the body of the HTTP POST request sent by Slack; in it, we have some useful data, such as the user's name, the message sent, and the team ID. We can use this data to customize the response or to perform some validation to make sure the user is authorized to use this webhook.

In our response, we simply sent back a Message received string; however, like with incoming webhooks, we can set our own username and icon. The channel cannot be different from the channel specified in the webhook's settings, however. The same restrictions apply when the webhook is not a custom integration. This means that if the webhook was installed as a Slack app for another team, it can only post messages as the username and icon specified in the setup screen.

An important thing to note is that webhooks, either incoming or outgoing, can only be set up in public channels. This is predominantly to discourage abuse and uphold privacy, as we've seen that it's simple to set up a webhook that can record all the activity on a channel.

Summary

In this article, you learned what webhooks are and how you can use them to get data in and out of Slack. You learned how to send messages as a bot user and how to interact with your users in the native Slack client.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Building Slack Bots

Explore Title
comments powered by Disqus