Whoops, screwed up the command line for unignore.
[halbot.git] / hal.py
blobf727dbc576bc355cd61e2c1be759c677f286c1f3
1 #!/usr/bin/env python
2 from __future__ import generators
3 import html, re, irclib, ircbot, thread, sys, atexit, bz2, logging, traceback, time, threadsafe_sched, os
4 from dateutil.parser import parse as raw_time_parser
5 from cPickle import dumps as pickle, loads as depickle
6 DEBUG = 0
7 irclib.DEBUG = DEBUG
9 nick = "Hal"
10 hal = ircbot.SingleServerIRCBot([("localhost", 6667)], nick, "Halbot")
11 hal.connect("localhost", 6667, nick)
12 time.sleep(0.1)
13 hal.connection.join("#casualgameplay")
14 hal.connection.privmsg("NickServ", "identify Orqv)>y!")
15 is_re = re.compile(" (is|are|was|were)( |$)", re.IGNORECASE)
16 nick_re = re.compile(nick + "[:,] ", re.IGNORECASE)
17 private = "p"
18 scheduler = threadsafe_sched.scheduler()
19 os.environ['TZ'] = "GMT"
20 time.tzset()
21 SAVE_VERSION = 2
23 class Perms(object):
24 symbols = {}
25 def __init__(self, level, symbol=None):
26 self.level = level
27 self.symbol = symbol
28 if symbol:
29 Perms.symbols[symbol] = self
30 def __cmp__(self, other):
31 return self.level - other.level
32 def __str__(self):
33 if self.symbol:
34 return self.symbol
35 else:
36 return str(self.level)
37 #@staticmethod
38 def from_symbol(symbol):
39 return Perms.symbols.get(symbol, Perms.present)
40 from_symbol = staticmethod(from_symbol)
41 Perms.ban = Perms(-2)
42 Perms.none = Perms(-1)
43 Perms.present = Perms(0)
44 Perms.voice = Perms(1, "+")
45 Perms.hop = Perms(2, "%")
46 Perms.op = Perms(3, "@")
47 Perms.admin = Perms(4, "&")
48 Perms.owner = Perms(5, "~")
49 Perms.ircop = Perms(6)
51 my_users = ircbot.IRCDict()
52 factoid_dbs = ircbot.IRCDict()
53 locked_dbs = ircbot.IRCDict()
54 reminder_dbs = ircbot.IRCDict()
55 try:
56 db = open("hal.db")
57 zipped = db.read()
58 pickled = bz2.decompress(zipped)
59 (version, data) = depickle(pickled)
60 if version == SAVE_VERSION:
61 (my_users, factoid_dbs, locked_dbs, reminder_dbs) = data
62 elif version == 1:
63 (my_users, factoid_dbs, locked_dbs) = data
64 else:
65 sys.exit("Unrecognized database version.")
66 db.close()
67 del db, zipped, pickled
68 except IOError:
69 pass
71 logging.getLogger().addHandler(logging.FileHandler("error.log"))
73 class Buffer(object):
74 def __init__(self, prefix=""):
75 self.data = prefix
76 def write(self, unbuffered):
77 self.data += unbuffered
79 def get_timestamp(when=None):
80 if when == None:
81 when = time.time()
82 return time.ctime(when) + " " + time.tzname[time.daylight]
84 def parse_time(time_string):
85 return time.mktime(raw_time_parser(time_string).timetuple())
87 def safe_call(func, args):
88 try:
89 func(*args)
90 except Exception, e:
91 if isinstance(e, SystemExit):
92 raise
93 buffer = Buffer("Exception in function %s at %s:\n"
94 % (func.__name__, get_timestamp()))
95 traceback.print_exc(file=buffer)
96 logging.getLogger().error(buffer.data)
97 # Try to report the error interactively.
98 if len(args) >= 2 and type(args[0]) == type(args[1]) == str:
99 try:
100 reply(args[0], args[1], "Ow! Ohh, man, that didn't feel good. " \
101 +"Somebody get FunnyMan, quick!")
102 except Exception:
103 pass
104 elif len(args) and type(args[0]) == str:
105 try:
106 reply('', args[0], "Ow! Ohh, man, that didn't feel good. " \
107 +"Somebody get FunnyMan, quick!", all=True)
108 except Exception:
109 pass
111 def safe(func):
112 return lambda *args: safe_call(func, args)
114 def troggie(func):
115 def continue_if_no_troggie(who, where, *args, **kwargs):
116 if where in hal.channels:
117 chan = hal.channels[where]
118 if "troggie" not in chan.userdict:
119 func(who, where, *args, **kwargs)
120 return continue_if_no_troggie
122 def resize(list_or_tuple, length):
123 '''Creates a new list or tuple of length 'length', using as many elements from
124 list_or_tuple as possible and right-padding with "". Does not modify
125 list_or_tuple.'''
126 if len(list_or_tuple) < length:
127 if type(list_or_tuple) == list:
128 return list_or_tuple + [""] * (length - len(list_or_tuple))
129 else:
130 return list_or_tuple + ("",) * (length - len(list_or_tuple))
131 else:
132 return list_or_tuple[:length]
134 irc = hal.ircobj
135 class Whois(object):
136 registered = False
137 ircop = False
138 channel_perms = None
139 def __init__(self):
140 self.channel_perms = {}
142 waiting_commands = {}
143 whois_info = {}
145 def got_command(who, where, command, args=""):
146 if not command in commands:
147 command = 'raise error'
148 args = "I don't understand that."
149 waiting = waiting_commands.get(who, [])
150 if not waiting:
151 (required_perms, do_it) = commands[command]
152 if where == private and required_perms <= Perms.owner:
153 safe_call(do_it, (who, where, args))
154 elif required_perms <= Perms.voice and where in hal.channels:
155 chan = hal.channels[where]
156 if who in chan.voiceddict or who in chan.hopdict or who in chan.operdict \
157 or who in chan.admindict or who in chan.ownerdict:
158 safe_call(do_it, (who, where, args))
159 else:
160 return
161 else:
162 waiting_commands[who] = [(where, command, args)]
163 hal.connection.whois((who,))
164 else:
165 waiting.append((where, command, args))
167 def reply(who, where, what, all=False):
168 if where == private:
169 hal.connection.privmsg(who, what)
170 elif all:
171 hal.connection.privmsg(where, what)
172 else:
173 hal.connection.privmsg(where, "%s: %s" % (who, what))
175 def get_perms(who, where):
176 user = whois_info[who]
177 if user.ircop:
178 return Perms.ircop
179 if where == private:
180 server_perm = Perms.owner
181 else:
182 server_perm = whois_info[who].channel_perms.get(where, Perms.none)
183 if user.registered:
184 my_perm = my_users.get(who, Perms.none)
185 else:
186 my_perm = Perms.none
187 return max(server_perm, my_perm)
189 commands = {}
191 commands['raise error'] = (Perms.voice, reply)
193 def ping(who, where, args):
194 reply(who, where, "Pong!")
195 commands['ping'] = (Perms.voice, ping)
197 class TestException(Exception): pass
199 def error(who, where, args):
200 raise TestException, str(args)
201 commands['error'] = (Perms.ircop, error)
203 def join(who, where, args):
204 hal.connection.join(where)
205 commands['do join'] = (Perms.op, join)
207 def save(who=None, where=None, args=None):
208 pickled = pickle((
209 SAVE_VERSION,
210 (my_users, factoid_dbs, locked_dbs, reminder_dbs)
212 zipped = bz2.compress(pickled)
213 db = open("hal.db", "w")
214 db.write(zipped)
215 db.close()
216 if who:
217 reply(who, where, "Database saved.")
218 commands['save'] = (Perms.ircop, save)
219 atexit.register(save)
221 def shutdown(who, where, args):
222 hal.connection.quit("Daisy, daaaisy...")
223 raise SystemExit
224 commands['shutdown'] = (Perms.ircop, shutdown)
226 def title(who, where, args, all=False):
227 try:
228 reply(who, where, "[%s]" % (html.get_title(args)), all)
229 except Exception, e:
230 print e
231 reply(who, where, "Error retrieving URL '%s'." % args, all)
232 title = troggie(title)
233 commands['title'] = (Perms.voice, title)
235 def title_implicit(who, where, args):
236 title(who, where, args, all=True)
237 commands['title implicit'] = (Perms.voice, title_implicit)
239 def google(who, where, args):
240 try:
241 url = html.first_google(args)
242 reply(who, where, "Google says: %s [%s]." % (url, html.get_title(url)), all=True)
243 except Exception, e:
244 print e
245 reply(who, where, "Ewwww... Google barfed on me.", all=True)
246 #google = troggie(google)
247 commands['google'] = (Perms.voice, google)
249 def jig(who, where, args):
250 google(who, where, args + " site:jayisgames.com")
251 #jig = troggie(jig)
252 commands['jig'] = (Perms.voice, jig)
254 def wp(who, where, args):
255 google(who, where, args + " site:en.wikipedia.org")
256 commands['wp'] = (Perms.voice, wp)
257 commands['wikipedia'] = (Perms.voice, wp)
259 weak_mask_re = re.compile(".+!.+@.+")
261 def ignore(who, where, args):
262 if args.lower().strip() == "list":
263 banlist = ", ".join([user for user in my_users if my_users[user] == Perms.ban])
264 reply(who, where, "Currently ignoring: %s" % banlist)
265 return
266 if not weak_mask_re.search(args):
267 reply(who, where, "Please give a full nick!user@host mask.")
268 return
269 my_users[args] = Perms.ban
270 reply(who, where, "Done.")
271 commands['ignore'] = (Perms.ircop, ignore)
273 def unignore(who, where, args):
274 if user in my_users and my_users[args] == Perms.ban:
275 my_users[args] = Perms.none
276 reply(who, where, "Done.")
277 else:
278 reply(who, where, "I wasn't ignoring them.")
279 commands['unignore'] = (Perms.ircop, unignore)
281 def extract_my_db(dbs, who, where, default_maker):
282 if where == private:
283 key = who
284 else:
285 key = where
286 if key not in dbs:
287 default = default_maker()
288 dbs[key] = default
289 return dbs[key]
291 def get_fact_dbs(who, where):
292 factoids = extract_my_db(factoid_dbs, who, where, ircbot.IRCDict)
293 locked = extract_my_db(locked_dbs, who, where, ircbot.IRCDict)
294 return (factoids, locked)
296 def do_is(who, where, args):
297 (key, to_be, garbage, fact) = args
298 (factoids, locked) = get_fact_dbs(who, where)
299 if key.endswith('?'):
300 key = key[:-1]
301 if locked.get(key):
302 reply(who, where, "I'm sorry, %s, I'm afraid I can't do that." % who, all=True)
303 return
304 elif fact:
305 factoids[key] = (to_be, fact)
306 elif key in factoids:
307 del factoids[key]
308 do_is = troggie(do_is)
309 commands['do is'] = (Perms.voice, do_is)
311 def forget(who, where, args):
312 (factoids, locked) = get_fact_dbs(who, where)
313 key = args
314 if locked.get(key):
315 reply(who, where, "I'm sorry, %s, I'm afraid I can't do that." % who, all=True)
316 elif key in factoids:
317 del factoids[key]
318 reply(who, where, "I forgot %s." % key)
319 else:
320 reply(who, where, "I don't have anything matching '%s'." % key)
321 forget = troggie(forget)
322 commands['forget'] = (Perms.voice, forget)
324 def force(who, where, args):
325 (factoids, locked) = get_fact_dbs(who, where)
326 if not is_re.search(args):
327 reply(who, where, "Syntax: $force a (is/are/was/were) b")
328 return
329 (key, to_be, garbage, fact) = resize(is_re.split(args, 1),4)
330 if key.endswith('?'):
331 key = key[:-1]
332 if fact:
333 factoids[key] = (to_be, fact)
334 elif key in factoids:
335 del factoids[key]
336 commands['force'] = (Perms.op, force)
338 def lock(who, where, args):
339 (factoids, locked) = get_fact_dbs(who, where)
340 if args.endswith('?'):
341 args = args[:-1]
342 if not locked.get(args):
343 locked[args] = True
344 reply(who, where, "Done.")
345 else:
346 reply(who, where, "It's already locked.")
347 commands['lock'] = (Perms.op, lock)
349 def unlock(who, where, args):
350 (factoids, locked) = get_fact_dbs(who, where)
351 if args.endswith('?'):
352 args = args[:-1]
353 if locked.get(args):
354 locked[args] = False
355 reply(who, where, "Done.")
356 else:
357 reply(who, where, "It's not locked.")
358 commands['unlock'] = (Perms.op, unlock)
360 def factoid(who, where, args, implicit=False):
361 (factoids, locked) = get_fact_dbs(who, where)
362 if args.endswith('?'):
363 args = args[:-1]
364 if args in factoids:
365 (to_be, fact) = factoids[args]
366 if not implicit and fact.count("$who"):
367 implicit = True
368 response = ""
369 lfact = fact.lower()
370 if lfact.startswith("<"):
371 if lfact.startswith("<raw>"):
372 response = fact[5:].lstrip()
373 elif lfact.startswith("<reply>"):
374 response = fact[7:].lstrip()
375 elif lfact.startswith("<action>"):
376 response = "\x01ACTION %s\x01" % fact[8:].lstrip()
377 if not response:
378 response = "I hear that %s %s %s" % (args, to_be, fact)
379 response = response.replace("$who", who)
380 reply(who, where, response, all=implicit)
381 elif not implicit:
382 reply(who, where, "I don't have anything matching '%s'." % args)
383 commands['fact'] = (Perms.voice, factoid)
384 commands['factoid'] = (Perms.voice, factoid)
386 def factoid_implicit(who, where, args):
387 factoid(who, where, args, implicit=True)
388 factoid_implicit = troggie(factoid_implicit)
389 commands['factoid implicit'] = (Perms.voice, factoid_implicit)
391 def get_time(who, where, args):
392 reply(who, where, "Current time is: " + get_timestamp())
393 commands['time'] = (Perms.voice, get_time)
395 def get_reminder_db(who, where):
396 return extract_my_db(reminder_dbs, who, where, lambda: [])
398 def describe_reminder(reminder):
399 (when, message, groups, repeat) = reminder
400 description = '"%s" at %s.' % (message, get_timestamp(when))
401 if groups:
402 groupstr = ",".join(groups)
403 description += " Groups: " + groupstr
404 if repeat:
405 hours = repeat / 3600
406 minutes = str(repeat / 60 % 60).zfill(2)
407 description += " Repeating every %d:%s." % (hours, minutes)
408 return description
410 reminder_res = {"add": re.compile(r'^"((?:[^"]|\")*)" +at +([^"]*)$',
411 re.IGNORECASE),
412 "set": re.compile(r'^(\d+) +(\w+) +(.*)$', re.IGNORECASE),
413 "repeat h+:mm": re.compile(r'^(\d+) +every +(\d+):(\d\d)$',
414 re.IGNORECASE),
415 "repeat x units": re.compile(r'^(\d+) +every +(\d+) +' +
416 r'(week|day|hour|minute)s?$',
417 re.IGNORECASE),
418 "repeat off": re.compile(r'^(\d+) +(off|disable|stop|never|none'
419 +r'|remove)$',
420 re.IGNORECASE)
423 time_units = {"minute": 60,
424 "hour": 60*60,
425 "day": 60*60*24,
426 "week": 60*60*24*7}
428 def safeint(intstr):
429 try:
430 return int(intstr)
431 except ValueError:
432 return -1
434 def reminder_index(who, where, reminders, whichstr):
435 which = safeint(whichstr)
436 if which < 1:
437 reply(who, where, "Which reminder?")
438 return -1
439 if which > len(reminders):
440 reply(who, where, "I don't have that many reminders.")
441 return -1
442 return which - 1
444 def real_where(who, where):
445 if where == private:
446 return who
447 return where
449 def fire_reminder(where, reminders, reminder):
450 (when, message, groups, repeat) = reminder
451 curtime = time.time()
452 if when < (curtime - 60):
453 delay = curtime - when
454 reply('', where, "Reminder delayed by %d minutes: %s" % (delay // 60,
455 message), all=True)
456 else:
457 delay = 0
458 reply('', where, message, all=True)
459 if repeat:
460 if repeat < delay:
461 skip = delay // repeat
462 reply('', where, "(skipping %d delayed repititions)" % skip, all=True)
463 real_repeat = (skip + 1) * repeat
464 else:
465 real_repeat = repeat
466 reminder[0] += real_repeat
467 schedule_reminder(where, reminders, reminder)
468 else:
469 reminders.remove(reminder)
470 fire_reminder = safe(fire_reminder)
472 def schedule_reminder(where, reminders, reminder):
473 scheduler.enterabs(reminder[0], 0, fire_reminder, (where, reminders, reminder))
475 def add_reminder(who, where, reminders, reminder):
476 if len(reminders) < 10 or where == "#casualgameplay":
477 reminders.append(reminder)
478 schedule_reminder(real_where(who, where), reminders, reminder)
479 reply(who, where, "Done.")
480 else:
481 reply(who, where,
482 "I'm sorry, %s, I'm afraid I can't do that. You're limited to 10."
483 % who)
485 def cancel_reminder(where, reminders, reminder):
486 try:
487 scheduler.cancel((reminder[0], 0, fire_reminder, (where, reminders, reminder)))
488 except ValueError:
489 pass
491 def reschedule(who, where, reminders, reminder, when):
492 rwhere = real_where(who, where)
493 cancel_reminder(rwhere, reminders, reminder)
494 reminder[0] = when
495 schedule_reminder(rwhere, reminders, reminder)
497 def reminder(who, where, reminder_args):
498 (command, args) = resize(reminder_args.split(" ", 1), 2)
499 reminders = get_reminder_db(who, where)
500 if not command:
501 reply(who, where, "Available reminder commands: add del list set repeat help")
502 elif command in ("add", "new", "create", "make"):
503 parsed = reminder_res["add"].search(args)
504 if not parsed:
505 reply(who, where, "I don't understand that. Try $reminder help add")
506 return
507 (msg, whenstr) = parsed.groups()
508 try:
509 when = parse_time(whenstr)
510 except ValueError:
511 reply(who, where, "I don't understand that time.")
512 return
513 reminder = [when, msg, [], 0]
514 add_reminder(who, where, reminders, reminder)
515 elif command in ("del", "delete", "remove", "rm"):
516 which = reminder_index(who, where, reminders, args)
517 if which == -1:
518 return
519 reminder = reminders[which]
520 del reminders[which]
521 cancel_reminder(real_where(who, where), reminders, reminder)
522 reply(who, where, "Deleted reminder %d: %s" %
523 (which+1, describe_reminder(reminder)))
524 elif command in ("list", "show"):
525 if len(reminders) > 5:
526 reply(who, where, "Listing %d reminders in private " % len(reminders)
527 + "to avoid channel clutter")
528 where = private
529 if not reminders:
530 reply(who, where, "No reminders.")
531 index = 1
532 for reminder in reminders:
533 reply(who, where, "%2d. %s" % (index, describe_reminder(reminder)))
534 index += 1
535 elif command in ("change", "alter", "set"):
536 parsed = reminder_res["set"].search(args)
537 if not parsed:
538 reply(who, where, "I don't understand that. Try $reminder help set")
539 return
540 (whichstr, property, value) = parsed.groups()
541 which = reminder_index(who, where, reminders, whichstr)
542 if which == -1:
543 return
544 reminder = reminders[which]
545 lproperty = property.lower()
546 if lproperty in ("message", "msg"):
547 reminder[1] = value
548 elif lproperty == "time":
549 try:
550 when = parse_time(value)
551 reschedule(who, where, reminders, reminder, when)
552 except ValueError:
553 reply(who, where, "I don't understand that time.")
554 return
555 elif lproperty in ("group", "groups"):
556 groups = value.split(",")
557 groups = [group.strip() for group in groups]
558 reminder[2] = groups
559 reply(who, where, "Done.")
560 elif command == "repeat":
561 parse_hm = reminder_res["repeat h+:mm"].search(args)
562 parse_units = reminder_res["repeat x units"].search(args)
563 parse_off = reminder_res["repeat off"].search(args)
564 parsed = parse_hm or parse_units or parse_off
565 if parsed:
566 which = reminder_index(who, where, reminders, parsed.groups()[0])
567 if which == -1:
568 return
569 reminder = reminders[which]
570 else:
571 reply(who, where, "I don't understand that. Try $reminder help repeat")
572 return
573 if parse_hm:
574 (whichstr, hourstr, minutestr) = parsed.groups()
575 hours = safeint(hourstr)
576 if hours < 0:
577 reply(who, where, "Bad number of hours.")
578 return
579 minutes = safeint(minutestr)
580 # Mathematicians, read at your own peril.
581 if 59 < minutes < 0:
582 reply(who, where, "Bad number of minutes.")
583 return
584 if hours == minutes == 0:
585 reply(who, where, "Repeating continuously sounds like a bad idea.")
586 return
587 reminder[3] = 60 * (minutes + 60 * hours)
588 reply(who, where,
589 "Reminder number %d now repeating every %d hours and %d minutes."
590 % (which+1, hours, minutes))
591 elif parse_units:
592 (whichstr, numstr, unit) = parsed.groups()
593 unit = unit.lower()
594 num = safeint(numstr)
595 if numstr < 1:
596 reply(who, where, "Bad number of %ss." % unit)
597 return
598 if unit not in time_units:
599 reply(who, where, "I don't know that unit.")
600 return
601 reminder[3] = num * time_units[unit]
602 reply(who, where, "Reminder number %d now repeating every %d %ss."
603 % (which+1, num, unit))
604 else: # parse_off
605 reminder[3] = 0
606 reply(who, where, "Repeating disabled on reminder %d." % which+1)
608 elif command == "help":
609 if not args:
610 reply(who, where, "To get help on a specific command, type $reminder " \
611 + "help <command> (e.g. $reminder help add). " \
612 + "Available reminder commands: add del list set "\
613 + "repeat help")
614 elif args in ("add", "new", "create", "make"):
615 reply(who, where, '$reminder add "<msg>" at <time>: Adds a new ' \
616 + "reminder. When <time> comes, I will say <msg>. A" \
617 + " variety of time formats are supported. Use $time" \
618 + " to get my current time for comparison.")
619 elif args in ("del", "delete", "remove", "rm"):
620 reply(who, where, "$reminder del <reminder>: Delete reminder number " \
621 + "<reminder>.");
622 elif args in ("change", "alter", "set"):
623 reply(who, where, "$reminder set <reminder> <property> <value>: Change" \
624 + " <property> of reminder number <reminder> to " \
625 + "<value>. Availale properties: message, time, group")
626 elif args in ("list", "show"):
627 reply(who, where, "$reminder list: Print a list of reminders. If there" \
628 + " are more than 5, I will reply in private.")
629 elif args == "repeat":
630 reply(who, where, "$reminder repeat <reminder> every <interval>: " \
631 + "Reminder number <reminder> will be repeated every " \
632 + "<interval>. Use $reminder repeat <reminder> off to"\
633 + " stop repeating a reminder.")
634 elif args == "help":
635 reply(who, where, "$reminder help <command>: get help on <command>. " \
636 + "You know, like you just did.")
637 else:
638 reply(who, where, "I don't understand that.")
639 commands['reminder'] = (Perms.admin, reminder)
640 commands['reminders'] = (Perms.admin, reminder)
642 def do_commands(who):
643 user = whois_info[who]
644 for where, command, args in waiting_commands[who]:
645 if command not in commands:
646 command = 'raise error'
647 args = "I don't understand that."
648 (required_perms, do_it) = commands[command]
649 if required_perms <= get_perms(who, where):
650 safe_call(do_it, (who, where, args))
651 waiting_commands[who] = []
653 #@safe
654 def got_whois(conn, event):
655 user = event.arguments()[0]
656 whois_info[user] = Whois()
657 got_whois = safe(got_whois)
658 irc.add_global_handler("whoisuser", got_whois)
659 irc.add_global_handler("nosuchnick", got_whois)
661 #@safe
662 def got_registered(conn, event):
663 args = event.arguments()
664 if len(args) == 2 and args[1] == "is a registered nick":
665 user = args[0]
666 whois_info[user].registered = True
667 got_registered = safe(got_registered)
668 irc.add_global_handler("307", got_registered)
670 #@safe
671 def got_channels(conn, event):
672 args = event.arguments()
673 if len(args) == 2:
674 user = args[0]
675 channels = args[1].split()
676 for c in channels:
677 if c[0] != "#":
678 whois_info[user].channel_perms[c[1:]] = Perms.from_symbol(c[0])
679 else:
680 whois_info[user].channel_perms[c] = Perms.present
681 got_channels = safe(got_channels)
682 irc.add_global_handler("whoischannels", got_channels)
684 #@safe
685 def got_servop(conn, event):
686 args = event.arguments()
687 if len(args) == 2:
688 user = args[0]
689 whois_info[user].ircop = True
690 got_servop = safe(got_servop)
691 irc.add_global_handler("whoisoperator", got_servop)
693 #@safe
694 def got_whoend(conn, event):
695 who = event.arguments()[0]
696 do_commands(who)
697 if False:
698 user = whois_info[who]
699 print "Who info for %s:" % who
700 print "Registered: %s" % user.registered
701 print "IRCop: %s" % user.ircop
702 p = user.channel_perms
703 perms = "".join([("%s: %s, " % (k, p[k])) for k in p])[:-2]
704 print "Channel info: %s" % perms
705 got_whoend = safe(got_whoend)
706 irc.add_global_handler("endofwhois", got_whoend)
708 #@safe
709 def got_invite(conn, event):
710 who = irclib.nm_to_n(event.source())
711 where = event.arguments()[0]
712 got_command(who, where, "do join")
713 got_invite = safe(got_invite)
714 irc.add_global_handler("invite", got_invite)
716 #@safe
717 def got_msg(conn, event):
718 for user in my_users:
719 if irclib.mask_matches(event.source(), user):
720 if my_users[user] == Perms.ban:
721 return
722 msg = event.arguments()[0]
723 who = irclib.nm_to_n(event.source())
724 (command, args) = resize(msg.split(" ", 1),2)
725 if command and command[0] == "$":
726 command = command[1:]
728 if event.eventtype() == 'privmsg':
729 where = private
730 else:
731 where = event.target()
732 if msg.count("http://"):
733 got_command(who, where, "title implicit", html.extract_url(msg))
734 if msg[0] != "$":
735 if nick_re.match(msg):
736 (me, command, args) = resize(msg.split(" ", 2),3)
737 if command in commands:
738 pass
739 elif is_re.search(msg):
740 command = "do is"
741 args = is_re.split(msg.split(" ", 1)[1], 1)
742 else:
743 command = "factoid implicit"
744 args = msg.split(" ", 1)[1]
745 else:
746 if command in commands and commands[command][0] <= Perms.voice:
747 pass
748 elif is_re.search(msg):
749 command = "do is"
750 args = is_re.split(msg, 1)
751 else:
752 command = "factoid implicit"
753 args = msg
754 got_command(who, where, command, args)
755 got_msg = safe(got_msg)
756 irc.add_global_handler("privmsg", got_msg)
757 irc.add_global_handler("pubmsg", got_msg)
760 def debug_console():
761 while DEBUG:
762 cmd = sys.stdin.readline().strip()
763 if cmd:
764 hal.connection.send_raw(cmd)
765 thread.start_new_thread(debug_console, ())
767 def run_reminders():
768 for where in reminder_dbs.keys():
769 db = reminder_dbs[where]
770 for reminder in db:
771 schedule_reminder(where, db, reminder)
772 while True: # Looping in case of errors.
773 safe(scheduler.run_forever)()
774 thread.start_new_thread(run_reminders, ())
776 hal.ircobj.process_forever(None)