MADNESS
[scrappy.git] / scrappy.py
blobc93a72d56b922e1fdd8a69192014557fb886cdc4
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 ################################################################################
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
41 #our event lists.
42 #each module adds functions to be called to these events.
43 #each event handler calls all of the functions within its list.
44 self.events = ["connect", "disconnect", "error", "invite",
45 "join", "kick", "load", "mode", "msg", "part", "ping", "pong",
46 "privmsg", "privnotice", "pubmsg", "pubnotice", "quit"]
47 self.modulelist = []
48 self.connect_events = []
49 self.disconnect_events = []
50 self.error_events = []
51 self.invite_events = []
52 self.join_events = []
53 self.kick_events = []
54 self.load_events = []
55 self.mode_events = []
56 self.msg_events = []
57 self.part_events = []
58 self.ping_events = []
59 self.pong_events = []
60 self.privmsg_events = []
61 self.privnotice_events = []
62 self.pubmsg_events = []
63 self.pubnotice_events = []
64 self.quit_events = [] #for other users quitting, not the bot
65 #self.what_other_events? = []
67 sys.path.append(os.path.join(os.getcwd(), "modules"))
68 #need to load a few modules here - the rest are done elsewhere
69 self.load_module("modmanage")
71 #self.register_onload_event(loadme)
73 #on_load event
74 #self.on_load()
76 #start the bot
77 self.__main()
78 ########################################################################
79 def parse_argv(self):
80 """Parse the commandline args and print a usage message if incorrect."""
81 if len(sys.argv) < 3: #Need at least server
82 self.print_usage()
83 sys.exit(1)
85 #split out the port if specified
86 s = sys.argv[1].split(":", 1)
87 self.server = s[0]
89 #a port is given
90 if len(s) == 2:
91 try:
92 self.port = int(s[1])
93 except ValueError:
94 print "Error: Erroneous port."
95 sys.exit(1)
97 self.nickname = sys.argv[2]
98 #add channels to chanlist
99 for ch in sys.argv[3:]:
100 self.chanlist.append('#%s' % ch)
102 def print_usage(self):
103 print 'Usage: %s <server[:port]> <nickname> [channel 1 channel 2 ... channelN]' % sys.argv[0]
105 ########################################################################
106 def __main(self):
107 """The real work. Initialize our connection and register events."""
108 #parse comamnd line and create a new socket
109 self.parse_argv()
110 self.ircsock = irclib_scrappy.IRC()
112 #attempt to create a socket and connect to the server
113 try:
114 self.connection = self.ircsock.server().connect(self.server,
115 self.port, self.nickname,
116 username=self.username,ircname=self.realname)
117 #connection failed, print error and exit
118 except irclib_scrappy.ServerConnectionError, x:
119 print x
120 sys.exit(1)
122 #if all goes well, register handlers
123 self.connection.add_global_handler("welcome", self.on_connect)
124 self.connection.add_global_handler("disconnect", self.on_disconnect)
125 self.connection.add_global_handler("error", self.on_error)
126 self.connection.add_global_handler("invite", self.on_invite)
127 self.connection.add_global_handler("join", self.on_join)
128 self.connection.add_global_handler("kick", self.on_kick)
129 self.connection.add_global_handler("mode", self.on_mode)
130 self.connection.add_global_handler("part", self.on_part)
131 self.connection.add_global_handler("ping", self.on_ping)
132 self.connection.add_global_handler("pong", self.on_pong)
133 self.connection.add_global_handler("privmsg", self.on_privmsg)
134 self.connection.add_global_handler("privnotice", self.on_privnotice)
135 self.connection.add_global_handler("pubmsg", self.on_privmsg)
136 self.connection.add_global_handler("quit", self.on_quit)
139 #self.list_modules()
141 #enter main event loop after this
142 #no code after here
143 try:
144 self.ircsock.process_forever()
145 except KeyboardInterrupt:
146 self.connection.quit("Keyboard interrupt!")
149 ########################################################################
150 ###################
151 #Event Registering#
152 ###################
154 def register_event(self, event_type, func):
155 """Call this with an event_type and a function to call when that event_type happens."""
156 #list of legal event types
157 #keep in ABC order
159 if not event_type in self.events:
160 debug("I don't know what an %s event is." % event_type)
161 return
163 #event type is good. Add it to appropriate event list
166 #BUGGY NONWORKING STUFF
168 listname = "self."+event_type+"_events"
169 #debug(func.__name__)
170 if func.__name__ in eval(listname):
171 debug("%s already exists in %s! Removing old copy and inserting this..." % (func, listname))
172 eval(listname).remove(func.__name__)
174 eval(listname).append(func)
176 if event_type == "msg":
177 self.privmsg_events.append(func)
178 self.pubmsg_events.append(func)
181 def unregister_event(self, event_type, func):
182 pass
185 ########################################################################
186 ##################
187 #Event Handlers #
188 ##################
190 def on_connect(self, conn, eventlist):
191 """Called when bot makes a connection to the server."""
192 #do all of our events
193 for func in self.connect_events:
194 thread.start_new_thread(func)
196 #join channels
197 for chan in self.chanlist:
198 if irclib_scrappy.is_channel(chan):
199 conn.join(chan)
201 ########################################################################
202 def on_disconnect(self, conn, eventlist):
203 """Called when the connection to the server is closed."""
204 for func in self.disconnect_events:
205 thread.start_new_thread(func)
206 conn.quit("Scrappy bot signing off.")
207 #do we need to clean stuff up?
208 sys.exit(0)
210 ########################################################################
211 def on_error(self, conn, eventlist):
212 debug("Error received: %s" % eventlist.arguments())
213 for func in self.error_events:
214 thread.start_new_thread(func)
217 ########################################################################
218 def on_invite(self, conn, eventlist):
219 debug("Received an invite: %s" % eventlist.arguments())
220 for func in self.invite_events:
221 thread.start_new_thread(func)
223 ########################################################################
224 def on_join(self, conn, eventlist):
225 debug("User joined: %s" % eventlist.arguments())
226 for func in self.join_events:
227 thread.start_new_thread(func)
229 ########################################################################
230 def on_kick(self, conn, eventlist):
231 debug("Someone was kicked: %s" % eventlist.arguments())
232 for func in self.kick_events:
233 thread.start_new_thread(func)
235 ########################################################################
236 def on_mode(self, conn, eventlist):
237 debug("Mode change: %s" % eventlist.arguments())
238 for func in self.mode_events:
239 thread.start_new_thread(func)
241 ########################################################################
242 def on_part(self, conn, eventlist):
243 debug("Part: %s" % eventlist.arguments())
244 for func in self.part_events:
245 thread.start_new_thread(func)
247 ########################################################################
248 def on_ping(self, conn, eventlist):
249 debug("Ping: %s" % eventlist.arguments())
250 for func in self.ping_events:
251 thread.start_new_thread(func)
253 ########################################################################
254 def on_pong(self, conn, eventlist):
255 debug("Pong: %s" % eventlist.arguments())
256 for func in self.pong_events:
257 thread.start_new_thread(func)
259 ########################################################################
260 def on_privmsg(self, conn, eventlist):
261 """Called when bot receives a private or channel (public) message."""
262 #eventlist.arguments() = the message body
263 arg = eventlist.arguments()[0]
265 iscmd = False #?
267 nick = irclib_scrappy.nm_to_n(eventlist.source())
268 user = irclib_scrappy.nm_to_u(eventlist.source())
269 host = irclib_scrappy.nm_to_h(eventlist.source())
271 if arg[0] == self.cmdchar:
272 cmd = arg[1:]
273 iscmd = True
274 else:
275 cmd = arg
277 #how can we make the list that's passed to functions more friendly?
278 #we end up parsing the list again in the called function...
279 for func in self.privmsg_events:
280 thread.start_new_thread(func, (conn, [nick, user, host, iscmd, cmd, eventlist.target()], self))
284 ########################################################################
285 def on_privnotice(self, conn, eventlist):
286 debug("Privnotice: %s" % eventlist.arguments())
287 for func in self.privnotice_events:
288 thread.start_new_thread(func)
292 ########################################################################
293 #right now this isn't used because it's assumed that privmsg == pubmsg
294 #this should probably be changed...
295 def on_pubmsg(self, conn, eventlist):
296 debug("Pubmsg: % " % eventlist.arguments())
297 for func in pubmsg_events:
298 func()
300 ########################################################################
301 def on_quit(self, conn, eventlist):
302 debug("Quit: %s" % eventlist.arguments())
303 for func in quit_events:
304 thread.start_new_thread(func)
307 ################
308 #Module Loading#
309 ################
310 def load_module(self,name):
312 #for whatever reason, OS X needs your modules folder to be in PYTHONPATH to load
313 try:
314 #sys.path.append(os.path.join(os.getcwd(), "modules"))
315 exec("from %s import %s" % (name, name))
316 #module = __import__(name)
317 except ImportError:
318 #should be error output
319 print "No such module\n"
320 print traceback.print_exc()
321 return "Sorry, there was an error loading %s." % name
323 eval(name).init(self)
324 self.register_module(name,'foo','foo')
325 return "Loaded %s." % name
327 def unload_module(self, name):
328 try:
329 self.modulelist.index(name)
330 except:
331 print "No such module!"
332 return "Sorry, no module named %s is loaded." % name
333 self.unregister_module(name)
335 return "%s unloaded. (but not really)" % name
337 def register_module(self, name, eventlist, function):
338 self.modulelist.append(name)
340 def unregister_module(self, name):
341 self.modulelist.remove(name)
345 def list_modules(self):
346 """List currently loaded modules."""
347 print "Currently loaded modules:"
348 for mod in self.modulelist:
349 print mod
352 if(__name__ == "__main__"):
353 bot = scrappy()