Build Your Own Custom SlackBot with Node.js

Share this article

This article was peer reviewed by Dan Prince and Matthew Wilkin. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Slack has a certain appeal and huge fan following in both developer and non-developer tech communities. Its slick user-interface, concept of teams and channels to keep communication separate and relevant, tons of integrations for productivity (Dropbox, Box, Google Calendar, Hangouts etc) and things like giphy and reminders, make it fun to use. Also, their APIs help developers extend the functionality and build a custom experience for their team.

If you’re thinking “no way that’s unique to Slack, HipChat (or your favorite app) has all that too!”, you might wanna take a look at this: http://slackvshipchat.com/

Goal of the Tutorial

This tutorial aims to help you get up and running with a simple node app that turns your Slack channel into a custom command-line terminal. We’ll use a helper module called slack-terminalize (disclaimer: I developed it), that abstracts away the initial processing of messages. It uses Slack’s Real-Time API Node client and prepares a bot to listen and respond to your requests.

Note that we won’t be using Slash Commands here, but instead we’ll interpret regular messages as commands. So if you were looking to learn about Slash Commands, this might not be the appropriate tutorial.

Before Getting Started

My assumption is that you have a working knowledge of JavaScript and NodeJS and that you’re familiar with Slack jargon: teams, channels, bots and integrations. You’ll need node and npm installed; You can follow this wonderful SitePoint introduction to npm, to set up your development environment.

Motivation to Develop slack-terminalize

While there are many fancy hubot scripts that respond to natural language queries, a lot can be achieved with short commands and minimal keystrokes, as any Linux fan would agree. Simple commands especially make sense in a mobile device, helping you type less, do more. If you think about a command-line system, most of the times what the shell is doing is the grunt work of fetching, parsing, tokenizing and dispatching the commands (a gross oversimplification, I know).

With that in mind, I felt the need for a module which could do exactly that. A shell for Slack channels, if you will. With a process-and-dispatch approach and a plugin-like architecture to add custom commands, slack-terminalize abstracts things so you can focus more on defining the behavior of the app instead.

Enough Talk, Let’s Get Started

First, let’s create a new bot user for your team, that can take your orders! Go to https://<your-team-name>.slack.com/services/new/bot, choose a username for it, and hit Add Bot Integration.

Add Bot User

Copy the API token shown to you, as this is required for your bot to be able to interact with the channels. Configure the other details of the bot, its profile image and real name, and hit Save Integration.

Save Bot User

Now, let’s clone the sample app and install the dependencies:

git clone https://github.com/ggauravr/slack-sample-cli.git
cd slack-sample-cli
npm install

Project Structure Walkthrough

From the list of dependencies in package.json, the only required dependency is slack-terminalize, but since the sample app has an example to show how to handle asynchronous commands, the request module is used to make REST calls.

Project Structure

config/

All the JSON files you might need for your app can go here. And I say ‘can’ because it’s fairly flexible and you can change it to work with a different directory via the configuration parameters (more on that later). This is just one of the many ways you can structure your app, but if you’re new to Slack integrations, I suggest you stick with this.

commands.json

This is what makes adding custom commands a piece of cake. Each command is represented by a key-value pair: with the key being the command name (I’ll call it the primary name), and the value being an object with custom key-value pairs that you would want to use for the command.

Here I use the following custom fields for each command:

  • alias – these are the aliases (let’s call them secondary names) for the command, which can be used in the slack channel to invoke the command as well. It’s best to keep the smallest name as the primary name and more meaningful, longer names as aliases.

  • description – a short readable description of what the command does

  • help – a help message, to do something like man <command-name> or help <command-name>

  • exclude – a flag to indicate if this command should be shown in the list of commands available to the user. Some commands might just be for development purposes and/or helpers that need not be exposed to the user (e.g. the error command above).

  • endpoint – REST endpoint that the command should talk to, in case it depends on any external services to perform its task

Of the above, alias is the only key that is looked up to map user-typed commands to its primary name. The rest of them are optional and you’re free to use any fields inside the command object as you see fit.

commands/

This is where the magic happens, the place where you define the command behavior. Each command specified in config/commands.json should have its matching implementation here, with the filename matching the key (primary name) used in that JSON. That is how the dispatcher invokes the correct handler. Yes, a bit opinionated I agree, but useful and customizable nonetheless.

