Initial import
[halbot.git] / hal.py
blob75cd15158a6d7340aadb3918d48e60093d0e3eb6
1 #!/usr/bin/env python
2 import html
3 import re
4 import irclib
5 import ircbot
6 import thread
7 import sys
8 from gzip import zlib as gz
9 from pickle import dumps as pickle, loads as depickle
10 DEBUG = 0
11 irclib.DEBUG = DEBUG
13 hal = ircbot.SingleServerIRCBot([("localhost", 6667)], "Hal", "Bot")
14 hal.connect("localhost", 6667, "Hal")
15 hal.connection.join("#casualgameplay")
16 hal.connection.privmsg("NickServ", "identify Orqv)>y!")
17 is_re = re.compile(" (is|are|was|were)( |$)")
19 my_users = {}
20 factoids = ircbot.IRCDict()
21 locked = ircbot.IRCDict()
22 try:
23 db = open("hal.db")
24 zipped = db.read()
25 pickled = gz.decompress(zipped)
26 (my_users, factoids, locked) = depickle(pickled)
27 db.close()
28 del db, zipped, pickled
29 except IOError:
30 pass
32 irc = hal.ircobj
33 class Whois(object):
34 registered = False
35 ircop = False
36 channel_perms = None
37 def __init__(self):
38 self.channel_perms = {}
40 class Perms(object):
41 symbols = {}
42 def __init__(self, level, symbol=None):
43 self.level = level
44 self.symbol = symbol
45 if symbol:
46 Perms.symbols[symbol] = self
47 def __cmp__(self, other):
48 return self.level - other.level
49 def __str__(self):
50 if self.symbol:
51 return self.symbol
52 else:
53 return str(self.level)
54 def from_symbol(symbol):
55 return Perms.symbols.get(symbol, Perms.present)
56 from_symbol = staticmethod(from_symbol)
57 Perms.ban = Perms(-2)
58 Perms.none = Perms(-1)
59 Perms.present = Perms(0)
60 Perms.voice = Perms(1, "+")
61 Perms.hop = Perms(2, "%")
62 Perms.op = Perms(3, "@")
63 Perms.admin = Perms(4, "&")
64 Perms.owner = Perms(5, "~")
65 Perms.ircop = Perms(6)
67 waiting_commands = {}
68 whois_info = {}
70 def got_command(who, where, command, args=""):
71 if not command in commands:
72 command = 'raise_error'
73 args = "I don't understand that."
74 waiting = waiting_commands.get(who, [])
75 if not waiting:
76 (required_perms, do_it) = commands[command]
77 if where == "private" and required_perms <= Perms.owner:
78 do_it(who, where, args)
79 elif required_perms <= Perms.voice and where in hal.channels:
80 chan = hal.channels[where]
81 if who in chan.voiceddict or who in chan.hopdict or who in chan.operdict \
82 or who in chan.admindict or who in chan.ownerdict:
83 do_it(who, where, args)
84 else:
85 return
86 else:
87 waiting_commands[who] = [(where, command, args)]
88 hal.connection.whois((who,))
89 else:
90 waiting.append((where, command, args))
92 def reply(who, where, what, all=False):
93 if where == "private":
94 hal.connection.privmsg(who, what)
95 elif all:
96 hal.connection.privmsg(where, what)
97 else:
98 hal.connection.privmsg(where, "%s: %s" % (who, what))
100 def get_perms(who, where):
101 user = whois_info[who]
102 if user.ircop:
103 return Perms.ircop
104 if where == "private":
105 server_perm = Perms.owner
106 else:
107 server_perm = whois_info[who].channel_perms.get(where, Perms.none)
108 if user.registered:
109 my_perm = my_users.get(who, Perms.none)
110 else:
111 my_perm = Perms.none
112 return max(server_perm, my_perm)
114 commands = {}
116 commands['raise_error'] = (Perms.voice, reply)
118 def ping(who, where, args):
119 reply(who, where, "Pong!")
120 commands['ping'] = (Perms.voice, ping)
122 def join(who, where, args):
123 hal.connection.join(where)
124 commands['join'] = (Perms.ircop, join)
126 def save(who=None, where=None, args=None):
127 pickled = pickle((my_users, factoids, locked))
128 zipped = gz.compress(pickled)
129 db = open("hal.db", "w")
130 db.write(zipped)
131 db.close()
132 if who:
133 reply(who, where, "Database saved.")
134 commands['save'] = (Perms.ircop, save)
136 def shutdown(who, where, args):
137 save()
138 hal.connection.quit("Daisy, daaaisy...")
139 raise SystemExit
140 commands['shutdown'] = (Perms.ircop, shutdown)
142 def title(who, where, args, all=False):
143 try:
144 reply(who, where, "[%s]" % (html.get_title(args)), all)
145 except Exception, e:
146 print e
147 reply(who, where, "Error retrieving URL '%s'." % args, all)
148 commands['title'] = (Perms.voice, title)
150 def title_implicit(who, where, args):
151 title(who, where, args, all=True)
152 commands['title implicit'] = (Perms.voice, title_implicit)
154 def google(who, where, args):
155 try:
156 url = html.first_google(args)
157 reply(who, where, "Google says: %s [%s]." % (url, html.get_title(url)), all=True)
158 except Exception, e:
159 print e
160 reply(who, where, "Ewwww... Google barfed on me.", all=True)
161 commands['google'] = (Perms.voice, google)
163 def jig(who, where, args):
164 google(who, where, args + " site:jayisgames.com")
165 commands['jig'] = (Perms.voice, jig)
167 def wp(who, where, args):
168 google(who, where, args + " site:en.wikipedia.org")
169 commands['wp'] = (Perms.voice, wp)
170 commands['wikipedia'] = (Perms.voice, wp)
172 def do_is(who, where, args):
173 (key, to_be, garbage, fact) = args
174 if key.endswith('?'):
175 key = key[:-1]
176 if locked.get(key):
177 return
178 #reply(who, where, "I'm sorry, %s, I'm afraid I can't do that." % who)
179 elif fact:
180 factoids[key] = (to_be, fact)
181 elif key in factoids:
182 del factoids[key]
183 commands['do is'] = (Perms.voice, do_is)
185 def force(who, where, args):
186 if not is_re.search(args):
187 reply(who, where, "Syntax: $force a (is/are/was/were) b")
188 return
189 (key, to_be, garbage, fact) = is_re.split(args, 1)
190 if key.endswith('?'):
191 key = key[:-1]
192 if fact:
193 factoids[key] = (to_be, fact)
194 elif key in factoids:
195 del factoids[key]
196 commands['force'] = (Perms.op, force)
198 def lock(who, where, args):
199 if args.endswith('?'):
200 args = args[:-1]
201 if not locked.get(args):
202 locked[args] = True
203 reply(who, where, "Done.")
204 else:
205 reply(who, where, "It's already locked.")
206 commands['lock'] = (Perms.op, lock)
208 def unlock(who, where, args):
209 if args.endswith('?'):
210 args = args[:-1]
211 if locked.get(args):
212 locked[args] = False
213 reply(who, where, "Done.")
214 else:
215 reply(who, where, "It's not locked.")
216 commands['unlock'] = (Perms.op, unlock)
218 def factoid(who, where, args, implicit=False):
219 if args.endswith('?'):
220 args = args[:-1]
221 if args in factoids:
222 (to_be, fact) = factoids[args]
223 if not implicit and fact.count("$who"):
224 implicit = True
225 response = ""
226 lfact = fact.lower()
227 if lfact.startswith("<"):
228 if lfact.startswith("<raw>"):
229 response = fact[5:].lstrip()
230 elif lfact.startswith("<reply>"):
231 response = fact[7:].lstrip()
232 elif lfact.startswith("<action>"):
233 response = "\x01ACTION %s\x01" % fact[8:].lstrip()
234 if not response:
235 response = "I hear that %s %s %s" % (args, to_be, fact)
236 response = response.replace("$who", who)
237 reply(who, where, response, all=implicit)
238 elif not implicit:
239 reply(who, where, "I don't have anything matching '%s'." % args)
240 commands['fact'] = (Perms.voice, factoid)
241 commands['factoid'] = (Perms.voice, factoid)
243 def factoid_implicit(who, where, args):
244 factoid(who, where, args, implicit=True)
245 commands['factoid implicit'] = (Perms.voice, factoid_implicit)
247 def do_commands(who):
248 user = whois_info[who]
249 for where, command, args in waiting_commands[who]:
250 if command not in commands:
251 command = 'raise_error'
252 args = "I don't understand that."
253 (required_perms, do_it) = commands[command]
254 if required_perms <= get_perms(who, where):
255 do_it(who, where, args)
256 waiting_commands[who] = []
258 def got_whois(conn, event):
259 user = event.arguments()[0]
260 whois_info[user] = Whois()
261 irc.add_global_handler("whoisuser", got_whois)
262 irc.add_global_handler("nosuchnick", got_whois)
264 def got_registered(conn, event):
265 args = event.arguments()
266 if len(args) == 2 and args[1] == "is a registered nick":
267 user = args[0]
268 whois_info[user].registered = True
269 irc.add_global_handler("307", got_registered)
271 def got_channels(conn, event):
272 args = event.arguments()
273 if len(args) == 2:
274 user = args[0]
275 channels = args[1].split()
276 for c in channels:
277 if c[0] != "#":
278 whois_info[user].channel_perms[c[1:]] = Perms.from_symbol(c[0])
279 else:
280 whois_info[user].channel_perms[c] = Perms.present
281 irc.add_global_handler("whoischannels", got_channels)
283 def got_servop(conn, event):
284 args = event.arguments()
285 if len(args) == 2:
286 user = args[0]
287 whois_info[user].ircop = True
288 irc.add_global_handler("whoisoperator", got_servop)
290 def got_whoend(conn, event):
291 who = event.arguments()[0]
292 do_commands(who)
293 if False:
294 user = whois_info[who]
295 print "Who info for %s:" % who
296 print "Registered: %s" % user.registered
297 print "IRCop: %s" % user.ircop
298 p = user.channel_perms
299 perms = "".join([("%s: %s, " % (k, p[k])) for k in p])[:-2]
300 print "Channel info: %s" % perms
301 irc.add_global_handler("endofwhois", got_whoend)
303 def got_invite(conn, event):
304 who = irclib.nm_to_n(event.source())
305 where = event.arguments()[0]
306 got_command(who, where, "join")
307 irc.add_global_handler("invite", got_invite)
309 def got_msg(conn, event):
310 msg = event.arguments()[0]
311 who = irclib.nm_to_n(event.source())
312 split = msg.split(" ", 1)
313 command = split[0]
314 if len(split) != 1:
315 args = split[1]
316 else:
317 args = ""
318 if command and command[0] == "$":
319 command = command[1:]
321 if event.eventtype() == 'privmsg':
322 where = "private"
323 else:
324 where = event.target()
325 if msg[0] != "$":
326 if msg.count("http://"):
327 got_command(who, where, "title implicit", html.extract_url(msg))
328 if is_re.search(msg):
329 command = "do is"
330 args = is_re.split(msg, 1)
331 else:
332 command = "factoid implicit"
333 args = msg
334 got_command(who, where, command, args)
335 irc.add_global_handler("privmsg", got_msg)
336 irc.add_global_handler("pubmsg", got_msg)
339 def debug_console():
340 while DEBUG:
341 cmd = sys.stdin.readline().strip()
342 if cmd:
343 hal.connection.send_raw(cmd)
345 thread.start_new_thread(debug_console, ())
347 hal.ircobj.process_forever(.1)