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 Action
class.
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 slackhelper
and user_info
will be passed into Actions
on 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 .