{
    "help": {
        "alias": [ "halp" ],
        "endpoint": "#",
        "help": "help [command](optional)",
        "description": "To get help on all supported commands, or a specified command"
    },

    "gem": {
        "alias": [],
        "endpoint": "https://rubygems.org/api/v1/gems/{gem}.json",
        "help": "gem [gem-name]",
        "description": "Fetches details of the specified Ruby gem"
    },

    "error": {
        "exclude": true
    }
}

Notice again, that the key names in this file are same as the file names in commands/ directory.

Code Walkthrough

Replace the value for SLACK_TOKEN in index.js with the one for your bot. CONFIG_DIR and COMMAND_DIR are to tell slack-terminalize where to look for config and command implementations respectively.

var slackTerminal = require('slack-terminalize');

slackTerminal.init('xoxb-your-token-here', {
    // slack client options here
    }, {
    CONFIG_DIR: __dirname + '/config',
    COMMAND_DIR: __dirname + '/commands'
});

Next, start the app with the following command:

node .

Log in to your Slack team, either on web or app. The bot is added to #general channel by default, but you can invite the bot to any of the channels, even private ones, with the Slash Command: /invite @<your-bot-name>. As soon as you type /invite @, Slack should automatically suggest you the usernames. If you don’t see your bot listed there, go back and check that you have integrated the bot correctly.

Invite Bot

Type help or halp (the alias, remember?) in the channel and ‘voila!’, the bot should respond to your request. Go ahead and play around with commands/help.js to change what you see in the response. As you can see from the implementation, this command just loads the command details from config/commands.json file to respond, so it’s synchronous. Sometimes, you might need to do asynchronous tasks, like querying a database or calling a REST endpoint, to fetch the response. Let’s see how to go about that.

As I mentioned before, I use request module to make REST calls and the following code snippet (the gem command) searches for the gem name that user types in Slack. Take a peek at commands/gem.js and you’ll see that it remembers the channel that the message was posted in (using a closure) and posts back the response into the same channel!

var request = require('request'),
    util    = require('../util');

module.exports = function (param) {
    var channel  = param.channel,
        endpoint = param.commandConfig.endpoint.replace('{gem}', param.args[0]);

    request(endpoint, function (err, response, body) {
        var info = [];

        if (!err && response.statusCode === 200) {
            body = JSON.parse(body);

            info.push('Gem: ' + body.name + ' - ' + body.info);
            info.push('Authors: ' + body.authors);
            info.push('Project URI: ' + body.project_uri);
        }
        else {
            info = ['No such gem found!'];
        }

        util.postMessage(channel, info.join('\n\n'));
    });

};

Try typing gem ab in your Slack channel and you should see something like this:

Gem Response

Again, try playing around with the formatting of the response in commands/gem.js to get the hang of it. Now we have a bot listening on invited channels and responding to our requests. Let’s see how we can add custom commands.

Adding Custom Command Implementations

Add your new command in config/commands.json. As mentioned before, the key will be the primary command name. Aliases for the command go in as an array of values in alias, as shown below.

{
    "your-new-command": {
        "alias": [ "command-alias", "another-alias", "yet-another-alias" ],
        "help": "A short help message for the awesome new command",
        "description": "Brief description of what the command does"
    }
}

Currently, command names with space in them are not supported. Create a file with the same name as the primary name of your command above (in this case, your-command-name.js) in commands/ directory. Assign module.exports to the command implementation function, as shown below.

var util = require('../util');

module.exports = function (param) {
    // param object contains the following keys:
    // 1. command - the primary command name
    // 2. args - an array of strings, which is user's message posted in the channel, separated by space
    // 3. user - Slack client user id
    // 4. channel - Slack client channel id
    // 5. commandConfig - the json object for this command from config/commands.json

    // implement your logic here.. 
    // .. 

    // send back the response
    // more on this method here: https://api.slack.com/methods/chat.postMessage
    util.postMessage(param.channel, '<your-message-to-be-posted-back-in-the-channel>');
};

Refer to the documentation of the node-slack-client, for more on the User and Channel objects.

