Codementor Events

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

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

Slack integrations search background on SONYA ELLEN MANN’s article.

In Part 1 of this series, we structured our application and built the necessary helper files and methods. Now we will be moving onto the next step which will be to create our Webhook.

The Webhook

The Webhook is a function that loops continuously. At the desired notification time, It checks the Google sheet for the day’s task and notifies the channel. When we launch our app, our Webhook continuously runs using a Heroku Worker that prevents it from sleeping.

Let’s create a file called worker.py in our project folder, which would be the root of our application. Inside this file let’s paste the snippet below:

If you do this and run your server, you should get errors saying there is no module named Actions. Don’t panic, this is expected. Now let’s create this module.

Create a file actions.py in the app folder and include the following code snippet in it. We are importing the required modules and packages that will be used in our Actionclass.

from app.utils.gappshelper import GappsHelper
import time
from datetime import datetime, date, timedelta
from config import get_env

Below we are creating our Action class and declaring some properties/constants including our Google spreadsheet and slackhelper instances and our user_info
Our slackhelperand user_info will be passed into Actionson instantiation.

class Actions:
   def __init__ (self, slackhelper, user_info=None):
      self.gappshelper = GappsHelper()
      self.sheet = self.gappshelper.open_sheet()
      self.user_info = user_info
      self.slackhelper = slackhelper

Since we want our worker to always run and give us task updates for each day at a particular time on slack, we will use an infinite while loop inside our notify_channel method as seen below:

def notify_channel(self):
   while True: ... ...

We then create variables to hold the current time, hour and minute, which will be used to calculate the active time of our worker. We will be using a time.sleep(inactive_time) to achieve this.

def notify_channel(self):
   while True:
      curent_time = datetime.now()
      current_hour = curent_time.hour
      current_minute = curent_time.minute
      ... ...
      ... time.sleep(sleep_time * 3600)

NB  : We need a time.sleep, otherwise it would run indefinitely and use up the CPU resources even when there isn’t any task, and it will also endlessly send notifications to slack as long as a task exists. With the time.sleep(inactive_time), we have specified the worker to only run once in 24hrs.

Our sleep time needs to be calculated.

def notify_channel(self):
   while True:
      curent_time = datetime.now()
      current_hour = curent_time.hour
      current_minute = curent_time.minute

      if current_hour - 8 > 0:
         sleep_time = 24 - current_hour + 8 - (current_minute/60)
      elif current_hour - 8 < 0:
         sleep_time = 8 - current_hour - (current_minute/60)
      elif current_hour == 8:
         if current_minute == 0:
            sleep_time = 0
         else:
            sleep_time = 24 - current_hour + 8 - (current_minute/60) time.sleep(sleep_time * 3600)

What we are doing here is checking to maintain parallelism if the current hour is 8, and current minute is 0(i.e. it is 8:00 a.m). Then we set our app sleep_time to 0. When the task is complete, the app sleeps until 8:00 am the next day. Fun, right? I love mathematics!

We now need to tell our worker what actions to perform. We want to loop through the content of our sheet and check for tasks set to be completed on the current day. Once we determine that, we want to send a message to the slack channel providing details about the tasks (such as the title of the task, the assigned person, date, and a question on the status of the task) to be completed that day.

Let’s loop through the self.sheet and get the check_date , todays_date and calculate the send_notif_date .

def notify_channel(self):
   while True:
      ... ... ...      
      for index, row in enumerate(self.sheet):
         check_date = datetime.strptime(
             self.__num_suffix(
                row['Next Check-In']), '%d %B %Y').date()

todays_date = datetime.now().date()
         send_notif_date = check_date - todays_date
      ... ... ...

If our send_notif_date value is 0 , we then prepare our message text_detail to be sent to slack.

def notify_channel(self):
   while True:
      ... ... ...      
      for index, row in enumerate(self.sheet):
         ... ... ...
         if send_notif_date.days == 0:
            text_detail = (
               '*Task #{} for {}:* \n\n'
               '*Hey {},* Today is the check-in day for your writeup titled\n'
               '`{}`.\n\n'
               'Whats the status of the article?\n'
               'PS: Please reply to this thread, the managers will review and reply you ASAP').format(
               str(index + 1), 
               row['Next Check-In'], 
               row['Name'],
               row['Most Recent Learning Experience you\'d like to write about'])

self.slackhelper.post_message_to_channel(text_detail)
      ... ... ...

We send the message, by calling our post_message_to_channel(message) method in our slackhelper . This calls the slack api and sends our message to the predefined slack_channel in our .env file loaded that will be loaded in our system environment variable.

for index, row in enumerate(self.sheet):
         ... ... ...
            self.slackhelper.post_message_to_channel(text_detail)

Here is how our final notify_channel code snippet should look.

Before we test our app, let’s create a .env file and put in the following details in it.

FLASK_APP="ranti.py" # our file entry point

APP_ENV="development" # default environment

SLACK_CHANNEL="XXXXXXXXX" # slack channel Id

SECRET='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # some random characters

CLIENT_SECRET_FILE = 'client_secret.json'

GAPPS_SHEET_NAME = 'BT-Logs-Bot-Copy' #your sheet title

OAuth=xoxp-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxx-xxxxxxxxxxxxxxxxxx

SLACK_TOKEN=xoxb-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxx-xxxxxxxxxxxxxxxxxx

Now that we have completed the code for our worker, let’s test by running our app from the command line with this command:


extract from terminal showing the app running and logging actions to the console

Great, Now our worker works. In order not to wait till 8:00am to test our slack notification, we can move the time.sleep up and reduce the time to 3600 seconds.

NB: Once you rerun your app, you should get a slack notification. Reverse the changes we made to test the slack notification back to the original complete notify_channel method.

In the third part, we will implement our slash command so that users can request tasks that belong to them and tasks for a particular day using the /ranti command from slack.

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