Newer
Older
import socket, os, threading, re
import plugins
class PluginManager(object):
def __init__(self, server):
self.server = server
self.handlers = {
"join" : [ ],
"part" : [ ],
"quit" : [ ],
"message" : [ ],
"connect" : [ ],
"disconnect": [ ],
"action" : [ ],
"nick" : [ ],
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
"mode" : [ ],
"command" : { },
}
def handle(self, event, handler_func, handler_commands=None):
event = event.lower()
if event == "command":
if handler_commands is not None:
for command in handler_commands:
self.handlers[event][command.lower()] = handler_func
else:
self.handlers[event].append(handler_func)
def unhandle(self, event, handler_func, handler_commands=None):
event = event.lower()
if event == "command":
handlers = {}
for command, handler in self.handlers[event]:
if handler != handler_func and not command.lower() in handler_commands:
handlers.insert(0, handler)
self.handlers[event] = handlers
else:
handlers = []
for handler in self.handlers[event]:
if handler != handler_func:
handlers.insert(0, handler)
self.handlers[event] = handlers
def event(self, eventName, *args):
eventName = eventName.lower()
if eventName == "command":
handler = None
try:
handler = self.handlers[eventName][args[2].lower()]
except KeyError,ke:
return #no such command
try:
handler(*args)
except Exception, e:
s = handler.__self__
self.prnt("Plugin "+s.__class__.__name__+" "+e.__class__.__name__+": "+e.__str__())
else:
for handler in self.handlers[eventName]:
try:
handler(*args)
except Exception, e:
s = handler.__self__
self.prnt("Plugin "+s.__class__.__name__+" "+e.__class__.__name__+": "+e.__str__())
def loadedPlugin(self, pluginName):
return pluginName.lower() in self.plugins
def pluginExists(self, pluginName):
return plugins.getPlugin(pluginName.lower()) is not None
def loadPlugin(self, pluginName):
plugins.refresh()
if not self.loadedPlugin(pluginName.lower()):
if not self.pluginExists(pluginName.lower()):
err = "No such plugin."
return err
pClass = None
try:
pClass = plugins.getPlugin(pluginName.lower())
except Exception, e:
err = "Cannot load plugin "+pluginName+" ("+e.__class__.__name__+": "+e.__str__()+")"
return err
try:
self.plugins[pluginName.lower()] = pClass(self.server)
except Exception, e:
err = "Could not initialize "+pluginName+" ("+e.__class__.__name__+": "+e.__str__()+")"
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
return err
else:
err = "Module has already been loaded."
return err
def getPlugin(self, pluginName):
try:
return self.plugins[pluginName.lower()]
except KeyError:
return None
def reloadPlugin(self, pluginName):
errs = {}
err = self.unloadPlugin(pluginName.lower())
if err is not None:
errs["unload"] = err
err = self.loadPlugin(pluginName.lower())
if err is not None:
errs["load"] = err
return errs
def unloadPlugin(self, pluginName):
if self.loadedPlugin(pluginName.lower()):
inst = self.plugins[pluginName.lower()]
for event, eList in self.handlers.items():
if event == "command":
for cmd,func in eList.items():
if func.__self__ == inst:
destructor = getattr(inst, "__del__", None)
if destructor is not None:
destructor()
else:
newList[cmd] = func
self.handlers[event] = newList
newList = []
for func in eList:
if func.__self__ == inst:
destructor = getattr(inst, "__del__", None)
if destructor is not None:
destructor()
else:
newList.append(func)
self.handlers[event] = newList
del self.plugins[pluginName.lower()]
else:
err = "Plugin "+pluginName+" is not loaded."
return err
def loadAllPlugins(self):
errs = {}
plugins = [k for k in self.server.config["plugins"].split(",") if k != '']
for plugin in plugins:
err = self.loadPlugin(plugin.lower())
if err is not None:
errs[plugin] = err
return errs
def unloadAllPlugins(self):
errs = {}
for plugin in self.plugins.copy().keys():
err = self.unloadPlugin(plugin.lower())
if err is not None:
errs[plugin] = err
return errs
class IRC(threading.Thread):
def __init__(self, config):
super(IRC, self).__init__()
self.pluginManager = PluginManager(self)
self.config = config
self.handle = self.pluginManager.handle
self.unhandle = self.pluginManager.unhandle
self.handlers = self.pluginManager.handlers
self.plugins = self.pluginManager.plugins
self.getPlugin = self.pluginManager.getPlugin
errs = self.pluginManager.loadAllPlugins()
if errs:
for k,v in errs.items():
self.prnt("Plugin "+k+" error loading: "+v)
self.userlist = { }
def _readline(self):
ret = []
while True:
c = self.conn.recv(1)
if c == '\n':
break
else:
if c != '\r':
ret.append(c)
return "".join(ret)
def run(self):
self.conn = socket.socket()
self.conn.settimeout(60)
try:
self.conn.connect((self.config["host"], int(self.config["port"])))
self.goodstart = True
except socket.error:
self.prnt("Cannot connect to server.")
self.goodstart = False
if self.goodstart:
ident = self.config["ident"] if self.config["ident"] != "" else self.config["nickname"]
self.sendLine("USER "+ident+" * * *")
self.sendLine("NICK "+self.config["nickname"])
self.running = self.goodstart
while self.running:
try:
data = self._readline()
except socket.timeout:
self.sendLine("PING BACK")
try:
data = self._readline()
continue
except socket.timeout:
raise IOError("Connection was broken.")
if not self.running: continue
if data == "":
self.prnt("Connection lost to server.")
self.running = False
continue
words = data.split(" ")
if words[0] == "PING":
# The server pinged us, we need to pong or we'll be disconnected
# (make sure to send whatever follows the PING, in case they send a random hash)
self.sendLine("PONG " + data[5:])
continue
elif words[0] == "ERROR":
#there was an error, we probably had a KeyboardInterrupt
self.prnt("Connection lost to server.")
self.running = False
continue
# Takes the ':Nick!Ident@Host' chunk and assigns (nick,ident,host) to user
chunk = words[0].split(":")[1]
user = (chunk.split("!")[0], chunk.split("!")[1].split("@")[0], chunk.split("!")[0].split("@")[1])
if words[1] == "433":
#There was a nick collision
self.config["nickname"] = self.config["nickname"] + "_"
self.sendLine("NICK "+self.config["nickname"])
elif words[1] == "422" or words[1] == "376":
# We successfully logged in, do post-login procedures
if self.config["ns_name"] != "" and self.config["ns_pass"] != "":
self.doMessage("NickServ", "IDENTIFY "+self.config["ns_name"]+" "+self.config["ns_pass"])
self.pluginManager.event("connect", self.config["host"])
for chan in [k for k in self.config["channels"].split(",") if k != '']:
elif words[1] == "353":
#user list, if it's large, we only got part of it.
words = [k for k in data.replace(" =", "").split(" ") if k != '']
if self.userlist.get(words[3], None) is None:
self.userlist[words[3]] = []
userlist = [u.strip(":") for u in words[3:]]
self.userlist[words[3]] += userlist
elif words[1] == "PRIVMSG":
# We got a message
channel = (words[2] if words[2] != self.config["nickname"] else user[0])
message = data[data.find(":", data.find(channel[(channel.find("-") == -1 and 1 or channel.find("-")):]))+1:]
if message.find("\x01ACTION") == 0:
# String was found, it's an action
self.pluginManager.event("action", channel, user, message[8:-1])
elif message.find(self.config["comchar"]) == 0:
# String was found, it's a command!
args = message.split(" ")
cmd = args[0][len(self.config["comchar"]):]
args.pop(0)
self.pluginManager.event("message", channel, user, message)
self.pluginManager.event("command", channel, user, cmd.lower(), args)
if re.match("^"+self.config["nickname"]+"[^A-Za-z0-9] .+$", message):
#Someone addressed us, it's a command (probably)
args = message.split(" ")
cmd = args[1]
args = args[2:]
self.pluginManager.event("message", channel, user, message)
self.pluginManager.event("command", channel, user, cmd.lower(), args)
else:
#Nothing was found, it has to be a message
self.pluginManager.event("message", channel, user, message)
elif words[1] == "JOIN":
# Someone joined a channel that we're in
if user[0] != self.config["nickname"]:
self.pluginManager.event("join", words[2].strip(":"), user)
self.userlist[words[2].strip(":")].append(user)
elif words[1] == "PART":
if user != self.config["nickname"]:
# Someone parted a channel we're in
if user in self.userlist[words[2].strip(":")]:
self.userlist[words[2]].remove(user)
self.pluginManager.event("part", words[2].strip(":"), user, " ".join(words[3:]))
elif words[1] == "QUIT":
# Someone quit the server
for k in self.userlist.keys():
if user in self.userlist[k]:
self.userlist[k].remove(user)
self.pluginManager.event("quit", user, " ".join(words[2:])[1:])
elif words[1] == "NICK":
# Someone changed their nickname
for k in self.userlist.keys():
if user in self.userlist[k]:
self.userlist[k].remove(user)
self.userlist[k].append(words[2].strip(":"))
self.pluginManager.event("nick", user, words[2].strip(":"))
elif words[1] == "MODE":
# Someone set a mode
try:
self.pluginManager.event("mode", words[2], user, words[3], words[4])
except IndexError: # words[4] is not valid, it wasn't set on a user
self.pluginManager.event("mode", words[2], user, words[3], "")
elif words[1] == "KICK":
#someone kicked someone
if self.userlist.get(words[2].lower(), None) is not None:
self.userlist[words[2].lower()].remove(words[3])
self.pluginManager.event("kick", words[2], user, words[3], words[4][1:])
if self.goodstart:
self.pluginManager.event("disconnect", self.config["host"])
def prnt(self, line):
print("["+self.config["host"]+"] "+line)
def sendLine(self, line):
try:
self.conn.send(line+"\r\n")
except socket.error:
self.prnt("Connection lost to server.")
exit()
def doMessage(self, channel, message):
self.sendLine("PRIVMSG "+channel+" :"+message)
self.pluginManager.event("message", channel, self.config["nickname"], message)
def doAction(self, channel, action):
self.sendLine("PRIVMSG "+channel+" :\x01ACTION "+action+" \x01")
self.pluginManager.event("action", channel, self.config["nickname"], action)
def doQuit(self, message=""):
self.sendLine("QUIT :" + message)
self.pluginManager.event("quit", self.config["nickname"], message)
self.running = False
def doNotice(self, user, message):
self.sendLine("NOTICE "+user+" :"+message)
def doNick(self, newnick):
self.sendLine("NICK " + newnick)
self.pluginManager.event("nick", self.config["nickname"], newnick)
self.config["nickname"] = newnick
def doJoin(self, channel):
self.sendLine("JOIN "+channel)
self.pluginManager.event("join", channel, self.config["nickname"])
def doKick(self, channel, user, message=""):
self.sendLine("KICK %s %s :%s" % (channel, user, message))
self.pluginManager.event("kick", channel, self.config["nickname"], user, message)
def doPart(self, channel, message=""):
if channel in self.userlist.keys():
del self.userlist[channel]
self.pluginManager.event("part", channel, self.config["nickname"], message)
def doMode(self, channel, mode, user=""):
self.sendLine("MODE "+channel+" "+mode+user)
self.pluginManager.event("mode", channel, self.config["nickname"], mode, user)