Program your new command, restart the app and that’s it! You should have your new command working. Type in the command and see if you get the expected response back.

Customizing Behavior with Configurations

The slack-terminalize module takes two parameters, an options object and a config object.

var slackTerminal = require('slack-terminalize');

slackTerminal.init({
    autoReconnect: true // or false, indicates if it should re-connect after error response from Slack
    // other supported options can be seen here: https://github.com/slackhq/node-slack-client/blob/master/lib/clients/rtm/client.js
}, {
    CONFIG_DIR: __dirname + '/config',
    COMMAND_DIR: __dirname + '/commands',
    ERROR_COMMAND: "error" // The filename it looks for in COMMAND_DIR, in case the user entered command is invalid
})

For more on the parameters, you can check the documentation here.

What Next?

  • Go define some cool commands for your team: have fun and increase productivity.
  • Fork the project slack-terminalize and its sample app. Play around, contribute and help improve it. If you spot any bugs, create an issue on the repo!
  • Comment below on how you’re using Slack for productivity, or if you have any suggestions on how this can be improved. I’m all ears to learn the creative applications of the power bestowed upon developers by the Slack API

Links and Resources

Frequently Asked Questions (FAQs) about Custom Slackbot with Node

How can I create a custom Slackbot using Node.js?

Creating a custom Slackbot using Node.js involves several steps. First, you need to set up a new Slack app and bot user. This can be done on the Slack API website. After setting up the bot user, you need to install the necessary Node.js packages, such as the Slackbots module. Once the packages are installed, you can start writing the bot’s code in a new JavaScript file. The bot’s functionality can be customized according to your needs. For example, you can program the bot to respond to specific messages or commands.

What are the prerequisites for creating a custom Slackbot with Node.js?

Before you start creating a custom Slackbot with Node.js, you need to have a few things in place. First, you need to have a basic understanding of JavaScript and Node.js. You also need to have Node.js and npm (Node Package Manager) installed on your computer. Additionally, you need to have a Slack account and permission to install apps on your workspace.

How can I add custom responses to my Slackbot?

Custom responses can be added to your Slackbot by defining them in your bot’s code. You can use the ‘on’ method of the bot object to listen for specific events, such as messages. When the event occurs, the bot can respond with a predefined message. The message can be a fixed string or it can be dynamically generated based on the event’s data.

Can I use external APIs with my Slackbot?

Yes, you can use external APIs with your Slackbot. This can be done by making HTTP requests from your bot’s code. You can use the ‘axios’ package to make these requests. The data returned by the API can be used to generate responses or perform actions.

How can I debug my Slackbot’s code?

Debugging your Slackbot’s code can be done using console.log statements. These statements can print out the values of variables at different points in your code. This can help you identify where things are going wrong. Additionally, you can use Node.js debugging tools, such as the built-in debugger or external tools like Visual Studio Code’s debugging feature.

How can I deploy my Slackbot?

Once your Slackbot is ready, you can deploy it to a server. This can be a server you own or a cloud-based server. You need to install Node.js and npm on the server, and then you can use git to clone your bot’s code onto the server. After that, you can start the bot by running the main JavaScript file with Node.js.

Can I use databases with my Slackbot?

Yes, you can use databases with your Slackbot. This can be useful for storing data that your bot needs to remember, such as user preferences or conversation histories. You can use any database that has a Node.js driver, such as MongoDB or PostgreSQL.

How can I make my Slackbot interact with users?

Your Slackbot can interact with users by listening for and responding to messages. You can program your bot to respond to specific commands or keywords. Additionally, your bot can send messages to users or channels without being prompted.

Can I use Slack’s Web API with my Slackbot?

Yes, you can use Slack’s Web API with your Slackbot. This can be done by making HTTP requests to the API’s endpoints. The API provides a wide range of functionality, such as sending messages, managing channels, and interacting with users.

How can I test my Slackbot?

You can test your Slackbot by running it and interacting with it on Slack. You can send messages to the bot and check if it responds correctly. Additionally, you can use automated testing tools, such as Jest or Mocha, to write tests for your bot’s code.

Gaurav RameshGaurav Ramesh
View Author
Emerging TechnilsonjnodeNode-JS-Tutorialsslackslackbots
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week