← all builds

From-Scratch Build · 03 · ChatOps

Slack Command Bot

A Slack bot small enough to read in one sitting — and exactly the right size to finally understand how a bot actually works: a long-lived program listening to a firehose of messages, waking up only when someone @-mentions it.

PythonSlack RTM APIslackclient Event loopChatOps

What it is

A research group's helper, in ~90 lines

A busy team's chat needs a helper — something that lives in Slack and answers questions about the team's activities and software stack. This little bot does exactly that, and it's deliberately tiny, which is exactly why it's a perfect first bot to build from scratch.

Strip away the ambition and what's left is the canonical shape of every chat bot: connect, listen forever, and react when addressed. Learning that skeleton is worth far more than the feature it shipped with.

The core idea I wanted to learn: a bot is not a website that waits for requests. It's a process that holds an open connection and reads an endless stream of events, filtering for the few that are meant for it.

The stack

Tools under the hood

language

Python

The whole bot is two short scripts — one to find the bot's user ID, one to run it.

transport

Slack RTM API

Real Time Messaging — a WebSocket "firehose" that streams every event in the workspace as it happens.

library

slackclient

The Python SDK that opens the RTM socket (rtm_connect), reads events (rtm_read) and posts replies (chat.postMessage).

config

Env vars

Secrets stay out of the code: the bot token and bot ID are read from SLACK_BOT_TOKEN and BOT_ID.

How it works

The listen loop

The entire bot is one idea repeated forever: read the firehose, check if a message was aimed at me, respond. Here's the skeleton I built to internalise it:

# connect once, then loop forever
if slack.rtm_connect():
    while True:
        events = slack.rtm_read()          # the firehose
        for e in events:
            if AT_BOT in e.get("text", ""):  # was I mentioned?
                cmd = e["text"].split(AT_BOT)[1].strip()
                handle(cmd, e["channel"])     # react
        time.sleep(1)                       # don't busy-spin
  1. Find the bot's ID

    A one-off script calls users.list and matches the bot's name to learn its <@ID> mention string.

  2. Connect to the firehose

    rtm_connect() opens the WebSocket. From here, every message in the workspace streams in.

  3. Filter for mentions

    Most events aren't for the bot. It only acts on messages whose text contains its <@ID>.

  4. Parse & respond

    Take the text after the mention, decide what it means, and post a reply back to the same channel.

Reflection

What rebuilding it taught me