Guy's Bot - Inclusive Language in Slack
Tags: showdev, node, inclusion, opensource
Published: 2020-04-23 15:23:13

---
title: Guy's Bot - Inclusive Language in Slack
published: true
description: Working remake of popular Slack Bot guys-bot-for-slack with modern Slack APIs, using a basic implementation of Slack's Node.js SDKs
tags: showdev, nodejs, inclusion, opensource
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/pl0re7rsqh6aydbisumi.png
---

Every few weeks at work, we get an opportunity to hack something as a team or individual. The only rules are that it follows a theme and that you share it with the team.

This one though, I'm sharing with everyone!

Late last year, I tried to introduce [`guys-bot-for-slack`](https://glitch.com/~guys-bot-for-slack) into a Slack workspace. Unfortunately, it wasn't working :(. I actually reached out to [Knut](https://dev.to/kmelve) who wrote the original, hoping he'd be able to point out what I was doing wrong. It turns out that Slack's API has changed so much that the bot had broken.

## Social Good

With the global situation as it is (COVID-19), social good was a great choice for our hack theme. I've been meaning to revisit bot for a while, but I've been so busy it is hard to justify jumping into something (even like this) not knowing how long it could take to complete. 

This hack was exactly the opportunity I needed to work on it, as it lasts a few days.

And, just as Knut's bot did, and as his supporting post said before, this bot "suggests some alternative phrasing when you write guys in Slack".

{% post kmelve/the-problem-with-you-guys-51h7 %}

Not everyone thinks the same, but this bot attempts to find the best solution for a difficult situation.

## My Code

I made the app in full and it is available for everyone on GitHub.

{% github lukeocodes/guys-bot %}

## Build Your Own

The first step is to [create a new app on Slack](https://api.slack.com/apps?new_app=1). Give it an app name, and select a workspace you'll develop it on.

![Create a new guys bot app for Slack](https://dev-to-uploads.s3.amazonaws.com/i/67bok81hgmejsymm5bsl.png "Create a new guys bot app for Slack")

### Get the Code Ready

When building this, you have some options. You can [clone my repo and follow the install steps in the README](https://github.com/lukeocodes/guys-bot) to run it locally, or deploy to Heroku, or remix it to Glitch. All before continuing to follow the Slack setup steps, throughout the rest of this post. 

**Or, start from a blank project.**

So, from a blank project directory, you follow these steps, starting with enabling NPM.

```shell
npm init -y
```

Install the Slack Events SDK as a dependency.

```shell
npm install @slack/events-api
```

Create an `index.js` file and use this code. This is a basic listener for Slack events. It can start its own instance of an HTTP server.

```js
// index.js

const {createEventAdapter} = require('@slack/events-api');
const slackEvents = createEventAdapter(process.env.SLACK_SIGNING_SECRET);
const port = process.env.PORT || 3000;

slackEvents.on('message', (event) => {
  console.log(event)
});

slackEvents.on('error', console.error);

slackEvents.start(port).then(() => {
  console.log(`server listening on port ${port}`);
});
```

### Use a Local Server for Events

Developing this, you'll need to use [ngrok](https://ngrok.com) to enable you to provide Slack with a public Request URL. [Slack has a great guide on developing locally, and specifically about using ngrok](https://slack.dev/node-slack-sdk/tutorials/local-development#using-a-local-request-url-for-development).

Once you have a public Request URL, you need to verify requests from Slack before you can listen to events. Slack has a great feature, which invalidates your endpoint if it can't verify messages and respond correctly using a Signing Secret.

Slack's guides at this point become a little confusing, but if you find your Signing Secret on your Slack app's Basic Information page (https://api.slack.com/apps/YOUR_APP_ID/general), you need to set that as a local environment variable.

With your Signing Secret (e.g. `89fbcyourslacksigningsecreteb09e`) ready, run the application like this:

```shell
SLACK_SIGNING_SECRET=89fbcyourslacksigningsecreteb09e node index.js
```

Run ngrok in another shell window, but in the same working directory:

```shell
ngrok http 3000
```

### Setup Event Subscription

On your Slack app's Basic Information page (https://api.slack.com/apps/YOUR_APP_ID/general), you'll find a dropdown to **Add features and functionality**. Click on that!

![Expand the Add Features and Functionality dropdown](https://dev-to-uploads.s3.amazonaws.com/i/0fr4s7hvlj1o5hjfuidp.png "Expand the Add Features and Functionality dropdown")

Click on Event Subscriptions to add it as a feature.

![Click on Event Subscriptions to add it as a feature](https://dev-to-uploads.s3.amazonaws.com/i/j3pev5f35vilq4ed2qx0.png "Click on Event Subscriptions to add it as a feature")

This takes you to your app's Event Subscription page ( https://api.slack.com/apps/YOUR_APP_ID/event-subscriptions?), switch *Enable Events* on and enter your ngrok address followed by `/slack/events`. Notice that Slack will verify the address. This is because the SDK uses the Signing Secret for verification. 

![Add a Request URL as your ngrok address followed by /slack/events](https://dev-to-uploads.s3.amazonaws.com/i/iqewe5clmx13dmfarpe1.png "Add a Request URL as your ngrok address followed by /slack/events")

Once verified, expand the section **Subscribe to bot events** on the Event Subscription page, click on **Add Bot User Event**. 

Search for the *message.channels* event.
 
![Searching for message.channels event to add to the bot user events](https://dev-to-uploads.s3.amazonaws.com/i/8k0vi6ubds984z7fcioa.png "Searching for message.channels event to add to the bot user events")

Click to add it to your events. This will automatically add the correct permission scopes for authorizing your bot.

![message.channels event and channels:history scope added to bot user events](https://dev-to-uploads.s3.amazonaws.com/i/jwedjf3jqasm2m0g83d7.png "message.channels event and channels:history scope added to bot user events")

Now at the bottom of the screen, click on **Save Changes**.

### Install App to Workspace

Back to your Slack app's Basic Information page (https://api.slack.com/apps/YOUR_APP_ID/general) and expand the next dropdown to **Install your app to your workspace**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gbvsqc1aob5vuzd7jj4p.png)

Then click on the button **Install App to Workspace**. You'll be prompted on an OAuth page to allow your app to *view content and info about channels & conversations*. You can safely enable this, as it's only your app it is running.

Once approved, your App is configured ready to send events to your code.

### Add Your App to A Slack Channel

This bot works by joining a channel, so you can choose where to enable it.

In your channel, click on **Add an app** and select the app from the list, or type @Guy as a message in the channel inviting them in (assuming you named it Guy).

![Find your app and add it to your channel](https://dev-to-uploads.s3.amazonaws.com/i/vcsgkezn4itybetyalcq.png)

Now, any messages you write in the channel will be sent to your local node server.

![Console Log output of a Slack event](https://dev-to-uploads.s3.amazonaws.com/i/cyhtqlhq2naelpdreg7v.png)

## Looking for Language

There were a couple of ways I considered looking for words in a users message. In the end, I decided to normalise the sentence (make it all lowercase) and use the native [JavaScript string method `includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes). 

```diff
  // index.js

  ...
-   console.log(event)
+   if (
+     event.hasOwnProperty('text') &&
+     event.text.toLowerCase().includes('guys')
+   ) {
+     console.log(event)
+   }
  ...
```

Sending a message like "hey guys" will now be logged to the console.

![Logging messages when "guys" is used](https://dev-to-uploads.s3.amazonaws.com/i/x8gz9g98018ghyvf4nbm.png)

## Replying 

We receive events, we know when it matches our rule. Now, to reply.

The built-in auto-responses inside Slack reply with a public message. WHY they've never added the option to [send an ephemeral message](https://api.slack.com/methods/chat.postEphemeral) back, I'll never know!

Install the Slack Web SDK.

```shell
npm install @slack/web-api
```

```diff
  // index.js

+ const {WebClient} = require('@slack/web-api');
+ const web = new WebClient(process.env.SLACK_TOKEN);

  ...

    if (
      event.hasOwnProperty('text') &&
      event.text.toLowerCase().includes('guys')
    ) {
-     console.log(event)
+     (async () => {
+       await web.chat.postEphemeral({
+         channel: event.channel,
+         user: event.user,
+         text: 'Hey, perhaps consider team instead of _guys_?',
+       });
+     })();
    }
  ...
```

The Web SDK needs an access token now. 

To do that, it's over to your app's OAuth & Permissions page ( https://api.slack.com/apps/YOUR_APP_ID/event-subscriptions?) and scroll down to **Scopes**. Find the `chat:write` scope and add it to **Send messages as @guy** (or your app name).

![Find the chat:write scope and add it](https://dev-to-uploads.s3.amazonaws.com/i/229l19v4zgnz5f6w9x7w.png)

Once added, you'll be prompted to reinstall your app to your workspace. The big yellow alert gives you a link to do this. Clicking on the link, you'll be prompted to authorise the app for its new scope.

Having accepted the new permissions for the bot, you'll be directed back to the top of your app's OAuth & Permissions page. Here, find the **Bot User OAuth Access Token** which looks like `xoxb-9109-10904-Nhdjq`, which you'll need as your `SLACK_TOKEN` to send responses.

With your Signing Secret (e.g. `89fbcyourslacksigningsecreteb09e`) and Bot User OAuth Access Token (e.g. `xoxb-9109-10904-Nhdjq` ready, run the application like this:

```shell
SLACK_SIGNING_SECRET=89fbcyourslacksigningsecreteb09e \
SLACK_TOKEN=xoxb-9109-10904-Nhdjq \
node index.js
```

![Screenshot of the response from Slack working as expected](https://dev-to-uploads.s3.amazonaws.com/i/svgau4rv3vdie4vnjwtm.png "Screenshot of the response from Slack working as expected")

## What's Next?

### Capture More Language With Rules

This had a very specific use case, but there are lots of examples of non-inclusive language we use all the time. The next step would be to enable this app to accept an array of matches and the appropriate response.

### Slack App Directory

Slack appears to want an OAuth redirect to enable programmatic installation of the app against any workspace via auth on the app distributor's architecture. There's no scope for that here, but I'd love people to be able to install this easily into their workspaces. If you know how to achieve this simply, I'd love to hear from you.