How to Build Your First Slack Bot with Python

Post updated by Matt Makai on October 27, 2016. Originally posted on June 04, 2016.

Bots are a useful way to interact with chat services such as Slack. If you have never built a bot before, this post provides an easy starter tutorial for combining the Slack API with Python to create your first bot.

We will walk through setting up your development environment, obtaining a Slack API bot token and coding our simple bot in Python.

Tools We Need

Our bot, which we will name "StarterBot", requires Python and the Slack API. To run our Python code we need:

It is also useful to have the Slack API docs handy while you're building this tutorial.

All the code for this tutorial is available open source under the MIT license in the slack-starterbot public repository.

Establishing Our Environment

We now know what tools we need for our project so let's get our development environment set up. Go to the terminal (or Command Prompt on Windows) and change into the directory where you want to store this project. Within that directory, create a new virtualenv to isolate our application dependencies from other Python projects.

virtualenv starterbot

Activate the virtualenv:

source starterbot/bin/activate

Your prompt should now look like the one in this screenshot.

Command prompt with starterbot's virtualenv activated.

The official slackclient API helper library built by Slack can send and receive messages from a Slack channel. Install the slackclient library with the pip command:

pip install slackclient

When pip is finished you should see output like this and you'll be back at the prompt.

Output from using the pip install slackclient command with a virtualenv activated.

We also need to obtain an access token for our Slack team so our bot can use it to connect to the Slack API.

Slack Real Time Messaging (RTM) API

Slack grants programmatic access to their messaging channels via a web API. Go to the Slack web API page and sign up to create your own Slack team. You can also sign into an existing account where you have administrative privileges.

Use the sign in button on the top right corner of the Slack API page.

After you have signed in go to the Bot Users page.

Custom bot users webpage.

Name your bot "starterbot" then click the “Add bot integration” button.

Add a bot integration named starterbot.

The page will reload and you will see a newly-generated access token. You can also change the logo to a custom design. For example, I gave this bot the Full Stack Python logo.

Copy and paste the access token for your new Slack bot.

Click the "Save Integration" button at the bottom of the page. Your bot is now ready to connect to Slack's API.

A common practice for Python developers is to export secret tokens as environment variables. Export the Slack token with the name SLACK_BOT_TOKEN:

export SLACK_BOT_TOKEN='your slack token pasted here'

Nice, now we are authorized to use the Slack API as a bot.

There is one more piece of information we need to build our bot: our bot's ID. Next we will write a short script to obtain that ID from the Slack API.

Obtaining Our Bot’s ID

It is finally time to write some Python code! We'll get warmed up by coding a short Python script to obtain StarterBot's ID. The ID varies based on the Slack team.

We need the ID because it allows our application to determine if messages parsed from the Slack RTM are directed at StarterBot. Our script also tests that our SLACK_BOT_TOKEN environment variable is properly set.

Create a new file named print_bot_id.py and fill it with the following code.

import os
from slackclient import SlackClient


BOT_NAME = 'starterbot'

slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))


if __name__ == "__main__":
    api_call = slack_client.api_call("users.list")
    if api_call.get('ok'):
        # retrieve all users so we can find our bot
        users = api_call.get('members')
        for user in users:
            if 'name' in user and user.get('name') == BOT_NAME:
                print("Bot ID for '" + user['name'] + "' is " + user.get('id'))
    else:
        print("could not find bot user with the name " + BOT_NAME)

Our code imports the SlackClient and instantiates it with our SLACK_BOT_TOKEN, which we set as an environment variable. When the script is executed by the python command we call the Slack API to list all Slack users and get the ID for the one that matches the name "starterbot".

We only need to run this script once to obtain our bot’s ID.

python print_bot_id.py

The script prints a single line of output when it is run that provides us with our bot's ID.

Use the Python script to print the Slack bot's ID in your Slack team.

Copy the unique ID that your script prints out. Export the ID as an environment variable named BOT_ID.

(starterbot)$ export BOT_ID='bot id returned by script'

The script only needs to be run once to get the bot ID. We can now use that ID in our Python application that will run StarterBot.

Coding Our StarterBot

We've got everything we need to write the StarterBot code. Create a new file named starterbot.py and include the following code in it.

import os
import time
from slackclient import SlackClient

