summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bouncer.py335
-rw-r--r--cannon.py30
-rw-r--r--figlet.py20
-rw-r--r--irc3.py965
-rw-r--r--logger.py313
-rw-r--r--poker.py184
-rw-r--r--sedbot.py69
7 files changed, 1916 insertions, 0 deletions
diff --git a/bouncer.py b/bouncer.py
new file mode 100644
index 0000000..be4231d
--- /dev/null
+++ b/bouncer.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+import socket, ssl, os, re, time, sys, string
+from threading import Thread
+import Queue
+
+class Bouncer (Thread):
+ def __init__(self, addr, port, servers, ssl=False, certfile=None, keyfile=None, ignore=None):
+ self.__name__="Bouncer for pyIRC"
+ self.__version__="1.0.0alpha1"
+ self.__author__="Brian Sherson"
+ self.__date__="Apr 21, 2013"
+ #print "Initializing ListenThread..."
+ self.addr=addr
+ self.port=port
+ self.servers=servers
+ 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=[]
+ for server in servers.values(): server.modules.append(self)
+ self.ignore=ignore
+ self.whoexpected=[]
+ Thread.__init__ ( 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 cmd=="352": ### Who reply
+ if len(self.whoexpected) and self.whoexpected[0] in self.connections and self.whoexpected[0].IRC==IRC:
+ self.whoexpected[0].connection.send(line+"\n")
+ elif cmd=="315": ### End of Who reply
+ if len(self.whoexpected) and self.whoexpected[0] in self.connections and self.whoexpected[0].IRC==IRC:
+ self.whoexpected[0].connection.send(line+"\n")
+ del self.whoexpected[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):
+ #print "Bouncer onSend"
+ 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 in ("PRIVMSG", "NOTICE"):
+ for bouncer in self.connections:
+ if bouncer==origin: continue
+ #print bouncer.IRC
+ #print IRC
+ if bouncer.IRC==IRC: bouncer.connection.send(":%s!%s@%s %s\n" % (bouncer.IRC.identity.nick, bouncer.IRC.identity.idnt, bouncer.IRC.identity.host, line))
+ elif cmd=="WHO":
+ #print origin, line
+ self.whoexpected.append(origin)
+ def stop(self):
+ self.socket.shutdown(0)
+ def onDisconnect(self, IRC):
+ for bouncer in self.connections:
+ if bouncer.IRC==IRC:
+ quitmsg="Bouncer has been disconnected from IRC"
+ try:
+ bouncer.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.nick, self.addr[0], quitmsg))
+ bouncer.connection.close()
+ bouncer.connection.shutdown(0)
+ except:
+ pass
+ if bouncer in self.connections:
+ self.connections.remove(bouncer)
+
+class BouncerConnection (Thread):
+ def __init__(self, bouncerlistener, connection, addr):
+ #print "Initializing ListenThread..."
+ self.bouncerlistener=bouncerlistener
+ self.connection=connection
+ self.addr=addr
+ self.IRC=None
+ self.pwd=None
+ self.nick=None
+ self.idnt=None
+ self.realname=None
+ self.addr=addr
+ Thread.__init__ ( self )
+
+ def stop(self):
+ self.connection.shutdown(0)
+
+ def run(self):
+ #print "Bouncer Connection Started"
+ r=self.connection.makefile("r")
+ w=self.connection.makefile("w")
+ k=0
+ while self.pwd==None or self.nick==None or self.idnt==None:
+ line=r.readline().rstrip()
+ match=re.findall("^PASS :?(.*)$", line, re.I)
+ if match:
+ self.pwd=match[0]
+ match=re.findall("^NICK :?(.*)$", line, re.I)
+ if match:
+ self.nick=match[0]
+ match=re.findall("^USER\\s+(.+?)\\s+(.+?)\\s+(.+?):(.*)$", line, re.I)
+ if match:
+ self.idnt, a, b, self.realname=match[0]
+ if k>10:
+ self.connection.send("ERROR :Closing link: (%s@%s) [Access Denied]\n" % (self.nick, self.addr[0]))
+ self.connection.close()
+ sys.exit()
+ k+=1
+ for idnt, pwd in self.bouncerlistener.servers.keys():
+ if idnt.lower()==self.idnt.lower() and pwd==self.pwd:
+ self.IRC=self.bouncerlistener.servers[idnt, pwd]
+ if self.IRC==None or not self.IRC.registered:
+ self.connection.send("ERROR :Closing link: (%s@%s) [Access Denied]\n" % (self.nick, self.addr[0]))
+ self.connection.close()
+ sys.exit()
+
+ for bouncer in self.bouncerlistener.connections:
+ try:
+ bouncer.connection.send(":%s!%s@%s NOTICE %s :*** Bouncer Connection to %s originated from %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, bouncer.IRC.identity.nick, self.IRC, self.addr[0]))
+ except:
+ self.bouncerlistener.connections.remove(bouncer)
+
+ 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))
+ for params in self.IRC.serv005:
+ self.connection.send(":%s 005 %s %s :are supported by this server\n" % (self.IRC.serv, self.IRC.identity.nick, params))
+
+ 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))
+
+ 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))
+
+ for channel in self.IRC.identity.channels:
+ self.connection.send(":%s!%s@%s JOIN :%s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, channel.name))
+ 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))
+ secret="s" in channel.modes.keys() and channel.modes["s"]
+ private="p" in channel.modes.keys() and channel.modes["p"]
+ 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.bouncerlistener.connections.append(self)
+
+ quitmsg="Connection Closed"
+ readbuf=""
+ linebuf=[]
+
+ while True:
+ while len(linebuf)==0:
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ try:
+ read=self.connection.recv(512)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ print >>sys.stderr, "%(timestamp)s *** %(exc)s: %(excmsg)s" % vars()
+ server, port=self.addr
+ #server=self.addr[0]
+ #port=self.port
+ print >>sys.stderr, "%(timestamp)s *** Connection from %(server)s:%(port)s terminated" % vars()
+ self.bouncerlistener.connections.remove(self)
+ sys.stderr.flush()
+ raise
+ if read=="":
+ server, port=self.addr
+ #port=self.port
+ print >>sys.stderr, "%(timestamp)s *** Connection from %(server)s:%(port)s terminated" % vars()
+ self.bouncerlistener.connections.remove(self)
+ sys.stderr.flush()
+ 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 cmd.upper()=="QUIT":
+ quitmsg=extinfo
+ break
+ elif cmd.upper()=="PING":
+ try:
+ self.connection.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, self.IRC.identity.nick))
+ except:
+ sys.exit()
+ continue
+ elif cmd.upper() in ("PRIVMSG", "NOTICE"):
+ #print line
+ ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo)
+ if ctcp:
+ (ctcptype,ext)=ctcp[0]
+ if ctcptype=="LAGCHECK":
+ try:
+ self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ except:
+ self.connection.remove(self)
+ sys.exit()
+ elif ctcptype=="ACTION":
+ self.IRC.raw(line, origin=self)
+ #for bouncer in self.bouncerlistener.connections:
+ # if bouncer!=self and bouncer.IRC==self.IRC:
+ # try:
+ # bouncer.connection.send(":%s!%s@%s %s\n"%(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ # except:
+ # self.bouncerlistener.connections.remove(bouncer)
+ else:
+ self.IRC.raw(line, origin=self)
+ else:
+ self.IRC.raw(line, origin=self)
+ #for bouncer in self.bouncerlistener.connections:
+ # if bouncer!=self and bouncer.IRC==self.IRC:
+ # try:
+ # bouncer.connection.send(":%s!%s@%s %s\n"%(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ # except:
+ # self.bouncerlistener.connections.remove(bouncer)
+ elif cmd.upper() == "MODE":
+ #print "ddd", target, params, self.IRC.supports["CHANTYPES"]
+ if target 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.server, self.IRC.identity.nick, channel.name, modestr, params))
+ elif re.match("^\\+?[%s]+$"%self.IRC.supports["CHANMODES"][0], params) and extinfo=="":
+ #print "ddd Mode List Request", params
+ channel=self.IRC.channel(target)
+ 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"))
+ 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.server, i, channel.context.identity.nick, channel.name, mask, setby, settime))
+ self.connection.send(":%s %d %s %s :End of %s\n" % (self.IRC.server, 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.lower():
+ self.connection.send(":%s 221 %s +%s\n" % (self.IRC.server, self.IRC.identity.nick, channel.name, 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, channel.name, self.IRC.identity.snomask))
+ else:
+ self.IRC.raw(line, origin=self)
+ else:
+ self.IRC.raw(line, origin=self)
+
+
+
+ continue
+ #print line
+ match=re.findall("^quit(?:\\s:?(.*))?$", line, re.I)
+ #print match
+ if match:
+ quitmsg=match[0]
+ break
+ else:
+ #match=re.findall("^ping(?:\\s.*:?(.*))?$", line)
+ self.IRC.raw(line)
+ match=re.findall("^PRIVMSG (\\S+) :(.*)$", line, re.I)
+ #print match
+ if match:
+ (origin, ident, host, cmd, params)=(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, "PRIVMSG", "")
+ target, extinfo=match[0]
+ data=(origin, ident, host, cmd, target, params, extinfo)
+ modules=list(self.IRC.modules)
+ for channel in self.IRC.channels:
+ for module in channel.modules:
+ if module not in modules: modules.append(module)
+ for module in set(modules):
+ if module!=self.bouncerlistener:
+ #print ":%s!%s@%s %s"%(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line)
+ try:
+ module.process(self.IRC, ":%s!%s@%s %s"%(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line), data)
+ except:
+ pass
+ else:
+ for bouncer in self.bouncerlistener.connections:
+ if bouncer!=self and bouncer.IRC==self.IRC:
+ try:
+ bouncer.connection.send(":%s!%s@%s %s\n"%(self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ except:
+ self.bouncerlistener.connections.remove(bouncer)
+ self.connection.send("ERROR :Closing link: (%s@%s) [%s]\n" % (self.nick, self.addr[0], quitmsg))
+ self.connection.close()
+ self.bouncerlistener.connections.remove(self)
diff --git a/cannon.py b/cannon.py
new file mode 100644
index 0000000..3af725e
--- /dev/null
+++ b/cannon.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import re, 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))
diff --git a/figlet.py b/figlet.py
new file mode 100644
index 0000000..8f88416
--- /dev/null
+++ b/figlet.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+import re, 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()
diff --git a/irc3.py b/irc3.py
new file mode 100644
index 0000000..5f6fd08
--- /dev/null
+++ b/irc3.py
@@ -0,0 +1,965 @@
+#!/usr/bin/python
+from threading import Thread, Event, Lock
+import re, time, sys, string, socket, os, platform, traceback, Queue, ssl
+
+class Connection(Thread):
+ def __init__(self, nick="ircbot", ident="python", realname="Python IRC Library", passwd="", server="", port=None, ssl=False, autoreconnect=True, log=sys.stderr, onlogin=False):
+ self.debug=False
+ self.__name__="pyIRC"
+ self.__version__="1.0.0beta1"
+ self.__author__="Brian Sherson"
+ self.__date__="Apr 21, 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.identity=None
+
+ self.motdgreet=""
+ self.motd=[]
+
+ self.connected=False
+ self.registered=False
+ self.connection=None
+ self.autoreconnect=autoreconnect
+ self.maxretries=15
+ self.timeout=300
+ self.retrysleep=5
+ self.quitexpected=False
+ self.log=log
+
+ self.modules=[]
+ self.identity=None
+ self.users=[] #[self.self]
+ self.channels=[]
+ self.motdgreet=""
+ self.motd=[]
+
+ self.connected=False
+ self.registered=False
+ self.connection=None
+
+ self.mode=""
+ self.supports={}
+ self.log=log
+
+ self.lines=[]
+ self.quitexpected=False
+
+ self.lock=Lock()
+ self.loglock=Lock()
+ self.sendlock=Lock()
+ self.outgoing=Queue.Queue()
+
+ ### Initialize IRC environment variables
+ self.users=[]
+ self.channels=[]
+ if type(onlogin)==list:
+ self.onlogin=onlogin
+ else:
+ self.onlogin=[]
+ Thread.__init__(self)
+
+ def event(self, modlist, line, parsed):
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ for module in modlist:
+ if modlist is not self.modules and module in self.modules: continue
+ if "onRecv" in dir(module) and callable(module.onRecv):
+ try:
+ module.onRecv(self, line, parsed)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.loglock.acquire()
+ 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.loglock.release()
+
+ def run(self):
+ server=self.server
+ port=self.port
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ modules=list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules: modules.append(module)
+ for module in set(modules):
+ if "onLogOpen" in dir(module) and callable(module.onConnect): module.onLogOpen(self)
+ self.loglock.acquire()
+ print >>self.log, "%(timestamp)s ### Log session started" % vars()
+ self.loglock.release()
+ self.connected=False
+ self.registered=False
+ attempt=1
+ while True:
+ for t in xrange(self.maxretries):
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ modules=list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules: modules.append(module)
+ for module in set(modules):
+ try:
+ if "onConnectAttempt" in dir(module) and callable(module.onConnect): module.onConnectAttempt(self)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.loglock.acquire()
+ 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.loglock.release()
+ self.loglock.acquire()
+ print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars()
+ self.loglock.release()
+
+ try:
+ if self.ssl:
+ s=socket.socket(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_INET, socket.SOCK_STREAM)
+ self.connection.connect((self.server, self.port))
+ self.lock.acquire()
+ self.connected=True
+ except:
+ raise
+ if self.connected: break
+ time.sleep(self.retrysleep)
+
+ if not self.connected: raise SystemExit
+ self.connection.settimeout(self.timeout)
+
+ outgoingthread=Outgoing(self)
+ outgoingthread.daemon=True
+ outgoingthread.start()
+
+ modules=list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules: modules.append(module)
+ for module in set(modules):
+ try:
+ if "onConnect" in dir(module) and callable(module.onConnect): module.onConnect(self)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.loglock.acquire()
+ 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.loglock.release()
+ self.lock.release()
+
+ try:
+ nick=self.nick[0]
+ self.raw("NICK :%(nick)s" % vars())
+ self.raw("USER %(idnt)s * * :%(realname)s" % vars(self))
+ trynick=0
+ linebuf=[]
+ readbuf=""
+
+ while True:
+ while len(linebuf)==0:
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ try:
+ read=self.connection.recv(512)
+ except:
+ self.lock.acquire()
+ self.connected=False
+ self.registered=False
+ sys.stderr.flush()
+ break
+ if read=="":
+ self.lock.acquire()
+ self.connected=False
+ self.registered=False
+ sys.stderr.flush()
+ break
+ readbuf+=read
+ lastlf=readbuf.rfind("\n")
+ if lastlf>=0:
+ linebuf.extend(string.split(readbuf[0:lastlf], "\n"))
+ readbuf=readbuf[lastlf+1:]
+
+ if not self.connected:
+ if not self.lock.locked(): self.lock.acquire()
+ break
+ line=string.rstrip(linebuf.pop(0))
+
+ ping=re.findall("^PING :?(.*)$",line)
+ if len(ping):
+ self.lock.acquire()
+ try:
+ self.connection.send("PONG :%s\n" % ping[0])
+ except:
+ #self.lock.release()
+ self.registered=False
+ self.connected=False
+ break
+ self.lock.release()
+ continue
+
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ self.loglock.acquire()
+ print >> self.log, "%(timestamp)s <<< %(line)s" % vars()
+ self.log.flush()
+ self.loglock.release()
+
+ matches=re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$",line)
+ 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!="*":
+ self.lock.acquire()
+ 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)
+ for module in set(modules):
+ try:
+ if "onRegistered" in dir(module) and callable(module.onRegistered): module.onRegistered(self)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.loglock.acquire()
+ 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.loglock.release()
+ self.lock.release()
+
+ for event in self.onlogin:
+ self.raw(event)
+ elif cmd==433 and target=="*":
+ 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: continue
+ self.event(self.modules, line, parsed)
+
+ ### Major codeblock here!
+ self.lock.acquire()
+ 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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():
+ self.loglock.acquire()
+ 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.loglock.release()
+ 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(channel.modules, line, 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(channel.modules, line, 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]):
+ self.loglock.acquire()
+ 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.loglock.release()
+ 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(channel.modules, line, 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]):
+ self.loglock.acquire()
+ print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name)
+ self.loglock.release()
+ 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(channel.modules, line, parsed)
+ channel.lock.acquire(True)
+ 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)
+ #print mode
+ #print param
+ #print modeset
+ #print channel.modes.has_key(mode)
+ #if channel.modes.has_key(mode):
+ #print channel.modes[mode]
+ #print [p.lower() for p in channel.modes[mode]]
+ 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)
+ channel.lock.release()
+ 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(channel.modules, line, parsed)
+
+ channel.lock.acquire(True)
+ if channel.name!=target: channel.name=target ### Server seems to have changed the idea of the case of the channel name
+ channel.topic=extinfo
+ channel.lock.release()
+ elif cmd=="PRIVMSG":
+ #print target
+ 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(channel.modules, line, 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
+ #print chanobj
+ #print modules
+ #print modules
+
+ ### CTCP handling
+ ctcp=re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",extinfo)
+ #print ctcp
+ 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(channel.modules, line, 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(channel.modules, line, 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(channel.modules, line, 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[0])
+ self.event(channel.modules, line, parsed)
+ self.lock.release()
+
+ else:
+ self.event(self.modules, line, None)
+
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.loglock.acquire()
+ print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
+ print >>self.log, traceback.format_exc()
+ self.loglock.release()
+ try: self.quit("%(exc)s: %(excmsg)s" % vars())
+ except: pass
+ time.sleep(2)
+ #self.quitexpected=True
+ #raise
+ try:
+ self.connection.close()
+ except: pass
+ self.outgoing.put("quit")
+ while outgoingthread.isAlive():
+ time.sleep(0.01)
+ while True:
+ try:
+ self.outgoing.get(block=False)
+ except:
+ break
+ for channel in self.channels:
+ try:
+ channel.lock.release()
+ except:
+ pass
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ self.loglock.acquire()
+ print >>self.log, "%(timestamp)s *** Connection Terminated." % vars()
+ self.loglock.release()
+
+ modules=list(self.modules)
+ for channel in self.channels:
+ for module in channel.modules:
+ if module not in modules: modules.append(module)
+ for module in set(modules):
+ if "onDisconnect" in dir(module) and callable(module.onDisconnect): module.onDisconnect(self)
+ if self.lock.locked(): self.lock.release()
+
+ if (not self.autoreconnect) or self.quitexpected: break
+ self.identity.channels=[]
+ time.sleep(self.retrysleep)
+
+
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ self.loglock.acquire()
+ print >>self.log, "%(timestamp)s ### Log session ended" % vars()
+ self.log.flush()
+ self.loglock.release()
+ #self.log.close()
+
+ def __repr__(self):
+ server=self.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="*"
+ protocol="ircs" if self.ssl else "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):
+ self.quitexpected=True
+ 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]])
+ self.loglock.acquire()
+ print >>self.log, "%s *** User %s created."%(timestamp, nick)
+ self.log.flush()
+ self.loglock.release()
+ 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)
+ self.loglock.acquire()
+ print >>self.log, "%s *** Channel %s created."%(timestamp, name)
+ self.log.flush()
+ self.loglock.release()
+ 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)+">"
+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)
+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)
+
+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
+ if re.match("^quit(\\s.*)?$",line,re.I): self.IRC.quitexpected=True
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in time.localtime()[0:6]])
+ self.IRC.lock.acquire()
+ try:
+ self.IRC.connection.send("%(line)s\n" % vars())
+ except:
+ self.IRC.lock.release()
+ try:
+ self.IRC.connection.shutdown(0)
+ except:
+ pass
+ break
+ self.IRC.lock.release()
+ self.IRC.loglock.acquire()
+ print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars()
+ self.IRC.log.flush()
+ self.IRC.loglock.release()
+ match=re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
+ (cmd, target, params, extinfo)=match[0]
+ for module in self.IRC.modules:
+ #if module==origin: continue
+ if "onSend" in dir(module) and callable(module.onSend):
+ try:
+ module.onSend(self.IRC, line, (cmd, target, params, extinfo), origin)
+ except:
+ exc,excmsg,tb=sys.exc_info()
+ self.IRC.loglock.acquire()
+ print >>self.IRC.log, "%(timestamp)s !!! Exception in module %(module)s" % vars()
+ for tbline in traceback.format_exc().split("\n"):
+ print >>self.IRC.log, "%(timestamp)s !!! %(tbline)s" % vars()
+ self.IRC.loglock.release()
+ #self.IRC.connection.send(line)
+ 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
diff --git a/logger.py b/logger.py
new file mode 100644
index 0000000..7ee3e5e
--- /dev/null
+++ b/logger.py
@@ -0,0 +1,313 @@
+#!/usr/bin/python
+
+from threading import Thread, Lock
+import re, time, sys, string, socket, os, platform, traceback, Queue, ssl, urllib2
+
+class LogRotate(Thread):
+ def __init__(self, logger):
+ self.logger=logger
+ Thread.__init__(self)
+ 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:
+ #print "LogRotate will sleep until %d/%d/%d %d:%02d:%02d"%(time.localtime(nextrotate)[:6])
+ 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.25, nextrotate-time.time()))
+ self.logger.rotatelock.acquire()
+ 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
+ self.logger.logrotate=None
+ self.logger.rotatelock.release()
+ break
+ self.logger.rotatelock.release()
+ #print "Rotating Logs"
+ now=time.localtime()
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
+ for network in self.logger.labels.keys():
+ #network.loglock.acquire()
+ if network.connected:
+ network.lock.acquire()
+ self.logger.rotateConsoleLog(network)
+ if network.identity:
+ for channel in network.identity.channels:
+ self.logger.rotateChannelLog(channel)
+ network.lock.release()
+ nextrotate+=3600*24
+
+class Logger(object):
+ def __init__(self, logroot, **networks):
+ 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.networks=networks
+ self.labels={}
+ for (label,network) in networks.items():
+ if not os.path.isdir(os.path.join(self.logroot, label)):
+ os.mkdir(os.path.join(self.logroot, label))
+ if network in self.labels.keys():
+ raise BaseException, "Network already exists"
+ self.labels[network]=label
+ network.lock.acquire()
+ network.modules.append(self)
+ network.lock.release()
+ self.rotatelock=Lock()
+ self.logrotate=None
+ def addNetworks(self, **networks):
+ for (label,network) in networks.items():
+ if not os.path.isdir(os.path.join(self.logroot, label)):
+ os.mkdir(os.path.join(self.logroot, label))
+ if label in self.labels.values():
+ raise BaseException, "Label already exists"
+ if network in self.networks.keys():
+ raise BaseException, "Network already exists"
+ for (label,network) in networks.items():
+ self.labels[network]=label
+ network.lock.acquire()
+ network.modules.append(self)
+ if network.connected:
+ openConsoleLog(network)
+ network.lock.release()
+ def removeNetworks(self, *networks):
+ for network in networks:
+ if network not in self.networks.keys():
+ raise BaseException, "Network not added"
+ for network in networks:
+ network.lock.acquire()
+ network.modules.append(self)
+ if network.connected:
+ closeConsoleLog(network)
+ network.lock.release()
+ del self.labels[network]
+ del self.consolelogs[network]
+ def openConsoleLog(self, network):
+ self.rotatelock.acquire()
+ if not self.logrotate or not self.logrotate.isAlive():
+ self.logrotate=LogRotate(self)
+ self.logrotate.daemon=True
+ self.logrotate.start()
+ self.rotatelock.release()
+ now=time.localtime()
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
+ self.consolelogs[network]=open(os.path.join(self.logroot, self.labels[network], "console-%04d.%02d.%02d.log"%now[:3]), "a")
+ print >>self.consolelogs[network], "%s %s ### Log session started" % (timestamp, time.tzname[now[-1]])
+ self.consolelogs[network].flush()
+ def closeConsoleLog(self, network):
+ now=time.localtime()
+ timestamp=reduce(lambda x,y: x+":"+y,[str(t).rjust(2,"0") for t in now[0:6]])
+ print >>self.consolelogs[network], "%s %s ### Log session ended" % (timestamp, time.tzname[now[-1]])
+ self.consolelogs[network].close()
+ def rotateConsoleLog(self, network):
+ self.closeConsoleLog(network)
+ self.openConsoleLog(network)
+
+ def openChannelLog(self, channel):
+ self.rotatelock.acquire()
+ if not self.logrotate or not self.logrotate.isAlive():
+ self.logrotate=LogRotate(self)
+ self.logrotate.daemon=True
+ self.logrotate.start()
+ self.rotatelock.release()
+ 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):
+ 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()
diff --git a/poker.py b/poker.py
new file mode 100644
index 0000000..a4b8918
--- /dev/null
+++ b/poker.py
@@ -0,0 +1,184 @@
+#!/usr/bin/python
+import re, os, random, string, itertools
+
+spade='\xe2\x99\xa0'
+heart='\xe2\x99\xa5'
+diamond='\xe2\x99\xa6'
+club='\xe2\x99\xa3'
+faces=["A"]+range(2,11)+list("JQKA")
+suits=[(spade, 1), (club, 1), (heart, 4), (diamond, 4)]
+handsmapping=["High card", "One pair", "Two pair", "Three of a kind", "Straight", "Flush", "Full house", "Four of a kind", "Straight flush", "Royal flush"]
+
+class Game(object):
+ def __init__(self):
+ self.deck=deck=list(itertools.product(xrange(1,14), xrange(4)))
+ random.shuffle(deck)
+ random.shuffle(deck)
+ random.shuffle(deck)
+ random.shuffle(deck)
+ random.shuffle(deck)
+ self.status=0
+ self.players=[]
+ self.hands={}
+ self.waiting=None
+class Poker(object):
+ def __init__(self):
+ self.games={}
+ def onRecv(self, IRC, line, data):
+ if data==None: return
+ (origin, ident, host, cmd, target, params, extinfo)=data
+ #print data
+ if len(target) and target[0]=="#" and cmd=="PRIVMSG":
+ channel=IRC.channel(target)
+ user=IRC.user(origin)
+ matches=re.findall("^!poker (\\S+)(?:\\s+(.*))?$",extinfo)
+ if matches:
+ cmd, param=matches[0]
+ if cmd=="newgame":
+ if all([m not in channel.modes.keys() or user not in channel.modes[m] for m in "qao"]):
+ channel.msg("%s: You are not operator."%origin)
+ elif channel in self.games.keys():
+ channel.msg("%s: There is already a game going on in this channel."%origin)
+ else:
+ self.games[channel]=Game()
+ channel.msg("A new poker game has started. Type \x02!poker sit\x02 to join.")
+ elif cmd=="sit":
+ if channel not in self.games.keys():
+ channel.msg("%s: There is no game going on in this channel."%origin)
+ elif self.games[channel].status!=0:
+ channel.msg("%s: Cannot join the game at this time."%origin)
+ elif user in self.games[channel].players:
+ channel.msg("%s: You are already in the game."%origin)
+ elif len(self.games[channel].players)>=8:
+ channel.msg("%s: This game is full."%origin)
+ else:
+ self.games[channel].players.append(user)
+ channel.msg("%s: Welcome to the game."%origin)
+ elif cmd=="deal":
+ if all([m not in channel.modes.keys() or user not in channel.modes[m] for m in "qao"]):
+ channel.msg("%s: You are not operator."%origin)
+ elif channel not in self.games.keys():
+ channel.msg("%s: There is no game going on in this channel."%origin)
+ elif len(self.games[channel].players)==0:
+ channel.msg("%s: Nobody has sat yet."%origin)
+ elif self.games[channel].status>0:
+ channel.msg("%s: The cards have already been dealt."%origin)
+ else:
+ channel.me("deals poker hands to %s"%(string.join([user.nick for user in self.games[channel].players],", ")))
+ P=len(self.games[channel].players)
+ for user in self.games[channel].players:
+ hand=list(self.games[channel].deck[0:5*P:P])
+ del self.games[channel].deck[0:5*P:P]
+ self.games[channel].hands[user]=hand
+ user.notice("Your poker hand is: %s"%(string.join(["\x03%d,0\x02%s%s\x0f"%(suits[s][1], faces[f], suits[s][0]) for (f,s) in hand],", ")))
+ self.games[channel].status=1
+ self.games[channel].waiting=self.games[channel].players[0]
+ channel.msg("The cards have been dealt.")
+ channel.msg("%s: Do you wish to draw any cards? Type \x02!poker draw n1,n2,...\x02, where n1,n2,... is a list of cards \x02by index\x02 you wish to draw (0 for first card, 1 for second, etc...). Empty list means you wish to keep all cards."%self.games[channel].waiting.nick)
+ elif cmd=="draw":
+ if channel not in self.games.keys():
+ channel.msg("%s: There is no game going on in this channel."%origin)
+ elif user not in self.games[channel].players:
+ channel.msg("%s: You are not in this game."%origin)
+ elif self.games[channel].status!=1:
+ channel.msg("%s: We are not exchanging cards yet."%origin)
+ elif self.games[channel].waiting!=user:
+ channel.msg("%s: It is not your turn to draw cards yet."%origin)
+ else:
+ if param and any([card not in "01234" for card in param.split(",")]):
+ channel.msg("%s: I could not understand your request."%origin)
+ else:
+ if param=="":
+ channel.msg("%s is keeping all cards."%origin)
+ discards=[]
+ else:
+ discards=[]
+ #print "Param",param
+ for cardid in param.split(","):
+ card=self.games[channel].hands[user][int(cardid)]
+ #print "Discarding ",card
+ if card not in discards: discards.append(card)
+ for card in discards: self.games[channel].hands[user].remove(card)
+ channel.msg("%s is exchanging %d card%s."%(origin, len(discards), "s" if len(discards)>1 else ""))
+ self.games[channel].hands[user].extend(self.games[channel].deck[:len(discards)])
+ del self.games[channel].deck[:len(discards)]
+ self.games[channel].deck.extend(discards)
+ user.notice("Your new poker hand is: %s"%(string.join(["\x03%d,0\x02%s%s\x0f"%(suits[s][1], faces[f], suits[s][0]) for (f,s) in self.games[channel].hands[user]],", ")))
+ k=self.games[channel].players.index(user)
+ if k<len(self.games[channel].players)-1:
+ self.games[channel].waiting=self.games[channel].players[k+1]
+ channel.msg("%s: Do you wish to draw any cards? Type \x02!poker draw n1,n2,...\x02, where n1,n2,... is a list of cards \x02by index\x02 you wish to draw (0 for first card, 1 for second, etc...). Empty list means you wish to keep all cards."%self.games[channel].waiting.nick)
+ else:
+ self.games[channel].waiting=None
+ channel.msg("Exchanges done! Waiting for dealer to type \x02!poker show\x02.")
+ self.games[channel].status=2
+ elif cmd=="show":
+ if all([m not in channel.modes.keys() or user not in channel.modes[m] for m in "qao"]):
+ channel.msg("%s: Access denied."%origin)
+ elif channel not in self.games.keys():
+ channel.msg("%s: There is no game going on in this channel."%origin)
+ elif self.games[channel].status!=2:
+ channel.msg("%s: We are not ready to show cards."%origin)
+ else:
+ results=[]
+ for user in self.games[channel].players:
+ hand=self.games[channel].hands[user]
+ t=evalhand(hand)
+ channel.msg("%s\xe2\x80\x99s poker hand is: %s. A \x02%s\x02."%(user.nick, string.join(["\x03%d,0\x02%s%s\x0f"%(suits[s][1], faces[f], suits[s][0]) for (f,s) in hand],", "), handsmapping[t[0]]))
+ results.append((t,user))
+ results.sort(reverse=True)
+ top=results[0][0]
+ winners=[user.nick for (t,user) in results if t==top]
+ if len(winners)>2:
+ channel.msg("The winners are %s, and %s. A %d-way tie. Well played, gentlemen!"%(string.join(winners[:-1], ", "), winners[-1], len(winners)))
+ elif len(winners)==2:
+ channel.msg("The winners are %s and %s. A tie. Well played, gentlemen!"%tuple(winners))
+ else:
+ channel.msg("The winner is %s. Well played, gentlemen!"%winners[0])
+ del self.games[channel]
+ #matches=re.findall("^!shuffle(?:\\s+([\\d]+))?$",extinfo)
+ #if matches:
+ #if matches[0]: shuffles=int(matches[0])
+ #else: shuffles=1
+ #if shuffles>1000:
+ #channel.msg("Get real, %s!"%origin)
+ #return
+ #for s in xrange(shuffles): random.shuffle(deck)
+ #channel.me("shuffles the deck %d time%s."%(shuffles, "s" if shuffles>1 else ""))
+
+def evalhand(hand):
+ facevalues=[face for (face,suit) in hand]
+ facevalues.sort(reverse=True)
+ suits=[suit for (face,suit) in hand]
+
+ duplicities=[(facevalues.count(k),k) for k in xrange(1,14)]
+ duplicities.sort(reverse=True)
+ counts=[count for (count,k) in duplicities]
+ faces=[k for (count,k) in duplicities]
+ ### Check for flush
+ if suits==[0]*5: flush=True
+ elif suits==[1]*5: flush=True
+ elif suits==[2]*5: flush=True
+ elif suits==[3]*5: flush=True
+ else: flush=False
+
+ ### Check for straight
+ if (max(counts)==1 and max(facevalues)-min(facevalues)==4) or counts==[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]: straight=True
+ else: straight=False
+
+ if flush and not straight: return (5,)+tuple(faces)
+ elif straight and not flush: return (4,)+tuple(faces)
+ elif flush and counts==[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]: return (9,)+tuple(faces)
+ elif flush and straight: return (8,)+tuple(faces)
+
+ if 3 in counts and 2 in counts: return (6,)+tuple(faces)
+
+ if 4 in counts: return (7,)+tuple(faces)
+
+ if 3 in counts: return (3,)+tuple(faces)
+
+ if counts.count(2)==2: return (2,)+tuple(faces)
+
+ if 2 in counts: return (1,)+tuple(faces)
+
+ return (0,)+tuple(faces)
diff --git a/sedbot.py b/sedbot.py
new file mode 100644
index 0000000..962526e
--- /dev/null
+++ b/sedbot.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+import os, re, time
+
+class SED(object):
+ def __init__(self):
+ self.__name__="SED Bot"
+ self.__version__="0.0.1"
+ self.history=[]
+ self.pattern=r"^!s([,/#])((?:.|\\\1)*)\1((?:.|\\\1)*)\1([ig]*)$"
+ def onRecv(self, IRC, line, data):
+ if data==None: return
+ (origin, ident, host, cmd, target, params, extinfo)=data
+ if len(target) and target[0]=="#": target=IRC.channel(target)
+ if cmd=="PRIVMSG":
+ matches=re.findall(self.pattern,extinfo)
+ if matches:
+ separator, find, replace, flags=matches[0]
+ find=re.sub("\\\\([,/#\\\\])","\\1",find)
+ replace=re.sub("\\\\(,/#\\\\)","\\1",replace)
+ match=False
+ for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__():
+ if target!=IRC2.channel(target2): continue
+ 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, data))
+ while len(self.history) and self.history[0][0]<time.time()-1800: del self.history[0]
+ def onSend(self, IRC, line, data, origin):
+ if origin==self: return
+ #print data
+ (cmd, target, params, extinfo)=data
+ if len(target) and target[0]=="#": target=IRC.channel(target)
+ if cmd=="PRIVMSG":
+ matches=re.findall(self.pattern,extinfo)
+ #print matches
+ if matches:
+ separator, find, replace, flags=matches[0]
+ find=re.sub("\\\\(.)","\\1",find)
+ replace=re.sub("\\\\(.)","\\1",replace)
+ match=False
+ for t, IRC2, (origin2, ident2, host2, cmd2, target2, params2, extinfo2) in self.history.__reversed__():
+ #print target
+ #print target2
+ #print IRC2.channel(target2)
+ if target!=IRC2.channel(target2): continue
+ try:
+ if re.findall(find, extinfo2):
+ sub=re.sub(find, replace, extinfo2, flags=re.I if "i" in flags else 0)
+ #print sub
+ 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" % (IRC.identity.nick, find), origin=self)
+ else:
+ self.history.append((time.time(), IRC, (IRC.identity.nick, IRC.identity.idnt, IRC.identity.host)+data))
+ while len(self.history) and self.history[0][0]<time.time()-1800: del self.history[0]