newest
[scrappy.git] / scrappy.py
blobe77de611db3b2f4ddac9f90d62cca1679f0f6e73
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, threading
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 ################################################################################
22 #this is our main bot class. Once scrappy.py is called, an instance of this
23 #class (our bot) gets created and some initialization is done. The real work is
24 #done via modules that get loaded here.
25 class scrappy:
26 def __init__(self):
27 debug("Scrappy bot started.")
28 #hard-code these for now
29 #then write a config loading module
30 self.cmdchar = '@'
31 self.nickname = 'scrappy'
32 self.username = 'scrappy'
33 self.realname = 'Scrappy Bot'
34 self.server = ''
35 self.port = 6667
36 self.chanlist = []
37 self.ircsock = '' #this will be the socket
38 self.connection = '' #Thomas, explain this to me later
39 self.lock = threading.Lock()
42 #our event lists.
43 #each module adds functions to be called to these events.
44 #each event handler calls all of the functions within its list.
45 self.events = ["connect", "disconnect", "error", "invite",
46 "join", "kick", "load", "mode", "msg", "part", "ping", "pong",
47 "privmsg", "privnotice", "pubmsg", "pubnotice", "quit"]
48 self.modulelist = []
49 self.connect_events = []
50 self.disconnect_events = []
51 self.error_events = []
52 self.invite_events = []
53 self.join_events = []
54 self.kick_events = []
55 self.load_events = []
56 self.mode_events = []
57 self.msg_events = {}
58 self.part_events = []
59 self.ping_events = []
60 self.pong_events = []
61 self.privmsg_events = {}
62 self.privnotice_events = []
63 self.pubmsg_events = {}
64 self.pubnotice_events = []
65 self.quit_events = [] #for other users quitting, not the bot
66 #self.what_other_events? = []
68 sys.path.append(os.path.join(os.getcwd(), "modules"))
69 #need to load a few modules here - the rest are done elsewhere
70 self.load_module("modmanage")
71 self.load_module("core")
72 self.load_module("config")
74 #self.register_onload_event(loadme)
76 #on_load event
77 #self.on_load()
79 #start the bot
80 self.__main()
81 ########################################################################
82 def parse_argv(self):
83 """Parse the commandline args and print a usage message if incorrect."""
84 if len(sys.argv) < 3: #Need at least server
85 self.print_usage()
86 sys.exit(1)
88 #split out the port if specified
89 s = sys.argv[1].split(":", 1)
90 self.server = s[0]
92 #a port is given
93 if len(s) == 2:
94 try:
95 self.port = int(s[1])
96 except ValueError:
97 print "Error: Erroneous port."
98 sys.exit(1)
100 self.nickname = sys.argv[2]
101 #add channels to chanlist
102 for ch in sys.argv[3:]:
103 self.chanlist.append('#%s' % ch)
105 def print_usage(self):
106 print 'Usage: %s <server[:port]> <nickname> [channel 1 channel 2 ... channelN]' % sys.argv[0]
108 ########################################################################
109 def __main(self):
110 """The real work. Initialize our connection and register events."""
111 #parse comamnd line and create a new socket
112 self.parse_argv()
113 self.ircsock = irclib_scrappy.IRC()
115 #attempt to create a socket and connect to the server
116 try:
117 self.connection = self.ircsock.server().connect(self.server,
118 self.port, self.nickname,
119 username=self.username,ircname=self.realname)
120 #connection failed, print error and exit
121 except irclib_scrappy.ServerConnectionError, x:
122 print x
123 sys.exit(1)
125 #if all goes well, register handlers
126 self.connection.add_global_handler("welcome", self.on_connect)
127 self.connection.add_global_handler("disconnect", self.on_disconnect)
128 self.connection.add_global_handler("error", self.on_error)
129 self.connection.add_global_handler("invite", self.on_invite)
130 self.connection.add_global_handler("join", self.on_join)
131 self.connection.add_global_handler("kick", self.on_kick)
132 self.connection.add_global_handler("mode", self.on_mode)
133 self.connection.add_global_handler("part", self.on_part)
134 self.connection.add_global_handler("ping", self.on_ping)
135 self.connection.add_global_handler("pong", self.on_pong)
136 self.connection.add_global_handler("privmsg", self.on_privmsg)
137 self.connection.add_global_handler("privnotice", self.on_privnotice)
138 self.connection.add_global_handler("pubmsg", self.on_privmsg)
139 self.connection.add_global_handler("quit", self.on_quit)
142 #self.list_modules()
144 #enter main event loop after this
145 #no code after here
146 try:
147 self.ircsock.process_forever()
148 except KeyboardInterrupt:
149 self.connection.quit("Keyboard interrupt!")
152 ########################################################################
153 ###################
154 #Event Registering#
155 ###################
157 def register_event(self, modname, event_type, func):
158 """Call this with an event_type and a function to call when that event_type happens."""
159 #list of legal event types
160 #keep in ABC order
162 if not event_type in self.events:
163 debug("I don't know what an %s event is." % event_type)
164 return
166 #event type is good. Add it to appropriate event list
169 #BUGGY NONWORKING STUFF
171 listname = "self."+event_type+"_events"
172 #debug(func.__name__)
173 #if func.__name__ in eval(listname):
174 # debug("%s already exists in %s! Removing old copy and inserting this..." % (func, listname))
175 # eval(listname).remove(func.__name__)
177 eval(listname).setdefault(modname, set()).add(func)
178 if event_type == "msg":
179 #self.privmsg_events.append(func)
180 #self.pubmsg_events.append(func)
181 self.privmsg_events.setdefault(modname, set()).add(func)
182 self.pubmsg_events.setdefault(modname, set()).add(func)
185 def unregister_event(self, event_type, func):
186 pass
191 ########################################################################
192 ##################
193 #Event Handlers #
194 ##################
196 def on_connect(self, conn, eventlist):
197 """Called when bot makes a connection to the server."""
198 #do all of our events
199 for func in self.connect_events:
200 thread.start_new_thread(func)
202 if self.identify == True:
203 conn.privmsg("nickserv", "identify %s"
204 % self.nickservpass)
206 #join channels
207 for chan in self.chanlist:
208 if irclib_scrappy.is_channel(chan):
209 conn.join(chan)
211 ########################################################################
212 def on_disconnect(self, conn, eventlist):
213 """Called when the connection to the server is closed."""
214 for func in self.disconnect_events:
215 thread.start_new_thread(func)
216 conn.quit("Scrappy bot signing off.")
217 #do we need to clean stuff up?
218 sys.exit(0)
220 ########################################################################
221 def on_error(self, conn, eventlist):
222 debug("Error received: %s" % eventlist.arguments())
223 for func in self.error_events:
224 thread.start_new_thread(func)
227 ########################################################################
228 def on_invite(self, conn, eventlist):
229 debug("Received an invite: %s" % eventlist.arguments())
230 for func in self.invite_events:
231 thread.start_new_thread(func)
233 ########################################################################
234 def on_join(self, conn, eventlist):
235 debug("User joined: %s" % eventlist.arguments())
236 for func in self.join_events:
237 thread.start_new_thread(func)
239 ########################################################################
240 def on_kick(self, conn, eventlist):
241 debug("Someone was kicked: %s" % eventlist.arguments())
242 for func in self.kick_events:
243 thread.start_new_thread(func)
245 ########################################################################
246 def on_mode(self, conn, eventlist):
247 debug("Mode change: %s" % eventlist.arguments())
248 for func in self.mode_events:
249 thread.start_new_thread(func)
251 ########################################################################
252 def on_part(self, conn, eventlist):
253 debug("Part: %s" % eventlist.arguments())
254 for func in self.part_events:
255 thread.start_new_thread(func)
257 ########################################################################
258 def on_ping(self, conn, eventlist):
259 debug("Ping: %s" % eventlist.arguments())
260 for func in self.ping_events:
261 thread.start_new_thread(func)
263 ########################################################################
264 def on_pong(self, conn, eventlist):
265 debug("Pong: %s" % eventlist.arguments())
266 for func in self.pong_events:
267 thread.start_new_thread(func)
269 ########################################################################
270 def on_privmsg(self, conn, eventlist):
271 """Called when bot receives a private or channel (public) message."""
272 #eventlist.arguments() = the message body
273 arg = eventlist.arguments()[0]
275 iscmd = False #?
277 nick = irclib_scrappy.nm_to_n(eventlist.source())
278 user = irclib_scrappy.nm_to_u(eventlist.source())
279 host = irclib_scrappy.nm_to_h(eventlist.source())
281 if arg[0] == self.cmdchar:
282 cmd = arg[1:]
283 iscmd = True
284 else:
285 cmd = arg
287 #how can we make the list that's passed to functions more friendly?
288 #we end up parsing the list again in the called function...
289 #for func in self.privmsg_events:
290 # thread.start_new_thread(func, (conn, [nick, user, host, iscmd, cmd, eventlist.target()], self))
291 for funcs in self.privmsg_events.itervalues():
292 for f in funcs:
293 thread.start_new_thread(f, (conn, [nick, user, host, iscmd, cmd, eventlist.target()], self))
296 ########################################################################
297 def on_privnotice(self, conn, eventlist):
298 debug("Privnotice: %s" % eventlist.arguments())
299 for func in self.privnotice_events:
300 thread.start_new_thread(func)
304 ########################################################################
305 #right now this isn't used because it's assumed that privmsg == pubmsg
306 #this should probably be changed...
307 def on_pubmsg(self, conn, eventlist):
308 debug("Pubmsg: % " % eventlist.arguments())
309 for func in self.pubmsg_events:
310 func()
312 ########################################################################
313 def on_quit(self, conn, eventlist):
314 debug("Quit: %s" % eventlist.arguments())
315 for func in self.quit_events:
316 thread.start_new_thread(func)
319 ################
320 #Module Loading#
321 ################
322 def load_module(self,name):
323 try:
324 self.modulelist.index(name)
325 #module is already loaded
326 return self.reload_module(name)
327 except ValueError:
328 debug("Not Reloading")
329 try:
330 exec("from %s import %s" % (name, name))
331 #module = __import__(name)
332 #debug("Loading %s" % name)
333 except ImportError:
334 #should be error output
335 print "No such module\n"
336 print traceback.print_exc()
337 return "Sorry, there was an error loading %s." % name
338 debug("This should only print once")
339 debug(eval(name).init(self))
340 self.register_module(name,'foo','foo')
341 return "Loaded %s." % name
343 def reload_module(self, name):
344 debug("Module already loaded, reloading.")
345 self.unload_module(name)
346 reload(sys.modules[name])
347 self.register_module(name, 'foo', 'foo')
348 debug(eval(name).init(self))
349 return "Reloaded %s." % name
351 def unload_module(self, name):
352 self.lock.acquire()
353 try:
354 self.modulelist.index(name)
355 except:
356 print "No such module!"
357 self.lock.release()
358 return "Sorry, no module named %s is loaded." % name
359 self.unregister_module(name)
360 self.lock.release()
361 return "%s unloaded." % name
363 def register_module(self, name, eventlist, function):
364 self.modulelist.append(name)
366 def unregister_module(self, name):
367 self.modulelist.remove(name)
368 self.msg_events.pop(name)
369 self.privmsg_events.pop(name)
370 self.pubmsg_events.pop(name)
374 def list_modules(self):
375 """List currently loaded modules."""
376 print "Currently loaded modules:"
377 for mod in self.modulelist:
378 print mod
381 if(__name__ == "__main__"):
382 bot = scrappy()