summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Sherson <caretaker82@euclid.shersonb.net>2013-08-27 23:47:24 -0700
committerBrian Sherson <caretaker82@euclid.shersonb.net>2013-08-27 23:47:24 -0700
commit2011bf9bbd042bba4a649f9e52a52c1149ff09c8 (patch)
tree5e2a4b26105a13a7c4ee383870516bd03d489651
parent7a015d666c81f5dd44507b69728e774c15c72f6d (diff)
-rw-r--r--autoexec.py139
-rw-r--r--bouncer.py1007
-rw-r--r--cannon.py62
-rw-r--r--figlet.py39
-rw-r--r--irc.py2227
-rw-r--r--logger.py672
-rw-r--r--sedbot.py136
-rwxr-xr-xstartirc.py93
-rw-r--r--wallet.py74
9 files changed, 2460 insertions, 1989 deletions
diff --git a/autoexec.py b/autoexec.py
index 8a8b7e7..7953267 100644
--- a/autoexec.py
+++ b/autoexec.py
@@ -1,68 +1,83 @@
#!/usr/bin/python
import re
+
class Autoexec(object):
- def __init__(self):
- self.networks={}
- def onModuleAdd(self, IRC, label, onconnect=None, onregister=None, autojoin=None, usermodes=None, wallet=None, opername=None, opermodes=None, snomasks=None, operexec=None, operjoin=None):
- labels=[v[0] for v in self.networks.values()]
- if label in labels:
- raise BaseException, "Label already exists"
- if IRC in self.networks.keys():
- raise BaseException, "Network already exists"
- self.networks[IRC]=(label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)
- def onModuleRem(self, IRC):
- del self.networks[IRC]
- def onConnect(self, IRC):
- (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC]
- if onconnect:
- for line in onconnect:
- IRC.raw(line, origin=self)
- def onRegistered(self, IRC):
- (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC]
- if onregister:
- for line in onregister:
- IRC.raw(line, origin=self)
- if usermodes:
- IRC.raw("MODE %s %s"%(IRC.identity.nick, usermodes), origin=self)
- if opername and wallet and "%s/opers/%s"%(label, opername) in wallet.keys():
- IRC.raw("OPER %s %s"%(opername, wallet["%s/opers/%s"%(label, opername)]), origin=self)
- if autojoin:
- IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
- def onRecv(self, IRC, line, data):
- if data==None:
- return
- (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)=self.networks[IRC]
- (origin, ident, host, cmd, target, params, extinfo)=data
- if cmd=="381" and opermodes:
- if operexec:
- for line in operexec:
- IRC.raw(line, origin=self)
- if opermodes:
- IRC.raw("MODE %s %s"%(IRC.identity.nick, opermodes), origin=self)
- if snomasks:
- IRC.raw("MODE %s +s %s"%(IRC.identity.nick, snomasks), origin=self)
- if operjoin:
- IRC.raw("JOIN %s"%(",".join(operjoin)), origin=self)
+ def __init__(self):
+ self.networks = {}
+
+ def onModuleAdd(self, IRC, label, onconnect=None, onregister=None, autojoin=None, usermodes=None, wallet=None, opername=None, opermodes=None, snomasks=None, operexec=None, operjoin=None):
+ labels = [v[0] for v in self.networks.values()]
+ if label in labels:
+ raise BaseException("Label already exists")
+ if IRC in self.networks.keys():
+ raise BaseException("Network already exists")
+ self.networks[IRC] = (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)
+
+ def onModuleRem(self, IRC):
+ del self.networks[IRC]
+
+ def onConnect(self, IRC):
+ (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC]
+ if onconnect:
+ for line in onconnect:
+ IRC.raw(line, origin=self)
+
+ def onRegistered(self, IRC):
+ (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC]
+ if onregister:
+ for line in onregister:
+ IRC.raw(line, origin=self)
+ if usermodes:
+ IRC.raw("MODE %s %s"%(IRC.identity.nick, usermodes), origin=self)
+ if opername and wallet and "%s/opers/%s"%(label, opername) in wallet.keys():
+ IRC.raw("OPER %s %s"%(opername, wallet[
+ "%s/opers/%s"%(label, opername)]), origin=self)
+ if autojoin:
+ IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
+
+ def onRecv(self, IRC, line, data):
+ if data is None:
+ return
+ (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin) = self.networks[IRC]
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ if cmd == "381" and opermodes:
+ if operexec:
+ for line in operexec:
+ IRC.raw(line, origin=self)
+ if opermodes:
+ IRC.raw("MODE %s %s"%(IRC.identity.nick,
+ opermodes), origin=self)
+ if snomasks:
+ IRC.raw("MODE %s +s %s"%(
+ IRC.identity.nick, snomasks), origin=self)
+ if operjoin:
+ IRC.raw("JOIN %s"%(",".join(operjoin)), origin=self)
+
class NickServ(object):
- def __init__(self):
- self.networks={}
- def onModuleAdd(self, IRC, label, wallet=None, autojoin=None):
- labels=[v[0] for v in self.networks.values()]
- #print labels
- if label in labels:
- raise BaseException, "Label already exists"
- if IRC in self.networks.keys():
- raise BaseException, "Network already exists"
- self.networks[IRC]=(label, wallet, autojoin)
- def onModuleRem(self, IRC):
- del self.networks[IRC]
- def onRecv(self, IRC, line, data):
- if data==None: return
- (origin, ident, host, cmd, target, params, extinfo)=data
- label, wallet, autojoin=self.networks[IRC]
- if target==IRC.identity.nick and origin=="NickServ" and re.match("This nickname is registered and protected.", extinfo) and wallet and "%s/NickServ/%s"%(label, target.lower()) in wallet.keys():
- IRC.user("NickServ").msg("identify %s" % wallet["%s/NickServ/%s"%(label, target.lower())])
- if cmd=="900" and autojoin:
- IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
+ def __init__(self):
+ self.networks = {}
+
+ def onModuleAdd(self, IRC, label, wallet=None, autojoin=None):
+ labels = [v[0] for v in self.networks.values()]
+ #print labels
+ if label in labels:
+ raise BaseException("Label already exists")
+ if IRC in self.networks.keys():
+ raise BaseException("Network already exists")
+ self.networks[IRC] = (label, wallet, autojoin)
+
+ def onModuleRem(self, IRC):
+ del self.networks[IRC]
+
+ def onRecv(self, IRC, line, data):
+ if data is None:
+ return
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ label, wallet, autojoin = self.networks[IRC]
+ if target == IRC.identity.nick and origin == "NickServ" and re.match("This nickname is registered and protected.", extinfo) and wallet and "%s/NickServ/%s"%(label, target.lower()) in wallet.keys():
+ IRC.user("NickServ").msg("identify %s" % wallet[
+ "%s/NickServ/%s"%(label, target.lower())])
+ if cmd == "900" and autojoin:
+ IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
diff --git a/bouncer.py b/bouncer.py
index 2e6561a..db54644 100644
--- a/bouncer.py
+++ b/bouncer.py
@@ -1,452 +1,569 @@
#!/usr/bin/python
-import socket, ssl, os, re, time, sys, string, hashlib, traceback
+import socket
+import ssl
+import os
+import re
+import time
+import sys
+import string
+import hashlib
+import traceback
from threading import Thread, Lock
import Queue
+
class Bouncer (Thread):
- def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None):
- self.__name__="Bouncer for pyIRC"
- self.__version__="1.0.0rc1"
- self.__author__="Brian Sherson"
- self.__date__="May 23, 2013"
- #print "Initializing ListenThread..."
- self.addr=addr
- self.port=port
- self.servers={}
- self.passwd={}
- self.socket=s=socket.socket()
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.ssl=ssl
- self.certfile=certfile
- self.keyfile=keyfile
- s.bind((self.addr,self.port))
- self.connections=[]
- self.ignore=ignore
-
- ### Keep track of what extensions/connections are requesting WHO, WHOIS, and LIST, because we don't want to spam every bouncer connection with the server's replies.
- ### In the future, MAY implement this idea in the irc module.
- self.whoexpected={}
- self.whoisexpected={}
- self.listexpected={}
- #self.lock=Lock()
- self.starttime=int(time.time())
- Thread.__init__ ( self )
- self.daemon=True
- self.start()
- def __repr__(self):
- return "<Bouncer listening on port %(addr)s:%(port)s>" % vars(self)
-
- def run(self):
- self.socket.listen(5)
- #print ((self,"Now listening on port "+str(self.port)))
- while True:
- try:
- (connection,addr)=self.socket.accept()
- if self.ssl:
- connection=ssl.wrap_socket(connection, server_side=True, certfile=self.certfile, keyfile=self.keyfile, ssl_version=ssl.PROTOCOL_SSLv23)
- try:
- hostname, aliaslist, addresslist = socket.gethostbyaddr(addr[0])
- addr = (hostname, addr[1])
- except:
- pass
- #print ((self,"New client connecting from %s:%s"%addr))
- except socket.error:
- #print "Shutting down Listener"
- self.socket.close()
- #raise
- sys.exit()
- bouncer=BouncerConnection(self, connection, addr)
- #bouncer.daemon=True
- #self.connections.append(bouncer)
- #bouncer.start()
- #ccrecv.start()
- time.sleep(0.5)
- try:
- self.socket.close()
- except: pass
- def onRecv(self, IRC, line, data):
- if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]):
- if data:
- (origin, ident, host, cmd, target, params, extinfo)=data
- #print data
- if re.match("^\\d+$",cmd): cmd=int(cmd) ### Code is a numerical response
- if cmd in (352, 315): ### WHO reply
- if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:
- self.whoexpected[IRC][0].connection.send(line+"\n")
- if cmd==315: ### End of WHO reply
- del self.whoexpected[IRC][0]
- elif cmd in (307, 311, 312, 313, 317, 318, 319, 330, 335, 336, 378, 379): ### WHO reply
- if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
- self.whoisexpected[IRC][0].connection.send(line+"\n")
- if cmd==318: ### End of WHOIS reply
- del self.whoisexpected[IRC][0]
- elif cmd in (321, 322, 323): ### LIST reply
- if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:
- self.listexpected[IRC][0].connection.send(line+"\n")
- if cmd==323: ### End of LIST reply
- del self.listexpected[IRC][0]
- else:
- for bouncer in self.connections:
- #print bouncer.IRC
- #print IRC
- #print line
- if bouncer.IRC==IRC: bouncer.connection.send(line+"\n")
- def onSend(self, IRC, line, data, origin):
- if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]):
- (cmd, target, params, extinfo)=data
- if cmd.upper() in ("PRIVMSG", "NOTICE"):
- for bouncerconnection in self.connections:
- if bouncerconnection==origin: ### Do NOT send the message back to the originating client.
- continue
- if bouncerconnection.IRC==IRC: ### Send the message to the other clients connected to the bouncer.
- ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo)
- if ctcp:
- (ctcptype,ext)=ctcp[0]
- if ctcptype=="ACTION":
- bouncerconnection.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line))
- ### Unless the message is a CTCP that is not ACTION.
- else:
- bouncerconnection.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line))
- elif cmd.upper()=="WHO":
- #print origin, line
- self.whoexpected[IRC].append(origin)
- elif cmd.upper()=="WHOIS":
- #print origin, line
- self.whoisexpected[IRC].append(origin)
- elif cmd.upper()=="LIST":
- #print origin, line
- self.listexpected[IRC].append(origin)
- def onModuleAdd(self, IRC, label, passwd, hashtype="md5"):
- if IRC in [connection for (connection, passwd, hashtype) in self.servers.values()]: return # Silently do nothing
- if label in self.servers.keys(): return
- self.servers[label]=(IRC, passwd, hashtype)
- self.whoexpected[IRC]=[]
- self.whoisexpected[IRC]=[]
- self.listexpected[IRC]=[]
-
- def onModuleRem(self, IRC):
- for bouncerconnection in self.connections:
- if bouncerconnection.IRC==IRC:
- bouncerconnection.stop(quitmsg="Bouncer extension removed")
- for (label, (connection, passwd, hashtype)) in self.servers.items():
- if connection==IRC:
- del self.servers[label]
-
- def stop(self):
- #self.quitmsg=quitmsg
- #self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick, self.addr[0], self.quitmsg))
- self.socket.shutdown(0)
- def disconnectall(self, quitmsg="Disconnecting all sessions"):
- for bouncerconnection in self.connections:
- bouncerconnection.stop(quitmsg=quitmsg)
- def onDisconnect(self, IRC):
- self.whoexpected[IRC]=[]
- self.whoisexpected[IRC]=[]
- self.listexpected[IRC]=[]
- for bouncerconnection in self.connections:
- if bouncerconnection.IRC==IRC:
- bouncerconnection.stop(quitmsg="IRC connection lost")
+ def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr):
+ self.__name__ = "Bouncer for pyIRC"
+ self.__version__ = "1.0.0rc2"
+ self.__author__ = "Brian Sherson"
+ self.__date__ = "August 26, 2013"
+ #print "Initializing ListenThread..."
+ self.addr = addr
+ self.port = port
+ self.servers = {}
+ self.passwd = {}
+ self.socket = s = socket.socket()
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.ssl = ssl
+ self.certfile = certfile
+ self.keyfile = keyfile
+ s.bind((self.addr, self.port))
+ self.connections = []
+ self.ignore = ignore
+ self.debug = debug
+
+ ### Keep track of what extensions/connections are requesting WHO, WHOIS, and LIST, because we don't want to spam every bouncer connection with the server's replies.
+ ### In the future, MAY implement this idea in the irc module.
+ self.whoexpected = {}
+ self.whoisexpected = {}
+ self.listexpected = {}
+ self.lock = Lock()
+ self.starttime = int(time.time())
+ Thread.__init__(self)
+ self.daemon = True
+ self.start()
+
+ def __repr__(self):
+ return "<Bouncer listening on port %(addr)s:%(port)s>" % vars(self)
+
+ def run(self):
+ self.socket.listen(5)
+ #print ((self,"Now listening on port "+str(self.port)))
+ while True:
+ try:
+ (connection, addr) = self.socket.accept()
+ if self.ssl:
+ connection = ssl.wrap_socket(connection, server_side=True, certfile=self.certfile, keyfile=self.keyfile, ssl_version=ssl.PROTOCOL_SSLv23)
+ try:
+ hostname, aliaslist, addresslist = socket.gethostbyaddr(
+ addr[0])
+ addr = (hostname, addr[1])
+ except:
+ pass
+ #print ((self,"New client connecting from %s:%s"%addr))
+ except socket.error:
+ #print "Shutting down Listener"
+ self.socket.close()
+ #raise
+ sys.exit()
+ bouncer = BouncerConnection(self, connection, addr)
+ #bouncer.daemon=True
+ #self.connections.append(bouncer)
+ #bouncer.start()
+ #ccrecv.start()
+ time.sleep(0.5)
+ try:
+ self.socket.close()
+ except:
+ pass
+
+ def onRecv(self, IRC, line, data):
+ if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]):
+ if data:
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ #print data
+ if re.match("^\\d+$", cmd):
+ cmd = int(cmd) # Code is a numerical response
+ if cmd in (352, 315, 304) or (cmd == 461 and params.upper() == "WHO"): # WHO reply
+ if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:
+ self.whoexpected[IRC][0].connection.send(line+"\n")
+ if cmd == 315 or (cmd == 461 and params.upper() == "WHO"): # End of WHO reply
+ origin = self.whoexpected[IRC][0]
+ del self.whoexpected[IRC][0]
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ if self.debug:
+ with IRC.loglock:
+ if issubclass(type(origin), Thread):
+ name = origin.name
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] Removing %(origin)s (%(name)s) from WHO expected list." % vars()
+ else:
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] Removing %(origin)s from WHO expected list." % vars()
+ for obj in self.whoexpected[IRC]:
+ if issubclass(type(obj), Thread):
+ name = obj.name
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] WHO expected: %(obj)s (%(name)s)" % vars()
+ else:
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onRecv] WHO expected: %(obj)s" % vars()
+ IRC.log.flush()
+ elif cmd in (307, 311, 312, 313, 317, 318, 319, 330, 335, 336, 378, 379): # WHO reply
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ self.whoisexpected[IRC][0].connection.send(line+"\n")
+ if cmd == 318: # End of WHOIS reply
+ del self.whoisexpected[IRC][0]
+ elif cmd in (321, 322, 323): # LIST reply
+ if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:
+ self.listexpected[IRC][0].connection.send(line+"\n")
+ if cmd == 323: # End of LIST reply
+ del self.listexpected[IRC][0]
+ else:
+ for bouncer in self.connections:
+ #print bouncer.IRC
+ #print IRC
+ #print line
+ if bouncer.IRC == IRC:
+ try:
+ bouncer.connection.send(line+"\n")
+ except socket.error:
+ with IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>IRC.log, "%(timestamp)s !!! [Bouncer.onRecv] Exception in module %(module)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>IRC.log, "%(timestamp)s !!! [Bouncer.onRecv] %(tbline)s" % vars()
+ IRC.log.flush()
+
+ def onSend(self, IRC, line, data, origin):
+ if type(self.ignore) not in (list, tuple) or all([not re.match(pattern, line) for pattern in self.ignore]):
+ (cmd, target, params, extinfo) = data
+ if cmd.upper() in ("PRIVMSG", "NOTICE"):
+ for bouncerconnection in self.connections:
+ if bouncerconnection == origin: # Do NOT send the message back to the originating client.
+ continue
+ if bouncerconnection.IRC == IRC: # Send the message to the other clients connected to the bouncer.
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",
+ extinfo)
+ if ctcp:
+ (ctcptype, ext) = ctcp[0]
+ if ctcptype == "ACTION":
+ bouncerconnection.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line))
+ ### Unless the message is a CTCP that is not ACTION.
+ else:
+ bouncerconnection.connection.send(":%s!%s@%s %s\n" % (bouncerconnection.IRC.identity.nick, bouncerconnection.IRC.identity.idnt, bouncerconnection.IRC.identity.host, line))
+ elif cmd.upper() == "WHO":
+ self.whoexpected[IRC].append(origin)
+ if self.debug:
+ with IRC.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ if issubclass(type(origin), Thread):
+ name = origin.name
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] Adding %(origin)s (%(name)s) to WHO expected list." % vars()
+ else:
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] Adding %(origin)s to WHO expected list." % vars()
+ for obj in self.whoexpected[IRC]:
+ if issubclass(type(obj), Thread):
+ name = obj.name
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] WHO expected: %(obj)s (%(name)s)" % vars()
+ else:
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onSend] WHO expected: %(obj)s" % vars()
+ IRC.log.flush()
+ elif cmd.upper() == "WHOIS":
+ self.whoisexpected[IRC].append(origin)
+ elif cmd.upper() == "LIST":
+ self.listexpected[IRC].append(origin)
+
+ def onModuleAdd(self, IRC, label, passwd, hashtype="md5"):
+ if IRC in [connection for (connection, passwd, hashtype) in self.servers.values()]:
+ return # Silently do nothing
+ if label in self.servers.keys():
+ return
+ self.servers[label] = (IRC, passwd, hashtype)
+ self.whoexpected[IRC] = []
+ if self.debug:
+ with IRC.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onModuleAdd] Clearing WHO expected list." % vars()
+ IRC.log.flush()
+ self.whoisexpected[IRC] = []
+ self.listexpected[IRC] = []
+
+ def onModuleRem(self, IRC):
+ for bouncerconnection in self.connections:
+ if bouncerconnection.IRC == IRC:
+ bouncerconnection.quit(quitmsg="Bouncer extension removed")
+ for (label, (connection, passwd, hashtype)) in self.servers.items():
+ if connection == IRC:
+ del self.servers[label]
+
+ def stop(self):
+ #self.quitmsg=quitmsg
+ #self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick, self.addr[0], self.quitmsg))
+ self.socket.shutdown(0)
+
+ def disconnectall(self, quitmsg="Disconnecting all sessions"):
+ for bouncerconnection in self.connections:
+ bouncerconnection.stop(quitmsg=quitmsg)
+
+ def onDisconnect(self, IRC):
+ self.whoexpected[IRC] = []
+ if self.debug:
+ with IRC.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ print >>IRC.log, "%(timestamp)s dbg [Bouncer.onDisconnect] Clearing WHO expected list." % vars()
+ IRC.log.flush()
+ self.whoisexpected[IRC] = []
+ self.listexpected[IRC] = []
+ for bouncerconnection in self.connections:
+ if bouncerconnection.IRC == IRC:
+ bouncerconnection.quit(quitmsg="IRC connection lost")
+
class BouncerConnection (Thread):
- def __init__(self, bouncer, connection, addr):
- #print "Initializing ListenThread..."
- self.bouncer=bouncer
- self.connection=connection
- self.host, self.port=self.addr=addr
- self.IRC=None
- self.pwd=None
- self.nick=None
- self.label=None
- self.idnt=None
- self.realname=None
- self.addr=addr
- self.quitmsg="Connection Closed"
-
- Thread.__init__ ( self )
- self.daemon=True
- self.start()
-
- def __repr__(self):
- server=self.IRC.server if self.IRC else "*"
- port=self.IRC.port if self.IRC else "*"
- if self.IRC and self.IRC.identity:
- nick=self.IRC.identity.nick
- ident=self.IRC.identity.idnt if self.IRC.identity.idnt else "*"
- host=self.IRC.identity.host if self.IRC.identity.host else "*"
- else:
- nick="*"
- ident="*"
- host="*"
- protocol="ircs" if self.IRC.ssl else "irc"
- addr=self.host
- return "<Bouncer connection from %(addr)s to %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
-
- def stop(self, quitmsg="Disconnected"):
- self.quitmsg=quitmsg
- #self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick, self.host, self.quitmsg))
- self.connection.shutdown(0)
-
- def run(self):
- ### Add connection to connection list.
-
- listnumerics=dict(b=(367, 368, "channel ban list"), e=(348, 349, "Channel Exception List"), I=(346, 347, "Channel Invite Exception List"), w=(910, 911, "Channel Access List"), g=(941, 940, "chanel spamfilter list"), X=(954, 953, "channel exemptchanops list"))
-
- passwd=None
- nick=None
- user=None
-
- readbuf=""
- linebuf=[]
-
- try:
- while True:
- ### Read data (appending) into readbuf, then break lines and append lines to linebuf
- while len(linebuf)==0:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- read=self.connection.recv(512)
- if read=="" and len(linebuf)==0: ### No more data to process.
- #self.quitmsg="Connection Closed"
- sys.exit()
-
- readbuf+=read
- lastlf=readbuf.rfind("\n")
-
- if lastlf>=0:
- linebuf.extend(string.split(readbuf[0:lastlf], "\n"))
- readbuf=readbuf[lastlf+1:]
-
- line=string.rstrip(linebuf.pop(0))
- match=re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
-
- if len(match)==0: continue
- (cmd, target, params, extinfo)=match[0]
-
- if not passwd: ### Bouncer expects a password
- if cmd.upper()=="PASS":
- passwd=target if target else extinfo
- else:
- self.quitmsg="Access Denied"
- break
-
- elif not nick: ### Bouncer expects a NICK command
- if cmd.upper()=="NICK":
- nick=target if target else extinfo
- else:
- self.quitmsg="Access Denied"
- break
-
- elif not self.idnt: ### Bouncer expects a USER command to finish registration
- if cmd.upper()=="USER":
- self.idnt=target
- #print self.idnt
- if self.idnt in self.bouncer.servers.keys():
- self.IRC, passwdhash, hashtype=self.bouncer.servers[self.idnt]
- passmatch=hashlib.new(hashtype, passwd).hexdigest()==passwdhash
- self.IRC.lock.acquire()
- if not (self.IRC.connected and self.IRC.registered and type(self.IRC.supports)==dict and "CHANMODES" in self.IRC.supports.keys() and passmatch):
- self.quitmsg="Access Denied"
- self.IRC.lock.release()
- break
-
- ### If we have made it to this point, then access has been granted.
- self.bouncer.connections.append(self)
- labels=[bouncerconnection.label for bouncerconnection in self.bouncer.connections if bouncerconnection.IRC==self.IRC and bouncerconnection.label]
- n=1
- while "*%s_%d"%(self.idnt, n) in labels:
- n+=1
- self.label="*%s_%d"%(self.idnt, n)
-
- ### Request Version info.
- self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick))
-
- ### Send Greeting.
- self.connection.send(":%s 001 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.welcome))
- self.connection.send(":%s 002 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.hostinfo))
- self.connection.send(":%s 003 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servinfo))
- self.connection.send(":%s 004 %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.serv004))
-
- ### Send 005 response.
- supports=["CHANMODES=%s"%(",".join(value)) if name=="CHANMODES" else "PREFIX=(%s)%s"%value if name=="PREFIX" else "%s=%s"%(name, value) if value else name for name, value in self.IRC.supports.items()]
- supports.sort()
- supportsreply=[]
- supportsstr=" ".join(supports)
- index=0
- while True:
- if len(supportsstr)-index>196:
- nextindex=supportsstr.rfind(" ", index, index+196)
- supportsreply.append(supportsstr[index:nextindex])
- index=nextindex+1
- else:
- supportsreply.append(supportsstr[index:])
- break
- for support in supportsreply:
- self.connection.send(":%s 005 %s %s :are supported by this server\n" % (self.IRC.serv, self.IRC.identity.nick, support))
-
- ### Send MOTD
- self.connection.send(":%s 375 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdgreet))
- for motdline in self.IRC.motd:
- self.connection.send(":%s 372 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, motdline))
- self.connection.send(":%s 376 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend))
-
- ### Send user modes and snomasks.
- self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes))
- if "s" in self.IRC.identity.modes and self.IRC.identity.snomask:
- self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.server, self.IRC.identity.nick, self.IRC.identity.snomask))
-
- ### Join user to internal bouncer channel.
- self.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host))
-
- ### Set internal bouncer topic.
- self.connection.send(":$bouncer 332 %s $bouncer :Bouncer internal channel. Enter bouncer commands here.\n" % (self.IRC.identity.nick))
- self.connection.send(":$bouncer 333 %s $bouncer $bouncer %s\n" % (self.IRC.identity.nick, self.bouncer.starttime))
-
- ### Send NAMES for internal bouncer channel.
- self.connection.send(":$bouncer 353 %s @ $bouncer :%s\n" % (
- self.IRC.identity.nick,
- string.join(["@*Bouncer*"]+["@%s"%bouncerconnection.label for bouncerconnection in self.bouncer.connections]))
- )
- self.connection.send(":$bouncer 366 %s $bouncer :End of /NAMES list.\n" % (self.IRC.identity.nick))
-
- ### Give operator mode to user.
- self.connection.send(":*Bouncer* MODE $bouncer +o %s\n" % (self.IRC.identity.nick))
-
-
- ### Join user to channels.
- for channel in self.IRC.identity.channels:
- ### JOIN command
- self.connection.send(":%s!%s@%s JOIN :%s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, channel.name))
-
- ### Topic
- self.connection.send(":%s 332 %s %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topic))
- self.connection.send(":%s 333 %s %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topicsetby, channel.topictime))
-
- ### Determine if +s or +p modes are set in channel
- secret="s" in channel.modes.keys() and channel.modes["s"]
- private="p" in channel.modes.keys() and channel.modes["p"]
-
- ### Construct NAMES for channel.
- namesusers=[]
- modes, symbols=self.IRC.supports["PREFIX"]
- self.connection.send(":%s 353 %s %s %s :%s\n" % (
- self.IRC.serv,
- self.IRC.identity.nick,
- "@" if secret else ("*" if private else "="),
- channel.name,
- string.join([string.join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))],"")+user.nick for user in channel.users]))
- )
- self.connection.send(":%s 366 %s %s :End of /NAMES list.\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name))
-
- ### Announce connection to all other bouncer connections.
- for bouncerconnection in self.bouncer.connections:
- try:
- bouncerconnection.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.label, self.idnt, self.addr[0]))
- bouncerconnection.connection.send(":*Bouncer* MODE $bouncer +o %s\n" % (self.label))
- except:
- pass
- self.IRC.lock.release()
- else: ### User not found
- self.quitmsg="Access Denied"
- break
- else: ### Client did not send USER command when expected
- self.quitmsg="Access Denied"
- break
-
- elif cmd.upper()=="QUIT":
- self.quitmsg=extinfo
- break
-
- elif cmd.upper()=="PING":
- self.connection.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick))
-
- elif cmd.upper()=="WHO" and target.lower()=="$bouncer":
- for bouncerconnection in self.bouncer.connections:
- self.connection.send(":$bouncer 352 %s $bouncer %s %s $bouncer %s H@ :0 %s\n" % (self.IRC.identity.nick, bouncerconnection.idnt, bouncerconnection.host, bouncerconnection.label, bouncerconnection.IRC))
- self.connection.send(":$bouncer 315 %s $bouncer :End if /WHO list.\n" % (self.IRC.identity.nick))
-
- elif cmd.upper() in ("PRIVMSG", "NOTICE"):
- ### Check if CTCP
- ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo)
-
-
- if target.lower()=="$bouncer": ### Message to internal bouncer control channel
- if ctcp and cmd.upper()=="NOTICE":
- (ctcptype, ext)=ctcp[0] ### Unpack CTCP info
- if ctcptype=="VERSION": ### Client is sending back version reply
- for bouncerconnection in self.bouncer.connections:
- reply=":%s!%s@%s PRIVMSG $bouncer :Version reply: %s\n" % (self.label, self.idnt, self.addr[0], ext)
- try:
- bouncerconnection.connection.send(reply)
- except:
- pass
- elif ctcp: ### If CTCP, only want to
- (ctcptype, ext)=ctcp[0] ### Unpack CTCP info
-
- if ctcptype=="LAGCHECK": ### Client is doing a lag check. No need to send to IRC network, just reply back.
- self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
- else:
- self.IRC.raw(line, origin=self)
- else:
- self.IRC.raw(line, origin=self)
-
- elif cmd.upper() == "MODE": ### Will want to determine is requesting modes, or attempting to modify modes.
- if target and "CHANTYPES" in self.IRC.supports.keys() and target[0] in self.IRC.supports["CHANTYPES"]:
- if params=="":
- channel=self.IRC.channel(target)
- modes=channel.modes.keys()
- modestr="".join([mode for mode in modes if mode not in self.IRC.supports["CHANMODES"][0]+self.IRC.supports["PREFIX"][0] and channel.modes[mode]])
- params=" ".join([channel.modes[mode] for mode in modes if mode in self.IRC.supports["CHANMODES"][1]+self.IRC.supports["CHANMODES"][2] and channel.modes[mode]])
- self.connection.send(":%s 324 %s %s +%s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params))
- self.connection.send(":%s 329 %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.created))
- elif re.match("^\\+?[%s]+$"%self.IRC.supports["CHANMODES"][0], params) and extinfo=="":
- #print "ddd Mode List Request", params
- channel=self.IRC.channel(target)
- redundant=[]
- for mode in params.lstrip("+"):
- if mode in redundant or mode not in listnumerics.keys(): continue
- i,e,l=listnumerics[mode]
- if mode in channel.modes.keys():
- for (mask, setby, settime) in channel.modes[mode]:
- self.connection.send(":%s %d %s %s %s %s %s\n" % (self.IRC.serv, i, channel.context.identity.nick, channel.name, mask, setby, settime))
- self.connection.send(":%s %d %s %s :End of %s\n" % (self.IRC.serv, e, channel.context.identity.nick, channel.name, l))
- redundant.append(mode)
- else:
- self.IRC.raw(line, origin=self)
- elif params=="" and target.lower()==self.IRC.identity.nick.lower():
- self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes))
- if "s" in self.IRC.identity.modes and self.IRC.identity.snomask:
- self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.snomask))
- else:
- self.IRC.raw(line, origin=self)
- else:
- self.IRC.raw(line, origin=self)
-
-
-
-
-
-
-
-
- except SystemExit:
- pass ### No need to pass error message if break resulted from sys.exit()
- except:
- exc,excmsg,tb=sys.exc_info()
- self.quitmsg=str(excmsg)
- finally:
- if self.IRC and self.IRC.lock.locked(): self.IRC.lock.release() ### Release lock in case lock is locked.
- try:
- self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick if self.IRC else "*", self.host, self.quitmsg))
- self.connection.shutdown(1)
- self.connection.close()
- except:
- pass
-
- if self in self.bouncer.connections:
- self.bouncer.connections.remove(self)
-
- ### Announce QUIT to other bouncer connections.
- for bouncerconnection in self.bouncer.connections:
- try:
- bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.idnt, self.host, self.quitmsg))
- except:
- pass
+ def __init__(self, bouncer, connection, addr):
+ #print "Initializing ListenThread..."
+ self.bouncer = bouncer
+ self.connection = connection
+ self.host, self.port = self.addr = addr
+ self.IRC = None
+ self.pwd = None
+ self.nick = None
+ self.label = None
+ self.idnt = None
+ self.realname = None
+ self.addr = addr
+ self.quitmsg = "Connection Closed"
+
+ Thread.__init__(self)
+ self.daemon = True
+ self.start()
+
+ def __repr__(self):
+ server = self.IRC.server if self.IRC else "*"
+ port = self.IRC.port if self.IRC else "*"
+ if self.IRC and self.IRC.identity:
+ nick = self.IRC.identity.nick
+ ident = self.IRC.identity.idnt if self.IRC.identity.idnt else "*"
+ host = self.IRC.identity.host if self.IRC.identity.host else "*"
+ else:
+ nick = "*"
+ ident = "*"
+ host = "*"
+ protocol = "ircs" if self.IRC.ssl else "irc"
+ addr = self.host
+ return "<Bouncer connection from %(addr)s to %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
+
+ def quit(self, quitmsg="Disconnected"):
+ self.quitmsg = quitmsg
+ try:
+ self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.IRC.identity.nick if self.IRC else "*", self.host, quitmsg))
+ except:
+ pass
+ try:
+ self.connection.shutdown(1)
+ self.connection.close()
+ except:
+ pass
+
+ def run(self):
+ ### Add connection to connection list.
+
+ listnumerics = dict(b=(367, 368, "channel ban list"), e=(348, 349, "Channel Exception List"), I=(346, 347, "Channel Invite Exception List"), w=(910, 911, "Channel Access List"), g=(941, 940, "chanel spamfilter list"), X=(954, 953, "channel exemptchanops list"))
+
+ passwd = None
+ nick = None
+ user = None
+ addr = self.host
+
+ readbuf = ""
+ linebuf = []
+
+ try:
+ while True:
+ ### Read data (appending) into readbuf, then break lines and append lines to linebuf
+ while len(linebuf) == 0:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ read = self.connection.recv(512)
+ if read == "" and len(linebuf) == 0: # No more data to process.
+ #self.quitmsg="Connection Closed"
+ sys.exit()
+
+ readbuf += read
+ lastlf = readbuf.rfind("\n")
+
+ if lastlf >= 0:
+ linebuf.extend(string.split(readbuf[0:lastlf], "\n"))
+ readbuf = readbuf[lastlf+1:]
+
+ line = string.rstrip(linebuf.pop(0))
+ match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
+
+ if len(match) == 0:
+ continue
+ (cmd, target, params, extinfo) = match[0]
+
+ if not passwd: # Bouncer expects a password
+ if cmd.upper() == "PASS":
+ passwd = target if target else extinfo
+ else:
+ self.quit("Access Denied")
+ break
+
+ elif not nick: # Bouncer expects a NICK command
+ if cmd.upper() == "NICK":
+ nick = target if target else extinfo
+ else:
+ self.quit("Access Denied")
+ break
+
+ elif not self.idnt: # Bouncer expects a USER command to finish registration
+ if cmd.upper() == "USER":
+ self.idnt = target
+ #print self.idnt
+ if self.idnt in self.bouncer.servers.keys():
+ self.IRC, passwdhash, hashtype = self.bouncer.servers[self.idnt]
+ passmatch = hashlib.new(hashtype, passwd).hexdigest() == passwdhash
+ with self.IRC.lock:
+ with self.IRC.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.IRC.log, "%s *** [BouncerConnection] Incoming connection from %s to %s." % (timestamp, self.host, self.IRC)
+ self.IRC.log.flush()
+ with self.bouncer.lock:
+ self.bouncer.connections.append(self)
+ ### Announce connection to all other bouncer connections.
+ for bouncerconnection in self.bouncer.connections:
+ try:
+ bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC))
+ except:
+ with self.IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] Exception in module %(self)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] %(tbline)s" % vars()
+ self.IRC.log.flush()
+ bouncerconnection.quitmsg = excmsg
+ try:
+ bouncerconnection.connection.shutdown(0)
+ except:
+ pass
+
+ if not (self.IRC.connected and self.IRC.registered and type(self.IRC.supports) == dict and "CHANMODES" in self.IRC.supports.keys() and passmatch):
+ self.quit("Access Denied")
+ break
+
+ ### If we have made it to this point, then access has been granted.
+ labels = [bouncerconnection.label for bouncerconnection in self.bouncer.connections if bouncerconnection.IRC == self.IRC and bouncerconnection.label]
+ n = 1
+ while "*%s_%d"%(self.idnt, n) in labels:
+ n += 1
+ self.label = "*%s_%d"%(self.idnt, n)
+
+ ### Request Version info.
+ #self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick))
+
+ ### Log incoming connection
+
+ ### Send Greeting.
+ self.connection.send(":%s 001 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.welcome))
+ self.connection.send(":%s 002 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.hostinfo))
+ self.connection.send(":%s 003 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servinfo))
+ self.connection.send(":%s 004 %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.serv004))
+
+ ### Send 005 response.
+ supports = ["CHANMODES=%s"%(",".join(value)) if name == "CHANMODES" else "PREFIX=(%s)%s"%value if name == "PREFIX" else "%s=%s"%(name, value) if value else name for name, value in self.IRC.supports.items()]
+ supports.sort()
+ supportsreply = []
+ supportsstr = " ".join(supports)
+ index = 0
+ while True:
+ if len(supportsstr)-index > 196:
+ nextindex = supportsstr.rfind(" ", index, index+196)
+ supportsreply.append(supportsstr[index:nextindex])
+ index = nextindex+1
+ else:
+ supportsreply.append(supportsstr[index:])
+ break
+ for support in supportsreply:
+ self.connection.send(":%s 005 %s %s :are supported by this server\n" % (self.IRC.serv, self.IRC.identity.nick, support))
+
+ ### Send MOTD
+ self.connection.send(":%s 375 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdgreet))
+ for motdline in self.IRC.motd:
+ self.connection.send(":%s 372 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, motdline))
+ self.connection.send(":%s 376 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend))
+
+ ### Send user modes and snomasks.
+ self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes))
+ if "s" in self.IRC.identity.modes and self.IRC.identity.snomask:
+ self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.server, self.IRC.identity.nick, self.IRC.identity.snomask))
+
+ # ### Join user to internal bouncer channel.
+ # self.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host))
+
+ # ### Set internal bouncer topic.
+ # self.connection.send(":$bouncer 332 %s $bouncer :Bouncer internal channel. Enter bouncer commands here.\n" % (self.IRC.identity.nick))
+ # self.connection.send(":$bouncer 333 %s $bouncer $bouncer %s\n" % (self.IRC.identity.nick, self.bouncer.starttime))
+
+ # ### Send NAMES for internal bouncer channel.
+ # self.connection.send(":$bouncer 353 %s @ $bouncer :%s\n" % (
+ # self.IRC.identity.nick,
+ # string.join(["@*Bouncer*"]+["@%s"%bouncerconnection.label for bouncerconnection in self.bouncer.connections]))
+ # )
+ # self.connection.send(":$bouncer 366 %s $bouncer :End of /NAMES list.\n" % (self.IRC.identity.nick))
+
+ # ### Give operator mode to user.
+ # self.connection.send(":*Bouncer* MODE $bouncer +o %s\n" % (self.IRC.identity.nick))
+
+ ### Join user to channels.
+ for channel in self.IRC.identity.channels:
+ ### JOIN command
+ self.connection.send(":%s!%s@%s JOIN :%s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, channel.name))
+
+ ### Topic
+ self.connection.send(":%s 332 %s %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topic))
+ self.connection.send(":%s 333 %s %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.topicsetby, channel.topictime))
+
+ ### Determine if +s or +p modes are set in channel
+ secret = "s" in channel.modes.keys() and channel.modes["s"]
+ private = "p" in channel.modes.keys() and channel.modes["p"]
+
+ ### Construct NAMES for channel.
+ namesusers = []
+ modes, symbols = self.IRC.supports["PREFIX"]
+ self.connection.send(":%s 353 %s %s %s :%s\n" % (
+ self.IRC.serv,
+ self.IRC.identity.nick,
+ "@" if secret else ("*" if private else "="),
+ channel.name,
+ string.join([string.join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))], "")+user.nick for user in channel.users]))
+ )
+ self.connection.send(":%s 366 %s %s :End of /NAMES list.\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name))
+
+ else: # User not found
+ self.quit("Access Denied")
+ break
+ else: # Client did not send USER command when expected
+ self.quit("Access Denied")
+ break
+
+ elif cmd.upper() == "QUIT":
+ self.quit(extinfo)
+ break
+
+ elif cmd.upper() == "PING":
+ self.connection.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick))
+
+ elif cmd.upper() == "WHO" and target.lower() == "$bouncer":
+ for bouncerconnection in self.bouncer.connections:
+ self.connection.send(":$bouncer 352 %s $bouncer %s %s $bouncer %s H@ :0 %s\n" % (self.IRC.identity.nick, bouncerconnection.idnt, bouncerconnection.host, bouncerconnection.label, bouncerconnection.IRC))
+ self.connection.send(":$bouncer 315 %s $bouncer :End if /WHO list.\n" % (self.IRC.identity.nick))
+
+ elif cmd.upper() in ("PRIVMSG", "NOTICE"):
+ ### Check if CTCP
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",
+ extinfo)
+
+ if target.lower() == "$bouncer": # Message to internal bouncer control channel
+ if ctcp and cmd.upper() == "NOTICE":
+ (ctcptype, ext) = ctcp[0] # Unpack CTCP info
+ if ctcptype == "VERSION": # Client is sending back version reply
+ for bouncerconnection in self.bouncer.connections:
+ reply = ":%s!%s@%s PRIVMSG $bouncer :Version reply: %s\n" % (self.label, self.idnt, self.addr[0], ext)
+ try:
+ bouncerconnection.connection.send(reply)
+ except:
+ pass
+ elif ctcp: # If CTCP, only want to
+ (ctcptype, ext) = ctcp[0] # Unpack CTCP info
+
+ if ctcptype == "LAGCHECK": # Client is doing a lag check. No need to send to IRC network, just reply back.
+ self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ else:
+ self.IRC.raw(line, origin=self)
+ else:
+ self.IRC.raw(line, origin=self)
+
+ elif cmd.upper() == "MODE": # Will want to determine is requesting modes, or attempting to modify modes.
+ if target and "CHANTYPES" in self.IRC.supports.keys() and target[0] in self.IRC.supports["CHANTYPES"]:
+ if params == "":
+ channel = self.IRC.channel(target)
+ modes = channel.modes.keys()
+ modestr = "".join([mode for mode in modes if mode not in self.IRC.supports["CHANMODES"][0]+self.IRC.supports["PREFIX"][0] and channel.modes[mode]])
+ params = " ".join([channel.modes[mode] for mode in modes if mode in self.IRC.supports["CHANMODES"][1]+self.IRC.supports["CHANMODES"][2] and channel.modes[mode]])
+ self.connection.send(":%s 324 %s %s +%s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params))
+ self.connection.send(":%s 329 %s %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, channel.created))
+ elif re.match("^\\+?[%s]+$"%self.IRC.supports["CHANMODES"][0], params) and extinfo == "":
+ #print "ddd Mode List Request", params
+ channel = self.IRC.channel(target)
+ redundant = []
+ for mode in params.lstrip("+"):
+ if mode in redundant or mode not in listnumerics.keys():
+ continue
+ i, e, l = listnumerics[mode]
+ if mode in channel.modes.keys():
+ for (mask, setby, settime) in channel.modes[mode]:
+ self.connection.send(":%s %d %s %s %s %s %s\n" % (self.IRC.serv, i, channel.context.identity.nick, channel.name, mask, setby, settime))
+ self.connection.send(":%s %d %s %s :End of %s\n" % (self.IRC.serv, e, channel.context.identity.nick, channel.name, l))
+ redundant.append(mode)
+ else:
+ self.IRC.raw(line, origin=self)
+ elif params == "" and target.lower() == self.IRC.identity.nick.lower():
+ self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes))
+ if "s" in self.IRC.identity.modes and self.IRC.identity.snomask:
+ self.connection.send(":%s 008 %s +%s :Server notice mask\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.snomask))
+ else:
+ self.IRC.raw(line, origin=self)
+ else:
+ self.IRC.raw(line, origin=self)
+
+ except SystemExit:
+ pass # No need to pass error message if break resulted from sys.exit()
+ except:
+ exc, excmsg, tb = sys.exc_info()
+ self.quitmsg = str(excmsg)
+ if self.IRC:
+ with self.IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] Exception in module %(self)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] %(tbline)s" % vars()
+ self.IRC.log.flush()
+ finally:
+ ### Juuuuuuust in case.
+ try:
+ self.connection.shutdown(1)
+ self.connection.close()
+ except:
+ pass
+
+ if self.IRC:
+ with self.IRC.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.IRC.log, "%s *** [BouncerConnection] Connection from %s terminated (%s)." % (timestamp, self.host, self.quitmsg)
+ self.IRC.log.flush()
+
+ if self in self.bouncer.connections:
+ with self.bouncer.lock:
+ self.bouncer.connections.remove(self)
+ for bouncerconnection in self.bouncer.connections:
+ try:
+ bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg))
+ except:
+ with self.IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] Exception in module %(self)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection] %(tbline)s" % vars()
+ self.IRC.log.flush()
+ bouncerconnection.quitmsg = excmsg
+ try:
+ bouncerconnection.connection.shutdown(0)
+ except:
+ pass
+
+# ### Announce QUIT to other bouncer connections.
+# for bouncerconnection in self.bouncer.connections:
+# try:
+# bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.idnt, self.host, self.quitmsg))
+# except:
+# pass
diff --git a/cannon.py b/cannon.py
index 3af725e..55d9f00 100644
--- a/cannon.py
+++ b/cannon.py
@@ -1,30 +1,40 @@
#!/usr/bin/python
-import re, os
+import re
+import os
+
class Cannon(object):
- def __init__(self):
- self.firecount={}
- def onRecv(self, IRC, line, data):
- if data==None: return
- (origin, ident, host, cmd, target, params, extinfo)=data
- if len(target) and target[0]=="#" and cmd=="PRIVMSG":
- channel=IRC.channel(target)
- matches=re.findall("^!fire\\s+(.*)$",extinfo)
- if matches:
- nickname=matches[0]
- if any([nickname.lower()==user.nick.lower() for user in channel.users]):
- user=IRC.user(nickname)
- if user in self.firecount.keys():
- count=self.firecount[user]+1
- else:
- count=1
- self.firecount[user]=count
- if 10<=count%100<20: ordinal="th"
- elif count%10==1: ordinal="st"
- elif count%10==2: ordinal="nd"
- elif count%10==3: ordinal="rd"
- else: ordinal="th"
- channel.me("fires %s out of a cannon for the %d%s time."%(user.nick, count, ordinal))
- else:
- channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(origin, nickname))
+ def __init__(self):
+ self.firecount = {}
+
+ def onRecv(self, IRC, line, data):
+ if data is None:
+ return
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ if len(target) and target[0] == "#" and cmd == "PRIVMSG":
+ channel = IRC.channel(target)
+ matches = re.findall("^!fire\\s+(.*)$", extinfo)
+ if matches:
+ nickname = matches[0]
+ if any([nickname.lower() == user.nick.lower() for user in channel.users]):
+ user = IRC.user(nickname)
+ if user in self.firecount.keys():
+ count = self.firecount[user]+1
+ else:
+ count = 1
+ self.firecount[user] = count
+ if 10 <= count%100 < 20:
+ ordinal = "th"
+ elif count%10 == 1:
+ ordinal = "st"
+ elif count%10 == 2:
+ ordinal = "nd"
+ elif count%10 == 3:
+ ordinal = "rd"
+ else:
+ ordinal = "th"
+ channel.me("fires %s out of a cannon for the %d%s time." %
+ (user.nick, count, ordinal))
+ else:
+ channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(origin, nickname))
diff --git a/figlet.py b/figlet.py
index 8f88416..10120bb 100644
--- a/figlet.py
+++ b/figlet.py
@@ -1,20 +1,25 @@
#!/usr/bin/python
-import re, os
+import re
+import os
+
class Figlet(object):
- def onRecv(self, IRC, line, data):
- if data==None: return
- (origin, ident, host, cmd, target, params, extinfo)=data
- if len(target) and target[0]=="#" and cmd=="PRIVMSG":
- channel=IRC.channel(target)
- matches=re.findall("^!figlet\\s+(.*)$",extinfo)
- if matches:
- gif,fig=os.popen2("figlet")
- gif.write(matches[0])
- gif.close()
- while True:
- line=fig.readline()
- if line=="": break
- if re.match("^\\s+$", line.rstrip()): continue
- channel.msg(line.rstrip())
- fig.close()
+ def onRecv(self, IRC, line, data):
+ if data is None:
+ return
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ if len(target) and target[0] == "#" and cmd == "PRIVMSG":
+ channel = IRC.channel(target)
+ matches = re.findall("^!figlet\\s+(.*)$", extinfo)
+ if matches:
+ gif, fig = os.popen2("figlet")
+ gif.write(matches[0])
+ gif.close()
+ while True:
+ line = fig.readline()
+ if line == "":
+ break
+ if re.match("^\\s+$", line.rstrip()):
+ continue
+ channel.msg(line.rstrip())
+ fig.close()
diff --git a/irc.py b/irc.py
index fa7e7a2..d8d564e 100644
--- a/irc.py
+++ b/irc.py
@@ -1,1021 +1,1234 @@
#!/usr/bin/python
from threading import Thread, Event, Lock
-import re, time, sys, string, socket, os, platform, traceback, Queue, ssl
+import re
+import time
+import sys
+import string
+import socket
+import os
+import platform
+import traceback
+import Queue
+import ssl
-class Connection(Thread):
- def __init__(self, server, nick="ircbot", ident="python", realname="Python IRC Library", passwd=None, port=None, ipv6=False, ssl=False, autoreconnect=True, log=sys.stderr, timeout=300, retrysleep=5, maxretries=15, onlogin=None):
- self.__name__="pyIRC"
- self.__version__="1.0.0rc2"
- self.__author__="Brian Sherson"
- self.__date__="August 26, 2013"
-
- if port==None:
- self.port=6667 if not ssl else 6697
- else:
- self.port=port
-
- if type(nick) in (str, unicode): self.nick=[nick]
- elif type(nick) in (list, tuple): self.nick=nick
-
- self.realname=realname
- self.idnt=ident
- self.passwd=passwd
- self.server=server
- self.ssl=ssl
- self.ipv6=ipv6
- self.identity=None
-
- self.connected=False
- self.registered=False
- self.connection=None
-
- self.autoreconnect=autoreconnect
- self.maxretries=maxretries
- self.timeout=timeout
- self.retrysleep=retrysleep
-
- self.quitexpected=False
- self.log=log
-
- self.modules=[]
- self.trusted=[]
-
-
- ### Initialize IRC environment variables
- self.motdgreet=""
- self.motd=[]
- self.identity=None
- self.users=[]
- self.channels=[]
- self.supports={}
-
- self.lock=Lock()
- self.loglock=Lock()
- self.sendlock=Lock()
- self.outgoing=Queue.Queue()
-
- Thread.__init__(self)
-
- def event(self, method, modlist, exceptions=False, **params):
- #print method, modlist
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- for module in modlist:
- #print module, dir(module)
- #if modlist is not self.modules and module in self.modules: continue
- if method in dir(module) and callable(getattr(module, method)):
- try:
- getattr(module, method)(self, **params)
- except:
- with self.loglock:
- exc,excmsg,tb=sys.exc_info()
- print >>self.log, "%(timestamp)s !!! Exception in module %(module)s" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
- self.log.flush()
- if exceptions: ### If set to true, we raise the exception.
- raise
-
- def addModule(self, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException, "Module already added."
- with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.append(module)
- if trusted:
- self.trusted.append(module)
-
- def insertModule(self, index, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException, "Module already added."
- with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.insert(index, module)
- if trusted:
- self.trusted.append(module)
-
- def rmModule(self, module, **params):
- with self.lock:
- self.modules.remove(module)
- self.event("onModuleRem", [module], exceptions=True, **params)
- if module in self.trusted:
- self.trusted.remove(module)
-
- def run(self):
- self.quitexpected=False
- server=self.server
- if self.ipv6 and ":" in server:
- server="[%s]"%server
- port=self.port
- try:
- modules=list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- with self.lock:
- self.event("onLogOpen", modules)
-
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s ### Log session started" % vars()
- self.log.flush()
-
- attempt=1
- while True: ### An entire connection lives within this while loop. When the connection fails, will try to reestablish, unless self.autoreconnect is set to False.
- while True: ### Enter retry loop
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars()
- self.log.flush()
-
- with self.lock:
- self.event("onConnectAttempt", self.modules)
-
- try:
- if self.ssl:
- s=socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM)
- s.settimeout(self.timeout)
- self.connection=ssl.wrap_socket(s, cert_reqs=ssl.CERT_NONE)
- else:
- self.connection=socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM)
- self.connection.connect((self.server, self.port, 0, 0) if self.ipv6 else (self.server, self.port))
- self.connected=True
- self.connection.settimeout(self.timeout)
-
- ### Setting up thread responsible for sending data back to IRC server.
- outgoingthread=Outgoing(self)
- outgoingthread.daemon=True
- outgoingthread.start()
-
- ### Run onConnect on all modules to signal connection was established.
- modules=list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- self.event("onConnect", modules)
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s established." % vars()
- self.log.flush()
- break
- except socket.error:
- with self.loglock:
- exc,excmsg,tb=sys.exc_info()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
- self.log.flush()
-
- if self.quitexpected: sys.exit()
- if attempt<self.maxretries or self.maxretries==-1:
- time.sleep(self.retrysleep)
- attempt+=1
- else:
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars()
- self.log.flush()
- break
-
- ### Connection succeeded
- try:
- ### Attempt initial registration.
- nick=self.nick[0]
- trynick=0
- if self.passwd: self.raw("PASS :%(passwd)s" % vars(self))
- self.raw("NICK :%(nick)s" % vars())
- self.raw("USER %(idnt)s * * :%(realname)s" % vars(self))
-
- ### Initialize buffers
- linebuf=[]
- readbuf=""
-
- while True: ### Main loop of IRC connection.
- while len(linebuf)==0: ### Need Moar Data
- read=self.connection.recv(512)
-
- ### If read was empty, connection is terminated.
- if read=="": sys.exit()
-
- ### If read was successful, parse away!
- readbuf+=read
- lastlf=readbuf.rfind("\n")
- if lastlf>=0:
- linebuf.extend(string.split(readbuf[0:lastlf], "\n"))
- readbuf=readbuf[lastlf+1:]
-
- line=string.rstrip(linebuf.pop(0))
-
- ### If received PING, then just pong back transparently.
- ping=re.findall("^PING :?(.*)$",line)
- if len(ping):
- with self.lock:
- self.connection.send("PONG :%s\n" % ping[0])
- continue
-
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >> self.log, "%(timestamp)s <<< %(line)s" % vars()
- self.log.flush()
-
- ### Attempts to match against pattern ":src cmd target params :extinfo"
- matches=re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$",line)
-
- ### We have a match!
- if len(matches):
- parsed=(origin, ident, host, cmd, target, params, extinfo)=matches[0]
- if re.match("^\\d+$",cmd): cmd=int(cmd) ### Code is a numerical response
- else: cmd=cmd.upper()
-
- if not self.registered:
- if type(cmd)==int and target!="*": ### Registration complete!
- with self.lock:
- self.registered=True
- self.identity=self.user(target)
- self.serv=origin
-
- modules=list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- self.event("onRegistered", modules)
-
- elif cmd==433 and target=="*": ### Server reports nick taken, so we need to try another.
- trynick+=1
- (q,s)=divmod(trynick,len(self.nick))
- nick=self.nick[s]
- if q>0: nick+=str(q)
- self.raw("NICK :%(nick)s" % vars())
- if not self.registered: ### Registration is not yet complete
- continue
-
- ### Major codeblock here! Track IRC state.
- ### Send line to modules first
- with self.lock:
- self.event("onRecv", self.modules, line=line, data=parsed)
- if cmd==1: self.welcome=extinfo # Welcome message
- elif cmd==2: self.hostinfo=extinfo # Your Host
- elif cmd==3: self.servinfo=extinfo # Server Created
- elif cmd==4: self.serv004=params # What is this code?
- elif cmd==5: # Server Supports
- support=dict(re.findall("([A-Za-z0-9]+)(?:=(\\S*))?",params))
- if support.has_key("CHANMODES"): support["CHANMODES"]=support["CHANMODES"].split(",")
- if support.has_key("PREFIX"):
- matches=re.findall("\\((.*)\\)(.*)",support["PREFIX"])
- if matches: support["PREFIX"]=matches[0]
- else: del support["PREFIX"] # Might as well delete the info if it doesn't match expected pattern
- self.supports.update(support)
- if "serv005" in dir(self) and type(self.serv005)==list:
- self.serv005.append(params)
- else: self.serv005=[params]
- elif cmd==8: # Channel Modes
- self.identity.snomask=params.lstrip("+")
- if "s" not in self.identity.modes: self.snomask=""
- elif cmd==221: # Channel Modes
- self.identity.modes=(params if params else extinfo).lstrip("+")
- if "s" not in self.identity.modes: self.snomask=""
- elif cmd==251: self.netstats=extinfo
- elif cmd==252: self.opcount=int(params)
- elif cmd==254: self.chancount=int(params)
- elif cmd==311: # WHOIS data
- pass
- elif cmd==321: # Start LIST
- self.chanlistbegin=(params,extinfo)
- self.chanlist={}
- elif cmd==322: # LIST item
- (chan,pop)=params.split(" ",1)
- self.chanlist[chan]=(pop,extinfo)
- elif cmd==323: self.chanlistend=extinfo # End of LIST
- elif cmd==324: # Channel Modes
- modeparams=params.split()
- channame=modeparams.pop(0)
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- setmodes=modeparams.pop(0)
- for mode in setmodes:
- if mode=="+": continue
- elif mode in self.supports["CHANMODES"][2]:
- param=modeparams.pop(0)
- channel.modes[mode]=param
- elif mode in self.supports["CHANMODES"][3]: channel.modes[mode]=True
- elif cmd==329: # Channel created
- channame, created=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- channel.created=int(created)
- elif cmd==332: # Channel Topic
- channame=params
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- channel.topic=extinfo
- elif cmd==333: # Channel Topic info
- (channame,nick,dt)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- channel.topicsetby=nick
- channel.topictime=dt
- elif cmd==346: # Invite
- (channame,invite,nick,invtime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if channel.modes.has_key("I"):
- if invite.lower() not in [m.lower() for (m, b, t) in channel.modes["I"]]: channel.modes["I"].append((invite,nick,int(invtime)))
- else: channel.modes["I"]=[(invite,nick,int(invtime))]
- elif cmd==348: # Ban Exception
- (channame,exception,nick,exctime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if channel.modes.has_key("e"):
- if exception.lower() not in [m.lower() for (m, b, t) in channel.modes["e"]]: channel.modes["e"].append((exception, nick, int(exctime)))
- else: channel.modes["e"]=[(exception, nick, int(exctime))]
- elif cmd==352: # WHO reply
- (channame,ident,host,serv,nick,flags)=params.split()
- (hops,realname)=extinfo.split(" ",1)
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- user=self.user(nick)
- if user.nick!=nick: user.nick=nick
- user.hops=hops
- user.realname=realname
- user.idnt=ident
- user.host=host
- user.server=serv
- user.away="G" in flags
- user.ircop="*" in flags
- if user not in channel.users: channel.users.append(user)
- if channel not in user.channels: user.channels.append(channel)
- for (mode, prefix) in zip(*self.supports["PREFIX"]):
- if prefix in flags:
- if mode in channel.modes.keys() and user not in channel.modes[mode]:
- channel.modes[mode].append(user)
- elif mode not in channel.modes.keys():
- channel.modes[mode]=[user]
- elif cmd==353: # NAMES reply
- (devnull,channame)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if self.supports.has_key("PREFIX"):
- names=re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)",extinfo)
- else: names=[("",name) for name in extinfo.split()] # Still put it into tuple form for compatibility in the next structure
- for (symbs,nick) in names:
- user=self.user(nick)
- if user.nick!=nick: user.nick=nick
- if channel not in user.channels: user.channels.append(channel)
- if user not in channel.users: channel.users.append(user)
- if self.supports.has_key("PREFIX"):
- for symb in symbs:
- mode=self.supports["PREFIX"][0][self.supports["PREFIX"][1].index(symb)]
- if not channel.modes.has_key(mode):
- channel.modes[mode]=[user]
- elif user not in channel.modes[mode]: channel.modes[mode].append(user)
- elif cmd==367: # Channel Ban
- (channame,ban,nick,bantime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if "b" in channel.modes.keys():
- if ban.lower() not in [m.lower() for (m, b, t) in channel.modes["b"]]:
- channel.modes["b"].append((ban,nick,int(bantime)))
- else: channel.modes["b"]=[(ban,nick,int(bantime))]
- elif cmd==372: self.motd.append(extinfo) # MOTD item
- elif cmd==375: # Begin MOTD
- self.motdgreet=extinfo
- self.motd=[]
- elif cmd==376: self.motdend=extinfo # End of MOTD
- elif cmd==386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
- (channame,admin)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- user=self.user(owner)
- if user.nick!=owner: user.nick=owner
- if channel.modes.has_key("q"):
- if user not in channel.modes["q"]: channel.modes["q"].append(user)
- else: channel.modes["q"]=[user]
- elif cmd==388 and "a" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
- (channame,admin)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- user=self.user(admin)
- if user.nick!=admin: user.nick=admin
- if channel.modes.has_key("a"):
- if user not in channel.modes["a"]: channel.modes["a"].append(user)
- else: channel.modes["a"]=[user]
- elif cmd=="NICK":
- user=self.user(origin)
- modules=[]
- for channel in user.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- self.event(modules, line, parsed)
- newnick=extinfo if len(extinfo) else target
- #print user, newnick
- ### Need to check if newnick is still listed
- for u in self.users:
- #print u
- #print [u.nick, newnick, u.nick.lower(), newnick.lower()]
- if u.nick.lower()==newnick.lower():
- with self.loglock:
- print >>self.log, "%s *** Orphaned user %s!%s@%s was removed when %s!%s@%s changed his/her nick to %s."%(timestamp, u.nick, u.idnt, u.host, user.nick, user.idnt, user.host, newnick)
- self.log.flush()
- self.users.remove(u)
- for channel in self.channels:
- if u in channel.users:
- channel.users.remove(u)
- user.nick=newnick
- elif cmd=="JOIN":
- channame=target if len(target) else extinfo
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
-
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
-
- if user==self.identity: # This means the bot is entering the room,
- # and will reset all the channel data, on the assumption that such data may have changed.
- # Also, the bot must request modes
- channel.topic=""
- channel.topicmod=""
- channel.modes={}
- channel.users=[]
- self.raw("MODE %(channame)s" % vars())
- self.raw("WHO %(channame)s" % vars())
- self.raw("MODE %s :%s" % (channame, self.supports["CHANMODES"][0]))
- if channel not in user.channels: user.channels.append(channel)
- if user not in channel.users: channel.users.append(user)
- elif cmd=="KICK":
- kicker=self.user(origin)
- if kicker.nick!=origin: kicker.nick=origin
- if kicker.idnt!=ident: kicker.idnt=ident
- if kicker.host!=host: kicker.host=host
-
- kicked=self.user(params)
- if kicked.nick!=params: kicked.nick=params
-
- channel=self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
- if channel in kicked.channels: kicked.channels.remove(channel)
- if kicked in channel.users: channel.users.remove(kicked)
- if self.supports.has_key("PREFIX"):
- for mode in self.supports["PREFIX"][0]:
- if channel.modes.has_key(mode) and kicked in channel.modes[mode]: channel.modes[mode].remove(kicked)
- #if not len(chanobj.users): #Let's remove this channel
- # del self.channels[target.lower()]
- if all([kicked not in c.users for c in self.identity.channels]):
- with self.loglock:
- print >>self.log, "%s *** User %s!%s@%s was orphaned when being kicked from %s."%(timestamp, kicked.nick, kicked.idnt, kicked.host, channel.name)
- self.log.flush()
- elif cmd=="PART":
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
-
- channel=self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
-
- if channel in user.channels: user.channels.remove(channel)
- if user in channel.users: channel.users.remove(user)
- if self.supports.has_key("PREFIX"):
- for mode in self.supports["PREFIX"][0]:
- if channel.modes.has_key(mode) and user in channel.modes[mode]: channel.modes[mode].remove(user)
- if all([user not in c.users for c in self.identity.channels]):
- with self.loglock:
- print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name)
- self.log.flush()
- elif cmd=="QUIT":
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
- channels=list(user.channels)
- for channel in user.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- self.event(modules, line, parsed)
- for channel in channels:
- channel.lock.acquire(True)
- for channel in user.channels:
- if user in channel.users: channel.users.remove(user)
- if self.supports.has_key("PREFIX"):
- for mode in self.supports["PREFIX"][0]:
- if channel.modes.has_key(mode) and user in channel.modes[mode]: channel.modes[mode].remove(user)
- #if not len(chanobj.users): #Let's remove this channel
- # del self.channels[chan]
- # On second thought, no, since we may want to save certain info
- user.channels=[]
- for channel in channels:
- channel.lock.release()
- print >>self.log, "%s *** User %s!%s@%s was orphaned when quitting IRC."%(timestamp, user.nick, user.idnt, user.host)
- elif cmd=="MODE":
- if target[0] in self.supports["CHANTYPES"]:
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
-
- channel=self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- with channel.lock:
- if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
-
- modeparams=params.split()
- setmodes=modeparams.pop(0)
- modeset="+"
- for mode in setmodes:
- if mode in "+-": modeset=mode
- else:
- if mode in self.supports["CHANMODES"][0]:
- param=modeparams.pop(0)
- if modeset=="+":
- if channel.modes.has_key(mode):
- if param.lower() not in [mask.lower() for (mask, setby, settime) in channel.modes[mode]]: channel.modes[mode].append((param, origin, int(time.time())))
- else: channel.modes[mode]=[(param, origin, int(time.time()))]
- else:
- if mode in channel.modes.keys():
- if mode=="b": ### Inspircd mode is case insentive when unsetting the mode
- masks=[mask.lower() for (mask, setby, settime) in channel.modes[mode]]
- if param.lower() in masks:
- index=masks.index(param.lower())
- #print "Index: %d"%index
- del channel.modes[mode][index]
- else:
- masks=[mask for (mask, setby, settime) in channel.modes[mode]]
- if param in masks:
- index=masks.index(param)
- del channel.modes[mode][index]
- elif mode in self.supports["CHANMODES"][1]:
- param=modeparams.pop(0)
- if modeset=="+": channel.modes[mode]=param
- else: channel.modes[mode]=None
- elif mode in self.supports["CHANMODES"][2]:
- if modeset=="+":
- param=modeparams.pop(0)
- channel.modes[mode]=param
- else: channel.modes[mode]=None
- elif mode in self.supports["CHANMODES"][3]:
- if modeset=="+": channel.modes[mode]=True
- else: channel.modes[mode]=False
- elif self.supports.has_key("PREFIX") and mode in self.supports["PREFIX"][0]:
- modenick=modeparams.pop(0)
- modeuser=self.user(modenick)
- if modeuser.nick!=modenick: modeuser.nick=modenick
- if modeset=="+":
- if channel.modes.has_key(mode) and modeuser not in channel.modes[mode]: channel.modes[mode].append(modeuser)
- if not channel.modes.has_key(mode): channel.modes[mode]=[modeuser]
- elif channel.modes.has_key(mode) and modeuser in channel.modes[mode]: channel.modes[mode].remove(modeuser)
- else:
- user=self.user(target)
- modeparams=(params if params else extinfo).split()
- setmodes=modeparams.pop(0)
- modeset="+"
- for mode in setmodes:
- if mode in "+-":
- modeset=mode
- continue
- if modeset=="+":
- if mode not in user.modes: user.modes+=mode
- if mode=="s" and len(modeparams):
- snomask=modeparams.pop(0)
- for snomode in snomask:
- if snomode in "+-":
- snomodeset=snomode
- continue
- if snomodeset=="+" and snomode not in user.snomask: user.snomask+=snomode
- if snomodeset=="-" and snomode in user.snomask: user.snomask=user.snomask.replace(snomode,"")
- if modeset=="-":
- if mode in user.modes: user.modes=user.modes.replace(mode,"")
- if mode=="s": user.snomask=""
- elif cmd=="TOPIC":
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
-
- channel=self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
-
- with channel.lock:
- if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
- channel.topic=extinfo
- elif cmd=="PRIVMSG":
- user=self.user(origin)
- if user.nick!=origin: user.nick=origin
- if user.idnt!=ident: user.idnt=ident
- if user.host!=host: user.host=host
-
- if target[0] in self.supports["CHANTYPES"]:
- channel=self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
- elif target[0]=="$":
- pass ### Server message -- Not implemented
- else:
- targetuser=self.user(target)
- if targetuser.nick!=target: targetuser.nick=target ### Server seems to have changed the idea of the case of the nickname
-
- ### CTCP handling
- ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo)
- if ctcp:
- (ctcptype,ext)=ctcp[0]
- if ctcptype.upper()=="VERSION":
- user.ctcpreply("VERSION",self.ctcpversion())
- if ctcptype.upper()=="TIME":
- tformat=time.ctime()
- tz=time.tzname[0]
- user.ctcpreply("TIME","%(tformat)s %(tz)s" % vars())
- if ctcptype.upper()=="PING": user.ctcpreply("PING","%(ext)s" % vars())
- if ctcptype.upper()=="FINGER": user.ctcpreply("FINGER","%(ext)s" % vars())
- elif cmd==910: # Channel Access List
- (channame,mask,setby,settime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if "w" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["w"]]: channel.modes["w"].append((mask, setby, int(settime)))
- else: channel.modes["w"]=[(mask, setby, int(settime))]
- elif cmd==941: # Channel spamfilter List
- (channame,mask,setby,settime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if "g" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["g"]]: channel.modes["g"].append((mask, setby, int(settime)))
- else: channel.modes["g"]=[(mask, setby, int(settime))]
- elif cmd==954: # Channel spamfilter List
- (channame,mask,setby,settime)=params.split()
- channel=self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name!=channame: channel.name=channame ### Server seems to have changed the idea of the case of the channel name
- if "X" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["X"]]: channel.modes["X"].append((mask, setby, int(settime)))
- else: channel.modes["X"]=[(mask, setby, int(settime))]
- elif cmd in (495, 384, 385, 386, 468, 470, 366, 315, 482, 484, 953, 368, 482, 349, 940, 911, 489, 490, 492, 520, 530): # Channels which appear in params
- for param in params.split():
- if len(param) and param[0] in self.supports["CHANTYPES"]:
- channel=self.channel(param)
- self.event("onRecv", channel.modules, line=line, data=parsed)
-
- else: ### Line does NOT match ":src cmd target params :extinfo"
- self.event("onRecv", self.modules, line=line, data=None)
- except SystemExit: ### Connection lost normally.
- pass
- except socket.error: ### Connection lost due to either ping timeout or connection reset by peer. Not a fatal error.
- with self.loglock:
- exc,excmsg,tb=sys.exc_info()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
- self.log.flush()
- except: ### Unknown exception, treated as FATAL. Try to quit IRC and terminate thread with exception.
- ### Quit with a (hopefully) useful quit message, or die trying.
- try:
- self.quit("%s" % traceback.format_exc().rstrip().split("\n")[-1])
- except: pass
- raise
- finally: ### Post-connection operations after connection is lost, and must be executed, even if exception occurred.
- with self.lock:
- modules=list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules: modules.append(module)
- self.event("onDisconnect", self.modules)
-
- ### Tell outgoing thread to quit.
- self.outgoing.put("quit")
-
- ### Wait until the outgoing thread dies.
- outgoingthread.join()
-
- self.connected=False
- self.registered=False
-
- try: self.connection.close()
- except: pass
-
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection Terminated." % vars()
- self.log.flush()
-
- if self.quitexpected or not self.autoreconnect: sys.exit()
-
- ### If we make it to this point, then it is because connection was lost unexpectedly, and will attempt to reconnect if self.autoreconnect is True.
- time.sleep(self.retrysleep)
-
- except SystemExit:
- pass
-
- except: ### Print exception to log file
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
- self.log.flush()
- sys.exit()
-
- finally:
- with self.loglock:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s ### Log session ended" % vars()
- self.log.flush()
-
- Thread.__init__(self) ### Makes thread restartable
-
- def __repr__(self):
- server=self.server
- if self.ipv6 and ":" in server:
- server="[%s]"%server
- port=self.port
- if self.identity:
- nick=self.identity.nick
- ident=self.identity.idnt if self.identity.idnt else "*"
- host=self.identity.host if self.identity.host else "*"
- else:
- nick="*"
- ident="*"
- host="*"
- if self.ssl and self.ipv6:
- protocol="ircs6"
- elif self.ssl:
- protocol="ircs"
- elif self.ipv6:
- protocol="irc6"
- else:
- protocol="irc"
- return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
- #else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals()
- def quit(self, msg="", origin=None):
- with self.lock:
- self.quitexpected=True
- if self.connected:
- if len(msg): self.raw("QUIT :%(msg)s" % vars(), origin=origin)
- else: self.raw("QUIT", origin=origin)
- def ctcpversion(self):
- reply=[]
- ### Prepare reply for module
- reply.append("%(__name__)s %(__version__)s, %(__author__)s" % vars(self))
-
- ### Prepare reply for Python and OS versions
- pyver=sys.version.split("\n")
- pyver[0]="Python "+pyver[0]
- reply.extend(pyver)
- reply.extend(platform.platform().split("\n"))
- ### Prepare reply for extension modules
- for module in self.modules:
- try:
- r="%(__name__)s %(__version__)s" % vars(module)
- if "__extinfo__" in vars(module): r+=", %(__extinfo__)s" % vars()
- reply.append(r)
- except:
- pass
- return reduce(lambda x, y: "%s; %s" % (x,y),reply)
-
- def raw(self, line, origin=None):
- self.outgoing.put((line, origin))
-
-
- def user(self,nick):
- users=[user for user in self.users if user.nick.lower()==nick.lower()]
- if len(users):
- return users[0]
- else:
- user=User(nick,self)
- self.users.append(user)
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- with self.loglock:
- print >>self.log, "%s *** User %s created."%(timestamp, nick)
- self.log.flush()
- return user
- def channel(self,name):
- channels=[chan for chan in self.channels if name.lower()==chan.name.lower()]
- if len(channels):
- return channels[0]
- else:
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- chan=Channel(name,self)
- self.channels.append(chan)
- with self.loglock:
- print >>self.log, "%s *** Channel %s created."%(timestamp, name)
- self.log.flush()
- return chan
+class Connection(Thread):
+ def __init__(self, server, nick="ircbot", ident="python", realname="Python IRC Library", passwd=None, port=None, ipv6=False, ssl=False, autoreconnect=True, log=sys.stderr, timeout=300, retrysleep=5, maxretries=15, onlogin=None):
+ self.__name__ = "pyIRC"
+ self.__version__ = "1.0.0rc2"
+ self.__author__ = "Brian Sherson"
+ self.__date__ = "August 26, 2013"
+
+ if port is None:
+ self.port = 6667 if not ssl else 6697
+ else:
+ self.port = port
+
+ if type(nick) in (str, unicode):
+ self.nick = [nick]
+ elif type(nick) in (list, tuple):
+ self.nick = nick
+
+ self.realname = realname
+ self.idnt = ident
+ self.passwd = passwd
+ self.server = server
+ self.ssl = ssl
+ self.ipv6 = ipv6
+
+ self.connected = False
+ self.registered = False
+ self.connection = None
+
+ self.autoreconnect = autoreconnect
+ self.maxretries = maxretries
+ self.timeout = timeout
+ self.retrysleep = retrysleep
+
+ self.quitexpected = False
+ self.log = log
+
+ self.modules = []
+ self.trusted = []
+
+ ### Initialize IRC environment variables
+ self.motdgreet = ""
+ self.motd = []
+ self.identity = None
+ self.users = []
+ self.channels = []
+ self.supports = {}
+
+ self.lock = Lock()
+ self.loglock = Lock()
+ self.sendlock = Lock()
+ self.outgoing = Queue.Queue()
+
+ Thread.__init__(self)
+
+ def event(self, method, modlist, exceptions=False, **params):
+ #print method, modlist
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in time.localtime()[0:6]])
+ for module in modlist:
+ #print module, dir(module)
+ #if modlist is not self.modules and module in self.modules: continue
+ if method in dir(module) and callable(getattr(module, method)):
+ try:
+ getattr(module, method)(self, **params)
+ except:
+ with self.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.log, "%(timestamp)s !!! Exception in module %(module)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
+ self.log.flush()
+ if exceptions: # If set to true, we raise the exception.
+ raise
+
+ def addModule(self, module, trusted=False, **params):
+ if module in self.modules:
+ raise BaseException("Module already added.")
+ with self.lock:
+ self.event("onModuleAdd", [module], exceptions=True, **params)
+ self.modules.append(module)
+ if trusted:
+ self.trusted.append(module)
+
+ def insertModule(self, index, module, trusted=False, **params):
+ if module in self.modules:
+ raise BaseException("Module already added.")
+ with self.lock:
+ self.event("onModuleAdd", [module], exceptions=True, **params)
+ self.modules.insert(index, module)
+ if trusted:
+ self.trusted.append(module)
+
+ def rmModule(self, module, **params):
+ with self.lock:
+ self.modules.remove(module)
+ self.event("onModuleRem", [module], exceptions=True, **params)
+ if module in self.trusted:
+ self.trusted.remove(module)
+
+ def run(self):
+ self.quitexpected = False
+ outgoingthread = None
+ server = self.server
+ if self.ipv6 and ":" in server:
+ server = "[%s]"%server
+ port = self.port
+ try:
+ modules = list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ with self.lock:
+ self.event("onLogOpen", modules)
+
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s ### Log session started" % vars()
+ self.log.flush()
+
+ attempt = 1
+ while True: # An entire connection lives within this while loop. When the connection fails, will try to reestablish, unless self.autoreconnect is set to False.
+ while True: # Enter retry loop
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars()
+ self.log.flush()
+
+ with self.lock:
+ self.event("onConnectAttempt", self.modules)
+
+ try:
+ if self.ssl:
+ s = socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM)
+ s.settimeout(self.timeout)
+ self.connection = ssl.wrap_socket(s, cert_reqs=ssl.CERT_NONE)
+ else:
+ self.connection = socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM)
+ self.connection.connect((self.server, self.port, 0, 0) if self.ipv6 else (self.server, self.port))
+ self.connected = True
+ self.connection.settimeout(self.timeout)
+
+ ### Setting up thread responsible for sending data back to IRC server.
+ outgoingthread = Outgoing(self)
+ outgoingthread.daemon = True
+ outgoingthread.start()
+
+ ### Run onConnect on all modules to signal connection was established.
+ modules = list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ self.event("onConnect", modules)
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s established." % vars()
+ self.log.flush()
+ break
+ except socket.error:
+ with self.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
+ self.log.flush()
+
+ if self.quitexpected:
+ sys.exit()
+ if attempt < self.maxretries or self.maxretries == -1:
+ time.sleep(self.retrysleep)
+ attempt += 1
+ else:
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars()
+ self.log.flush()
+ break
+
+ ### Connection succeeded
+ try:
+ ### Attempt initial registration.
+ nick = self.nick[0]
+ trynick = 0
+ if self.passwd:
+ self.raw("PASS :%(passwd)s" % vars(self))
+ self.raw("NICK :%(nick)s" % vars())
+ self.raw("USER %(idnt)s * * :%(realname)s" % vars(self))
+
+ ### Initialize buffers
+ linebuf = []
+ readbuf = ""
+
+ while True: # Main loop of IRC connection.
+ while len(linebuf) == 0: # Need Moar Data
+ read = self.connection.recv(512)
+
+ ### If read was empty, connection is terminated.
+ if read == "":
+ sys.exit()
+
+ ### If read was successful, parse away!
+ readbuf += read
+ lastlf = readbuf.rfind("\n")
+ if lastlf >= 0:
+ linebuf.extend(string.split(readbuf[0:lastlf],
+ "\n"))
+ readbuf = readbuf[lastlf+1:]
+
+ line = string.rstrip(linebuf.pop(0))
+
+ ### If received PING, then just pong back transparently.
+ ping = re.findall("^PING :?(.*)$", line)
+ if len(ping):
+ with self.lock:
+ self.connection.send("PONG :%s\n" % ping[0])
+ continue
+
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >> self.log, "%(timestamp)s <<< %(line)s" % vars()
+ self.log.flush()
+
+ ### Attempts to match against pattern ":src cmd target params :extinfo"
+ matches = re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line)
+
+ ### We have a match!
+ if len(matches):
+ parsed = (origin, ident, host, cmd, target, params, extinfo) = matches[0]
+ if re.match("^\\d+$", cmd):
+ cmd = int(cmd) # Code is a numerical response
+ else:
+ cmd = cmd.upper()
+
+ if not self.registered:
+ if type(cmd) == int and target != "*": # Registration complete!
+ with self.lock:
+ self.registered = True
+ self.identity = self.user(target)
+ self.serv = origin
+
+ modules = list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ self.event("onRegistered", modules)
+
+ elif cmd == 433 and target == "*": # Server reports nick taken, so we need to try another.
+ trynick += 1
+ (q, s) = divmod(trynick, len(self.nick))
+ nick = self.nick[s]
+ if q > 0:
+ nick += str(q)
+ self.raw("NICK :%(nick)s" % vars())
+ if not self.registered: # Registration is not yet complete
+ continue
+
+ ### Major codeblock here! Track IRC state.
+ ### Send line to modules first
+ with self.lock:
+ self.event("onRecv", self.modules, line=line, data=parsed)
+ if cmd == 1:
+ self.welcome = extinfo # Welcome message
+ elif cmd == 2:
+ self.hostinfo = extinfo # Your Host
+ elif cmd == 3:
+ self.servinfo = extinfo # Server Created
+ elif cmd == 4:
+ self.serv004 = params # What is this code?
+ elif cmd == 5: # Server Supports
+ support = dict(re.findall("([A-Za-z0-9]+)(?:=(\\S*))?", params))
+ if "CHANMODES" in support:
+ support["CHANMODES"] = support["CHANMODES"].split(",")
+ if "PREFIX" in support:
+ matches = re.findall("\\((.*)\\)(.*)", support["PREFIX"])
+ if matches:
+ support["PREFIX"] = matches[0]
+ else:
+ del support["PREFIX"] # Might as well delete the info if it doesn't match expected pattern
+ self.supports.update(support)
+ if "serv005" in dir(self) and type(self.serv005) == list:
+ self.serv005.append(params)
+ else:
+ self.serv005 = [params]
+ elif cmd == 8: # Channel Modes
+ self.identity.snomask = params.lstrip("+")
+ if "s" not in self.identity.modes:
+ self.snomask = ""
+ elif cmd == 221: # Channel Modes
+ self.identity.modes = (params if params else extinfo).lstrip("+")
+ if "s" not in self.identity.modes:
+ self.snomask = ""
+ elif cmd == 251:
+ self.netstats = extinfo
+ elif cmd == 252:
+ self.opcount = int(params)
+ elif cmd == 254:
+ self.chancount = int(params)
+ elif cmd == 311: # WHOIS data
+ pass
+ elif cmd == 321: # Start LIST
+ self.chanlistbegin = (params, extinfo)
+ self.chanlist = {}
+ elif cmd == 322: # LIST item
+ (chan, pop) = params.split(" ", 1)
+ self.chanlist[chan] = (pop, extinfo)
+ elif cmd == 323:
+ self.chanlistend = extinfo # End of LIST
+ elif cmd == 324: # Channel Modes
+ modeparams = params.split()
+ channame = modeparams.pop(0)
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ setmodes = modeparams.pop(0)
+ for mode in setmodes:
+ if mode == "+":
+ continue
+ elif mode in self.supports["CHANMODES"][2]:
+ param = modeparams.pop(0)
+ channel.modes[mode] = param
+ elif mode in self.supports["CHANMODES"][3]:
+ channel.modes[mode] = True
+ elif cmd == 329: # Channel created
+ channame, created = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ channel.created = int(created)
+ elif cmd == 332: # Channel Topic
+ channame = params
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ channel.topic = extinfo
+ elif cmd == 333: # Channel Topic info
+ (channame, nick, dt) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ channel.topicsetby = nick
+ channel.topictime = dt
+ elif cmd == 346: # Invite
+ (channame, invite, nick, invtime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "I" in channel.modes:
+ if invite.lower() not in [m.lower() for (m, b, t) in channel.modes["I"]]:
+ channel.modes["I"].append((invite, nick, int(invtime)))
+ else:
+ channel.modes["I"] = [(invite, nick, int(invtime))]
+ elif cmd == 348: # Ban Exception
+ (channame, exception, nick, exctime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "e" in channel.modes:
+ if exception.lower() not in [m.lower() for (m, b, t) in channel.modes["e"]]:
+ channel.modes["e"].append((exception, nick, int(exctime)))
+ else:
+ channel.modes["e"] = [(exception, nick, int(exctime))]
+ elif cmd == 352: # WHO reply
+ (channame, ident, host, serv, nick, flags) = params.split()
+ (hops, realname) = extinfo.split(" ", 1)
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ user = self.user(nick)
+ if user.nick != nick:
+ user.nick = nick
+ user.hops = hops
+ user.realname = realname
+ user.idnt = ident
+ user.host = host
+ user.server = serv
+ user.away = "G" in flags
+ user.ircop = "*" in flags
+ if user not in channel.users:
+ channel.users.append(user)
+ if channel not in user.channels:
+ user.channels.append(channel)
+ for (mode, prefix) in zip(*self.supports["PREFIX"]):
+ if prefix in flags:
+ if mode in channel.modes.keys() and user not in channel.modes[mode]:
+ channel.modes[mode].append(user)
+ elif mode not in channel.modes.keys():
+ channel.modes[mode] = [user]
+ elif cmd == 353: # NAMES reply
+ (devnull, channame) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "PREFIX" in self.supports:
+ names = re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)", extinfo)
+ else:
+ names = [("", name) for name in extinfo.split()] # Still put it into tuple form for compatibility in the next structure
+ for (symbs, nick) in names:
+ user = self.user(nick)
+ if user.nick != nick:
+ user.nick = nick
+ if channel not in user.channels:
+ user.channels.append(channel)
+ if user not in channel.users:
+ channel.users.append(user)
+ if "PREFIX" in self.supports:
+ for symb in symbs:
+ mode = self.supports["PREFIX"][0][self.supports["PREFIX"][1].index(symb)]
+ if mode not in channel.modes:
+ channel.modes[mode] = [user]
+ elif user not in channel.modes[mode]:
+ channel.modes[mode].append(user)
+ elif cmd == 367: # Channel Ban
+ (channame, ban, nick, bantime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "b" in channel.modes.keys():
+ if ban.lower() not in [m.lower() for (m, b, t) in channel.modes["b"]]:
+ channel.modes["b"].append((ban, nick, int(bantime)))
+ else:
+ channel.modes["b"] = [(ban, nick, int(bantime))]
+ elif cmd == 372:
+ self.motd.append(extinfo) # MOTD item
+ elif cmd == 375: # Begin MOTD
+ self.motdgreet = extinfo
+ self.motd = []
+ elif cmd == 376:
+ self.motdend = extinfo # End of MOTD
+ elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
+ (channame, admin) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ user = self.user(owner)
+ if user.nick != owner:
+ user.nick = owner
+ if "q" in channel.modes:
+ if user not in channel.modes["q"]:
+ channel.modes["q"].append(user)
+ else:
+ channel.modes["q"] = [user]
+ elif cmd == 388 and "a" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
+ (channame, admin) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ user = self.user(admin)
+ if user.nick != admin:
+ user.nick = admin
+ if "a" in channel.modes:
+ if user not in channel.modes["a"]:
+ channel.modes["a"].append(user)
+ else:
+ channel.modes["a"] = [user]
+ elif cmd == "NICK":
+ user = self.user(origin)
+ modules = []
+ for channel in user.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ self.event(modules, line, parsed)
+ newnick = extinfo if len(extinfo) else target
+ #print user, newnick
+ ### Need to check if newnick is still listed
+ for u in self.users:
+ #print u
+ #print [u.nick, newnick, u.nick.lower(), newnick.lower()]
+ if u.nick.lower() == newnick.lower():
+ with self.loglock:
+ print >>self.log, "%s *** Orphaned user %s!%s@%s was removed when %s!%s@%s changed his/her nick to %s."%(timestamp, u.nick, u.idnt, u.host, user.nick, user.idnt, user.host, newnick)
+ self.log.flush()
+ self.users.remove(u)
+ for channel in self.channels:
+ if u in channel.users:
+ channel.users.remove(u)
+ user.nick = newnick
+ elif cmd == "JOIN":
+ channame = target if len(target) else extinfo
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+
+ if user == self.identity: # This means the bot is entering the room,
+ # and will reset all the channel data, on the assumption that such data may have changed.
+ # Also, the bot must request modes
+ channel.topic = ""
+ channel.topicmod = ""
+ channel.modes = {}
+ channel.users = []
+ self.raw("MODE %(channame)s" % vars())
+ self.raw("WHO %(channame)s" % vars())
+ self.raw("MODE %s :%s" % (channame, self.supports["CHANMODES"][0]))
+ if channel not in user.channels:
+ user.channels.append(channel)
+ if user not in channel.users:
+ channel.users.append(user)
+ elif cmd == "KICK":
+ kicker = self.user(origin)
+ if kicker.nick != origin:
+ kicker.nick = origin
+ if kicker.idnt != ident:
+ kicker.idnt = ident
+ if kicker.host != host:
+ kicker.host = host
+
+ kicked = self.user(params)
+ if kicked.nick != params:
+ kicked.nick = params
+
+ channel = self.channel(target)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != target:
+ channel.name = target # Server seems to have changed the idea of the case of the channel name
+ if channel in kicked.channels:
+ kicked.channels.remove(channel)
+ if kicked in channel.users:
+ channel.users.remove(kicked)
+ if "PREFIX" in self.supports:
+ for mode in self.supports["PREFIX"][0]:
+ if mode in channel.modes and kicked in channel.modes[mode]:
+ channel.modes[mode].remove(kicked)
+ #if not len(chanobj.users): #Let's remove this channel
+ # del self.channels[target.lower()]
+ if all([kicked not in c.users for c in self.identity.channels]):
+ with self.loglock:
+ print >>self.log, "%s *** User %s!%s@%s was orphaned when being kicked from %s."%(timestamp, kicked.nick, kicked.idnt, kicked.host, channel.name)
+ self.log.flush()
+ elif cmd == "PART":
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+
+ channel = self.channel(target)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != target:
+ channel.name = target # Server seems to have changed the idea of the case of the channel name
+
+ if channel in user.channels:
+ user.channels.remove(channel)
+ if user in channel.users:
+ channel.users.remove(user)
+ if "PREFIX" in self.supports:
+ for mode in self.supports["PREFIX"][0]:
+ if mode in channel.modes and user in channel.modes[mode]:
+ channel.modes[mode].remove(user)
+ if all([user not in c.users for c in self.identity.channels]):
+ with self.loglock:
+ print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name)
+ self.log.flush()
+ elif cmd == "QUIT":
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+ channels = list(user.channels)
+ for channel in user.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ self.event(modules, line, parsed)
+ for channel in channels:
+ channel.lock.acquire(True)
+ for channel in user.channels:
+ if user in channel.users:
+ channel.users.remove(user)
+ if "PREFIX" in self.supports:
+ for mode in self.supports["PREFIX"][0]:
+ if mode in channel.modes and user in channel.modes[mode]:
+ channel.modes[mode].remove(user)
+ #if not len(chanobj.users): #Let's remove this channel
+ # del self.channels[chan]
+ # On second thought, no, since we may want to save certain info
+ user.channels = []
+ for channel in channels:
+ channel.lock.release()
+ print >>self.log, "%s *** User %s!%s@%s was orphaned when quitting IRC."%(timestamp, user.nick, user.idnt, user.host)
+ elif cmd == "MODE":
+ if target[0] in self.supports["CHANTYPES"]:
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+
+ channel = self.channel(target)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ with channel.lock:
+ if channel.name != target:
+ channel.name = target # Server seems to have changed the idea of the case of the channel name
+
+ modeparams = params.split()
+ setmodes = modeparams.pop(0)
+ modeset = "+"
+ for mode in setmodes:
+ if mode in "+-":
+ modeset = mode
+ else:
+ if mode in self.supports["CHANMODES"][0]:
+ param = modeparams.pop(0)
+ if modeset == "+":
+ if mode in channel.modes:
+ if param.lower() not in [mask.lower() for (mask, setby, settime) in channel.modes[mode]]:
+ channel.modes[mode].append((param, origin, int(time.time())))
+ else:
+ channel.modes[mode] = [(param, origin, int(time.time()))]
+ else:
+ if mode in channel.modes.keys():
+ if mode == "b": # Inspircd mode is case insentive when unsetting the mode
+ masks = [mask.lower() for (mask, setby, settime) in channel.modes[mode]]
+ if param.lower() in masks:
+ index = masks.index(param.lower())
+ #print "Index: %d"%index
+ del channel.modes[mode][index]
+ else:
+ masks = [mask for (mask, setby, settime) in channel.modes[mode]]
+ if param in masks:
+ index = masks.index(param)
+ del channel.modes[mode][index]
+ elif mode in self.supports["CHANMODES"][1]:
+ param = modeparams.pop(0)
+ if modeset == "+":
+ channel.modes[mode] = param
+ else:
+ channel.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][2]:
+ if modeset == "+":
+ param = modeparams.pop(0)
+ channel.modes[mode] = param
+ else:
+ channel.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][3]:
+ if modeset == "+":
+ channel.modes[mode] = True
+ else:
+ channel.modes[mode] = False
+ elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
+ modenick = modeparams.pop(0)
+ modeuser = self.user(modenick)
+ if modeuser.nick != modenick:
+ modeuser.nick = modenick
+ if modeset == "+":
+ if mode in channel.modes and modeuser not in channel.modes[mode]:
+ channel.modes[mode].append(modeuser)
+ if mode not in channel.modes:
+ channel.modes[mode] = [modeuser]
+ elif mode in channel.modes and modeuser in channel.modes[mode]:
+ channel.modes[mode].remove(modeuser)
+ else:
+ user = self.user(target)
+ modeparams = (params if params else extinfo).split()
+ setmodes = modeparams.pop(0)
+ modeset = "+"
+ for mode in setmodes:
+ if mode in "+-":
+ modeset = mode
+ continue
+ if modeset == "+":
+ if mode not in user.modes:
+ user.modes += mode
+ if mode == "s" and len(modeparams):
+ snomask = modeparams.pop(0)
+ for snomode in snomask:
+ if snomode in "+-":
+ snomodeset = snomode
+ continue
+ if snomodeset == "+" and snomode not in user.snomask:
+ user.snomask += snomode
+ if snomodeset == "-" and snomode in user.snomask:
+ user.snomask = user.snomask.replace(snomode, "")
+ if modeset == "-":
+ if mode in user.modes:
+ user.modes = user.modes.replace(mode, "")
+ if mode == "s":
+ user.snomask = ""
+ elif cmd == "TOPIC":
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+
+ channel = self.channel(target)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+
+ with channel.lock:
+ if channel.name != target:
+ channel.name = target # Server seems to have changed the idea of the case of the channel name
+ channel.topic = extinfo
+ elif cmd == "PRIVMSG":
+ user = self.user(origin)
+ if user.nick != origin:
+ user.nick = origin
+ if user.idnt != ident:
+ user.idnt = ident
+ if user.host != host:
+ user.host = host
+
+ if target[0] in self.supports["CHANTYPES"]:
+ channel = self.channel(target)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != target:
+ channel.name = target # Server seems to have changed the idea of the case of the channel name
+ elif target[0] == "$":
+ pass # Server message -- Not implemented
+ else:
+ targetuser = self.user(target)
+ if targetuser.nick != target:
+ targetuser.nick = target # Server seems to have changed the idea of the case of the nickname
+
+ ### CTCP handling
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
+ if ctcp:
+ (ctcptype, ext) = ctcp[0]
+ if ctcptype.upper() == "VERSION":
+ user.ctcpreply("VERSION", self.ctcpversion())
+ if ctcptype.upper() == "TIME":
+ tformat = time.ctime()
+ tz = time.tzname[0]
+ user.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars())
+ if ctcptype.upper() == "PING":
+ user.ctcpreply("PING", "%(ext)s" % vars())
+ if ctcptype.upper() == "FINGER":
+ user.ctcpreply("FINGER", "%(ext)s" % vars())
+ elif cmd == 910: # Channel Access List
+ (channame, mask, setby, settime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "w" in channel.modes.keys():
+ if mask not in [m for (m, b, t) in channel.modes["w"]]:
+ channel.modes["w"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["w"] = [(mask, setby, int(settime))]
+ elif cmd == 941: # Channel spamfilter List
+ (channame, mask, setby, settime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "g" in channel.modes.keys():
+ if mask not in [m for (m, b, t) in channel.modes["g"]]:
+ channel.modes["g"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["g"] = [(mask, setby, int(settime))]
+ elif cmd == 954: # Channel spamfilter List
+ (channame, mask, setby, settime) = params.split()
+ channel = self.channel(channame)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+ if channel.name != channame:
+ channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ if "X" in channel.modes.keys():
+ if mask not in [m for (m, b, t) in channel.modes["X"]]:
+ channel.modes["X"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["X"] = [(mask, setby, int(settime))]
+ elif cmd in (495, 384, 385, 386, 468, 470, 366, 315, 482, 484, 953, 368, 482, 349, 940, 911, 489, 490, 492, 520, 530): # Channels which appear in params
+ for param in params.split():
+ if len(param) and param[0] in self.supports["CHANTYPES"]:
+ channel = self.channel(param)
+ self.event("onRecv", channel.modules, line=line, data=parsed)
+
+ else: # Line does NOT match ":src cmd target params :extinfo"
+ self.event("onRecv", self.modules, line=line,
+ data=None)
+ except SystemExit: # Connection lost normally.
+ pass
+ except socket.error: # Connection lost due to either ping timeout or connection reset by peer. Not a fatal error.
+ with self.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
+ self.log.flush()
+ except: # Unknown exception, treated as FATAL. Try to quit IRC and terminate thread with exception.
+ ### Quit with a (hopefully) useful quit message, or die trying.
+ try:
+ self.quit("%s" % traceback.format_exc()
+ .rstrip().split("\n")[-1])
+ except:
+ pass
+ raise
+ finally: # Post-connection operations after connection is lost, and must be executed, even if exception occurred.
+ with self.lock:
+ modules = list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules:
+ modules.append(module)
+ self.event("onDisconnect", self.modules)
+
+ ### Tell outgoing thread to quit.
+ self.outgoing.put("quit")
+
+ ### Wait until the outgoing thread dies.
+ if outgoingthread:
+ outgoingthread.join()
+ outgoingthread = None
+
+ self.connected = False
+ self.registered = False
+ self.identity = None
+
+ try:
+ self.connection.close()
+ except:
+ pass
+
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s *** Connection Terminated." % vars()
+ self.log.flush()
+
+ if self.quitexpected or not self.autoreconnect:
+ sys.exit()
+
+ ### If we make it to this point, then it is because connection was lost unexpectedly, and will attempt to reconnect if self.autoreconnect is True.
+ time.sleep(self.retrysleep)
+
+ except SystemExit:
+ pass
+
+ except: # Print exception to log file
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
+ self.log.flush()
+ sys.exit()
+
+ finally:
+ with self.loglock:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ print >>self.log, "%(timestamp)s ### Log session ended" % vars(
+ )
+ self.log.flush()
+
+ Thread.__init__(self) # Makes thread restartable
+
+ def __repr__(self):
+ server = self.server
+ if self.ipv6 and ":" in server:
+ server = "[%s]"%server
+ port = self.port
+ if self.identity:
+ nick = self.identity.nick
+ ident = self.identity.idnt if self.identity.idnt else "*"
+ host = self.identity.host if self.identity.host else "*"
+ else:
+ nick = "*"
+ ident = "*"
+ host = "*"
+ if self.ssl and self.ipv6:
+ protocol = "ircs6"
+ elif self.ssl:
+ protocol = "ircs"
+ elif self.ipv6:
+ protocol = "irc6"
+ else:
+ protocol = "irc"
+ return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
+ #else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals()
+
+ def quit(self, msg="", origin=None):
+ with self.lock:
+ self.quitexpected = True
+ if self.connected:
+ if len(msg):
+ self.raw("QUIT :%(msg)s" % vars(), origin=origin)
+ else:
+ self.raw("QUIT", origin=origin)
+
+ def ctcpversion(self):
+ reply = []
+ ### Prepare reply for module
+ reply.append("%(__name__)s %(__version__)s, %(__author__)s" %
+ vars(self))
+
+ ### Prepare reply for Python and OS versions
+ pyver = sys.version.split("\n")
+ pyver[0] = "Python "+pyver[0]
+ reply.extend(pyver)
+ reply.extend(platform.platform().split("\n"))
+ ### Prepare reply for extension modules
+ for module in self.modules:
+ try:
+ r = "%(__name__)s %(__version__)s" % vars(module)
+ if "__extinfo__" in vars(module):
+ r += ", %(__extinfo__)s" % vars()
+ reply.append(r)
+ except:
+ pass
+ return reduce(lambda x, y: "%s; %s" % (x, y), reply)
+
+ def raw(self, line, origin=None):
+ self.outgoing.put((line, origin))
+
+ def user(self, nick):
+ users = [user for user in self.users if user.nick.lower(
+ ) == nick.lower()]
+ if len(users):
+ return users[0]
+ else:
+ user = User(nick, self)
+ self.users.append(user)
+ timestamp = reduce(lambda x, y: x+":"+y, [str(
+ t).rjust(2, "0") for t in time.localtime()[0:6]])
+ with self.loglock:
+ print >>self.log, "%s *** User %s created."%(timestamp, nick)
+ self.log.flush()
+ return user
+
+ def channel(self, name):
+ channels = [chan for chan in self.channels if name.lower(
+ ) == chan.name.lower()]
+ if len(channels):
+ return channels[0]
+ else:
+ timestamp = reduce(lambda x, y: x+":"+y, [str(
+ t).rjust(2, "0") for t in time.localtime()[0:6]])
+ chan = Channel(name, self)
+ self.channels.append(chan)
+ with self.loglock:
+ print >>self.log, "%s *** Channel %s created."%(
+ timestamp, name)
+ self.log.flush()
+ return chan
class Channel(object):
- def __init__(self,name,context):
- self.name=name
- self.context=context
- self.modules=[]
- self.topic=""
- self.topicsetby=""
- self.topictime=()
- self.topicmod=""
- self.modes={}
- self.users=[]
- self.created=None
- self.lock=Lock()
- def msg(self, msg, origin=None):
- chan=self.name
- self.context.raw("PRIVMSG %(chan)s :%(msg)s" % vars(), origin=origin)
- def settopic(self, msg, origin=None):
- chan=self.name
- self.context.raw("TOPIC %(chan)s :%(msg)s" % vars(), origin=origin)
- def notice(self, msg, target="", origin=None):
- chan=self.name
- self.context.raw("NOTICE %(target)s%(chan)s :%(msg)s" % vars(), origin=origin)
- def ctcp(self, act, msg="", origin=None):
- if len(msg): self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin)
- else: self.msg("\01%(act)s\01" % vars())
- def ctcpreply(self, act, msg="", origin=None):
- if len(msg): self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin)
- else: self.notice("\01%(act)s\01" % vars(), origin=origin)
- def me(self,msg="", origin=None): self.ctcp("ACTION", msg, origin=origin)
- def part(self, msg="", origin=None):
- chan=self.name
- if msg: self.context.raw("PART %(chan)s :%(msg)s" % vars(), origin=origin)
- else: self.context.raw("PART %(chan)s" % vars(), origin)
- def join(self, key="", origin=None):
- chan=self.name
- if key: self.context.raw("JOIN :%(chan)s %(key)s" % vars(), origin=origin)
- else: self.context.raw("JOIN :%(chan)s" % vars(), origin=origin)
- def kick(self, nick, msg="", origin=None):
- chan=self.name
- if len(msg): self.context.raw("KICK %(chan)s %(nick)s :%(msg)s" % vars(), origin=origin)
- else: self.context.raw("KICK %(chan)s %(nick)s" % vars(), origin=origin)
- def __repr__(self):
- return "<Channel: "+self.name+"@"+self.context.server+"/"+str(self.context.port)+">"
+ def __init__(self, name, context):
+ self.name = name
+ self.context = context
+ self.modules = []
+ self.topic = ""
+ self.topicsetby = ""
+ self.topictime = ()
+ self.topicmod = ""
+ self.modes = {}
+ self.users = []
+ self.created = None
+ self.lock = Lock()
+
+ def msg(self, msg, origin=None):
+ chan = self.name
+ self.context.raw("PRIVMSG %(chan)s :%(msg)s" % vars(), origin=origin)
+
+ def settopic(self, msg, origin=None):
+ chan = self.name
+ self.context.raw("TOPIC %(chan)s :%(msg)s" % vars(), origin=origin)
+
+ def notice(self, msg, target="", origin=None):
+ chan = self.name
+ self.context.raw("NOTICE %(target)s%(chan)s :%(msg)s" %
+ vars(), origin=origin)
+
+ def ctcp(self, act, msg="", origin=None):
+ if len(msg):
+ self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin)
+ else:
+ self.msg("\01%(act)s\01" % vars())
+
+ def ctcpreply(self, act, msg="", origin=None):
+ if len(msg):
+ self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin)
+ else:
+ self.notice("\01%(act)s\01" % vars(), origin=origin)
+
+ def me(self, msg="", origin=None):
+ self.ctcp("ACTION", msg, origin=origin)
+
+ def part(self, msg="", origin=None):
+ chan = self.name
+ if msg:
+ self.context.raw("PART %(chan)s :%(msg)s" % vars(), origin=origin)
+ else:
+ self.context.raw("PART %(chan)s" % vars(), origin)
+
+ def join(self, key="", origin=None):
+ chan = self.name
+ if key:
+ self.context.raw("JOIN :%(chan)s %(key)s" % vars(), origin=origin)
+ else:
+ self.context.raw("JOIN :%(chan)s" % vars(), origin=origin)
+
+ def kick(self, nick, msg="", origin=None):
+ chan = self.name
+ if len(msg):
+ self.context.raw("KICK %(chan)s %(nick)s :%(msg)s" %
+ vars(), origin=origin)
+ else:
+ self.context.raw("KICK %(chan)s %(nick)s" % vars(), origin=origin)
+
+ def __repr__(self):
+ return "<Channel: "+self.name+"@"+self.context.server+"/"+str(self.context.port)+">"
+
+
class User(object):
- def __init__(self,nick,context):
- self.nick=nick
- self.idnt=""
- self.host=""
- self.channels=[]
- self.context=context
- self.modes=""
- self.snomask=""
- self.server=None
- self.hops=None
- self.ircop=False
- def __repr__(self):
- return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self)
- def msg(self, msg, origin=None):
- nick=self.nick
- self.context.raw("PRIVMSG %(nick)s :%(msg)s" % vars(), origin=origin)
- def notice(self, msg, origin=None):
- nick=self.nick
- self.context.raw("NOTICE %(nick)s :%(msg)s" % vars(), origin=origin)
- def ctcp(self, act, msg="", origin=None):
- if len(msg): self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin)
- else: self.msg("\01%(act)s\01" % vars(), origin=origin)
- def ctcpreply(self, act, msg="", origin=None):
- if len(msg): self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin)
- else: self.notice("\01%(act)s\01" % vars(), origin=origin)
- def me(self, msg, origin=None): self.ctcp("ACTION", msg, origin=origin)
+ def __init__(self, nick, context):
+ self.nick = nick
+ self.idnt = ""
+ self.host = ""
+ self.channels = []
+ self.context = context
+ self.modes = ""
+ self.snomask = ""
+ self.server = None
+ self.hops = None
+ self.ircop = False
+
+ def __repr__(self):
+ return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self)
+
+ def msg(self, msg, origin=None):
+ nick = self.nick
+ self.context.raw("PRIVMSG %(nick)s :%(msg)s" % vars(), origin=origin)
+
+ def notice(self, msg, origin=None):
+ nick = self.nick
+ self.context.raw("NOTICE %(nick)s :%(msg)s" % vars(), origin=origin)
+
+ def ctcp(self, act, msg="", origin=None):
+ if len(msg):
+ self.msg("\01%(act)s %(msg)s\01" % vars(), origin=origin)
+ else:
+ self.msg("\01%(act)s\01" % vars(), origin=origin)
+
+ def ctcpreply(self, act, msg="", origin=None):
+ if len(msg):
+ self.notice("\01%(act)s %(msg)s\01" % vars(), origin=origin)
+ else:
+ self.notice("\01%(act)s\01" % vars(), origin=origin)
+
+ def me(self, msg, origin=None):
+ self.ctcp("ACTION", msg, origin=origin)
+
+
class SendLines(Thread):
- def __init__(self, connection, lines, origin=None):
- self.connection=connection
- self.lines=lines
- self.origin=origin
- Thread.__init__(self)
- def run(self):
- for line in self.lines:
- self.connection.raw(reply, origin=self.origin)
- self.connection.log.flush()
- time.sleep(2)
+ def __init__(self, connection, lines, origin=None):
+ self.connection = connection
+ self.lines = lines
+ self.origin = origin
+ Thread.__init__(self)
+
+ def run(self):
+ for line in self.lines:
+ self.connection.raw(reply, origin=self.origin)
+ self.connection.log.flush()
+ time.sleep(2)
+
class Outgoing(Thread):
- def __init__(self, IRC, throttle=0.25, lines=40, t=5):
- self.IRC=IRC
- self.throttle=throttle
- self.lines=lines
- self.time=t
- #self.queue=Queue()
- Thread.__init__(self)
- def run(self):
- throttled=False
- timestamps=[]
- while True:
- q=self.IRC.outgoing.get()
- if q=="quit" or not self.IRC.connected: break
- line, origin=q
- match=re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
- (cmd, target, params, extinfo)=match[0]
- if cmd.upper()=="QUIT": self.IRC.quitexpected=True
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
- with self.IRC.lock:
- try:
- self.IRC.connection.send("%(line)s\n" % vars())
- except socket.error:
- try:
- self.IRC.connection.shutdown(0)
- except:
- pass
- raise
-
- ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy modules
- #if re.match("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I):
- if cmd.upper()=="PRIVMSG":
- if target.upper()=="NICKSERV":
- nscmd=re.findall(r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
- if nscmd:
- nscmd=nscmd[0]
- if nscmd[0].upper() in ("IDENTIFY", "REGISTER"):
- extinfo="%s ********"%nscmd[0]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- extinfo="%s %s ********"%nscmd[:2]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() == "SET":
- if nscmd[1].upper() == "PASSWORD":
- extinfo="%s %s ********"%nscmd[:2]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- extinfo="********"
- line="%s %s :%s"%(cmd, target, extinfo)
- if target.upper()=="CHANSERV":
- cscmd=re.findall(r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
- if cscmd:
- cscmd=cscmd[0]
- if cscmd[0].upper() in ("IDENTIFY", "REGISTER"):
- extinfo="%s %s ********"%cscmd[:2]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- extinfo="%s %s %s ********"%cscmd[:3]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() == "SET":
- if cscmd[2].upper() == "PASSWORD":
- extinfo="%s %s %s ********"%cscmd[:3]
- line="%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- extinfo="********"
- line="%s %s :%s"%(cmd, target, extinfo)
- #elif target.upper()=="CHANSERV":
- #msg=extinfo.split(" ")
- #if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2:
- #msg[2]="********"
- #extinfo=" ".join(msg)
- #line="%s %s :%s"%(cmd, target, extinfo)
- elif cmd.upper()=="NS":
- if target.upper() in ("IDENTIFY", "REGISTER"):
- params=params.split(" ")
- while "" in params: params.remove("")
- if len(params):
- params[0]="********"
- params=" ".join(params)
- line="%s %s %s"%(cmd, target, params)
- elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- params=params.split(" ")
- while "" in params: params.remove("")
- if len(params)>1:
- params[1]="********"
- params=" ".join(params)
- line="%s %s %s"%(cmd, target, params)
- elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- params=""
- target="********"
- line="%s %s"%(cmd, target)
- elif cmd.upper()=="OPER":
- params="********"
- line="%s %s %s"%(cmd, target, params)
- elif cmd.upper()=="PASS":
- extinfo="********"
- target=""
- line="%s :%s"%(cmd, extinfo)
- elif cmd.upper()=="IDENTIFY":
- target="********"
- line="%s %s"%(cmd, target)
- self.IRC.event("onSend", self.IRC.modules, line=line, data=(cmd, target, params, extinfo), origin=origin)
- with self.IRC.loglock:
- print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars()
- self.IRC.log.flush()
- timestamps.append(time.time())
- while timestamps[0]<timestamps[-1]-self.time-0.1: del timestamps[0]
- if throttled:
- if len(timestamps)<2: throttled=False
- else:
- if len(timestamps)>=self.lines: throttled=True
- if throttled: time.sleep(max(timestamps[-1]+self.throttle-time.time(),0))
+ def __init__(self, IRC, throttle=0.25, lines=40, t=5):
+ self.IRC = IRC
+ self.throttle = throttle
+ self.lines = lines
+ self.time = t
+ #self.queue=Queue()
+ Thread.__init__(self)
+
+ def run(self):
+ throttled = False
+ timestamps = []
+ while True:
+ q = self.IRC.outgoing.get()
+ if q == "quit" or not self.IRC.connected:
+ break
+ line, origin = q
+ match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
+ (cmd, target, params, extinfo) = match[0]
+ if cmd.upper() == "QUIT":
+ self.IRC.quitexpected = True
+ timestamp = reduce(lambda x, y: x+":"+y, [str(
+ t).rjust(2, "0") for t in time.localtime()[0:6]])
+ with self.IRC.lock:
+ try:
+ self.IRC.connection.send("%(line)s\n" % vars())
+ except socket.error:
+ try:
+ self.IRC.connection.shutdown(0)
+ except:
+ pass
+ raise
+
+ ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy modules
+ #if re.match("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I):
+ if cmd.upper() == "PRIVMSG":
+ if target.upper() == "NICKSERV":
+ nscmd = re.findall(r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
+ if nscmd:
+ nscmd = nscmd[0]
+ if nscmd[0].upper() in ("IDENTIFY", "REGISTER"):
+ extinfo = "%s ********"%nscmd[0]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
+ extinfo = "%s %s ********"%nscmd[:2]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif nscmd[0].upper() == "SET":
+ if nscmd[1].upper() == "PASSWORD":
+ extinfo = "%s %s ********"%nscmd[:2]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ extinfo = "********"
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ if target.upper() == "CHANSERV":
+ cscmd = re.findall(r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
+ if cscmd:
+ cscmd = cscmd[0]
+ if cscmd[0].upper() in ("IDENTIFY", "REGISTER"):
+ extinfo = "%s %s ********"%cscmd[:2]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
+ extinfo = "%s %s %s ********"%cscmd[:3]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif cscmd[0].upper() == "SET":
+ if cscmd[2].upper() == "PASSWORD":
+ extinfo = "%s %s %s ********"%cscmd[:3]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ extinfo = "********"
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ #elif target.upper()=="CHANSERV":
+ #msg=extinfo.split(" ")
+ #if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2:
+ #msg[2]="********"
+ #extinfo=" ".join(msg)
+ #line="%s %s :%s"%(cmd, target, extinfo)
+ elif cmd.upper() == "NS":
+ if target.upper() in ("IDENTIFY", "REGISTER"):
+ params = params.split(" ")
+ while "" in params:
+ params.remove("")
+ if len(params):
+ params[0] = "********"
+ params = " ".join(params)
+ line = "%s %s %s"%(cmd, target, params)
+ elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
+ params = params.split(" ")
+ while "" in params:
+ params.remove("")
+ if len(params) > 1:
+ params[1] = "********"
+ params = " ".join(params)
+ line = "%s %s %s"%(cmd, target, params)
+ elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ params = ""
+ target = "********"
+ line = "%s %s"%(cmd, target)
+ elif cmd.upper() == "OPER":
+ params = "********"
+ line = "%s %s %s"%(cmd, target, params)
+ elif cmd.upper() == "PASS":
+ extinfo = "********"
+ target = ""
+ line = "%s :%s"%(cmd, extinfo)
+ elif cmd.upper() == "IDENTIFY":
+ target = "********"
+ line = "%s %s"%(cmd, target)
+ self.IRC.event("onSend", self.IRC.modules, line=line, data=(cmd, target, params, extinfo), origin=origin)
+ with self.IRC.loglock:
+ print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars()
+ self.IRC.log.flush()
+ timestamps.append(time.time())
+ while timestamps[0] < timestamps[-1]-self.time-0.1:
+ del timestamps[0]
+ if throttled:
+ if len(timestamps) < 2:
+ throttled = False
+ else:
+ if len(timestamps) >= self.lines:
+ throttled = True
+ if throttled:
+ time.sleep(max(timestamps[-1]+self.throttle-time.time(), 0))
class Pinger(Thread):
- def __init__(self, connection, lock=None):
- self.connection=connection
- self.lock=lock
- self.daemon=True
- Thread.__init__(self)
-
- def run(self):
- pass \ No newline at end of file
+ def __init__(self, connection, lock=None):
+ self.connection = connection
+ self.lock = lock
+ self.daemon = True
+ Thread.__init__(self)
+
+ def run(self):
+ pass
diff --git a/logger.py b/logger.py
index 043b0f8..9df6020 100644
--- a/logger.py
+++ b/logger.py
@@ -1,310 +1,382 @@
#!/usr/bin/python
from threading import Thread, Lock
-import re, time, sys, string, socket, os, platform, traceback, Queue, ssl, urllib2
+import re
+import time
+import sys
+import string
+import socket
+import os
+import platform
+import traceback
+import Queue
+import ssl
+import urllib2
+
class LogRotate(Thread):
- def __init__(self, logger):
- self.logger=logger
- Thread.__init__(self)
- self.daemon=True
- self.start()
- def run(self):
- Y, M, D, h, m, s, w, d, dst=time.localtime()
- nextrotate=int(time.mktime((Y, M, D+1, 0, 0, 0, 0, 0, -1)))
- #print time.time()-nextrotate
- #print time.localtime(), time.localtime(nextrotate)
- while True:
- while nextrotate>time.time(): ### May need to do this in a loop in case the following time.sleep command wakes up a second too early.
- time.sleep(max(0.1, min((nextrotate-time.time(), 3600))))
- with self.logger.rotatelock:
- if all([not log or log.closed for log in self.logger.consolelogs.values()+self.logger.channellogs.values()]):
- ### If there are no logs to rotate, we are going to terminate this thread. Logger will spawn another LogRotate thread when needed.
- self.logger.logrotate=None
- break
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- for IRC in self.logger.labels.keys():
- if IRC.connected:
- with IRC.lock:
- try:
- self.logger.rotateConsoleLog(IRC)
- except:
- with IRC.loglock:
- exc,excmsg,tb=sys.exc_info()
- print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars()
- IRC.log.flush()
- if IRC.identity:
- for channel in IRC.identity.channels:
- try:
- self.logger.rotateChannelLog(channel)
- except:
- with IRC.loglock:
- exc,excmsg,tb=sys.exc_info()
- print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars()
- IRC.log.flush()
- nextrotate+=3600*24
+ def __init__(self, logger):
+ self.logger = logger
+ Thread.__init__(self)
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ Y, M, D, h, m, s, w, d, dst = time.localtime()
+ nextrotate = int(time.mktime((Y, M, D+1, 0, 0, 0, 0, 0, -1)))
+ #print time.time()-nextrotate
+ #print time.localtime(), time.localtime(nextrotate)
+ while True:
+ while nextrotate > time.time(): # May need to do this in a loop in case the following time.sleep command wakes up a second too early.
+ time.sleep(max(0.1, min((nextrotate-time.time(), 3600))))
+ with self.logger.rotatelock:
+ if all([not log or log.closed for log in self.logger.consolelogs.values()+self.logger.channellogs.values()]):
+ ### If there are no logs to rotate, we are going to terminate this thread. Logger will spawn another LogRotate thread when needed.
+ self.logger.logrotate = None
+ break
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t)
+ .rjust(2, "0") for t in now[0:6]])
+ for IRC in self.logger.labels.keys():
+ if IRC.connected:
+ with IRC.lock:
+ try:
+ self.logger.rotateConsoleLog(IRC)
+ except:
+ with IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars()
+ IRC.log.flush()
+ if IRC.identity:
+ for channel in IRC.identity.channels:
+ try:
+ self.logger.rotateChannelLog(channel)
+ except:
+ with IRC.loglock:
+ exc, excmsg, tb = sys.exc_info()
+ print >>IRC.log, "%(timestamp)s !!! [LogRotate] Exception in module %(module)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>IRC.log, "%(timestamp)s !!! [LogRotate] %(tbline)s" % vars()
+ IRC.log.flush()
+ nextrotate += 3600*24
+
class Logger(object):
- def __init__(self, logroot):
- self.logroot=logroot
- path=[logroot]
- #print path
- while not os.path.isdir(path[0]):
- split=os.path.split(path[0])
- path.insert(1,split[1])
- path[0]=split[0]
- #print path
- while len(path)>1:
- path[0]=os.path.join(*path[:2])
- del path[1]
- #print path
- os.mkdir(path[0])
- #return
- self.consolelogs={}
- self.channellogs={}
- self.labels={}
- self.rotatelock=Lock()
- self.logrotate=None
- def onModuleAdd(self, IRC, label):
- if label in self.labels.values():
- raise BaseException, "Label already exists"
- if IRC in self.labels.keys():
- raise BaseException, "Network already exists"
- if not os.path.isdir(os.path.join(self.logroot, label)):
- os.mkdir(os.path.join(self.logroot, label))
- self.labels[IRC]=label
- if IRC.connected:
- self.openConsoleLog(IRC)
- if IRC.identity:
- for channel in IRC.identity.channels:
- self.openChannelLog(channel)
+ def __init__(self, logroot):
+ self.logroot = logroot
+ path = [logroot]
+ #print path
+ while not os.path.isdir(path[0]):
+ split = os.path.split(path[0])
+ path.insert(1, split[1])
+ path[0] = split[0]
+ #print path
+ while len(path) > 1:
+ path[0] = os.path.join(*path[:2])
+ del path[1]
+ #print path
+ os.mkdir(path[0])
+ #return
+ self.consolelogs = {}
+ self.channellogs = {}
+ self.labels = {}
+ self.rotatelock = Lock()
+ self.logrotate = None
+
+ def onModuleAdd(self, IRC, label):
+ if label in self.labels.values():
+ raise BaseException("Label already exists")
+ if IRC in self.labels.keys():
+ raise BaseException("Network already exists")
+ if not os.path.isdir(os.path.join(self.logroot, label)):
+ os.mkdir(os.path.join(self.logroot, label))
+ self.labels[IRC] = label
+ if IRC.connected:
+ self.openConsoleLog(IRC)
+ if IRC.identity:
+ for channel in IRC.identity.channels:
+ self.openChannelLog(channel)
+
+ def onModuleRem(self, IRC):
+ if IRC.connected:
+ for channel in self.channellogs.keys():
+ if channel in IRC.channels:
+ if not self.channellogs[channel].closed:
+ self.closeChannelLog(channel)
+ del self.channellogs[channel]
+ if not self.consolelogs[IRC].closed:
+ self.closeConsoleLog(IRC)
+ del self.labels[IRC]
+ del self.consolelogs[IRC]
+
+ def openConsoleLog(self, IRC):
+ with self.rotatelock:
+ if not self.logrotate or not self.logrotate.isAlive():
+ self.logrotate = LogRotate(self)
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ self.consolelogs[IRC] = open(os.path.join(self.logroot, self.labels[
+ IRC], "console-%04d.%02d.%02d.log"%now[:3]), "a")
+ print >>self.consolelogs[IRC], "%s %s ### Log session started" % (
+ timestamp, time.tzname[now[-1]])
+ self.consolelogs[IRC].flush()
+
+ def closeConsoleLog(self, IRC):
+ if IRC in self.consolelogs.keys() and type(self.consolelogs[IRC]) == file and not self.consolelogs[IRC].closed:
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t)
+ .rjust(2, "0") for t in now[0:6]])
+ print >>self.consolelogs[IRC], "%s %s ### Log session ended" % (
+ timestamp, time.tzname[now[-1]])
+ self.consolelogs[IRC].close()
+
+ def rotateConsoleLog(self, IRC):
+ self.closeConsoleLog(IRC)
+ self.openConsoleLog(IRC)
+
+ def openChannelLog(self, channel):
+ with self.rotatelock:
+ if not self.logrotate or not self.logrotate.isAlive():
+ self.logrotate = LogRotate(self)
+ self.logrotate.daemon = True
+ self.logrotate.start()
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ label = self.labels[channel.context]
+ self.channellogs[channel] = open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(channel.name.lower()).replace("/", "%2f"),)+now[:3])), "a")
+ print >>self.channellogs[channel], "%s %s ### Log session started" % (
+ timestamp, time.tzname[now[-1]])
+ self.channellogs[channel].flush()
+ if channel.context.identity in channel.users:
+ if channel.topic:
+ print >>self.channellogs[channel], "%s %s <<< :%s 332 %s %s :%s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topic)
+ if channel.topicsetby and channel.topictime:
+ print >>self.channellogs[channel], "%s %s <<< :%s 333 %s %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topicsetby, channel.topictime)
+ if channel.users:
+ secret = "s" in channel.modes.keys() and channel.modes["s"]
+ private = "p" in channel.modes.keys() and channel.modes["p"]
+ namesusers = []
+ modes, symbols = channel.context.supports["PREFIX"]
+ print >>self.channellogs[channel], "%s %s <<< :%s 353 %s %s %s :%s" % (timestamp, time.tzname[now[-1]],
+ channel.context.serv,
+ channel.context.identity.nick,
+ "@" if secret else ("*" if private else "="),
+ channel.name,
+ " ".join(["".join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))])+user.nick for user in channel.users]))
+ if channel.modes:
+ modes = channel.modes.keys()
+ modestr = "".join([mode for mode in modes if mode not in channel.context.supports["CHANMODES"][0]+channel.context.supports["PREFIX"][0] and channel.modes[mode]])
+ params = " ".join([channel.modes[mode] for mode in modes if mode in channel.context.supports["CHANMODES"][1]+channel.context.supports["CHANMODES"][2] and channel.modes[mode]])
+ print >>self.channellogs[channel], "%s %s <<< :%s 324 %s %s +%s %s" % (timestamp, time.tzname[now[-1]], channel.context.server, channel.context.identity.nick, channel.name, modestr, params)
+ if channel.created:
+ print >>self.channellogs[channel], "%s %s <<< :%s 329 %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.created)
+ self.channellogs[channel].flush()
+
+ def closeChannelLog(self, channel):
+ if channel in self.channellogs.keys() and type(self.channellogs[channel]) == file and not self.channellogs[channel].closed:
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t)
+ .rjust(2, "0") for t in now[0:6]])
+ print >>self.channellogs[channel], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]])
+ self.channellogs[channel].close()
+
+ def rotateChannelLog(self, channel):
+ self.closeChannelLog(channel)
+ self.openChannelLog(channel)
+
+ def onRecv(self, IRC, line, data):
+ modemapping = dict(Y="ircop", q="owner", a="admin", o="op",
+ h="halfop", v="voice")
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ if data is None:
+ print >>self.consolelogs[IRC], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.consolelogs[IRC].flush()
+ return
+ (origin, ident, host, cmd, target, params, extinfo) = data
+ if re.match("^\\d+$", cmd):
+ cmd = int(cmd)
+ if cmd in (324, 329):
+ modeparams = params.split()
+ channame = modeparams[0]
+ channel = IRC.channel(channame)
+ if channel in self.channellogs.keys() and not self.channellogs[channel].closed:
+ log = self.channellogs[channel]
+ else:
+ log = self.consolelogs[IRC]
+ print >>log, "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ log.flush()
+ elif cmd == 332:
+ channel = IRC.channel(params)
+ if channel in self.channellogs.keys() and not self.channellogs[channel].closed:
+ log = self.channellogs[channel]
+ else:
+ log = self.consolelogs[IRC]
+ print >>log, "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ log.flush()
+ elif cmd == 333:
+ (channame, nick, dt) = params.split()
+ channel = IRC.channel(channame)
+ if not self.channellogs[channel].closed:
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif cmd == 353:
+ (flag, channame) = params.split()
+ channel = IRC.channel(channame)
+ if not self.channellogs[channel].closed:
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif cmd == "JOIN":
+ user = IRC.user(origin)
+ channel = IRC.channel(target if len(target) else extinfo)
+ if user == IRC.identity:
+ self.openChannelLog(channel)
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif cmd == "PRIVMSG":
+ if target and target[0] in IRC.supports["CHANTYPES"]:
+ channel = IRC.channel(target)
+ if ident and host:
+ user = IRC.user(origin)
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])
+ else:
+ classes = "server"
+ if classes:
+ print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line)
+ else:
+ print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif cmd == "NOTICE":
+ if target and (target[0] in IRC.supports["CHANTYPES"] or (len(target) > 1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])):
+ if target[0] in IRC.supports["PREFIX"][1]:
+ channel = IRC.channel(target[1:])
+ else:
+ channel = IRC.channel(target)
+ if ident and host:
+ user = IRC.user(origin)
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])
+ else:
+ classes = "server"
+ if classes:
+ print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line)
+ else:
+ print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif target.lower() == IRC.identity.nick.lower() and not ident and not host:
+ print >>self.consolelogs[IRC], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.consolelogs[IRC].flush()
+ elif cmd == "TOPIC":
+ channel = IRC.channel(target)
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ elif cmd == "PART":
+ user = IRC.user(origin)
+ channel = IRC.channel(target)
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ if user == IRC.identity:
+ self.closeChannelLog(channel)
+ elif cmd == "KICK":
+ kicked = IRC.user(params)
+ channel = IRC.channel(target)
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ if kicked == IRC.identity:
+ self.closeChannelLog(channel)
+ elif cmd == "MODE":
+ if target and target[0] in IRC.supports["CHANTYPES"]:
+ channel = IRC.channel(target)
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ else:
+ print >>self.consolelogs[IRC], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.consolelogs[IRC].flush()
+ elif cmd in ("NICK", "QUIT"):
+ user = IRC.user(origin)
+ for channel in user.channels:
+ print >>self.channellogs[channel], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.channellogs[channel].flush()
+ else:
+ print >>self.consolelogs[IRC], "%s %s <<< %s" % (
+ timestamp, time.tzname[now[-1]], line)
+ self.consolelogs[IRC].flush()
+
+ def onConnectAttempt(self, IRC):
+ if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed:
+ self.openConsoleLog(IRC)
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ print >>self.consolelogs[IRC], "%s %s *** Attempting connection to %s:%s." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- def onModuleRem(self, IRC):
- if IRC.connected:
- for channel in self.channellogs.keys():
- if channel in IRC.channels:
- if not self.channellogs[channel].closed: self.closeChannelLog(channel)
- del self.channellogs[channel]
- if not self.consolelogs[IRC].closed: self.closeConsoleLog(IRC)
- del self.labels[IRC]
- del self.consolelogs[IRC]
+ def onConnect(self, IRC):
+ if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed:
+ self.openConsoleLog(IRC)
+ #self.openConsoleLog(IRC)
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ print >>self.consolelogs[IRC], "%s %s *** Connection to %s:%s established." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- def openConsoleLog(self, IRC):
- with self.rotatelock:
- if not self.logrotate or not self.logrotate.isAlive():
- self.logrotate=LogRotate(self)
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- self.consolelogs[IRC]=open(os.path.join(self.logroot, self.labels[IRC], "console-%04d.%02d.%02d.log"%now[:3]), "a")
- print >>self.consolelogs[IRC], "%s %s ### Log session started" % (timestamp, time.tzname[now[-1]])
- self.consolelogs[IRC].flush()
- def closeConsoleLog(self, IRC):
- if IRC in self.consolelogs.keys() and type(self.consolelogs[IRC])==file and not self.consolelogs[IRC].closed:
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- print >>self.consolelogs[IRC], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]])
- self.consolelogs[IRC].close()
- def rotateConsoleLog(self, IRC):
- self.closeConsoleLog(IRC)
- self.openConsoleLog(IRC)
+ def onDisconnect(self, IRC):
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ for channel in IRC.identity.channels:
+ print >>self.channellogs[channel], "%s %s *** Connection to %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
+ self.channellogs[channel].flush()
+ self.closeChannelLog(channel)
+ print >>self.consolelogs[IRC], "%s %s *** Connection %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
+ self.consolelogs[IRC].flush()
+ self.closeConsoleLog(IRC)
- def openChannelLog(self, channel):
- with self.rotatelock:
- if not self.logrotate or not self.logrotate.isAlive():
- self.logrotate=LogRotate(self)
- self.logrotate.daemon=True
- self.logrotate.start()
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- label=self.labels[channel.context]
- self.channellogs[channel]=open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(channel.name.lower()).replace("/","%2f"),)+now[:3])), "a")
- print >>self.channellogs[channel], "%s %s ### Log session started" % (timestamp, time.tzname[now[-1]])
- self.channellogs[channel].flush()
- if channel.context.identity in channel.users:
- if channel.topic: print >>self.channellogs[channel], "%s %s <<< :%s 332 %s %s :%s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topic)
- if channel.topicsetby and channel.topictime: print >>self.channellogs[channel], "%s %s <<< :%s 333 %s %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.topicsetby, channel.topictime)
- if channel.users:
- secret="s" in channel.modes.keys() and channel.modes["s"]
- private="p" in channel.modes.keys() and channel.modes["p"]
- namesusers=[]
- modes, symbols=channel.context.supports["PREFIX"]
- print >>self.channellogs[channel], "%s %s <<< :%s 353 %s %s %s :%s" % (timestamp, time.tzname[now[-1]],
- channel.context.serv,
- channel.context.identity.nick,
- "@" if secret else ("*" if private else "="),
- channel.name,
- " ".join(["".join([symbols[k] if modes[k] in channel.modes.keys() and user in channel.modes[modes[k]] else "" for k in xrange(len(modes))])+user.nick for user in channel.users]))
- if channel.modes:
- modes=channel.modes.keys()
- modestr="".join([mode for mode in modes if mode not in channel.context.supports["CHANMODES"][0]+channel.context.supports["PREFIX"][0] and channel.modes[mode]])
- params=" ".join([channel.modes[mode] for mode in modes if mode in channel.context.supports["CHANMODES"][1]+channel.context.supports["CHANMODES"][2] and channel.modes[mode]])
- print >>self.channellogs[channel], "%s %s <<< :%s 324 %s %s +%s %s" % (timestamp, time.tzname[now[-1]], channel.context.server, channel.context.identity.nick, channel.name, modestr, params)
- if channel.created: print >>self.channellogs[channel], "%s %s <<< :%s 329 %s %s %s" % (timestamp, time.tzname[now[-1]], channel.context.serv, channel.context.identity.nick, channel.name, channel.created)
- self.channellogs[channel].flush()
- def closeChannelLog(self, channel):
- if channel in self.channellogs.keys() and type(self.channellogs[channel])==file and not self.channellogs[channel].closed:
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- print >>self.channellogs[channel], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]])
- self.channellogs[channel].close()
- def rotateChannelLog(self, channel):
- self.closeChannelLog(channel)
- self.openChannelLog(channel)
- def onRecv(self, IRC, line, data):
- modemapping=dict(Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice")
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- if data==None:
- print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.consolelogs[IRC].flush()
- return
- (origin, ident, host, cmd, target, params, extinfo)=data
- if re.match("^\\d+$",cmd): cmd=int(cmd)
- if cmd in (324, 329):
- modeparams=params.split()
- channame=modeparams[0]
- channel=IRC.channel(channame)
- if channel in self.channellogs.keys() and not self.channellogs[channel].closed: log=self.channellogs[channel]
- else: log=self.consolelogs[IRC]
- print >>log, "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- log.flush()
- elif cmd == 332:
- channel=IRC.channel(params)
- if channel in self.channellogs.keys() and not self.channellogs[channel].closed: log=self.channellogs[channel]
- else: log=self.consolelogs[IRC]
- print >>log, "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- log.flush()
- elif cmd == 333:
- (channame,nick,dt)=params.split()
- channel=IRC.channel(channame)
- if not self.channellogs[channel].closed:
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif cmd == 353:
- (flag, channame)=params.split()
- channel=IRC.channel(channame)
- if not self.channellogs[channel].closed:
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif cmd=="JOIN":
- user=IRC.user(origin)
- channel=IRC.channel(target if len(target) else extinfo)
- if user==IRC.identity:
- self.openChannelLog(channel)
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif cmd == "PRIVMSG":
- if target and target[0] in IRC.supports["CHANTYPES"]:
- channel=IRC.channel(target)
- if ident and host:
- user=IRC.user(origin)
- classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])
- else:
- classes="server"
- if classes: print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line)
- else: print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif cmd == "NOTICE":
- if target and (target[0] in IRC.supports["CHANTYPES"] or (len(target)>1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])):
- if target[0] in IRC.supports["PREFIX"][1]:
- channel=IRC.channel(target[1:])
- else:
- channel=IRC.channel(target)
- if ident and host:
- user=IRC.user(origin)
- classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])
- else:
- classes="server"
- if classes: print >>self.channellogs[channel], "%s %s %s <<< %s" % (timestamp, time.tzname[now[-1]], classes, line)
- else: print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif target.lower()==IRC.identity.nick.lower() and not ident and not host:
- print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.consolelogs[IRC].flush()
- elif cmd == "TOPIC":
- channel=IRC.channel(target)
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- elif cmd == "PART":
- user=IRC.user(origin)
- channel=IRC.channel(target)
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- if user==IRC.identity:
- self.closeChannelLog(channel)
- elif cmd == "KICK":
- kicked=IRC.user(params)
- channel=IRC.channel(target)
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- if kicked==IRC.identity:
- self.closeChannelLog(channel)
- elif cmd == "MODE":
- if target and target[0] in IRC.supports["CHANTYPES"]:
- channel=IRC.channel(target)
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- else:
- print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.consolelogs[IRC].flush()
- elif cmd in ("NICK", "QUIT"):
- user=IRC.user(origin)
- for channel in user.channels:
- print >>self.channellogs[channel], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.channellogs[channel].flush()
- else:
- print >>self.consolelogs[IRC], "%s %s <<< %s" % (timestamp, time.tzname[now[-1]], line)
- self.consolelogs[IRC].flush()
- def onConnectAttempt(self, IRC):
- if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed:
- self.openConsoleLog(IRC)
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- print >>self.consolelogs[IRC], "%s %s *** Attempting connection to %s:%s." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- def onConnect(self, IRC):
- if IRC not in self.consolelogs.keys() or (not self.consolelogs[IRC]) or self.consolelogs[IRC].closed:
- self.openConsoleLog(IRC)
- #self.openConsoleLog(IRC)
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- print >>self.consolelogs[IRC], "%s %s *** Connection to %s:%s established." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- def onDisconnect(self, IRC):
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- for channel in IRC.identity.channels:
- print >>self.channellogs[channel], "%s %s *** Connection to %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- self.channellogs[channel].flush()
- self.closeChannelLog(channel)
- print >>self.consolelogs[IRC], "%s %s *** Connection %s:%s terminated." % (timestamp, time.tzname[now[-1]], IRC.server, IRC.port)
- self.consolelogs[IRC].flush()
- self.closeConsoleLog(IRC)
- def onSend(self, IRC, line, data, origin):
- modemapping=dict(Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice")
- now=time.localtime()
- timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
- (cmd, target, params, extinfo)=data
- if IRC.registered and cmd=="PRIVMSG" and "CHANTYPES" in IRC.supports.keys() and len(target) and target[0] in IRC.supports["CHANTYPES"]:
- channel=IRC.channel(target)
- if channel in IRC.identity.channels:
- classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]])
- if classes: print >>self.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- else: print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- self.channellogs[channel].flush()
- else:
- print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- self.consolelogs[IRC].flush()
- if IRC.registered and len(target) and (target[0] in IRC.supports["CHANTYPES"] or (len(target)>1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])) and cmd=="NOTICE":
- channel=IRC.channel(target[1:] if target[0] in IRC.supports["PREFIX"][1] else target)
- if channel in IRC.identity.channels:
- classes=" ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]])
- if classes: print >>self.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- else: print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- self.channellogs[channel].flush()
- else:
- print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
- self.consolelogs[IRC].flush()
+ def onSend(self, IRC, line, data, origin):
+ modemapping = dict(Y="ircop", q="owner", a="admin", o="op",
+ h="halfop", v="voice")
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ (cmd, target, params, extinfo) = data
+ if IRC.registered and cmd == "PRIVMSG" and "CHANTYPES" in IRC.supports.keys() and len(target) and target[0] in IRC.supports["CHANTYPES"]:
+ channel = IRC.channel(target)
+ if channel in IRC.identity.channels:
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]])
+ if classes:
+ print >>self.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ else:
+ print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ self.channellogs[channel].flush()
+ else:
+ print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ self.consolelogs[IRC].flush()
+ if IRC.registered and len(target) and (target[0] in IRC.supports["CHANTYPES"] or (len(target) > 1 and target[0] in IRC.supports["PREFIX"][1] and target[1] in IRC.supports["CHANTYPES"])) and cmd == "NOTICE":
+ channel = IRC.channel(target[1:] if target[0]
+ in IRC.supports["PREFIX"][1] else target)
+ if channel in IRC.identity.channels:
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and IRC.identity in channel.modes[mode]])
+ if classes:
+ print >>self.channellogs[channel], "%s %s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], classes, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ else:
+ print >>self.channellogs[channel], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ self.channellogs[channel].flush()
+ else:
+ print >>self.consolelogs[IRC], "%s %s >>> :%s!%s@%s %s" % (timestamp, time.tzname[now[-1]], IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, line)
+ self.consolelogs[IRC].flush()
diff --git a/sedbot.py b/sedbot.py
index 9a23006..9cbffb7 100644
--- a/sedbot.py
+++ b/sedbot.py
@@ -1,64 +1,78 @@
#!/usr/bin/python
-import os, re, time
+import os
+import re
+import time
+
class SED(object):
- def __init__(self, expiry=1800):
- self.__name__="SED Bot"
- self.__version__="0.0.1"
- self.expiry=expiry
- self.history=[]
- self.pattern=r"^!?s([,/#])((?:.|\\\1)*)\1((?:.|\\\1)*)\1([ig]*)$"
- def onRecv(self, IRC, line, data):
- if data==None: return
- self.replace(IRC, *data)
- def onSend(self, IRC, line, data, origin):
- if origin==self: return
- #print data
- (cmd, target, params, extinfo)=data
- if IRC.identity: self.replace(IRC, IRC.identity.nick, IRC.identity.idnt, IRC.identity.host, *data)
- def replace(self, IRC, origin, ident, host, cmd, target, params, extinfo):
- ### Clear out old data that has expired.
- while len(self.history) and self.history[0][0]<time.time()-self.expiry:
- del self.history[0]
- if len(target) and target[0]=="#" and cmd=="PRIVMSG":
- target=IRC.channel(target)
- matches=re.findall(self.pattern,extinfo)
- if matches:
- separator, find, replace, flags=matches[0]
- #print matches
- find=re.sub("\\\\([,/#\\\\])","\\1",find)
- replace=re.sub("\\\\(,/#\\\\)","\\1",replace)
- #print find, replace
- match=False
- #print self.history
- #print find
- #print replace
- for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__():
- #print target, target2, origin2, extinfo2
- if target!=target2: continue
- action=re.findall("^\x01ACTION\\s+(.*)\x01$", extinfo2)
- #print action
- if action:
- try:
- if re.findall(find, action[0]):
- sub=re.sub(find, replace, action[0], flags=re.I if "i" in flags else 0)
- target.msg("What %s really meant was: *%s %s" % (origin2, origin2, sub), origin=self)
- match=True
- break
- except:
- target.msg("%s: Invalid syntax" % (origin), origin=self)
- raise
- else:
- try:
- if re.findall(find, extinfo2):
- sub=re.sub(find, replace, extinfo2, flags=re.I if "i" in flags else 0)
- target.msg("What %s really meant to say was: %s" % (origin2, sub), origin=self)
- match=True
- break
- except:
- target.msg("%s: Invalid syntax" % (origin), origin=self)
- raise
- if not match:
- target.msg("%s: I tried. I really tried! But I could not find the pattern: %s" % (origin, find), origin=self)
- else:
- self.history.append((time.time(), IRC, (origin, ident, host, cmd, target, params, extinfo)))
+ def __init__(self, expiry=1800):
+ self.__name__ = "SED Bot"
+ self.__version__ = "0.0.1"
+ self.expiry = expiry
+ self.history = []
+ self.pattern = r"^!?s([,/#])((?:.|\\\1)*)\1((?:.|\\\1)*)\1([ig]*)$"
+
+ def onRecv(self, IRC, line, data):
+ if data is None:
+ return
+ self.replace(IRC, *data)
+
+ def onSend(self, IRC, line, data, origin):
+ if origin == self:
+ return
+ #print data
+ (cmd, target, params, extinfo) = data
+ if IRC.identity:
+ self.replace(IRC, IRC.identity.nick,
+ IRC.identity.idnt, IRC.identity.host, *data)
+
+ def replace(self, IRC, origin, ident, host, cmd, target, params, extinfo):
+ ### Clear out old data that has expired.
+ while len(self.history) and self.history[0][0] < time.time()-self.expiry:
+ del self.history[0]
+ if len(target) and target[0] == "#" and cmd == "PRIVMSG":
+ target = IRC.channel(target)
+ matches = re.findall(self.pattern, extinfo)
+ if matches:
+ separator, find, replace, flags = matches[0]
+ #print matches
+ find = re.sub("\\\\([,/#\\\\])", "\\1", find)
+ replace = re.sub("\\\\(,/#\\\\)", "\\1", replace)
+ #print find, replace
+ match = False
+ #print self.history
+ #print find
+ #print replace
+ for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__():
+ #print target, target2, origin2, extinfo2
+ if target != target2:
+ continue
+ action = re.findall("^\x01ACTION\\s+(.*)\x01$", extinfo2)
+ #print action
+ if action:
+ try:
+ if re.findall(find, action[0]):
+ sub = re.sub(find, replace, action[0], flags=re.I if "i" in flags else 0)
+ target.msg("What %s really meant was: *%s %s" % (origin2, origin2, sub), origin=self)
+ match = True
+ break
+ except:
+ target.msg("%s: Invalid syntax" % (origin),
+ origin=self)
+ raise
+ else:
+ try:
+ if re.findall(find, extinfo2):
+ sub = re.sub(find, replace, extinfo2, flags=re.I if "i" in flags else 0)
+ target.msg("What %s really meant to say was: %s" % (origin2, sub), origin=self)
+ match = True
+ break
+ except:
+ target.msg("%s: Invalid syntax" % (origin),
+ origin=self)
+ raise
+ if not match:
+ target.msg("%s: I tried. I really tried! But I could not find the pattern: %s" % (origin, find), origin=self)
+ else:
+ self.history.append((time.time(), IRC, (origin, ident,
+ host, cmd, target, params, extinfo)))
diff --git a/startirc.py b/startirc.py
index 2acf1b9..6d0d8e7 100755
--- a/startirc.py
+++ b/startirc.py
@@ -1,55 +1,72 @@
#!/usr/bin/python -i
-import os, re, time, logger, signal, figlet, cannon, wallet, autoexec, sys, irc, bouncer, readline
+import os
+import re
+import time
+import logger
+import signal
+import figlet
+import cannon
+import wallet
+import autoexec
+import sys
+import irc
+import bouncer
+import readline
readline.parse_and_bind("tab: complete")
-networks={}
+networks = {}
+
def quit(quitmsg="Goodbye!"):
- global networks
- modules=[]
- for IRC in networks.values():
- if IRC.isAlive():
- IRC.quit(quitmsg)
- while any([IRC.isAlive() for IRC in networks.values()]):
- time.sleep(0.25)
- for IRC in networks.values():
- for module in list(IRC.modules):
- IRC.rmModule(module)
- if module not in modules:
- modules.append(module)
- for module in modules:
- if "stop" in dir(module) and callable(module.stop) and "isAlive" in dir(module) and callable(module.isAlive) and module.isAlive():
- try:
- module.stop()
- except:
- pass
- print "Goodbye!"
- sys.exit()
-
-termcaught=False
+ global networks
+ modules = []
+ for IRC in networks.values():
+ if IRC.isAlive():
+ IRC.quit(quitmsg)
+ while any([IRC.isAlive() for IRC in networks.values()]):
+ time.sleep(0.25)
+ for IRC in networks.values():
+ for module in list(IRC.modules):
+ IRC.rmModule(module)
+ if module not in modules:
+ modules.append(module)
+ for module in modules:
+ if "stop" in dir(module) and callable(module.stop) and "isAlive" in dir(module) and callable(module.isAlive) and module.isAlive():
+ try:
+ module.stop()
+ except:
+ pass
+ print "Goodbye!"
+ sys.exit()
+
+termcaught = False
+
+
def sigterm(signum, frame):
- global termcaught
- if not termcaught:
- termcaught=True
- quit("Caught SIGTERM")
+ global termcaught
+ if not termcaught:
+ termcaught = True
+ quit("Caught SIGTERM")
signal.signal(signal.SIGTERM, sigterm)
-logroot=os.path.join(os.environ["HOME"], "IRC")
+logroot = os.path.join(os.environ["HOME"], "IRC")
-insomnialog=open(os.path.join(logroot, "insomnia.log"), "a")
-InsomniaIRC=networks["InsomniaIRC"]=irc.Connection(server="irc.insomniairc.net", ipv6=False, ssl=True, log=insomnialog)
+insomnialog = open(os.path.join(logroot, "insomnia.log"), "a")
+InsomniaIRC = networks["InsomniaIRC"] = irc.Connection(
+ server="irc.insomniairc.net", ipv6=False, ssl=True, log=insomnialog)
-ax=autoexec.Autoexec()
-log=logger.Logger(logroot)
-BNC=bouncer.Bouncer("", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem")
+ax = autoexec.Autoexec()
+log = logger.Logger(logroot)
+BNC = bouncer.Bouncer(
+ "", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem")
for (label, IRC) in networks.items():
- IRC.addModule(log, label=label)
- ### The password is 'hunter2'
- IRC.addModule(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512")
+ IRC.addModule(log, label=label)
+ ### The password is 'hunter2'
+ IRC.addModule(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512")
InsomniaIRC.addModule(ax, label="InsomniaIRC", autojoin=["#chat"])
for (label, IRC) in networks.items():
- IRC.start()
+ IRC.start()
diff --git a/wallet.py b/wallet.py
index 05321f0..a5126d5 100644
--- a/wallet.py
+++ b/wallet.py
@@ -1,37 +1,45 @@
#!/usr/bin/python
-import pickle, Crypto.Cipher.Blowfish, os, getpass
+import pickle
+import Crypto.Cipher.Blowfish
+import os
+import getpass
from threading import Lock
+
class Wallet(dict):
- def __init__(self, filename):
- self.lock=Lock()
- if os.path.isfile(filename):
- self.f=open(filename,"rb+")
- self.passwd=getpass.getpass()
- self.crypt=Crypto.Cipher.Blowfish.new(self.passwd)
- contents_encrypted=self.f.read()
- contents=self.crypt.decrypt(contents_encrypted+"\x00"*((-len(contents_encrypted))%8))
- if contents.startswith(self.passwd):
- self.update(dict(pickle.loads(contents[len(self.passwd):])))
- else:
- self.f.close()
- raise BaseException, "Incorrect Password"
- else:
- self.f=open(filename,"wb+")
- passwd=self.passwd=None
- while passwd==None or passwd!=self.passwd:
- passwd=getpass.getpass("Enter new password: ")
- self.passwd=getpass.getpass("Confirm new password: ")
- if passwd!=self.passwd: print "Passwords do not match!"
- self.crypt=Crypto.Cipher.Blowfish.new(self.passwd)
- self.flush()
- def flush(self):
- contents=self.passwd+pickle.dumps(self.items(), protocol=2)
- self.lock.acquire()
- try:
- self.f.seek(0)
- self.f.write(self.crypt.encrypt(contents+"\x00"*((-len(contents))%8)))
- self.f.truncate()
- self.f.flush()
- finally:
- self.lock.release()
+ def __init__(self, filename):
+ self.lock = Lock()
+ if os.path.isfile(filename):
+ self.f = open(filename, "rb+")
+ self.passwd = getpass.getpass()
+ self.crypt = Crypto.Cipher.Blowfish.new(self.passwd)
+ contents_encrypted = self.f.read()
+ contents = self.crypt.decrypt(contents_encrypted +
+ "\x00"*((-len(contents_encrypted))%8))
+ if contents.startswith(self.passwd):
+ self.update(dict(pickle.loads(contents[len(self.passwd):])))
+ else:
+ self.f.close()
+ raise BaseException("Incorrect Password")
+ else:
+ self.f = open(filename, "wb+")
+ passwd = self.passwd = None
+ while passwd is None or passwd != self.passwd:
+ passwd = getpass.getpass("Enter new password: ")
+ self.passwd = getpass.getpass("Confirm new password: ")
+ if passwd != self.passwd:
+ print "Passwords do not match!"
+ self.crypt = Crypto.Cipher.Blowfish.new(self.passwd)
+ self.flush()
+
+ def flush(self):
+ contents = self.passwd+pickle.dumps(self.items(), protocol=2)
+ self.lock.acquire()
+ try:
+ self.f.seek(0)
+ self.f.write(self.crypt.encrypt(
+ contents+"\x00"*((-len(contents))%8)))
+ self.f.truncate()
+ self.f.flush()
+ finally:
+ self.lock.release()