How to Build a Python Slack Bot Game

February 21, 2018
Written by
Lesley Cordero
Contributor
Opinions expressed by Twilio contributors are their own

Slack_bot_with_Python

One of my all-time favorite Facebook groups is “DogSpotting.” For those of you unfamiliar with this revolutionary group, it’s a Facebook group dedicated to posting pictures of random dogs you see as you go along your regular day. There are tons of “spotting” rules, but any way you slice it, this group is awesome.

Using this model for inspiration, I built a Slack bot with Python for a college student group I was involved in once upon a time. We named it ADI Spotting and dedicated an entire Slack channel to posting “spottings” whenever we’d see each other on campus, outside of our own events and meetings. In this tutorial, I will walk you through the steps to create this bot using Python for your own Slack organization.

Python Environment Setup

But before we even get started, we have to set our environment up. This guide was written in Python 3.6. If you haven’t already, download Python and Pip. Next, you’ll need to install several packages that we’ll use throughout this tutorial on the command line in our project directory:

pip3 install slackclient==1.1.0

If you don’t already have a Slack organization to work with, first create that. To make this tutorial more interesting, I also recommend adding some people to it! Since we’ll be using the Slack API, click the “Create a Slack App” button circled below:

Create a Slack App button

This will return this web page, where you can enter your app’s name. For this tutorial, I named mine adispotting, but feel free to adjust for your own organization! Once you fill out the information, click “Create App”.

Dialog stepping through creating a Slack App

Several options will appear that you can add to your application, including “Bots” which is circled below.

Create a Slack Bot for API Keys in the Python integration

Once you click the “Bots” option, there will be an “Add a Bot User” which you’ll need to click to continue the process.

Add a bot user button

Just as you’ve done before, fill out the needed fields and select “Add Bot User”.

Fill out the add bot user form in Slack

Next, on the left sidebar click the option ‘OAuth & Permissions’, then click ‘Install App to Workspace’.  These are your API keys — make sure to save the second key for later.

OAuth user tokens for the Python Slack Bot

Since we’ll be working with Python throughout this tutorial, a Jupyter Notebook is the best experience for completing these steps.

A Quick Note on Jupyter Notebooks

For those of you who are unfamiliar with Jupyter notebooks, I’ve provided a brief review of which functions will be particularly useful to move along with this tutorial.

In the image below, you’ll see three buttons labeled 1-3 that will be important for you to get a grasp of — the save button (1), add cell button (2), and run cell button (3).

Jupyter notebook overview

The first button is the button you’ll use to save your work as you go along (1). Feel free to choose when to save your work.
Next, we have the “add cell” button (2). Cells are blocks of code that you can run together. These are the building blocks of jupyter notebook because they provide the option to run code incrementally without having to to run all your code at once. Throughout this tutorial, you’ll see lines of code blocked off — each blocked line should correspond to a cell.

Lastly, there’s the “run cell” button (3). Jupyter Notebook doesn’t automatically run it your code for you; you have to tell it when to execute by clicking this button. As with the add button, once you’ve written each block of code in this tutorial onto your cell, you should then run it to see the output (if any). If output is expected, note that I’ll also share it in this tutorial so you know what to expect. Make sure to run your code as you go along because many blocks of code in this tutorial rely on previous cells.

Spotting Game Rules

Before we get into the Python that will power ADISpotting, let’s review some of the key rules that go into it. I mentioned earlier that pictures are posted as part of the game; with these pictures, we’ll keep track of each person’s “points” and consistently update who the top scorer is.

We’ll implement this task first with a Python class, which we’ll call ADISpotting.

class ADISpotting:
    
    def __init__(self):
        # we'll add code here soon
        pass

There are two pieces of information we need to keep track of here: the players and their scores. Since we’re taking an object-oriented approach to this problem, we can initialize these in the constructor.

class ADISpotting:
    
    def __init__(self):
        self.users = {}

In the constructor, self.users is a dictionary containing each player’s account id and respective score. Since self.users was initialized to an empty dictionary, we have to fill it up with the players’s IDs, otherwise we’ll be playing alone. (And what fun is that?) An add_user() function will do us some good here. The only argument needed will be the user id, which the function will use to add to the users dictionary with a score of 0.

class ADISpotting:
    
    def __init__(self):
        self.users = {}

    def add_user(self, user):
        self.users[user] = 0

Whenever someone submits a spot, points need to be added to that person’s score. We’ll add a general purpose add_points() function whose parameters are the number of points to be added and the user id associated with the points.