The os and SlackClient imports will look familiar because we used them in the print_bot_id.py program.

With our dependencies imported we can use them to obtain the environment variable values and then instantiate the Slack client.

# starterbot's ID as an environment variable
BOT_ID = os.environ.get("BOT_ID")

# constants
AT_BOT = "<@" + BOT_ID + ">"
EXAMPLE_COMMAND = "do"

# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))

The code instantiates the SlackClient client with our SLACK_BOT_TOKEN exported as an environment variable.

if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("StarterBot connected and running!")
        while True:
            command, channel = parse_slack_output(slack_client.rtm_read())
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")

The Slack client connects to the Slack RTM API WebSocket then constantly loops while parsing messages from the firehose. If any of those messages are directed at StarterBot, a function named handle_command determines what to do.

Next add two new functions to parse Slack output and handle commands.

def handle_command(command, channel):
    """
        Receives commands directed at the bot and determines if they
        are valid commands. If so, then acts on the commands. If not,
        returns back what it needs for clarification.
    """
    response = "Not sure what you mean. Use the *" + EXAMPLE_COMMAND + \
               "* command with numbers, delimited by spaces."
    if command.startswith(EXAMPLE_COMMAND):
        response = "Sure...write some more code then I can do that!"
    slack_client.api_call("chat.postMessage", channel=channel,
                          text=response, as_user=True)


def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and AT_BOT in output['text']:
                # return text after the @ mention, whitespace removed
                return output['text'].split(AT_BOT)[1].strip().lower(), \
                       output['channel']
    return None, None

The parse_slack_output function takes messages from Slack and determines if they are directed at our StarterBot. Messages that start with a direct command to our bot ID are then handled by our code - which is currently just posts a message back in the Slack channel telling the user to write some more Python code!

Here is how the entire program should look when it's all put together (you can also view the file in GitHub):

import os
import time
from slackclient import SlackClient


# starterbot's ID as an environment variable
BOT_ID = os.environ.get("BOT_ID")

# constants
AT_BOT = "<@" + BOT_ID + ">"
EXAMPLE_COMMAND = "do"

# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))


def handle_command(command, channel):
    """
        Receives commands directed at the bot and determines if they
        are valid commands. If so, then acts on the commands. If not,
        returns back what it needs for clarification.
    """
    response = "Not sure what you mean. Use the *" + EXAMPLE_COMMAND + \
               "* command with numbers, delimited by spaces."
    if command.startswith(EXAMPLE_COMMAND):
        response = "Sure...write some more code then I can do that!"
    slack_client.api_call("chat.postMessage", channel=channel,
                          text=response, as_user=True)


def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is an events firehose.
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and AT_BOT in output['text']:
                # return text after the @ mention, whitespace removed
                return output['text'].split(AT_BOT)[1].strip().lower(), \
                       output['channel']
    return None, None


if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("StarterBot connected and running!")
        while True:
            command, channel = parse_slack_output(slack_client.rtm_read())
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")

Now that all of our code is in place we can run our StarterBot on the command line with the python starterbot.py command.

Console output when the StarterBot is running and connected to the API.

In Slack, create a new channel and invite StarterBot or invite it to an existing channel.

In the Slack user interface create a new channel and invite StarterBot.

Now start giving StarterBot commands in your channel.

Give StarterBot commands in your Slack channel.

As it is currently written above in this tutorial, the line AT_BOT = "<@" + BOT_ID + ">" does not require a colon after the "@starter" (or whatever you named your particular bot) mention. Previous versions of this tutorial did have a colon because Slack clients would auto-insert the ":" but that is no longer the case.

Wrapping Up

Alright, now you've got a simple StarterBot with a bunch of places in the code you can add whatever features you want to build.

There is a whole lot more that could be done using the Slack RTM API and Python. Check out these posts to learn what you could do:

Questions? Contact me via Twitter @fullstackpython or @mattmakai. I'm also on GitHub with the username mattmakai.

See something wrong in this post? Fork this page's source on GitHub and submit a pull request.


Sign up here to receive a monthly email with major updates to this site, tutorials and discount codes for Python books.

Sponsored By

Rollbar logo

Fix errors in your Python applications before your users ever see them by monitoring your app with Rollbar.


Matt Makai 2012-2017