Codementor Events

How to Build A Task Notification Bot for Slack with Python (Part 3)

Published Aug 13, 2018
How to Build A Task Notification Bot for Slack with Python (Part 3)

“A robot named Pepper holding an iPad” by Alex Knight on Unsplash

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-2018or/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:


a snapshot of response from our bot showing a Task and it’s details on slack.

Hurray, we have completed building our slack-bot. We can test the app on postman.


postman showing a sample request made to the app route with the help command and it’s response

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.


Originally posted on The Andela Way .

Discover and read more posts from Aminujatto Abdulqahhar
get started
post commentsBe the first to share your opinion
Show more replies