-
Notifications
You must be signed in to change notification settings - Fork 1
/
bot.py
145 lines (116 loc) · 3.96 KB
/
bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
from concurrent.futures.thread import ThreadPoolExecutor
import os
import logging
import datetime
import requests
from slack_sdk import WebClient
from slackeventsapi import SlackEventAdapter
from menu import get_menu
executor = ThreadPoolExecutor(5)
client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
slack_events_adapter = SlackEventAdapter(
os.environ['SLACK_SIGNING_SECRET'],
endpoint="/slack/events"
)
# commands
MONDAY = 'monday'
TUESDAY = 'tuesday'
WEDNESDAY = 'wednesday'
THURSDAY = 'thursday'
FRIDAY = 'friday'
SATURDAY = 'saturday'
SUNDAY = 'sunday'
TODAY = 'today'
TOMORROW = 'tomorrow'
weekdays = [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
weekends = [SATURDAY, SUNDAY]
all_days = weekdays + weekends
relative = [TODAY, TOMORROW]
def next_day(day):
return (day + 1) % 7
def get_date(day):
# want nearest future date on the given day
desired_day = all_days.index(day)
current = datetime.date.today()
while current.weekday() != desired_day:
current = current.replace(day=current.day+1)
return current
def get_actual_day(day):
# for day = TODAY or YESTERDAY
today = datetime.date.today().weekday()
return all_days[today if day == TODAY else next_day(today)]
def get_cat():
r = requests.get('https://thecatapi.com/api/images/get')
if r.status_code == 200:
return [{
'image_url': r.url,
'text': '<' + r.url + '|src>'
}]
return None
def get_response_with_attachments(date):
response = "Here's what's for lunch! Yum :yum:"
attach = []
try:
all_items, all_allergens, all_may_contains = get_menu(date)
except Exception as e:
logging.exception(e)
response = f"Sorry, couldn't get a menu for {date.year}/{date.month}/{date.day}. Have a cat instead :cat:"
attach = get_cat()
return response, attach
for item, allergens, may_contains in zip(all_items, all_allergens, all_may_contains):
attach.append({
'title': item,
'fields': [
{
'title': 'Allergens',
'value': ', '.join(a for a in allergens) if allergens else 'None',
'short': True
},
{
'title': 'May contain',
'value': ', '.join(m for m in may_contains) if may_contains else 'None',
'short': True
}
]
})
return response, attach
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.
"""
command = command.lower()
response = "Not sure what you mean. Try typing a day of the week!"
attach = None
# weekdays
for day in weekdays:
if day in command:
date = get_date(day)
response, attach = get_response_with_attachments(date)
# weekends
for day in weekends:
if day in command:
response = "There's no lunch on the weekends! Have a cat instead :cat:"
attach = get_cat()
# relative
for day in relative:
if day in command:
# compute which day, and handle command with that day
actual_day = get_actual_day(day)
handle_command(actual_day, channel)
return # so it doesn't post twice
# Send response
client.chat_postMessage(channel=channel, text=response, attachments=attach)
@slack_events_adapter.on('app_mention')
def mention(event_data):
text = event_data['event']['text']
channel = event_data['event']['channel']
# use threads to ensure we respond within 3 sec
executor.submit(handle_command, text, channel)
logging.info(f'received message in {channel}: {text}')
@slack_events_adapter.on('error')
def error_handler(err):
logging.error(err)
if __name__ == '__main__':
slack_events_adapter.start(port=3000)