It's part of the event handling framework for the core bot. When one of the bot's modules wants to register with a new event (like registering an action to be taken when someone types a bot !command in an IRC channel), it simply calls bot.register_event() and passes the type of event to hook on to (msg, CTCP, join, etc), the name of the function(s) to be called for that event, and a reference back to the calling module.
Whenever the bot's IRC socket handles an event, the appropriate function for that event type is called, and a new thread is spawned for each module-registered event function. You may recognize this as being very similar to the observer design pattern.
(To view this without scrolling, visit https://gist.github.com/mharrison/19156f2af72fa8048fab)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def on_privmsg(self, conn, eventlist): | |
"""Called when bot receives a private or channel (public) message.""" | |
#eventlist.arguments() = the message body | |
arg = eventlist.arguments()[0] | |
#check to see if first character of message is the command char (cmdchr) | |
#i.e., "is this a bot command or just message text?" | |
if arg[0] == self.cmdchar: | |
cmd = arg[1:] | |
iscmd = True | |
else: | |
cmd = arg | |
iscmd = False | |
#build a dict with all of the elements of the message | |
params = {'conn' : conn, | |
'nick' : irclib_scrappy.nm_to_n(eventlist.source()), | |
'user' : irclib_scrappy.nm_to_u(eventlist.source()), | |
'host' : irclib_scrappy.nm_to_h(eventlist.source()), | |
'iscmd' : iscmd, | |
'cmd' : cmd, | |
'source' : eventlist.target() | |
} | |
#iterate through all registered events for a privmsg | |
for funcs in self.privmsg_events.itervalues(): | |
#iterate through all functions associated with an event | |
for f in funcs: | |
#spawn a new thread for each function, passing the | |
#params dict and a reference back to the bot | |
thread.start_new_thread(f, (params, self)) |
Here's why I chose this sample:
- It's simple. The code is short and sweet, and it's both easy to understand and simple to write.
- It's intuitive. This is the most logical and "common sense" way to call a series of event-based functions. It's similar to the way your brain would handle such a task.
- It's elegant. The code is formatted nicely, and the source is easy to read.
- It's user-friendly. Any user-written module easily interfaces with this via the register_event() function, and the different parts of the message are passed on to the called module in a nice package (dict) that other programmers can easily use.
- It's pythonic. No code is reinvented. It uses the "batteries included" thread module to do the "heavy lifting" of spawning multiple threads. The thread module automagically handles all of the necessary locking and signaling associated with threading. It also handles KeyboardInterrupt and sys.exit() for you, ensuring that a thread cleanly finishes before the program terminates. Also, if one of the called functions should fail or raise an error, it is cleanly handled in the "background" and the rest of the bot will continue running without interruption.
So there you have it. A code sample which reflects my design philosophies of being simple, clean, elegant, and taking advantage of any system/language libraries available to the programmer.
No comments:
Post a Comment