-
Notifications
You must be signed in to change notification settings - Fork 1
/
hmbot.py
186 lines (155 loc) · 6.81 KB
/
hmbot.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
"""
All hmbot functions and state.
"""
import logging, random, requests, subprocess, fcntl, os, pty, time
import database
from parser import oneof, maybe, Parser, NotHandled
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('hmbot')
logger.setLevel(logging.DEBUG)
db = database.DatabaseProvider('hmbot')
parser = Parser(ignore=(',', "'"), remove=("'",))
greetings = oneof('hello', 'hi', 'greetings', 'howdy', '你好', 'goddag', 'hej', 'hejsa', 'hey', 'sup', 'yo')
verbose_request = oneof('will you please', 'can you please', 'will you', 'can you', 'please')
class Help:
fun = "#5cb85c"
util = "#5bc0de"
admin = "#d9534f"
def __init__(self):
self.commands = []
def usage(self, name, doc=None, command_type="#999999"):
def dec(func):
self.commands.append((name, doc or (func.__doc__ or "").split("\n")[0], command_type))
return func
return dec
helper = Help()
@helper.usage("hmbot, help!", command_type=Help.util)
@parser.action(maybe(greetings), "hmbot", maybe(verbose_request), oneof("help me", "help", "--help"))
def help_me(tokens, msg, api=None, **kwargs):
"""Displays this very message."""
attachments = []
for (name, doc, color) in helper.commands:
attachments.append({
"title": name,
"text": doc,
"color" : color,
"mrkdwn_in": ["text", "pretext", "title"]
})
api.slack.respond(msg, "Okay, here are some of the things I can do:", attachments=attachments, thread_ts=msg.get('thread_ts'))
@parser.action(">")
@parser.action("&", "gt", ";")
def process_write(tokens, msg, queue=None, **kwargs):
"""Request that the text of this message be sent to the process associated with this thread."""
args = ' '.join(tokens).strip() + '\n'
queue.send_json({
'slack_msg' : msg,
'thread_id' : msg['thread_ts'],
'command' : 'write',
'input' : args
})
@helper.usage("# ps", command_type=Help.admin)
@parser.action("# ps")
def ps(tokens, msg, queue=None, api=None, **kwargs):
"""List all active processes"""
logger.debug("Queueing request.")
queue.send_json({
'slack_msg' : msg,
'thread_id' : msg.get('thread_ts'),
'command' : 'ps'
})
@helper.usage("# kill <pid>", command_type=Help.admin)
@parser.action("# kill")
def kill(tokens, msg, queue=None, api=None, **kwargs):
"""Kill a process."""
if len(tokens) != 1:
api.slack.respond(msg, "Usage: # kill <pid>", thread_ts=msg.get('thread_ts'))
return
logger.debug("Queueing request.")
queue.send_json({
'slack_msg' : msg,
'thread_id' : msg.get('thread_ts'),
'command' : 'kill',
'pid' : int(tokens[0])
})
@helper.usage("Yo hmbot, let's play <game>!", command_type=Help.fun)
@parser.action(maybe(greetings), "hmbot", maybe("lets"), "play")
def games(tokens, msg, queue=None, api=None, **kwargs):
"""Start playing a game. Right now the only game I know is 'Adventure'."""
if not tokens or 'adventure' not in tokens[0]:
api.slack.respond(msg, "Sorry, I don't know that game :slightly_frowning_face:")
return False
logger.debug("Queueing request to spawn adventure subprocess.")
queue.send_json({
'slack_msg' : msg,
'thread_id' : msg['ts'],
'command' : 'create',
'width' : 55,
'input' : '/usr/games/adventure'
})
api.slack.respond(msg, "Okay! Let's play. Send me game commands by starting your message with '>'", thread_ts=msg['ts'])
@db.table(choose=(("choices", "TEXT"), ("choice", "TEXT")))
@helper.usage("Yo hmbot, (re)choose: <a>, <b>, ...", command_type=Help.fun)
@parser.action(maybe(greetings), "hmbot", maybe(verbose_request), oneof("rechoose between", "rechoose from", "rechoose", "choose between", "choose from", "choose"), maybe(":"))
def choose(tokens, msg, db=None, api=None, **kwargs):
"""Choose an item from a list. Often with a comedic results. I will remember my choices, but you can always ask me to rechoose :wink:"""
tokens = ' '.join(tokens)
choices = [e.strip() for e in tokens.split(',')]
if len(choices) == 1:
if ' or ' in choices[0]:
choices = [e.strip() for e in choices.split('or')]
elif ' and ' in choices[0]:
choices = [e.strip() for e in choices.split('or')]
if choices:
choice = random.choice(choices)
else:
choice = "FAIL!"
try:
if 'rechoose' in msg['text'][:-(len(tokens) - 1)]:
logger.debug(f'rechoosing: "{choice}" from "{choices}".')
db.execute(f"DELETE FROM choose WHERE choices = ?", (tokens,))
db.execute(f"INSERT INTO choose VALUES (?, ?)", (tokens, choice))
else:
logger.debug('fetching choice from database.')
q = db.execute(f"SELECT choice FROM choose WHERE choices = ?", (tokens,))
cache = q.fetchone()
if cache:
choice = cache[0]
else:
logger.debug(f'inserting {choice} into database.')
db.execute(f"INSERT INTO choose VALUES (?, ?)", (tokens, choice))
db.commit()
except:
logger.exception("Database error.")
api.slack.respond(msg, choice)
@parser.action(maybe(greetings), "hmbot", "i hate you")
def i_hate_you(tokens, msg, api=None, **kwargs):
api.slack.respond(msg, ":broken_heart:")
@helper.usage("Yo hmbot, I love you.", command_type=Help.fun)
@parser.action(maybe(greetings), "hmbot", oneof("i love you", "i am in love with you"))
def i_love_you(tokens, msg, api=None, **kwargs):
"""Feel free to express your undying love for Hmbot."""
api.slack.respond(msg, ":heart:")
@helper.usage("Yo hmbot, what are the happs?", command_type=Help.util)
@parser.action(maybe(greetings), "hmbot", oneof("whats happening", "what are the haps", "what are the happs"), "?")
def what_are_the_haps(text, msg, api=None, **kwargs):
"""Get a list of upcoming events from the hackmanhattan meetup."""
okay, value = api.meetup.events('hackmanhattan', 5)
if okay:
api.slack.respond(msg, "Here are some upcoming events:", attachments=value)
else:
api.slack.respond(msg, f'Sorry, I dunno. I get a `{value}` when I try to talk to meetup.')
@parser.action(maybe(greetings), oneof("I am", "Im"), "hmbot")
def no_im_hmbot(text, msg, api=None, **kwargs):
api.slack.respond(msg, 'Liar!')
@parser.action(greetings, "hmbot")
def hello(text, msg, api=None, **kwargs):
api.slack.respond(msg, 'Hello, I am hmbot!')
def handle_message(msg, **kwargs):
logger.debug(f"Received message {msg}.")
if 'text' in msg:
try:
return parser.parse(msg['text'], msg, **kwargs)
except NotHandled as ex:
logger.debug(f"unhandled message '{msg}', with tokens '{ex.tokens}'")
return
logger.debug(f"No message text in {msg}.")