Guy's Bot - Inclusive Language in Slack

@lukeocodes 🕹👨‍💻 - Apr 23 '20 - - Dev Community

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 into a Slack workspace. Unfortunately, it wasn't working :(. I actually reached out to Knut 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".

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 logo lukeocodes / guys-bot

Remake of https://glitch.com/~guys-bot-for-slack to work with new Slack APIs using Slack Node SDK.

Simple Guys Bot

Guy - the bot

If you're as concious as I am about the use of language in your Slack workspace, this will interest you.

With the existing guys-bot-for-slack needing some updates to work with the latest flavour of Slack API, I chose to recreate it using the most basic implementation of the Slack SDKs.

Screenshot of the bot responding to the message

The bot responds with an ephemeral message, not seen by anyone but the user who originally messaged.

It requires the bot be invited into the channels you want it to listen on, so it can be implemented by individuals, or teams, as channels require, if that is what you want.

This guide has steps on the Slack API control panel. Please read Who's a Good Bot? A Slack Bot For Inclusive Language, the supporting blog post for this application, on how to set up your Slack App to retrieve your SLACK_TOKEN and SLACK_SIGNING_SECRET.

Installation

Get the…

Build Your Own

The first step is to create a new app on Slack. Give it an app name, and select a workspace you'll develop it on.

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 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.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install the Slack Events SDK as a dependency.

npm install @slack/events-api
Enter fullscreen mode Exit fullscreen mode

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.

// 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}`);
});
Enter fullscreen mode Exit fullscreen mode

Use a Local Server for Events

Developing this, you'll need to use ngrok to enable you to provide Slack with a public Request URL. Slack has a great guide on developing locally, and specifically about using ngrok.

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:

SLACK_SIGNING_SECRET=89fbcyourslacksigningsecreteb09e node index.js
Enter fullscreen mode Exit fullscreen mode

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

ngrok http 3000
Enter fullscreen mode Exit fullscreen mode

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

Click on Event Subscriptions to add it as a feature.

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

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

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

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

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

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

Console Log output of a Slack event

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.

  // index.js

  ...
-   console.log(event)
+   if (
+     event.hasOwnProperty('text') &&
+     event.text.toLowerCase().includes('guys')
+   ) {
+     console.log(event)
+   }
  ...
Enter fullscreen mode Exit fullscreen mode

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

Logging messages when

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 back, I'll never know!

Install the Slack Web SDK.

npm install @slack/web-api
Enter fullscreen mode Exit fullscreen mode
  // 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_?',
+       });
+     })();
    }
  ...
Enter fullscreen mode Exit fullscreen mode

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

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:

SLACK_SIGNING_SECRET=89fbcyourslacksigningsecreteb09e \
SLACK_TOKEN=xoxb-9109-10904-Nhdjq \
node index.js
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . .