Holy rewrite, batman
[scrappy.git] / scrappy.py
blob284b5c3157c969ccbb1335fad3741fa91ad7cff8
1 #!/usr/bin/env python
2 #Let's keep this file in particular as clean and neatly organized as possible.
3 #If this is nice and organized, then writing new modules will be a snap and this
4 #file should rarely have to be edited.
6 import irclib_scrappy
7 import sys, os
8 import traceback
9 import thread
11 ################################################################################
12 #set to False to turn off debugging to stdout
13 DEBUG = True
15 def debug(msg):
16 if DEBUG:
17 print msg
19 ################################################################################
21 def loadme(self):
22 debug("LOADME")
24 def modload_cmd(c,list,bot):
25 """modload - Loads a module"""
26 cmd = list[4].split(" ")[0]
28 if cmd == "modload" and list[3]:
29 param = list[4].split(" ")[1]
30 c.privmsg(list[5], bot.load_module(param))
32 def modunload_cmd(c, list, bot):
33 """modunload - Unloads a module"""
34 cmd = list[4].split(" ")[0]
35 if cmd == "modunload" and list[3]:
36 param = list[4].split(" ")[1]
37 c.privmsg(list[5], bot.unload_module(param))
38 exec(list[4].split(" ")[1])
40 def modlist_cmd(c,list,bot):
41 """modlist - Lists loaded modules"""
42 if list[4] == "modlist" and list[3]:
43 c.privmsg(list[5],bot.modulelist)
45 def getevents_cmd(c, list, bot):
46 cmd = list[4].split(" ")[0]
47 if cmd == "getevents" and list[3]:
48 param = list[4].split(" ")[1]
49 param = "bot."+param+"_events"
50 c.privmsg(list[5], eval(param))
52 #this is our main bot class. Once scrappy.py is called, an instance of this
53 #class (our bot) gets created and some initialization is done. The real work is
54 #done via modules that get loaded here.
55 class scrappy:
56 def __init__(self):
57 debug("Scrappy bot started.")
58 #hard-code these for now
59 #then write a config loading module
60 self.cmdchar = '!'
61 self.nickname = 'scrappy'
62 self.username = 'scrappy'
63 self.realname = 'Scrappy Bot'
64 self.server = ''
65 self.port = 6667
66 self.chanlist = []
67 self.ircsock = '' #this will be the socket
68 self.connection = '' #Thomas, explain this to me later
71 #our event lists.
72 #each module adds functions to be called to these events.
73 #each event handler calls all of the functions within its list.
74 self.events = ["connect", "disconnect", "error", "invite",
75 "join", "kick", "load", "mode", "msg", "part", "ping", "pong",
76 "privmsg", "privnotice", "pubmsg", "pubnotice", "quit"]
77 self.modulelist = []
78 self.connect_events = []
79 self.disconnect_events = []
80 self.error_events = []
81 self.invite_events = []
82 self.join_events = []
83 self.kick_events = []
84 self.load_events = []
85 self.mode_events = []
86 self.msg_events = []
87 self.part_events = []
88 self.ping_events = []
89 self.pong_events = []
90 self.privmsg_events = []
91 self.privnotice_events = []
92 self.pubmsg_events = []
93 self.pubnotice_events = []
94 self.quit_events = [] #for other users quitting, not the bot
95 #self.what_other_events? = []
97 #load modules(currently in main())
98 #self.load_module("config")
99 #self.load_module("core")
100 #self.load_module("markov")
102 #self.register_onload_event(loadme)
104 #on_load event
105 #self.on_load()
107 #start the bot
108 self.__main()
109 ########################################################################
110 def parse_argv(self):
111 """Parse the commandline args and print a usage message if incorrect."""
112 if len(sys.argv) < 3: #Need at least server
113 self.print_usage()
114 sys.exit(1)
116 #split out the port if specified
117 s = sys.argv[1].split(":", 1)
118 self.server = s[0]
120 #a port is given
121 if len(s) == 2:
122 try:
123 self.port = int(s[1])
124 except ValueError:
125 print "Error: Erroneous port."
126 sys.exit(1)
128 self.nickname = sys.argv[2]
129 #add channels to chanlist
130 for ch in sys.argv[3:]:
131 self.chanlist.append('#%s' % ch)
133 def print_usage(self):
134 print 'Usage: %s <server[:port]> <nickname> [channel 1 channel 2 ... channelN]' % sys.argv[0]
136 ########################################################################
137 def __main(self):
138 """The real work. Initialize our connection and register events."""
139 #parse comamnd line and create a new socket
140 self.parse_argv()
141 self.ircsock = irclib_scrappy.IRC()
143 #attempt to create a socket and connect to the server
144 try:
145 self.connection = self.ircsock.server().connect(self.server,
146 self.port, self.nickname,
147 username=self.username,ircname=self.realname)
148 #connection failed, print error and exit
149 except irclib_scrappy.ServerConnectionError, x:
150 print x
151 sys.exit(1)
153 #if all goes well, register handlers
154 self.connection.add_global_handler("welcome", self.on_connect)
155 self.connection.add_global_handler("disconnect", self.on_disconnect)
156 self.connection.add_global_handler("error", self.on_error)
157 self.connection.add_global_handler("invite", self.on_invite)
158 self.connection.add_global_handler("join", self.on_join)
159 self.connection.add_global_handler("kick", self.on_kick)
160 self.connection.add_global_handler("mode", self.on_mode)
161 self.connection.add_global_handler("part", self.on_part)
162 self.connection.add_global_handler("ping", self.on_ping)
163 self.connection.add_global_handler("pong", self.on_pong)
164 self.connection.add_global_handler("privmsg", self.on_privmsg)
165 self.connection.add_global_handler("privnotice", self.on_privnotice)
166 self.connection.add_global_handler("pubmsg", self.on_privmsg)
167 self.connection.add_global_handler("quit", self.on_quit)
169 #register some events
170 #self.register_msg_event(modload_cmd)
171 #self.register_msg_event(modlist_cmd)
172 #self.register_msg_event(modunload_cmd)
173 self.register_event("msg", modload_cmd)
174 self.register_event("msg", modlist_cmd)
175 self.register_event("msg", modunload_cmd)
176 self.register_event("privmsg", getevents_cmd)
178 #self.list_modules()
180 #enter main event loop after this
181 #no code after here
182 try:
183 self.ircsock.process_forever()
184 except KeyboardInterrupt:
185 self.connection.quit("Keyboard interrupt!")
188 ########################################################################
189 ###################
190 #Event Registering#
191 ###################
193 def register_event(self, event_type, func):
194 """Call this with an event_type and a function to call when that event_type happens."""
195 #list of legal event types
196 #keep in ABC order
198 if not event_type in self.events:
199 debug("I don't know what an %s event is." % event_type)
200 return
202 #event type is good. Add it to appropriate event list
203 #special case for convenience of msg
204 if event_type == "msg":
205 self.msg_events.append(func)
206 self.privmsg_events.append(func)
207 self.pubmsg_events.append(func)
208 else:
209 listname = "self."+event_type+"_events.append(func)"
210 exec(listname)
211 #debug("Added '%s' to '%s'." % (func, event_type+"_events[]"))
214 def unregister_event(self, event_type, func):
215 pass
218 ########################################################################
219 ##################
220 #Event Handlers #
221 ##################
223 def on_connect(self, conn, eventlist):
224 """Called when bot makes a connection to the server."""
225 #do all of our events
226 for func in self.connect_events:
227 thread.start_new_thread(func)
229 #join channels
230 for chan in self.chanlist:
231 if irclib_scrappy.is_channel(chan):
232 conn.join(chan)
234 ########################################################################
235 def on_disconnect(self, conn, eventlist):
236 """Called when the connection to the server is closed."""
237 for func in self.disconnect_events:
238 thread.start_new_thread(func)
239 conn.quit("Scrappy bot signing off.")
240 #do we need to clean stuff up?
241 sys.exit(0)
243 ########################################################################
244 def on_error(self, conn, eventlist):
245 debug("Error received: %s" % eventlist.arguments())
246 for func in self.error_events:
247 thread.start_new_thread(func)
250 ########################################################################
251 def on_invite(self, conn, eventlist):
252 debug("Received an invite: %s" % eventlist.arguments())
253 for func in self.invite_events:
254 thread.start_new_thread(func)
256 ########################################################################
257 def on_join(self, conn, eventlist):
258 debug("User joined: %s" % eventlist.arguments())
259 for func in self.join_events:
260 thread.start_new_thread(func)
262 ########################################################################
263 def on_kick(self, conn, eventlist):
264 debug("Someone was kicked: %s" % eventlist.arguments())
265 for func in self.kick_events:
266 thread.start_new_thread(func)
268 ########################################################################
269 def on_mode(self, conn, eventlist):
270 debug("Mode change: %s" % eventlist.arguments())
271 for func in self.mode_events:
272 thread.start_new_thread(func)
274 ########################################################################
275 def on_part(self, conn, eventlist):
276 debug("Part: %s" % eventlist.arguments())
277 for func in self.part_events:
278 thread.start_new_thread(func)
280 ########################################################################
281 def on_ping(self, conn, eventlist):
282 debug("Ping: %s" % eventlist.arguments())
283 for func in self.ping_events:
284 thread.start_new_thread(func)
286 ########################################################################
287 def on_pong(self, conn, eventlist):
288 debug("Pong: %s" % eventlist.arguments())
289 for func in self.pong_events:
290 thread.start_new_thread(func)
292 ########################################################################
293 def on_privmsg(self, conn, eventlist):
294 """Called when bot receives a private or channel (public) message."""
295 #eventlist.arguments() = the message body
296 arg = eventlist.arguments()[0]
297 debug(dir(eventlist))
298 debug(eventlist.arguments())
299 debug(eventlist.eventtype())
300 debug(eventlist.source())
301 debug(eventlist.target())
302 iscmd = False #?
304 nick = irclib_scrappy.nm_to_n(eventlist.source())
305 user = irclib_scrappy.nm_to_u(eventlist.source())
306 host = irclib_scrappy.nm_to_h(eventlist.source())
308 if arg[0] == self.cmdchar:
309 cmd = arg[1:]
310 iscmd = True
311 else:
312 cmd = arg
314 for func in self.privmsg_events:
315 thread.start_new_thread(func, (conn, [nick, user, host, iscmd, cmd, eventlist.target()], self))
319 ########################################################################
320 def on_privnotice(self, conn, eventlist):
321 debug("Privnotice: %s" % eventlist.arguments())
322 for func in self.privnotice_events:
323 thread.start_new_thread(func)
327 ########################################################################
328 #right now this isn't used because it's assumed that privmsg == pubmsg
329 #this should probably be changed...
330 def on_pubmsg(self, conn, eventlist):
331 debug("Pubmsg: % " % eventlist.arguments())
332 for func in pubmsg_events:
333 func()
335 ########################################################################
336 def on_quit(self, conn, eventlist):
337 debug("Quit: %s" % eventlist.arguments())
338 for func in quit_events:
339 thread.start_new_thread(func)
342 ##################
343 #Event Processing#
344 ##################
345 def register_privmsg_event(self,func):
346 self.privmsg_events.append(func)
348 def register_pubmsg_event(self,func):
349 self.pubmsg_events.append(func)
351 #Convenience function, registers the func
352 #for both privmsgs and pubmsgs
353 def register_msg_event(self,func):
354 self.register_privmsg_event(func)
355 self.register_pubmsg_event(func)
357 def register_onload_event(self,func):
358 self.onload_events.append(func)
360 def register_onquit_event(self,func):
361 self.onquit_events.append(func)
363 # ################
364 # #Event Handlers#
365 # ################
366 # def on_connect(self, c, event):
367 # for ch in self.chanlist:
368 # if irclib_scrappy.is_channel(ch) :
369 # c.join(ch)
371 # if self.identify == True:
372 # self.c.privmsg("nickserv", "identify %s" % self.nickservpass)
374 # def on_pubmsg(self, c, event):
375 # arg = event.arguments()[0]
376 # iscmd = False
378 # nick = irclib_scrappy.nm_to_n(event.source())
379 # user = irclib_scrappy.nm_to_u(event.source())
380 # host = irclib_scrappy.nm_to_h(event.source())
382 # if arg[0] == self.cmdchar:
383 # cmd = arg[1:]
384 # iscmd = True
385 # else:
386 # cmd = arg
388 # #dispatch the command
389 # for func in self.pubmsg_events:
390 # thread.start_new_thread(func, (c,[nick,user,host,iscmd,cmd,event.target()],self))
392 # def on_privmsg(self, c, event):
393 # cmd = event.arguments()[0]
394 # iscmd = False
396 # nick = irclib_scrappy.nm_to_n(event.source())
397 # user = irclib_scrappy.nm_to_u(event.source())
398 # host = irclib_scrappy.nm_to_h(event.source())
400 # if cmd[0] == self.cmdchar:
401 # c.privmsg(nick,"You privmsged me, you don't need to " +
402 # "prefix commands with %s" % self.cmdchar)
403 # cmd = cmd[1:]
405 # iscmd = True
407 # #dispatch the command
408 # for func in self.privmsg_events:
409 # thread.start_new_thread(func, (c,[nick,user,host,iscmd,cmd,event.target()],self))
412 # def on_disconnect(self, c, event):
413 # for func in self.onquit_events:
414 # func()
416 # sys.exit(0)
418 # def on_load(self):
419 # for func in self.onload_events:
420 # func(self)
422 ################
423 #Module Loading#
424 ################
425 def load_module(self,name):
426 #for whatever reason, OS X needs your modules folder to be in PYTHONPATH to load
427 try:
428 sys.path.append(os.path.join(os.getcwd(), "modules"))
429 exec("from %s import %s" % (name, name))
430 #module = __import__(name)
431 except ImportError:
432 #should be error output
433 print "No such module\n"
434 print traceback.print_exc()
435 return "Sorry, there was an error loading %s." % name
437 eval(name).init(self)
438 self.register_module(name,'foo','foo')
439 return "Loaded %s." % name
441 def unload_module(self, name):
442 try:
443 self.modulelist.index(name)
444 except:
445 print "No such module!"
446 return "Sorry, no module named %s is loaded." % name
447 self.unregister_module(name)
448 return "%s unloaded." % name
450 def register_module(self, name, eventlist, function):
451 self.modulelist.append(name)
453 def unregister_module(self, name):
454 self.modulelist.remove(name)
458 def list_modules(self):
459 """List currently loaded modules."""
460 print "Currently loaded modules:"
461 for mod in self.modulelist:
462 print mod
465 if(__name__ == "__main__"):
466 bot = scrappy()