def add_points(self, user, points):

To update the actual user’s point tally, we can refer to the users dictionary we initiated earlier and increment it by the points parameter.

   def add_points(self, user, points):
        self.users[user] = self.users[user] + points

 

Connecting the Game to Slack

We now have a lot of the code written to power ADISpotting, but what we don’t have is all the information about the Slack channel. We’ll need to know the users that are playing and the channel they will use for the game.. To accomplish this, we’ll use Slack’s API.

To access their API, we’ll begin by importing the needed module and initializing the Slack client.

from slackclient import SlackClient

slack_client = SlackClient('your-key-here')

Great! Now that we’ve connected to Slack, we can use the API client to request the list of channels in your Slack workspace.

channels = slack_client.api_call(
  "channels.list",
   exclude_archived=1
)['channels']
print(channels)

Here we have to do a little digging in the response to find the channel id for the Slack channel you want to use for your game. In my example I named this channel #i-adispotting. I found that the id for my channel is C55UAGM3N from this part of the json:

 {'id': 'C55UAGM3N', 'name': 'i-adispotting',.

We need this id so that we can find the list of members in the #i-adispotting channel. Additionally, we’ll keep track of the index number so we can access the specific dictionary that refers to the channel information.

ind = 0
# iterate through the Slack channels
for i in channels: 
    # access the list of members
    channel_id = "C55UAGM3N"
    if i['id'] == channel_id:
        members = channels[ind]['members']
        break
    else:
        ind += 1

Now that we’ve successfully extracted the users in the channel we can add each person to the class instance. First we create the ADISpotting() instance and then call the add_user() function for each user in the list of members.

adispot = ADISpotting()
for i in members:
    adispot.add_user(i)

 

Getting the Slack Game Rolling

Alright, so now we have all the pieces set up to the game and it’s time to get it going! We’ll make a function called parse_slack_output that will take the messages sent to the #i-adispotting channel and respond (or not respond) accordingly.

def parse_slack_output(output_list):

This first line of code checks to see if there’s even a message to parse.

def parse_slack_output(output_list):
    if output_list and len(output_list) > 0:

If there is a message, we’ll iterate through each word in the message and check to see if the adispotting bot should respond.

def parse_slack_output(output_list):
    if output_list and len(output_list) > 0:
        for output in output_list:

For this tutorial, we’ll only write a response when someone in the core group uploads a photo to #i-adispotting (or whatever you called your channel). To do this, we’ll check the message for the keywords subtype and file to confirm that there was an image uploaded. If this check comes back true, we’ll call the function add_points() to increment the score of the person who sent the message.

Lastly, we want our bot to send a confirmation so we make a POST request to the channel indicating that the message was received and the person’s updated score.

def parse_slack_output(output_list):
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'subtype' in output and 'file' in output:
                adispot.add_points(output['file']['user'], 5) 
                slack_client.api_call("chat.postMessage", channel=channel_id, text=output['username'] 
                                      + " now has " + str(adispot.get_points(output['user'])) + " points!", 
                                      as_user=True)

Now that all the needed functions are written we can put them all together. In this tutorial, we won’t review deployment, we’ll simply review running this game from your local environment.

First, we want to make sure that the Slack client is connected to in the first place; and if it is, we’ll print a message saying so. If it isn’t we’ll print out an error message.

if slack_client.rtm_connect():
    print("ADISpotting connected and running!")
else:
    print("Connection failed. Invalid Slack token or bot ID")

If the Slack client is connected, we want to call parse_slack_output() on any input that comes through. If we call this just once, only the first input will be parsed so we need a way of making sure this function is called on all input. Since we’re only working in our local environment, we can accomplish this with a while True: loop.

if slack_client.rtm_connect():
    print("ADISpotting connected and running!")
    while True:
        parse_slack_output(slack_client.rtm_read())
else:
    print("Connection failed. Invalid Slack token or bot ID")

 

A Live Python Slack Bot

Now your Slack bot with Python is live! You can go onto your Slack and start adispotting. If you look at the image below, I posted a picture of my dog, Lennon, and @adispotting responded with my new score.

Slack bot with Python picture of a dog

We have a good basis for our spotting game, but there’s much more we could do. We could keep track of who is currently in the lead and announce a weekly winner, we could build a command for the Slack bot so that players can ask for the current scores — the possibilities are limitless and I encourage you to add your own features to your bot and comment below on what you did!

If you liked what you did here, check out my GitHub (@lesley2958) and Twitter (@lesleyclovesyou) for more content!