Slack was founded in 2014 and is being touted as the fastest-growing business application in history. It currently has over 50,000 paying companies using its product—including my current place of employment.
Slack has really distinguished itself from its predecessors that were more focused on being a messaging system with some voice and video capabilities. Slack has really pushed the envelope and is working diligently on building on a very powerful App Directory. The App Directory contains hundreds to thousands of integrations that can provide an incredible amount of flexibility to improve your company’s efficiency.
The goal of this article is to demonstrate how you can build your own unique integration with Slack, leveraging Python for the logic.
To help focus on understanding the mechanics of a Slack Bot’s basics, I will focus on a custom bot.
Why Build a Slack Bot?
Even though Slack has a world-class App Directory, each business has unique business processes. This leaves many companies in a situation where they simply cannot find the perfect application for them.
This is where building your own Slack Bot comes into play.
A Slack Bot’s job is to receive and handle events generated by your team’s Slack interaction. Slack provides at least 50 different event types, such as:
-
message
: A message was sent to a channel. -
team_join
: A new member has joined the team. -
member_joined_channel
: A user joined a public or private channel.
And of course, there are many more event types that your bot can optionally handle and react to. For example, the team_join
event is a fantastic event that can begin an entire onboarding process.
The goal of this article will demonstrate how a Slack Bot will interact with the message
event to perform specific actions when team members interact with the bot.
Picking the Right Slack Bot Type
In this article, I will create a Python application and a Slack Bot that can be added to your team project to respond to the message
event.
To begin, I need to create a Bot on Slack. Two types of bots can be created:
- a custom bot
- creating an application and adding a bot user
This article will create a custom bot because an application bot user would be more appropriate if I were planning to write and publish an application on Slack. Given that I wish this bot to be private to my team, a custom bot will suffice.
Create a Custom Slack Bot
A custom bot can be created here: https://my.slack.com/apps/A0F7YS25R-bots. If you are already logged in to your Slack account, on the left select the Add Configuration button; otherwise, log in to your Slack account before proceeding. If you do not have a Slack account, you can sign up for free.
This will take you to a new page that requires you to provide a username for your bot. Enter your username now, ensuring you follow Slack’s naming guidelines. Once you have selected an awesome bot name, press Add bot configuration.
After you have successfully created your bot, Slack redirects you to a page that allows for further customization of your bot. I’ll leave that part to your creative self. The only thing needed from this page is the API Token that starts with xoxb-
. I would either copy this token to a safe place for later use or simply leave this page open until we need the token for the Python application.
Setting Up Your Python Application
According to Slack’s official documentation for the Slack Developer Kit for Python, it currently supports version 2.7. It does make mention that version 3 will be supported in the near future.
I’ve already got version 2.7 installed, so I will stick with that version of Python for now. If your Python installation is already set up and ready to go, you can move on to the next step; otherwise, please visit the Python Download page and download the appropriate version for your operating system.
To install the Slack Developer Kit, I will use PyPi to simplify the installation of the Slack Client. On some operating systems, the pip
command can be run directly from a command prompt. If not, you would need to reference the entire path to the pip
program.
For example, on my Windows system, I ran the following command to install the Slack Client from a command prompt (referencing the full path to the pip.exe file): Python27Scriptspip
.
install slackclient
Next, you will want to choose a location to house your application. I enjoy using GitHub, so I created a main python
folder that contains all of my different Python applications. Inside this folder, I made a sub-folder appropriately called slackbot
.
Once I’ve chosen where my application will be hosted, I’m going to store the core of my application in a file called slackbot.py
.
Your First Slack API Call
It’s time to lay fingers to the keyboard and connect to Slack and make our first API call. Let’s get right to the code, and I’ll explain what’s happening after:
from slackclient import SlackClient slack_client = SlackClient("xoxb-*******************") api_call = slack_client.api_call("users.list") if api_call.get('ok'): users = api_call.get('members') for user in users: print user.get('name')
The code begins with importing the Slack Client library, followed by instantiating the SlackClient
class with your Slack Bot’s API Token that you saved earlier. Be sure to replace the example token in this example with your token.
The SlackClient
object is stored in a local variable called slack_client
that will be used to interact further with Slack.
Using the slack_client
, an API call is made to retrieve a list of your team’s users. If the API call succeeded, the list of team members is stored in the users
variable. The users
variable is an array that, using a for loop, prints each team member’s name to the console application.
Slack supports several different types of interactions with the system. The first, which we just completed, made an API call. Slack offers many other types of API calls: Web API, Events API, Conversations API, Real Time Messaging API, and SCIM API. The API call we made to retrieve the list of users leveraged the SCIM API.
In the next example, I will demonstrate how to use the Real Time Messaging System. Once we begin building the final bot, the Conversations API will be used to send messages in response to the commands our bot will respond to.
Connecting to the Real Time Messaging System
The RTM system provides a lot of power because Slack sends events that your application can handle and respond to immediately. Of course, there are so many events that your bot may not need to handle every event. To demonstrate the many different events that occur simply upon connection, the following example will output each event that is received.
Let’s immediately look at the code to connect and begin receiving Slack events:
from slackclient import SlackClient import time slack_client = SlackClient("xoxb-****************") if slack_client.rtm_connect(with_team_state=False): print "Successfully connected, listening for events" while True: print slack_client.rtm_read() time.sleep(1) else: print "Connection Failed"
Just like the previous example, this code begins with importing the Slack Client library and instantiates the SlackClient
class with the same API Token as earlier. This example also imports the Time library that is used later in the code.
With the SlackClient
successfully created, the next line of code makes a call to the rtm_connect
method. This is done with an if statement. If the connection fails for some reason, an error message is outputted to the console. When successful, a success message is printed to let us know that we are connected and ready to begin interacting with Slack events.
An endless while loop is then started. Inside this loop, I call the rtm_read
method of the Slack Client library.
The results of this call are logged to the console. After this occurs, the application sleeps for 1 second before reading the next potential event from Slack. Below is an example of what it looks like reading events upon first connection:
Successfully connected, listening for events [] [{u'type': u'hello'}] [{u'url': u'wss://lbmulti-yeo0.lb.slack-msgs.com/websocket/Rm8R-Q0PLxK_8UQmBo0Apru-AtL7qnICzeNazVUDQGUCnIY8N51kO07ZUw37jZc4KvXJlu4c1EWDNwTtrXkLzkwn0GBmak_RATHLSFVCCCcht0YLqlgZAS0-6cb1marGhznvmnQStgdW6rd3yub0CpCzmJdgIkRPgIOIB2JurYA=', u'type': u'reconnect_url'}] [{u'type': u'presence_change', u'user': u'U6RM1S17T', u'presence': u'active'}] [] []
When the bot is connected, three events are sent by Slack as seen above. Because this is in a while loop, when there is no event, it receives an empty array as seen above with the empty brackets [].
Now that we have a basic understanding of making an API call and connecting to Slack’s Real Time Messaging system, it’s time to build out a fully functional Slack Bot.
My Slack Bot will listen to events using the RTM system. When it receives a message event that is directed to my bot, my application will respond back to the user with a response to the command that was received.
Building the Slack Bot
To build a full bot, it requires quite a bit of code. To help organize and simplify the final code, I am going to split the functionality into three different classes: Bot, Event, and Command. These classes should be extensible by your own application, improving the functionality of your own bot. Let’s explore the purpose of each of the three different classes:
- The Bot class will be responsible for connecting to Slack and will begin the while loop to listen for events.
- The Event class is responsible for reading the events received from Slack, parsing them out to only deal with message events that are aimed directly to our bot. When a message is received, it will call the Command class and send an API call with the response from the Command class.
- The Command class will receive the text from the event and provide a customized message based on the command received. This message will then be sent back to the originating Slack Channel Event class to be sent to the originating channel of the message.
Initializing the Slack Bot
I had previously mentioned that my Python application entry point is placed in the slackbot.py
file. This file contains the bare minimum to get the application rolling which is to instantiate the Bot class that will handle the rest of the process:
import bot bot.Bot()
Creating the Bot Class
The Bot Class contains the heart of the bot’s configuration and setup. Let’s look at the entire Bot Class that I’ve placed inside a bot.py
file:
import time import event from slackclient import SlackClient class Bot(object): def __init__(self): self.slack_client = SlackClient("xoxb-*****************") self.bot_name = "jamiestest" self.bot_id = self.get_bot_id() if self.bot_id is None: exit("Error, could not find " + self.bot_name) self.event = event.Event(self) self.listen() def get_bot_id(self): api_call = self.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') == self.bot_name: return "<@" + user.get('id') + ">" return None def listen(self): if self.slack_client.rtm_connect(with_team_state=False): print "Successfully connected, listening for commands" while True: self.event.wait_for_event() time.sleep(1) else: exit("Error, Connection Failed")
The file begins by importing the necessary libraries: time, event, and SlackClient. The event library will be created next.
With the libraries imported, the Bot Class is now created. The class’s constructor inside the __init__
function sets up a few variables that will be used throughout the remainder of the code. This includes the slack_client
, the bot_name
, and the bot_id
.
The name of the bot is used to find the ID of the bot. The ID will be used later to parse out events that are aimed directly to the bot. If the application cannot find the bot, the application is exited with an error as it cannot proceed without the ID.
The Event Class is then instantiated to be used a bit later in the class. The final thing the constructor does is call the listen
function, which connects to the RTM system and begins the endless loop waiting for events that the bot will handle.
The next function, get_bot_id
, is quite similar to the first example that loops through the users, this time finding our bot’s ID by finding its name in the list of users and returning the ID. In the event that the bot cannot be found, None
is returned, which will cause the previous code to exit because it was unable to find the bot.
The final function in the Bot class is the aforementioned listen
function. This function looks very similar to the second example where we first connected to Slack’s RTM system. The key difference in this example is that it calls the wait_for_event
function that will be explored next in the Event class.
This completes the Bot class, making it responsible for creating the SlackClient and starting the endless loop waiting for events. It, however, does not do anything with those events, leaving that responsibility to the Event class.
The Event Class
The purpose of the Event class is to read any events returned from Slack’s RTM system. Each event received will be examined for a message containing a reference to the Bot’s ID. The following is the Event class that I’ve placed inside an event.py file:
import command class Event: def __init__(self, bot): self.bot = bot self.command = command.Command() def wait_for_event(self): events = self.bot.slack_client.rtm_read() if events and len(events) > 0: for event in events: #print event self.parse_event(event) def parse_event(self, event): if event and 'text' in event and self.bot.bot_id in event['text']: self.handle_event(event['user'], event['text'].split(self.bot.bot_id)[1].strip().lower(), event['channel']) def handle_event(self, user, command, channel): if command and channel: print "Received command: " + command + " in channel: " + channel + " from user: " + user response = self.command.handle_command(user, command) self.bot.slack_client.api_call("chat.postMessage", channel=channel, text=response, as_user=True)
This class begins by importing the final class that will be explored, the Command class. The constructor of the Event class receives a single parameter: a reference to the Bot object. This is stored in a variable that can be accessed by the other functions in this class. Inside the __init__
function, another variable is created that instantiates the previously imported Command class.
The next function, wait_for_event
, is the function that was called by the Bot’s class listen
function. This function reads any events that have been received from Slack’s RTM system. The rtm_read()
function returns an array of events. The wait_for_event
function checks if the array contains any events. If it does, the events are looped through and call the Event’s internal function parse_event
.
The parse_event
function receives the event as input. It proceeds to check for a property in the event called text
. If this property exists, it then checks that the text
property contains a reference to our Bot’s ID. When this condition is true, this function calls the final function in this class, the handle_event
function.
Before calling the handle_event
function, the text
property uses the Python split
function, the string separator being represented by the bot’s ID. This converts the text
property into an array. The first element in the array is the string containing the text with the bot’s ID. The second element contains the remainder of the message. This element is passed to the above-mentioned handle_event
function as the command.
The final function, handle_event
, accepts three properties: the user who sent the message, the command that was sent, and the channel it was sent in.
The handle_event
function ensures the command and channel contain valid values. When they do, a friendly debug message is outputted to the console that indicates what command was received, the channel it was sent in, and which user sent it.
After the friendly debug message, the handle_event
function calls the main function from the earlier mentioned Command class. The result of this function is used by the handle_event
function by making an API call that posts the response from the Command class function to the channel that initiated the event.
Let’s now look at the Command class to see how it generates a custom response based on the command received from the user.
The Command Class
To complete our bot, it’s time to create the final class, Command, in an aptly named command.py file:
class Command(object): def __init__(self): self.commands = { "jump" : self.jump, "help" : self.help } def handle_command(self, user, command): response = "<@" + user + ">: " if command in self.commands: response += self.commands[command]() else: response += "Sorry I don't understand the command: " + command + ". " + self.help() return response def jump(self): return "Kris Kross will make you jump jump" def help(self): response = "Currently I support the following commands:rn" for command in self.commands: response += command + "rn" return response
I really like how this class turned out because it provides a solid foundation that is easily extendable to handle many more commands than I outlined above.
The constructor to the Command class creates a dictionary of keys with an accompanying function name that will be executed when the command is received from the Event class. In this abbreviated example, the commands
dictionary contains two commands: jump and help. This dictionary can be extended to include other commands you wish to handle with your own bot.
The next function, handle_command
, is the function that is called when a successful event that contains a message directed to our bot is called from the Event’s class handle_event
function.
The handle_command
function accepts two parameters: the user who sent the message and the command. The function begins by building a response string that will direct a message to the user who sent the command. The function then checks that the command received is a valid command in the dictionary of commands defined in the constructor.
When the command is valid, the associated function to that command is called, appending the string to the response
variable created earlier.
If the command does not exist, the response is appended to indicate that the command is not valid. It also calls the help
command function to aid the user in understanding what commands are supported by this bot.
The remaining functions, jump
and help
, generate a custom response which will be sent to the user who initiated the command.
As I mentioned during the constructor, the dictionary of commands can be extended with a new command. To complete that process, an accompanying function must be created that is called automatically by the handle_command
function.
Testing the Slack Bot
Now that all of the coding is completed, it’s time to test our new bot. To start, we must run our main Python script: slackbot.py. In a command prompt, execute this script with Python, e.g. python slackbot.py
.
This will launch our bot and connect to Slack’s Real Time Messaging system. Upon success, our debug message should be printed to the console indicating our Slack bot is ready to receive commands.
To execute a command, our bot needs to be invited into a public or private channel. Once the bot is in the channel, a user can tell the bot to jump or ask for help. In my case I would say: @jamietest jump
. The bot would aptly respond: @endyourif: Kris Kross will make you jump jump
.
This bot is not limited to a single channel. Because it parses out the channel from the event message, it can handle commands from many different channels.
Now it’s your turn to give your bot a go and see what you can make it do!
Conclusion
My bot is now complete. I have hopefully shown you the power of creating a Slack bot. With the multiple classes (Bot, Event, and Command) each handling a single concern, the Command class can be extended to handle many more commands.
To see the full source code, I’ve created a GitHub Repository.
Remember, don’t hesitate to see what we have available for sale and for study in Envato Market, and don’t hesitate to ask any questions and provide your valuable feedback using the feed below.
The sky is truly endless in how this bot can be extended. Below is a short list of ideas to extend the initial setup of classes:
- To add a new command, you would create a new function following the pattern of the
jump
andhelp
functions inside the Command class. When the function is created, it needs to be added to the dictionary of available commands. - Another great way to further enhance your bot is to extend the functionality of the
parse_event
function in the Event class. Currently, this function is explicitly looking for a message event that contains our Bot ID in the text. This function could be further extended to look for other events, such asteam_join
. This event could call a new command (in the Command class) that provides the new team member with your company’s onboarding documents and policies. - Finally, if you are interested in creating a custom application or wish to create your own Slack Commands, you can explore creating a custom application and adding a bot user to the application. Many of the code examples work with either bot type.
I hope you’ve enjoyed this article on creating a Slack Bot using Python. Use the comments form below to let your fellow readers know how you have extended the examples above to create an extremely robust Slack Bot!
Powered by WPeMatico