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
10 hal
= ircbot
.SingleServerIRCBot([("localhost", 6667)], nick
, "Halbot")
11 hal
.connect("localhost", 6667, nick
)
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
)
18 scheduler
= threadsafe_sched
.scheduler()
19 os
.environ
['TZ'] = "GMT"
25 def __init__(self
, level
, symbol
=None):
29 Perms
.symbols
[symbol
] = self
30 def __cmp__(self
, other
):
31 return self
.level
- other
.level
36 return str(self
.level
)
38 def from_symbol(symbol
):
39 return Perms
.symbols
.get(symbol
, Perms
.present
)
40 from_symbol
= staticmethod(from_symbol
)
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()
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
63 (my_users
, factoid_dbs
, locked_dbs
) = data
65 sys
.exit("Unrecognized database version.")
67 del db
, zipped
, pickled
71 logging
.getLogger().addHandler(logging
.FileHandler("error.log"))
74 def __init__(self
, prefix
=""):
76 def write(self
, unbuffered
):
77 self
.data
+= unbuffered
79 def get_timestamp(when
=None):
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
):
91 if isinstance(e
, SystemExit):
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:
100 reply(args
[0], args
[1], "Ow! Ohh, man, that didn't feel good. " \
101 +"Somebody get FunnyMan, quick!")
104 elif len(args
) and type(args
[0]) == str:
106 reply('', args
[0], "Ow! Ohh, man, that didn't feel good. " \
107 +"Somebody get FunnyMan, quick!", all
=True)
112 return lambda *args
: safe_call(func
, args
)
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
126 if len(list_or_tuple
) < length
:
127 if type(list_or_tuple
) == list:
128 return list_or_tuple
+ [""] * (length
- len(list_or_tuple
))
130 return list_or_tuple
+ ("",) * (length
- len(list_or_tuple
))
132 return list_or_tuple
[:length
]
140 self
.channel_perms
= {}
142 waiting_commands
= {}
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
, [])
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
))
162 waiting_commands
[who
] = [(where
, command
, args
)]
163 hal
.connection
.whois((who
,))
165 waiting
.append((where
, command
, args
))
167 def reply(who
, where
, what
, all
=False):
169 hal
.connection
.privmsg(who
, what
)
171 hal
.connection
.privmsg(where
, what
)
173 hal
.connection
.privmsg(where
, "%s: %s" % (who
, what
))
175 def get_perms(who
, where
):
176 user
= whois_info
[who
]
180 server_perm
= Perms
.owner
182 server_perm
= whois_info
[who
].channel_perms
.get(where
, Perms
.none
)
184 my_perm
= my_users
.get(who
, Perms
.none
)
187 return max(server_perm
, my_perm
)
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):
210 (my_users
, factoid_dbs
, locked_dbs
, reminder_dbs
)
212 zipped
= bz2
.compress(pickled
)
213 db
= open("hal.db", "w")
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...")
224 commands
['shutdown'] = (Perms
.ircop
, shutdown
)
226 def title(who
, where
, args
, all
=False):
228 reply(who
, where
, "[%s]" % (html
.get_title(args
)), all
)
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
):
241 url
= html
.first_google(args
)
242 reply(who
, where
, "Google says: %s [%s]." % (url
, html
.get_title(url
)), all
=True)
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")
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
)
266 if not weak_mask_re
.search(args
):
267 reply(who
, where
, "Please give a full nick!user@host mask.")
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.")
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
):
287 default
= default_maker()
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('?'):
302 reply(who
, where
, "I'm sorry, %s, I'm afraid I can't do that." % who
, all
=True)
305 factoids
[key
] = (to_be
, fact
)
306 elif key
in factoids
:
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
)
315 reply(who
, where
, "I'm sorry, %s, I'm afraid I can't do that." % who
, all
=True)
316 elif key
in factoids
:
318 reply(who
, where
, "I forgot %s." % key
)
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")
329 (key
, to_be
, garbage
, fact
) = resize(is_re
.split(args
, 1),4)
330 if key
.endswith('?'):
333 factoids
[key
] = (to_be
, fact
)
334 elif key
in factoids
:
336 commands
['force'] = (Perms
.op
, force
)
338 def lock(who
, where
, args
):
339 (factoids
, locked
) = get_fact_dbs(who
, where
)
340 if args
.endswith('?'):
342 if not locked
.get(args
):
344 reply(who
, where
, "Done.")
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('?'):
355 reply(who
, where
, "Done.")
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('?'):
365 (to_be
, fact
) = factoids
[args
]
366 if not implicit
and fact
.count("$who"):
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()
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
)
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
))
402 groupstr
= ",".join(groups
)
403 description
+= " Groups: " + groupstr
405 hours
= repeat
/ 3600
406 minutes
= str(repeat
/ 60 % 60).zfill(2)
407 description
+= " Repeating every %d:%s." % (hours
, minutes
)
410 reminder_res
= {"add": re
.compile(r
'^"((?:[^"]|\")*)" +at +([^"]*)$',
412 "set": re
.compile(r
'^(\d+) +(\w+) +(.*)$', re
.IGNORECASE
),
413 "repeat h+:mm": re
.compile(r
'^(\d+) +every +(\d+):(\d\d)$',
415 "repeat x units": re
.compile(r
'^(\d+) +every +(\d+) +' +
416 r
'(week|day|hour|minute)s?$',
418 "repeat off": re
.compile(r
'^(\d+) +(off|disable|stop|never|none'
423 time_units
= {"minute": 60,
434 def reminder_index(who
, where
, reminders
, whichstr
):
435 which
= safeint(whichstr
)
437 reply(who
, where
, "Which reminder?")
439 if which
> len(reminders
):
440 reply(who
, where
, "I don't have that many reminders.")
444 def real_where(who
, 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,
458 reply('', where
, message
, all
=True)
461 skip
= delay
// repeat
462 reply('', where
, "(skipping %d delayed repititions)" % skip
, all
=True)
463 real_repeat
= (skip
+ 1) * repeat
466 reminder
[0] += real_repeat
467 schedule_reminder(where
, reminders
, reminder
)
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.")
482 "I'm sorry, %s, I'm afraid I can't do that. You're limited to 10."
485 def cancel_reminder(where
, reminders
, reminder
):
487 scheduler
.cancel((reminder
[0], 0, fire_reminder
, (where
, reminders
, reminder
)))
491 def reschedule(who
, where
, reminders
, reminder
, when
):
492 rwhere
= real_where(who
, where
)
493 cancel_reminder(rwhere
, reminders
, reminder
)
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
)
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
)
505 reply(who
, where
, "I don't understand that. Try $reminder help add")
507 (msg
, whenstr
) = parsed
.groups()
509 when
= parse_time(whenstr
)
511 reply(who
, where
, "I don't understand that time.")
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
)
519 reminder
= 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")
530 reply(who
, where
, "No reminders.")
532 for reminder
in reminders
:
533 reply(who
, where
, "%2d. %s" % (index
, describe_reminder(reminder
)))
535 elif command
in ("change", "alter", "set"):
536 parsed
= reminder_res
["set"].search(args
)
538 reply(who
, where
, "I don't understand that. Try $reminder help set")
540 (whichstr
, property, value
) = parsed
.groups()
541 which
= reminder_index(who
, where
, reminders
, whichstr
)
544 reminder
= reminders
[which
]
545 lproperty
= property.lower()
546 if lproperty
in ("message", "msg"):
548 elif lproperty
== "time":
550 when
= parse_time(value
)
551 reschedule(who
, where
, reminders
, reminder
, when
)
553 reply(who
, where
, "I don't understand that time.")
555 elif lproperty
in ("group", "groups"):
556 groups
= value
.split(",")
557 groups
= [group
.strip() for group
in 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
566 which
= reminder_index(who
, where
, reminders
, parsed
.groups()[0])
569 reminder
= reminders
[which
]
571 reply(who
, where
, "I don't understand that. Try $reminder help repeat")
574 (whichstr
, hourstr
, minutestr
) = parsed
.groups()
575 hours
= safeint(hourstr
)
577 reply(who
, where
, "Bad number of hours.")
579 minutes
= safeint(minutestr
)
580 # Mathematicians, read at your own peril.
582 reply(who
, where
, "Bad number of minutes.")
584 if hours
== minutes
== 0:
585 reply(who
, where
, "Repeating continuously sounds like a bad idea.")
587 reminder
[3] = 60 * (minutes
+ 60 * hours
)
589 "Reminder number %d now repeating every %d hours and %d minutes."
590 % (which
+1, hours
, minutes
))
592 (whichstr
, numstr
, unit
) = parsed
.groups()
594 num
= safeint(numstr
)
596 reply(who
, where
, "Bad number of %ss." % unit
)
598 if unit
not in time_units
:
599 reply(who
, where
, "I don't know that unit.")
601 reminder
[3] = num
* time_units
[unit
]
602 reply(who
, where
, "Reminder number %d now repeating every %d %ss."
603 % (which
+1, num
, unit
))
606 reply(who
, where
, "Repeating disabled on reminder %d." % which
+1)
608 elif command
== "help":
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 "\
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 " \
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.")
635 reply(who
, where
, "$reminder help <command>: get help on <command>. " \
636 + "You know, like you just did.")
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
] = []
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
)
662 def got_registered(conn
, event
):
663 args
= event
.arguments()
664 if len(args
) == 2 and args
[1] == "is a registered nick":
666 whois_info
[user
].registered
= True
667 got_registered
= safe(got_registered
)
668 irc
.add_global_handler("307", got_registered
)
671 def got_channels(conn
, event
):
672 args
= event
.arguments()
675 channels
= args
[1].split()
678 whois_info
[user
].channel_perms
[c
[1:]] = Perms
.from_symbol(c
[0])
680 whois_info
[user
].channel_perms
[c
] = Perms
.present
681 got_channels
= safe(got_channels
)
682 irc
.add_global_handler("whoischannels", got_channels
)
685 def got_servop(conn
, event
):
686 args
= event
.arguments()
689 whois_info
[user
].ircop
= True
690 got_servop
= safe(got_servop
)
691 irc
.add_global_handler("whoisoperator", got_servop
)
694 def got_whoend(conn
, event
):
695 who
= event
.arguments()[0]
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
)
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
)
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
:
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':
731 where
= event
.target()
732 if msg
.count("http://"):
733 got_command(who
, where
, "title implicit", html
.extract_url(msg
))
735 if nick_re
.match(msg
):
736 (me
, command
, args
) = resize(msg
.split(" ", 2),3)
737 if command
in commands
:
739 elif is_re
.search(msg
):
741 args
= is_re
.split(msg
.split(" ", 1)[1], 1)
743 command
= "factoid implicit"
744 args
= msg
.split(" ", 1)[1]
746 if command
in commands
and commands
[command
][0] <= Perms
.voice
:
748 elif is_re
.search(msg
):
750 args
= is_re
.split(msg
, 1)
752 command
= "factoid implicit"
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
)
762 cmd
= sys
.stdin
.readline().strip()
764 hal
.connection
.send_raw(cmd
)
765 thread
.start_new_thread(debug_console
, ())
768 for where
in reminder_dbs
.keys():
769 db
= reminder_dbs
[where
]
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)