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.
9 import thread
, threading
11 ################################################################################
12 #set to False to turn off debugging to stdout
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.
27 debug("Scrappy bot started.")
28 #hard-code these for now
29 #then write a config loading module
31 self
.nickname
= 'scrappy'
32 self
.username
= 'scrappy'
33 self
.realname
= 'Scrappy Bot'
37 self
.ircsock
= '' #this will be the socket
38 self
.connection
= '' #Thomas, explain this to me later
39 self
.lock
= threading
.Lock()
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"]
49 self
.connect_events
= []
50 self
.disconnect_events
= []
51 self
.error_events
= []
52 self
.invite_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)
81 ########################################################################
83 """Parse the commandline args and print a usage message if incorrect."""
84 if len(sys
.argv
) < 3: #Need at least server
88 #split out the port if specified
89 s
= sys
.argv
[1].split(":", 1)
97 print "Error: Erroneous port."
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 ########################################################################
110 """The real work. Initialize our connection and register events."""
111 #parse comamnd line and create a new socket
113 self
.ircsock
= irclib_scrappy
.IRC()
115 #attempt to create a socket and connect to the server
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
:
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
)
144 #enter main event loop after this
147 self
.ircsock
.process_forever()
148 except KeyboardInterrupt:
149 self
.connection
.quit("Keyboard interrupt!")
152 ########################################################################
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
162 if not event_type
in self
.events
:
163 debug("I don't know what an %s event is." % event_type
)
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
):
191 ########################################################################
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"
207 for chan
in self
.chanlist
:
208 if irclib_scrappy
.is_channel(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?
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]
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
:
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():
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
:
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
)
322 def load_module(self
,name
):
324 self
.modulelist
.index(name
)
325 #module is already loaded
326 return self
.reload_module(name
)
328 debug("Not Reloading")
330 exec("from %s import %s" % (name
, name
))
331 #module = __import__(name)
332 #debug("Loading %s" % name)
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
):
354 self
.modulelist
.index(name
)
356 print "No such module!"
358 return "Sorry, no module named %s is loaded." % name
359 self
.unregister_module(name
)
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
:
381 if(__name__
== "__main__"):