/
starterbot.py
198 lines (185 loc) · 7.79 KB
/
starterbot.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import os
import time
from slackclient import SlackClient
import re
import requests
from fuzzywuzzy import fuzz
import database as db
import pickle
# starterbot's ID as an environment variable
BOT_ID = os.environ.get("BOT_ID")
SLACK_TOKEN = os.environ.get("SLACK_TOKEN")
# constants
AT_BOT = "<@" + BOT_ID + ">:"
SAVE_COMMAND = "save"
GET_COMMAND = "get"
# to be replaced by database add row function
def save(ch, command):
command = command[len(SAVE_COMMAND):]
# db call to save the string here
db.addToDB(ch, msg = command)
db.save("/volume/slackdb.pickle")
# to be used to look up a person
def isperson(name):
if "name=" in name:
return True
return False
# to be used to get history
def get(ch, command):
command = command[len(SAVE_COMMAND):]
datereg = re.compile(r'(1[0-2]|[1-9])\/(3[01]|[12][0-9]|[0-9])\/\d{4}')
date = datereg.search(command)
querywords = command.split(' ')
if date is not None:
date = date.group()
date.lstrip("0")
querywords = command.split(' ')
# here is where you get the full info for that date
hist = db.get(ch, date)
return hist
else:
return -1
# used to parse a snippet's text to return personwise standup
def parsesnippet(fileid, personlist):
url = "https://slack.com/api/files.info?token=%s&file=%s&pretty=1" % (SLACK_TOKEN, fileid)
response = requests.request("GET", url)
resjson = response.json()
contentlist = []
if resjson and "file" in resjson and "url_private_download" in resjson["file"]:
url = resjson['file']["url_private_download"]
if personlist==[]:
return url
auth = "Bearer %s" % SLACK_TOKEN
headers = {
'authorization': auth,
'cache-control': "no-cache",
}
response = requests.request("GET", url, headers=headers)
sniptxt = response.text
#here is where we'd be filtering by person
lines = sniptxt.split("\n")
for line in lines[1:]:
personreg = re.compile(r'([A-Z]|[a-z])+:.+')
personst = personreg.search(line)
if personst is not None:
personst = personst.group()
splitst = personst.split(":", 1)
person = splitst[0]
if person in personlist:
content = splitst[1]
contentlist.append(content)
return '\n'.join(contentlist)
# returns true if it is a standup or a sync
def isstandup(sniptxt):
lines = sniptxt.split("\n")
matchst = fuzz.partial_ratio("standup", lines[0].lower())
matchsy = fuzz.partial_ratio("sync", lines[0].lower())
if matchst > 70 or matchsy > 70:
return True
return False
# to be used to save a snippet where we save the id of it if it meets a criterion
def savesnippet(ch, fileid):
url = "https://slack.com/api/files.info?token=%s&file=%s&pretty=1" % (SLACK_TOKEN, fileid)
response = requests.request("GET", url)
resjson = response.json()
if resjson and "file" in resjson and "url_private_download" in resjson["file"]:
url = resjson['file']["url_private_download"]
if url.endswith(".txt"):
auth = "Bearer %s" % SLACK_TOKEN
headers = {
'authorization': auth,
'cache-control': "no-cache",
}
response = requests.request("GET", url, headers=headers)
sniptxt = response.text
if isstandup(sniptxt):
# put in a database call to save the fileid here
db.addToDB(ch, fid = fileid)
db.save("/volume/slackdb.pickle")
return True
return False
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.
"""
response = "Not sure what you mean. Use the *" + SAVE_COMMAND + \
"* command with numbers, delimited by spaces."
if command.startswith(SAVE_COMMAND):
response = save(channel, command)
return
if command.startswith(GET_COMMAND):
response = get(channel, command)
if response == -1:
slack_client.api_call("chat.postMessage", channel=channel,
text="Please specify a date in (M)M/DD/YYYY format and use the optional --name=user flags for specific persons", as_user=True)
return
if response == []:
slack_client.api_call("chat.postMessage", channel=channel,
text="No history stored for this date for your channel", as_user=True)
return
querywords = command.split(' ')
# filter all occurences of name=xyz
people = filter((lambda word: isperson(word)), querywords)
# make all name=xyz -> xyz
people = map((lambda name: name[5:]), people)
for resp in response:
if "msg" in resp:
# don't return anything about saved texts if we're querying by person
if len(people)>0:
continue
resp = resp["msg"]
elif "fid" in resp:
# will throw back relevant mentions of the person in the snippet or if not searching for a person, will throw back a link which will display all
resp = parsesnippet(resp["fid"], people)
slack_client.api_call("chat.postMessage", channel=channel,
text=resp, as_user=True)
return
# to track all snippets which have standup or sync with a > 70 fuzzy match ratio in their first line
if command.startswith("id:"):
# make sure it's a snippet and not another file that we may end up caching
command = command[3:]
response = savesnippet(channel, command)
if response is False:
return
else:
response = "I have tracked your file, you can call me if you need it"
slack_client.api_call("chat.postMessage", channel=channel,
text=response, as_user=True)
def parse_slack_output(slack_rtm_output):
"""
The Slack Real Time Messaging API is an events firehose.
this parsing function returns None unless a message is
directed at the Bot, based on its ID.
"""
output_list = slack_rtm_output
if output_list and len(output_list) > 0:
for output in output_list:
if output and 'text' in output and "uploaded" in output['text']:
if output and 'file' in output and output['file'] and 'id' in output['file']:
id = "id:"+output['file']['id']
return id, \
output['channel']
if output and 'text' in output and AT_BOT in output['text']:
# return text after the @ mention, whitespace removed
return output['text'].split(AT_BOT)[1].strip().lower(), \
output['channel']
return None, None
if os.path.exists("/volume/slackdb.pickle"):
db.store = db.load("/volume/slackdb.pickle")
else:
pickle.dump({}, open("/volume/slackdb.pickle", 'w+'))
db.store = db.load("/volume/slackdb.pickle")
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
if __name__ == "__main__":
READ_WEBSOCKET_DELAY = 0.5 # 1 second delay between reading from firehose
if slack_client.rtm_connect():
print("StarterBot connected and running!")
while True:
command, channel = parse_slack_output(slack_client.rtm_read())
if command and channel:
handle_command(command, channel)
time.sleep(READ_WEBSOCKET_DELAY)
else:
print("Connection failed. Invalid Slack token or bot ID?")