No ide what I changed
[scrappy.git] / scrappy.py
blob8f80565b626cd610c9e5da2154b371ecd885490a
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")
70 self.load_module("core")
71 self.load_module("config")
73 #self.register_onload_event(loadme)
75 #on_load event
76 #self.on_load()
78 #start the bot
79 self.__main()
80 ########################################################################
81 def parse_argv(self):
82 """Parse the commandline args and print a usage message if incorrect."""
83 if len(sys.argv) < 3: #Need at least server
84 self.print_usage()
85 sys.exit(1)
87 #split out the port if specified
88 s = sys.argv[1].split(":", 1)
89 self.server = s[0]
91 #a port is given
92 if len(s) == 2:
93 try:
94 self.port = int(s[1])
95 except ValueError:
96 print "Error: Erroneous port."
97 sys.exit(1)
99 self.nickname = sys.argv[2]
100 #add channels to chanlist
101 for ch in sys.argv[3:]:
102 self.chanlist.append('#%s' % ch)
104 def print_usage(self):
105 print 'Usage: %s <server[:port]> <nickname> [channel 1 channel 2 ... channelN]' % sys.argv[0]
107 ########################################################################
108 def __main(self):
109 """The real work. Initialize our connection and register events."""
110 #parse comamnd line and create a new socket
111 self.parse_argv()
112 self.ircsock = irclib_scrappy.IRC()
114 #attempt to create a socket and connect to the server
115 try:
116 self.connection = self.ircsock.server().connect(self.server,
117 self.port, self.nickname,
118 username=self.username,ircname=self.realname)
119 #connection failed, print error and exit
120 except irclib_scrappy.ServerConnectionError, x:
121 print x
122 sys.exit(1)
124 #if all goes well, register handlers
125 self.connection.add_global_handler("welcome", self.on_connect)
126 self.connection.add_global_handler("disconnect", self.on_disconnect)
127 self.connection.add_global_handler("error", self.on_error)
128 self.connection.add_global_handler("invite", self.on_invite)
129 self.connection.add_global_handler("join", self.on_join)
130 self.connection.add_global_handler("kick", self.on_kick)
131 self.connection.add_global_handler("mode", self.on_mode)
132 self.connection.add_global_handler("part", self.on_part)
133 self.connection.add_global_handler("ping", self.on_ping)
134 self.connection.add_global_handler("pong", self.on_pong)
135 self.connection.add_global_handler("privmsg", self.on_privmsg)
136 self.connection.add_global_handler("privnotice", self.on_privnotice)
137 self.connection.add_global_handler("pubmsg", self.on_privmsg)
138 self.connection.add_global_handler("quit", self.on_quit)
141 #self.list_modules()
143 #enter main event loop after this
144 #no code after here
145 try:
146 self.ircsock.process_forever()
147 except KeyboardInterrupt:
148 self.connection.quit("Keyboard interrupt!")
151 ########################################################################
152 ###################
153 #Event Registering#
154 ###################
156 def register_event(self, event_type, func):
157 """Call this with an event_type and a function to call when that event_type happens."""
158 #list of legal event types
159 #keep in ABC order
161 if not event_type in self.events:
162 debug("I don't know what an %s event is." % event_type)
163 return
165 #event type is good. Add it to appropriate event list
168 #BUGGY NONWORKING STUFF
170 listname = "self."+event_type+"_events"
171 #debug(func.__name__)
172 if func.__name__ in eval(listname):
173 debug("%s already exists in %s! Removing old copy and inserting this..." % (func, listname))
174 eval(listname).remove(func.__name__)
176 eval(listname).append(func)
178 if event_type == "msg":
179 self.privmsg_events.append(func)
180 self.pubmsg_events.append(func)
183 def unregister_event(self, event_type, func):
184 pass
187 ########################################################################
188 ##################
189 #Event Handlers #
190 ##################
192 def on_connect(self, conn, eventlist):
193 """Called when bot makes a connection to the server."""
194 #do all of our events
195 for func in self.connect_events:
196 thread.start_new_thread(func)
198 if self.identify == True:
199 conn.privmsg("nickserv", "identify %s"
200 % self.nickservpass)
202 #join channels
203 for chan in self.chanlist:
204 if irclib_scrappy.is_channel(chan):
205 conn.join(chan)
207 ########################################################################
208 def on_disconnect(self, conn, eventlist):
209 """Called when the connection to the server is closed."""
210 for func in self.disconnect_events:
211 thread.start_new_thread(func)
212 conn.quit("Scrappy bot signing off.")
213 #do we need to clean stuff up?
214 sys.exit(0)
216 ########################################################################
217 def on_error(self, conn, eventlist):
218 debug("Error received: %s" % eventlist.arguments())
219 for func in self.error_events:
220 thread.start_new_thread(func)
223 ########################################################################
224 def on_invite(self, conn, eventlist):
225 debug("Received an invite: %s" % eventlist.arguments())
226 for func in self.invite_events:
227 thread.start_new_thread(func)
229 ########################################################################
230 def on_join(self, conn, eventlist):
231 debug("User joined: %s" % eventlist.arguments())
232 for func in self.join_events:
233 thread.start_new_thread(func)
235 ########################################################################
236 def on_kick(self, conn, eventlist):
237 debug("Someone was kicked: %s" % eventlist.arguments())
238 for func in self.kick_events:
239 thread.start_new_thread(func)
241 ########################################################################
242 def on_mode(self, conn, eventlist):
243 debug("Mode change: %s" % eventlist.arguments())
244 for func in self.mode_events:
245 thread.start_new_thread(func)
247 ########################################################################
248 def on_part(self, conn, eventlist):
249 debug("Part: %s" % eventlist.arguments())
250 for func in self.part_events:
251 thread.start_new_thread(func)
253 ########################################################################
254 def on_ping(self, conn, eventlist):
255 debug("Ping: %s" % eventlist.arguments())
256 for func in self.ping_events:
257 thread.start_new_thread(func)
259 ########################################################################
260 def on_pong(self, conn, eventlist):
261 debug("Pong: %s" % eventlist.arguments())
262 for func in self.pong_events:
263 thread.start_new_thread(func)
265 ########################################################################
266 def on_privmsg(self, conn, eventlist):
267 """Called when bot receives a private or channel (public) message."""
268 #eventlist.arguments() = the message body
269 arg = eventlist.arguments()[0]
271 iscmd = False #?
273 nick = irclib_scrappy.nm_to_n(eventlist.source())
274 user = irclib_scrappy.nm_to_u(eventlist.source())
275 host = irclib_scrappy.nm_to_h(eventlist.source())
277 if arg[0] == self.cmdchar:
278 cmd = arg[1:]
279 iscmd = True
280 else:
281 cmd = arg
283 #how can we make the list that's passed to functions more friendly?
284 #we end up parsing the list again in the called function...
285 for func in self.privmsg_events:
286 thread.start_new_thread(func, (conn, [nick, user, host, iscmd, cmd, eventlist.target()], self))
290 ########################################################################
291 def on_privnotice(self, conn, eventlist):
292 debug("Privnotice: %s" % eventlist.arguments())
293 for func in self.privnotice_events:
294 thread.start_new_thread(func)
298 ########################################################################
299 #right now this isn't used because it's assumed that privmsg == pubmsg
300 #this should probably be changed...
301 def on_pubmsg(self, conn, eventlist):
302 debug("Pubmsg: % " % eventlist.arguments())
303 for func in self.pubmsg_events:
304 func()
306 ########################################################################
307 def on_quit(self, conn, eventlist):
308 debug("Quit: %s" % eventlist.arguments())
309 for func in self.quit_events:
310 thread.start_new_thread(func)
313 ################
314 #Module Loading#
315 ################
316 def load_module(self,name):
318 #for whatever reason, OS X needs your modules folder to be in PYTHONPATH to load
319 try:
320 #sys.path.append(os.path.join(os.getcwd(), "modules"))
321 exec("from %s import %s" % (name, name))
322 #module = __import__(name)
323 except ImportError:
324 #should be error output
325 print "No such module\n"
326 print traceback.print_exc()
327 return "Sorry, there was an error loading %s." % name
329 debug(eval(name).init(self))
330 self.register_module(name,'foo','foo')
331 return "Loaded %s." % name
333 def unload_module(self, name):
334 try:
335 self.modulelist.index(name)
336 except:
337 print "No such module!"
338 return "Sorry, no module named %s is loaded." % name
339 self.unregister_module(name)
341 return "%s unloaded. (but not really)" % name
343 def register_module(self, name, eventlist, function):
344 self.modulelist.append(name)
346 def unregister_module(self, name):
347 self.modulelist.remove(name)
351 def list_modules(self):
352 """List currently loaded modules."""
353 print "Currently loaded modules:"
354 for mod in self.modulelist:
355 print mod
358 if(__name__ == "__main__"):
359 bot = scrappy()