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:
- Either Python 2 or 3
- pip and virtualenv to handle Python application dependencies
- Free Slack account with a team on which you have API access or sign up for the Slack Developer Hangout team
- Official Python slackclient code library built by the Slack team
- Slack API testing token
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.
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.
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.
After you have signed in go to the Bot Users page.
Name your bot "starterbot" then click the “Add bot integration” button.
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.
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.
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.
In Slack, create a new channel and invite StarterBot or invite it to an existing channel.
Now start giving StarterBot commands in your 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:
- Attach a persistent relational database or NoSQL back-end such as PostgreSQL, MySQL or SQLite to save and retrieve user data
- Add another channel to interact with the bot via SMS or phone calls
- Integrate other web APIs such as GitHub, Twilio or api.ai
Questions? Contact me via Twitter @fullstackpython or @mattmakai. I'm also on GitHub with the username mattmakai.
Something wrong with this post? Fork this page's source on GitHub.
Learn more about these concepts
Learning Python Bots …or view all topics.
This site is based on Matt Makai's project Full Stack Python, thanks for his excellent work!
此网站由 @haiiiiiyun 和 开源爱好者们 共同维护。 若发现错误或想贡献,请访问: Github fullstackpython.cn 项目