How to Build A Task Notification Bot for Slack with Python (Part 3)
Part 2 of this tutorial series, was used to write our application webhook. In this part, we will be building out our slash commands.
Our Slash Command
In our app
folder, let’s update the app route in __init__.py
. In the create_app
method, we created a root app route for the bot, which we will change from /
to /ranti-bot
. Since our bot will be making only post
request to our Slack app, we need to change the route method from GET
to POST
. The updated code should look as follows:
...
...
...
def create_app(config_name):
app = FlaskAPI(__name__, instance_relative_config=False)
app.config.from_object(app_env[config_name])
app.config.from_pyfile('../config/env.py')
@app.route("/ranti-bot", methods=["POST"])
def ranti-bot():
"""This route renders a hello world text."""
# rendering text
return 'Hello World'
return app
Let’s also change the name of the method from home()
to ranti-bot()
as seen above. This counts for good readability of our code.
Now, we need to implement our app’s logic for interacting with user requests and providing them with appropriate responses in Slack.
When our users, use our slash command, slack makes a post request to our predefined route for our slash command /ranti-bot
and sends the parameters passed to the slash command as a post request payload with a text
key. We will make use of the text
data to determine which action the user wants to perform and other necessary details about that action.
Below are the slash commands and parameters that we will accept from the users of our bot slash commands in this for /command action [parameters]
.
/ranti show-task [today]
/ranti my-task
/ranti show-task [date {dth-month-yyyy}]
Let’s create an array that holds all the allowed_commands
for our app. I am creating the plural versions of our actions
, so we can check that either that or their singular version is used to make request. This I have placed before the create_app
method definition as seen below.
...
...
...
allowed_commands = [
'show-task'
'show-tasks'
'my-task'
'my-tasks'
'help'
]
def create_app(config_name):
...
...
...
In the ranti_bot
method that handles our route, we start by getting the text
payload from request.data
and setting it to command_text
. We then split the text value in command_text
to get the actions
in command_text[0]
and parameters
in command_text[1]
.
We also get the slack_uid
— the ID of the user making the request from slack.
...
...
...
@app.route('/ranti-bot', methods=['POST'])
def ranti_bot():
command_text = request.data.get('text')
command_text = command_text.split(' ')
slack_uid = request.data.get('user_id')
...
...
...
Instantiate the SlackHelper class and set the instance to slack_helper
, then get the slack_user_info
from the instance by calling the user_info
method on it, passing as parameter the slack_uid
gotten from the request.
...
...
...
@app.route('/ranti-bot', methods=['POST'])
def ranti_bot():
command_text = request.data.get('text')
command_text = command_text.split(' ')
slack_uid = request.data.get('user_id')
slackhelper = SlackHelper()
slack_user_info = slackhelper.user_info(slack_uid)
...
...
...
return app
After this, we create reference to our Action class instance. We also pass in the required parameters for instantiating this class — slackhelper
, and slack_user_info
created above.
actions = Actions(slackhelper, slack_user_info)
Now that we have all we need to perform our actions, let’s create the response_body
to be sent to our users based on the action they have requested to perform.
...
...
...
if command_text[0] not in allowed_commands:
response_body = {'text': 'Invalid Command Sent - `/ranti help` for available commands'}
if command_text[0] == 'help':
response_body = actions.help()
if command_text[0] in ['my-task', 'my-tasks']:
response_body = actions.my_tasks()
if command_text[0] in ['show-task', 'show-tasks']:
date = command_text[1]
response_body = actions.show_tasks(date)
...
...
...
So basically, we are saying if the command is in our allowed commands, perform certain actions based on the command sent to our app and set results of those methods to our response_body
, otherwise, send the users an error message stating the command they can call to get help.
Once the action is complete, return a response with the response_body
and a 200
status code as seen below.
response = jsonify(response_body)
response.status_code = 200
return response
NB: We must return a response_body
and status_code
as specified by the Slack API documentations.
The final code should look like this snippet below.
In the code above, we are calling some methods from our Action
class, which have not been created. Let’s get right into it.
When we get a help
command, we calling the help()
method in the Action class. Our help method will simply return a description and usage of our application commands. Inside actions.py
, let’s add the help
method, just above the notify_channel
method.
When a user requests for his task using the my-task
command, we are calling the my_task()
method in our action class. Let’s create this method.
In the above snippet, we get the email
and ID of the requester — recipient
, and get the task_cells
containing an array of rows of tasks with the user’s email on the Google sheet
. We then loop through the task_cells
and for each task in the array, we send the prepared text_detail
to the user via slack by calling the post_message
method in our slack helper, passing in the text_detail
and the recipient
‘s ID as parameters.
Lastly we will build out the show-task
by date functionality of our Slack bot application. This command takes a date
parameter in this formats {dth-month-yyyy}/{"today"}/{"tomorrow"}/{"yesterday"}
.
example: /ranti show-task 10th-July-2018
or/ranti show-task today
Let’s create some private methods we will use in our show_tasks
method.
The above snippet is a private method that will be used to convert {"today"}/{"tomorrow"}/{"yesterday"}
into actual dates to be used to search through our google sheet.
NB: Private methods in a python class are prefixed with two underscores ( __
). This is done to avoid the method to be called from outside their class.
The following method will be used to strip out these suffixes th
, nd
, st
from the day in the dates on the sheet before we compare with the date value converted above.
We will also abstract the functionality for preparing the text_detail
and actually sending the message to the Slack user. Below is the perform_send_action
private method.
Now, let’s define our show_tasks
method.
In the method above, we are checking if the date
passed from the request is one of the values in this array [‘today’, ‘tomorrow’, ‘yesterday’], then we convert the value to pythondate
and then get the task_cells
that contains task that are to be checked in on the date specified. If the date is explicitly specified, we repeat the search for the task by the date and send a message to the slack user. The notification sent to slack will look like this:
Hurray, we have completed building our slack-bot. We can test the app on postman.
In the last part, we will host our app on Heroku and setup our slash command on slack where we will link our slash command to our app route /ranti-bot
. This will enable us to test with our slack team.
Do you have any Questions? You can reach me on Twitter @jattorize. I’m also on GitHub with the username jattoabdul.
See something wrong in this post? Fork this tutorial’s source on GitHub and submit a pull request.