summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Sherson <caretaker82@euclid.shersonb.net>2013-12-04 01:35:44 -0800
committerBrian Sherson <caretaker82@euclid.shersonb.net>2013-12-04 01:35:44 -0800
commit0d802dab5b586d511a6291248702c910502acaf7 (patch)
treef370db15b1a29c2fa8b6699188d28923b1361462
parent5cf8d2185f89aaf30e856bd0dbca88fbb51789ba (diff)
Significant changes
-rw-r--r--autoexec.py55
-rw-r--r--bouncer.py513
-rw-r--r--cannon.py56
-rw-r--r--figlet.py33
-rw-r--r--iqueue.py287
-rw-r--r--irc.py1497
-rw-r--r--logger.py864
-rw-r--r--poker.py218
-rw-r--r--sedbot.py105
-rwxr-xr-xstartirc.py31
10 files changed, 2147 insertions, 1512 deletions
diff --git a/autoexec.py b/autoexec.py
index a1fa52b..978501e 100644
--- a/autoexec.py
+++ b/autoexec.py
@@ -1,12 +1,13 @@
#!/usr/bin/python
import re
+import irc
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):
+ def onAddonAdd(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")
@@ -14,7 +15,7 @@ class Autoexec(object):
raise BaseException("Network already exists")
self.networks[IRC] = (label, onconnect, onregister, autojoin, usermodes, wallet, opername, opermodes, snomasks, operexec, operjoin)
- def onModuleRem(self, IRC):
+ def onAddonRem(self, IRC):
del self.networks[IRC]
def onConnect(self, IRC):
@@ -36,30 +37,24 @@ class Autoexec(object):
if autojoin:
IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
- def onRecv(self, IRC, line, data):
- if data is None:
- return
+ def on381(self, IRC, line, origin, target, params, extinfo):
(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)
+ 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):
+ def onAddonAdd(self, IRC, label, wallet=None, autojoin=None):
labels = [v[0] for v in self.networks.values()]
#print labels
if label in labels:
@@ -68,16 +63,20 @@ class NickServ(object):
raise BaseException("Network already exists")
self.networks[IRC] = (label, wallet, autojoin)
- def onModuleRem(self, IRC):
+ def onAddonRem(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
+ def onPrivNotice(self, IRC, origin, msg):
+ label, wallet, autojoin = self.networks[IRC]
+ if type(origin) == irc.User and origin.nick.lower() == "nickserv":
+ if re.match("This nickname is registered( and protected)?", msg) and wallet and "%s/NickServ/%s"%(label, IRC.identity.nick.lower()) in wallet.keys():
+ origin.msg("identify %s" % wallet["%s/NickServ/%s" %
+ (label, IRC.identity.nick.lower())])
+ if re.match("You are now identified", msg):
+ if autojoin:
+ IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
+
+ def on900(self, IRC, line, origin, target, params, extinfo):
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:
+ if autojoin:
IRC.raw("JOIN %s"%(",".join(autojoin)), origin=self)
diff --git a/bouncer.py b/bouncer.py
index c367dfd..9cfc786 100644
--- a/bouncer.py
+++ b/bouncer.py
@@ -8,31 +8,35 @@ import sys
import string
import hashlib
import traceback
+import irc
from threading import Thread, Lock
import Queue
class Bouncer (Thread):
- def __init__(self, addr="", port=16667, ssl=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr, timeout=300):
+ def __init__(self, addr="", port=16667, ssl=False, ipv6=False, certfile=None, keyfile=None, ignore=None, debug=False, log=sys.stderr, timeout=300, BNC=None, autoaway=None):
self.__name__ = "Bouncer for pyIRC"
- self.__version__ = "1.0.0rc2"
+ self.__version__ = "1.1"
self.__author__ = "Brian Sherson"
- self.__date__ = "August 26, 2013"
- #print "Initializing ListenThread..."
+ self.__date__ = "December 1, 2013"
+
self.addr = addr
self.port = port
self.servers = {}
self.passwd = {}
- self.socket = s = socket.socket()
+ self.socket = socket.socket(
+ socket.AF_INET6 if ipv6 else socket.AF_INET)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ssl = ssl
+ self.ipv6 = ipv6
self.certfile = certfile
self.keyfile = keyfile
- s.bind((self.addr, self.port))
+ self.socket.bind((self.addr, self.port))
self.connections = []
self.ignore = ignore
self.debug = debug
self.timeout = timeout
+ self.autoaway = autoaway
### 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.
@@ -71,102 +75,13 @@ class Bouncer (Thread):
connection.settimeout(self.timeout)
bouncer = BouncerConnection(
self, connection, addr, debug=self.debug)
- #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].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].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].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.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.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.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"):
+ def onAddonAdd(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():
@@ -174,15 +89,11 @@ class Bouncer (Thread):
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()
+ IRC.logwrite("dbg [Bouncer.onAddonAdd] Clearing WHO expected list." % vars())
self.whoisexpected[IRC] = []
self.listexpected[IRC] = []
- def onModuleRem(self, IRC):
+ def onAddonRem(self, IRC):
for bouncerconnection in self.connections:
if bouncerconnection.IRC == IRC:
bouncerconnection.quit(quitmsg="Bouncer extension removed")
@@ -197,32 +108,263 @@ class Bouncer (Thread):
for bouncerconnection in self.connections:
bouncerconnection.stop(quitmsg=quitmsg)
- def onDisconnect(self, IRC):
+ def onDisconnect(self, IRC, expected=False):
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()
+ IRC.logwrite("dbg [Bouncer.onDisconnect] Clearing WHO expected list.")
self.whoisexpected[IRC] = []
self.listexpected[IRC] = []
for bouncerconnection in self.connections:
if bouncerconnection.IRC == IRC:
bouncerconnection.quit(quitmsg="IRC connection lost")
+ def onSendChanMsg(self, IRC, origin, channel, targetprefix, msg):
+ ### Called when bot sends a PRIVMSG to channel.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ for bouncerconnection in self.connections:
+ if IRC == bouncerconnection.IRC and origin != bouncerconnection:
+ bouncerconnection.send(":%s!%s@%s PRIVMSG %s%s :%s\n"%(IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg))
+
+ def onSendChanAction(self, IRC, origin, channel, targetprefix, action):
+ self.onSendChanMsg(IRC, origin, channel, targetprefix,
+ "\x01ACTION %s\x01"%action)
+
+ def onSendChanNotice(self, IRC, origin, channel, targetprefix, msg):
+ ### Called when bot sends a NOTICE to channel.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ for bouncerconnection in self.connections:
+ if IRC == bouncerconnection.IRC and origin != bouncerconnection:
+ bouncerconnection.send(":%s!%s@%s NOTICE %s%s :%s\n"%(IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg))
+
+ def onSend(self, IRC, origin, line, cmd, target, params, extinfo):
+ if cmd.upper() == "WHO":
+ self.whoexpected[IRC].append(origin)
+ if self.debug:
+ with IRC.loglock:
+ timestamp = irc.timestamp()
+ 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 onWhoEntry(self, IRC, origin, channel, user, channame, username, host, serv, nick, flags, hops, realname):
+ ### Called when a WHO list is received.
+ if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:
+ bncconnection = self.whoexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 352 %s %s %s %s %s %s %s :%s %s\n"%(origin, IRC.identity.nick, channame, username, host, serv, nick, flags, hops, realname))
+ except socket.error:
+ pass
+
+ def onWhoEnd(self, IRC, origin, param, endmsg):
+ ### Called when a WHO list is received.
+ if len(self.whoexpected[IRC]) and self.whoexpected[IRC][0] in self.connections:
+ bncconnection = self.whoexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 315 %s %s :%s\n"%(
+ origin, IRC.identity.nick, param, endmsg))
+ except socket.error:
+ pass
+ finally:
+ del self.whoexpected[IRC][0]
+
+ def onWho(self, IRC, params, wholist, endmsg):
+ ### Called when a WHO list is received.
+ if len(self.whoexpected[IRC]):
+ try:
+ if self.whoexpected[IRC][0] in self.connections:
+ bncconnection = self.whoexpected[IRC][0]
+ lines = [":%s 352 %s %s %s %s %s %s %s :%s %s\n"%(IRC.serv, IRC.identity.nick, channame, username, host, serv, nick, flags, hops, realname) for (channel, user, channame, username, host, serv, nick, flags, hops, realname) in wholist]+[":%s 315 %s %s :%s\n"%(IRC.serv, IRC.identity.nick, params, endmsg)]
+ bncconnection.send("".join(lines))
+ finally:
+ del self.whoexpected[IRC][0]
+
+ def onListStart(self, IRC, origin, params, extinfo):
+ ### Called when a WHO list is received.
+ if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:
+ bncconnection = self.listexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 321 %s %s :%s\n"%(origin,
+ IRC.identity.nick, params, extinfo))
+ except socket.error:
+ pass
+
+ def onListEntry(self, IRC, origin, channel, population, extinfo):
+ ### Called when a WHO list is received.
+ if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:
+ bncconnection = self.listexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 322 %s %s %d :%s\n"%(origin, IRC.identity.nick, channel.name, population, extinfo))
+ except socket.error:
+ pass
+
+ def onListEnd(self, IRC, origin, endmsg):
+ ### Called when a WHO list is received.
+ if len(self.listexpected[IRC]) and self.listexpected[IRC][0] in self.connections:
+ bncconnection = self.listexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 323 %s :%s\n"%(
+ origin, IRC.identity.nick, endmsg))
+ except socket.error:
+ pass
+ finally:
+ del self.listexpected[IRC][0]
+
+ def onWhoisStart(self, IRC, origin, user, nickname, username, host, realname):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]):
+ if self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 311 %s %s %s %s * :%s\n" % (origin, IRC.identity.nick, nickname, username, host, realname))
+ except socket.error:
+ pass
+
+ def onWhoisRegisteredNick(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 307 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisConnectingFrom(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 378 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisChannels(self, IRC, origin, user, nickname, chanlist):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 319 %s %s :%s\n" % (origin, IRC.identity.nick, nickname, " ".join(chanlist)))
+ except socket.error:
+ pass
+
+ def onWhoisAvailability(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 310 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisServer(self, IRC, origin, user, nickname, server, servername):
+ ### Called when a WHOIS reply is received.
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 312 %s %s %s :%s\n" % (origin, IRC.identity.nick, nickname, server, servername))
+ except socket.error:
+ pass
+
+ def onWhoisOp(self, IRC, origin, user, nickname, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 313 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisAway(self, IRC, origin, user, nickname, awaymsg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 301 %s %s :%s\n" % (origin,
+ IRC.identity.nick, nickname, awaymsg))
+ except socket.error:
+ pass
+
+ def onWhoisTimes(self, IRC, origin, user, nickname, idletime, signontime, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 317 %s %s %d %d :%s\n" % (origin, IRC.identity.nick, nickname, idletime, signontime, msg))
+ except socket.error:
+ pass
+
+ def onWhoisSSL(self, IRC, origin, user, nickname, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 671 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisModes(self, IRC, origin, user, nickname, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 339 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+
+ def onWhoisLoggedInAs(self, IRC, origin, user, nickname, loggedinas, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 330 %s %s %s :%s\n" % (origin, IRC.identity.nick, nickname, loggedinas, msg))
+ except socket.error:
+ pass
+
+ def onWhoisEnd(self, IRC, origin, user, nickname, msg):
+ if len(self.whoisexpected[IRC]) and self.whoisexpected[IRC][0] in self.connections:
+ bncconnection = self.whoisexpected[IRC][0]
+ try:
+ bncconnection.send(":%s 318 %s %s :%s\n" % (
+ origin, IRC.identity.nick, nickname, msg))
+ except socket.error:
+ pass
+ finally:
+ del self.whoisexpected[IRC][0]
+
+ def onUnhandled(self, IRC, line, origin, cmd, target, params, extinfo):
+ for bouncerconnection in self.connections:
+ if bouncerconnection.IRC == IRC:
+ bouncerconnection.send("%s\n"%line)
+
class BouncerConnection (Thread):
def __init__(self, bouncer, connection, addr, debug=False):
#print "Initializing ListenThread..."
self.bouncer = bouncer
self.connection = connection
- self.host, self.port = self.addr = addr
+ if len(addr) == 4:
+ self.host, self.port = self.addr = addr[:2]
+ self.ipv6 = True
+ else:
+ self.host, self.port = self.addr = addr
+ self.ipv6 = False
self.IRC = None
self.pwd = None
self.nick = None
self.label = None
- self.idnt = None
+ self.username = None
self.realname = None
self.addr = addr
self.debug = debug
@@ -237,16 +379,10 @@ class BouncerConnection (Thread):
def send(self, data, flags=0):
try:
with self.lock:
- self.connection.send(data, flags=flags)
+ self.connection.send(data)
except socket.error:
- with self.IRC.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
- 2, "0") for t in time.localtime()[0:6]])
- exc, excmsg, tb = sys.exc_info()
- print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection.send] Exception in module %(module)s" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>self.IRC.log, "%(timestamp)s !!! [BouncerConnection.send] %(tbline)s" % vars()
- self.IRC.log.flush()
+ exc, excmsg, tb = sys.exc_info()
+ print >>self.IRC.logwrite(*["!!! [BouncerConnection.send] Exception in thread %(self)s" % vars()]+["!!! [BouncerConnection.send] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
self.quit(quitmsg=excmsg.message)
def __repr__(self):
@@ -254,7 +390,7 @@ class BouncerConnection (Thread):
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 "*"
+ ident = self.IRC.identity.username if self.IRC.identity.username else "*"
host = self.IRC.identity.host if self.IRC.identity.host else "*"
else:
nick = "*"
@@ -282,7 +418,12 @@ class BouncerConnection (Thread):
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"))
+ 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
@@ -296,7 +437,7 @@ class BouncerConnection (Thread):
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]])
+ timestamp = irc.timestamp()
read = self.connection.recv(512)
if read == "" and len(linebuf) == 0: # No more data to process.
#self.quitmsg="Connection Closed"
@@ -330,38 +471,31 @@ class BouncerConnection (Thread):
self.quit("Access Denied")
break
- elif not self.idnt: # Bouncer expects a USER command to finish registration
+ elif not self.username: # 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]
+ self.username = target
+ #print self.username
+ if self.username in self.bouncer.servers.keys():
+ self.IRC, passwdhash, hashtype = self.bouncer.servers[self.username]
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()
+ self.IRC.logwrite("*** [BouncerConnection] Incoming connection from %s to %s." % (self.host, self.IRC))
with self.bouncer.lock:
### Announce connection to all other bouncer connections.
if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Attempting to broadcast incoming connection %(self)s." % vars()
- self.IRC.log.flush()
+ self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast incoming connection %(self)s." % vars())
for bouncerconnection in self.bouncer.connections:
+ if bouncerconnection.IRC != self.IRC:
+ continue
if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars()
- self.IRC.log.flush()
+ self.IRC.logwrite("dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars())
if not bouncerconnection.quitting:
bouncerconnection.send(":*Bouncer* NOTICE %s :Incoming connection from %s to %s\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC))
if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars()
- self.IRC.log.flush()
+ self.IRC.logwrite("dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars())
+ if len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and self.IRC.identity.away:
+ ### Bouncer connection should automatically return from away status.
+ self.IRC.raw("AWAY")
self.bouncer.connections.append(self)
if not (self.IRC.connected and self.IRC.registered and type(self.IRC.supports) == dict and "CHANMODES" in self.IRC.supports.keys() and passmatch):
@@ -371,9 +505,9 @@ class BouncerConnection (Thread):
### 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:
+ while "*%s_%d"%(self.username, n) in labels:
n += 1
- self.label = "*%s_%d"%(self.idnt, n)
+ self.label = "*%s_%d"%(self.username, n)
### Request Version info.
#self.connection.send(":$bouncer PRIVMSG %s :\x01VERSION\x01\n" % (self.IRC.identity.nick))
@@ -384,8 +518,8 @@ class BouncerConnection (Thread):
with self.lock:
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))
+ self.connection.send(":%s 003 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servcreated))
+ self.connection.send(":%s 004 %s %s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.servinfo))
### 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()]
@@ -405,10 +539,16 @@ class BouncerConnection (Thread):
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))
+ if self.IRC.motdgreet and self.IRC.motd and self.IRC.motdend:
+ 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))
+ try:
+ self.connection.send(":%s 376 %s :%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.motdend))
+ except AttributeError:
+ self.connection.send(":%s 376 %s\n" % (self.IRC.serv, self.IRC.identity.nick))
+ else:
+ self.connection.send(":%s 422 %s :MOTD File is missing\n" % (self.IRC.serv, self.IRC.identity.nick))
### Send user modes and snomasks.
self.connection.send(":%s 221 %s +%s\n" % (self.IRC.serv, self.IRC.identity.nick, self.IRC.identity.modes))
@@ -416,7 +556,7 @@ class BouncerConnection (Thread):
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))
+ # self.connection.send(":%s!%s@%s JOIN :$bouncer\n" % (self.IRC.identity.nick, self.IRC.identity.username, 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))
@@ -435,7 +575,7 @@ class BouncerConnection (Thread):
### 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))
+ self.connection.send(":%s!%s@%s JOIN :%s\n" % (self.IRC.identity.nick, self.IRC.identity.username, 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))
@@ -469,33 +609,18 @@ class BouncerConnection (Thread):
break
elif cmd.upper() == "PING":
- with self.lock:
- 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":
- with self.lock:
- 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))
+ self.send(":%s PONG %s :%s\n" % (self.IRC.serv, self.IRC.serv, 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
- reply = ":%s!%s@%s PRIVMSG $bouncer :Version reply: %s\n" % (self.label, self.idnt, self.addr[0], ext)
- for bouncerconnection in self.bouncer.connections:
- bouncerconnection.connection.send(reply)
- elif ctcp: # If CTCP, only want to
+ if 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.
- with self.lock:
- self.connection.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.idnt, self.IRC.identity.host, line))
+ self.send(":%s!%s@%s %s\n" % (self.IRC.identity.nick, self.IRC.identity.username, self.IRC.identity.host, line))
else:
self.IRC.raw(line, origin=self)
else:
@@ -509,8 +634,10 @@ class BouncerConnection (Thread):
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]])
with self.lock:
- 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))
+ if len(modestr):
+ self.connection.send(":%s 324 %s %s +%s %s\n" % (self.IRC.serv, self.IRC.identity.nick, channel.name, modestr, params))
+ if channel.created:
+ 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)
@@ -543,12 +670,8 @@ class BouncerConnection (Thread):
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()
+ exc, excmsg, tb = sys.exc_info()
+ self.IRC.logwrite(*["!!! [BouncerConnection] Exception in thread %(self)s" % vars()]+["!!! [BouncerConnection] %(tbline)s" % vars() for tbline in traceback.format_exc().split("\n")])
finally:
### Juuuuuuust in case.
with self.lock:
@@ -559,36 +682,28 @@ class BouncerConnection (Thread):
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()
+ self.IRC.logwrite("*** [BouncerConnection] Connection from %s terminated (%s)." % (self.host, self.quitmsg))
if self in self.bouncer.connections:
with self.bouncer.lock:
self.bouncer.connections.remove(self)
+ if self.IRC.connected and self.IRC.identity and len([bncconnection for bncconnection in self.bouncer.connections if bncconnection.IRC == self.IRC]) == 0 and not self.IRC.identity.away and self.bouncer.autoaway:
+ ### Bouncer automatically sets away status.
+ self.IRC.raw("AWAY :%s"%self.bouncer.autoaway)
if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars()
- self.IRC.log.flush()
+ self.IRC.logwrite("dbg [BouncerConnection] Attempting to broadcast terminated connection %(self)s." % vars())
for bouncerconnection in self.bouncer.connections:
- if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars()
- self.IRC.log.flush()
- if not bouncerconnection.quitting:
- bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg))
+ if bouncerconnection.IRC == self.IRC:
if self.debug:
- 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, "%(timestamp)s dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars()
- self.IRC.log.flush()
+ self.IRC.logwrite("dbg [BouncerConnection] Broadcasting to %(bouncerconnection)s." % vars())
+ if not bouncerconnection.quitting:
+ bouncerconnection.connection.send(":*Bouncer* NOTICE %s :Connection from %s to %s terminated (%s)\n" % (bouncerconnection.IRC.identity.nick, self.host, self.IRC, self.quitmsg))
+ if self.debug:
+ self.IRC.logwrite("dbg [BouncerConnection] Success: %(bouncerconnection)s." % vars())
# ### 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))
+# bouncerconnection.connection.send(":%s!%s@%s QUIT :%s\n" % (self.label, self.username, self.host, self.quitmsg))
# except:
# pass
diff --git a/cannon.py b/cannon.py
index 55d9f00..fe02fdc 100644
--- a/cannon.py
+++ b/cannon.py
@@ -8,33 +8,31 @@ class Cannon(object):
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))
+ def onChanMsg(self, IRC, user, channel, targetprefix, msg):
+ matches = re.findall("^!fire\\s+(.*)$", msg)
+ if matches:
+ nickname = matches[0]
+ if any([nickname.lower() == usr.nick.lower() for usr in channel.users]):
+ vic = IRC.user(nickname)
+ if vic in self.firecount.keys():
+ count = self.firecount[vic]+1
else:
- channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(origin, nickname))
+ count = 1
+ self.firecount[vic] = 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." %
+ (vic.nick, count, ordinal))
+ else:
+ channel.msg("%s: I cannot fire %s out of a cannon, as he or she is not here."%(user.nick, nickname))
+
+ def onSendChanMsg(self, IRC, origin, channel, targetprefix, msg):
+ self.onChanMsg(IRC, IRC.identity, channel, targetprefix, msg)
diff --git a/figlet.py b/figlet.py
index 10120bb..d36a2ed 100644
--- a/figlet.py
+++ b/figlet.py
@@ -4,22 +4,17 @@ import os
class Figlet(object):
- 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()
+ def onChanMsg(self, IRC, user, channel, targetprefix, msg):
+ matches = re.findall("^!figlet\\s+(.*)$", msg)
+ 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/iqueue.py b/iqueue.py
new file mode 100644
index 0000000..4ad2cab
--- /dev/null
+++ b/iqueue.py
@@ -0,0 +1,287 @@
+'''A multi-producer, multi-consumer queue.'''
+
+try:
+ import threading
+except ImportError:
+ import dummy_threading as threading
+from collections import deque
+from heapq import heappush, heappop
+try:
+ from time import monotonic as time
+except ImportError:
+ from time import time
+
+__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
+
+
+class Empty(Exception):
+ 'Exception raised by Queue.get(block=0)/get_nowait().'
+ pass
+
+
+class Full(Exception):
+ 'Exception raised by Queue.put(block=0)/put_nowait().'
+ pass
+
+
+class Interrupted(Exception):
+ 'Exception raised by Queue.put and Queue.get when Queue is interrupted.'
+ pass
+
+
+class Queue:
+ '''Create a queue object with a given maximum size.
+
+ If maxsize is <= 0, the queue size is infinite.
+ '''
+
+ def __init__(self, maxsize=0):
+ self.maxsize = maxsize
+ self._init(maxsize)
+
+ # mutex must be held whenever the queue is mutating. All methods
+ # that acquire mutex must release it before returning. mutex
+ # is shared between the three conditions, so acquiring and
+ # releasing the conditions also acquires and releases mutex.
+ self.mutex = threading.Lock()
+
+ # Notify not_empty whenever an item is added to the queue; a
+ # thread waiting to get is notified then.
+ self.not_empty = threading.Condition(self.mutex)
+
+ # Notify not_full whenever an item is removed from the queue;
+ # a thread waiting to put is notified then.
+ self.not_full = threading.Condition(self.mutex)
+
+ # Notify all_tasks_done whenever the number of unfinished tasks
+ # drops to zero; thread waiting to join() is notified to resume
+ self.all_tasks_done = threading.Condition(self.mutex)
+ self.unfinished_tasks = 0
+ self._interrupted = False
+
+ def task_done(self):
+ '''Indicate that a formerly enqueued task is complete.
+
+ Used by Queue consumer threads. For each get() used to fetch a task,
+ a subsequent call to task_done() tells the queue that the processing
+ on the task is complete.
+
+ If a join() is currently blocking, it will resume when all items
+ have been processed (meaning that a task_done() call was received
+ for every item that had been put() into the queue).
+
+ Raises a ValueError if called more times than there were items
+ placed in the queue.
+ '''
+ with self.all_tasks_done:
+ unfinished = self.unfinished_tasks - 1
+ if unfinished <= 0:
+ if unfinished < 0:
+ raise ValueError('task_done() called too many times')
+ self.all_tasks_done.notify_all()
+ self.unfinished_tasks = unfinished
+
+ def join(self):
+ '''Blocks until all items in the Queue have been gotten and processed.
+
+ The count of unfinished tasks goes up whenever an item is added to the
+ queue. The count goes down whenever a consumer thread calls task_done()
+ to indicate the item was retrieved and all work on it is complete.
+
+ When the count of unfinished tasks drops to zero, join() unblocks.
+ '''
+ with self.all_tasks_done:
+ while self.unfinished_tasks:
+ self.all_tasks_done.wait()
+
+ def qsize(self):
+ '''Return the approximate size of the queue (not reliable!).'''
+ with self.mutex:
+ return self._qsize()
+
+ def empty(self):
+ '''Return True if the queue is empty, False otherwise (not reliable!).
+
+ This method is likely to be removed at some point. Use qsize() == 0
+ as a direct substitute, but be aware that either approach risks a race
+ condition where a queue can grow before the result of empty() or
+ qsize() can be used.
+
+ To create code that needs to wait for all queued tasks to be
+ completed, the preferred technique is to use the join() method.
+ '''
+ with self.mutex:
+ return not self._qsize()
+
+ def full(self):
+ '''Return True if the queue is full, False otherwise (not reliable!).
+
+ This method is likely to be removed at some point. Use qsize() >= n
+ as a direct substitute, but be aware that either approach risks a race
+ condition where a queue can shrink before the result of full() or
+ qsize() can be used.
+ '''
+ with self.mutex:
+ return 0 < self.maxsize <= self._qsize()
+
+ def put(self, item, block=True, timeout=None):
+ '''Put an item into the queue.
+
+ If optional args 'block' is true and 'timeout' is None (the default),
+ block if necessary until a free slot is available. If 'timeout' is
+ a positive number, it blocks at most 'timeout' seconds and raises
+ the Full exception if no free slot was available within that time.
+ Otherwise ('block' is false), put an item on the queue if a free slot
+ is immediately available, else raise the Full exception ('timeout'
+ is ignored in that case).
+ '''
+ with self.not_full:
+ ### Check if queue is interrupted
+ if self._interrupted:
+ raise Interrupted
+ if self.maxsize > 0:
+ if not block:
+ if self._qsize() >= self.maxsize:
+ raise Full
+ elif timeout is None:
+ while self._qsize() >= self.maxsize:
+ self.not_full.wait()
+ if self._interrupted:
+ raise Interrupted
+ elif timeout < 0:
+ raise ValueError("'timeout' must be a positive number")
+ else:
+ endtime = time() + timeout
+ while self._qsize() >= self.maxsize:
+ remaining = endtime - time()
+ if remaining <= 0.0:
+ raise Full
+ self.not_full.wait(remaining)
+ if self._interrupted:
+ raise Interrupted
+ self._put(item)
+ self.unfinished_tasks += 1
+ self.not_empty.notify()
+
+ def get(self, block=True, timeout=None):
+ '''Remove and return an item from the queue.
+
+ If optional args 'block' is true and 'timeout' is None (the default),
+ block if necessary until an item is available. If 'timeout' is
+ a positive number, it blocks at most 'timeout' seconds and raises
+ the Empty exception if no item was available within that time.
+ Otherwise ('block' is false), return an item if one is immediately
+ available, else raise the Empty exception ('timeout' is ignored
+ in that case).
+ '''
+
+ with self.not_empty:
+ ### Check if queue is interrupted
+ if self._interrupted:
+ raise Interrupted
+ if not block:
+ if not self._qsize():
+ raise Empty
+ elif timeout is None:
+ while not self._qsize():
+ self.not_empty.wait()
+ if self._interrupted:
+ raise Interrupted
+ elif timeout < 0:
+ raise ValueError("'timeout' must be a positive number")
+ else:
+ endtime = time() + timeout
+ while not self._qsize():
+ remaining = endtime - time()
+ if remaining <= 0.0:
+ raise Empty
+ self.not_empty.wait(remaining)
+ if self._interrupted:
+ raise Interrupted
+ item = self._get()
+ self.not_full.notify()
+ return item
+
+ def interrupt(self):
+ '''Place Queue into interrupted state. Empties the queue and causes
+ all threads to raise the Interrupted exception when Queue.get and
+ Queue.put are called.
+ '''
+ with self.not_empty:
+ self._interrupted = True
+ self.not_empty.notifyAll()
+
+ with self.not_full:
+ while self._qsize():
+ self._get()
+ self.not_full.notifyAll()
+
+ def put_nowait(self, item):
+ '''Put an item into the queue without blocking.
+
+ Only enqueue the item if a free slot is immediately available.
+ Otherwise raise the Full exception.
+ '''
+ return self.put(item, block=False)
+
+ def get_nowait(self):
+ '''Remove and return an item from the queue without blocking.
+
+ Only get an item if one is immediately available. Otherwise
+ raise the Empty exception.
+ '''
+ return self.get(block=False)
+
+ # Override these methods to implement other queue organizations
+ # (e.g. stack or priority queue).
+ # These will only be called with appropriate locks held
+
+ # Initialize the queue representation
+ def _init(self, maxsize):
+ self.queue = deque()
+
+ def _qsize(self):
+ return len(self.queue)
+
+ # Put a new item in the queue
+ def _put(self, item):
+ self.queue.append(item)
+
+ # Get an item from the queue
+ def _get(self):
+ return self.queue.popleft()
+
+
+class PriorityQueue(Queue):
+ '''Variant of Queue that retrieves open entries in priority order (lowest first).
+
+ Entries are typically tuples of the form: (priority number, data).
+ '''
+
+ def _init(self, maxsize):
+ self.queue = []
+
+ def _qsize(self):
+ return len(self.queue)
+
+ def _put(self, item):
+ heappush(self.queue, item)
+
+ def _get(self):
+ return heappop(self.queue)
+
+
+class LifoQueue(Queue):
+ '''Variant of Queue that retrieves most recently added entries first.'''
+
+ def _init(self, maxsize):
+ self.queue = []
+
+ def _qsize(self):
+ return len(self.queue)
+
+ def _put(self, item):
+ self.queue.append(item)
+
+ def _get(self):
+ return self.queue.pop()
diff --git a/irc.py b/irc.py
index 7ed9d1a..13f1922 100644
--- a/irc.py
+++ b/irc.py
@@ -8,16 +8,26 @@ import socket
import os
import platform
import traceback
-import Queue
import ssl
+import glob
+import iqueue as Queue
+
+
+def timestamp():
+ t = time.time()
+ ms = 1000*t%1000
+ ymdhms = time.localtime(t)
+ tz = time.altzone if ymdhms.tm_isdst else time.timezone
+ sgn = "-" if tz >= 0 else "+"
+ return "%04d-%02d-%02d %02d:%02d:%02d.%03d%s%02d:%02d"%(ymdhms[:6]+(1000*t%1000, sgn, abs(tz)/3600, abs(tz)/60%60))
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):
+ def __init__(self, server, nick="ircbot", username="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.__version__ = "1.1"
self.__author__ = "Brian Sherson"
- self.__date__ = "August 26, 2013"
+ self.__date__ = "December 1, 2013"
if port is None:
self.port = 6667 if not ssl else 6697
@@ -30,7 +40,7 @@ class Connection(Thread):
self.nick = nick
self.realname = realname
- self.idnt = ident
+ self.username = username
self.passwd = passwd
self.server = server
self.ssl = ssl
@@ -48,12 +58,13 @@ class Connection(Thread):
self.quitexpected = False
self.log = log
- self.modules = []
+ self.addons = []
self.trusted = []
### Initialize IRC environment variables
- self.motdgreet = ""
- self.motd = []
+ self.motdgreet = None
+ self.motd = None
+ self.motdend = None
self.identity = None
self.users = []
self.channels = []
@@ -66,83 +77,104 @@ class Connection(Thread):
Thread.__init__(self)
+ def logwrite(self, *lines):
+ with self.loglock:
+ ts = timestamp()
+ for line in lines:
+ print >>self.log, "%s %s"%(ts, line)
+ self.log.flush()
+
+ def logopen(self, filename):
+ with self.loglock:
+ ts = timestamp()
+ newlog = open(filename, "a")
+ if type(self.log) == file and not self.log.closed:
+ print >>self.log, "%s ### Log file closed" % (ts)
+ if self.log not in (sys.stdout, sys.stderr):
+ self.log.close()
+ self.log = newlog
+ print >>self.log, "%s ### Log file opened" % (ts)
+ self.log.flush()
+
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)):
+ handled = []
+ unhandled = []
+ errors = []
+ for k, addon in enumerate(modlist):
+ if modlist.index(addon) < k:
+ continue
+ if method in dir(addon) and callable(getattr(addon, method)):
try:
- getattr(module, method)(self, **params)
+ getattr(addon, 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()
+ exc, excmsg, tb = sys.exc_info()
+ errors.append((addon, exc, excmsg, tb))
+ self.logwrite(*["!!! Exception in addon %(addon)s" % vars()]+["!!! %s"%line for line in traceback.format_exc().split("\n")])
+ print >>sys.stderr, "Exception in addon %(addon)s" % vars()
+ print >>sys.stderr, traceback.format_exc()
if exceptions: # If set to true, we raise the exception.
raise
+ else:
+ handled.append(addon)
+ else:
+ unhandled.append(addon)
+ return (handled, unhandled, errors)
- def addModule(self, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException("Module already added.")
+ def addAddon(self, addon, trusted=False, **params):
+ if addon in self.addons:
+ raise BaseException("Addon already added.")
with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.append(module)
+ self.event("onAddonAdd", [addon], exceptions=True, **params)
+ self.addons.append(addon)
if trusted:
- self.trusted.append(module)
+ self.trusted.append(addon)
- def insertModule(self, index, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException("Module already added.")
+ def insertAddon(self, index, addon, trusted=False, **params):
+ if addon in self.addons:
+ raise BaseException("Addon already added.")
with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.insert(index, module)
+ self.event("onAddonAdd", [addon], exceptions=True, **params)
+ self.addons.insert(index, addon)
if trusted:
- self.trusted.append(module)
+ self.trusted.append(addon)
- def rmModule(self, module, **params):
+ def rmAddon(self, addon, **params):
with self.lock:
- self.modules.remove(module)
- self.event("onModuleRem", [module], exceptions=True, **params)
- if module in self.trusted:
- self.trusted.remove(module)
+ self.addons.remove(addon)
+ self.event("onAddonRem", [addon], exceptions=True, **params)
+ if addon in self.trusted:
+ self.trusted.remove(addon)
def run(self):
+ privmodeeventnames = dict(q=("Owner", "Deowner"), a=("Admin", "Deadmin"), o=("Op", "Deop"), h=("Halfop", "Dehalfop"), v=("Voice", "Devoice"))
+ maskmodeeventnames = dict(b=("Ban", "Unban"), e=(
+ "BanExcept", "UnbanExcept"), I=("Invite", "Uninvite"))
self.quitexpected = False
outgoingthread = None
+ whoisstarted = False
+ nameslist = []
+ wholist = []
+ lists = {}
+ nameschan = 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)
+ self.event("onSessionOpen", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
- 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()
+ self.logwrite("### Log session started")
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()
+ self.logwrite("*** Attempting connection to %(server)s:%(port)s." % vars())
with self.lock:
- self.event("onConnectAttempt", self.modules)
+ self.event("onConnectAttempt", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
try:
if self.ssl:
@@ -153,31 +185,22 @@ class Connection(Thread):
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))
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()
+ exc, excmsg, tb = sys.exc_info()
+ self.event("onConnectFail", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb)
+ self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars())
else:
self.connected = True
self.connection.settimeout(self.timeout)
### Setting up thread responsible for sending data back to IRC server.
+ self.outgoing._interrupted = False
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()
+ ### Run onConnect on all addons to signal connection was established.
+ self.event("onConnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
+ self.logwrite("*** Connection to %(server)s:%(port)s established." % vars())
break
if self.quitexpected:
@@ -188,11 +211,8 @@ class Connection(Thread):
sys.exit()
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
+ self.logwrite("*** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars())
+ sys.exit()
### Connection succeeded
try:
@@ -202,7 +222,8 @@ class Connection(Thread):
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))
+ self.raw("USER %(username)s * * :%(realname)s" %
+ vars(self))
### Initialize buffers
linebuf = []
@@ -233,17 +254,16 @@ class Connection(Thread):
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()
+ self.logwrite("<<< %(line)s" % vars())
### Attempts to match against pattern ":src cmd target params :extinfo"
- matches = re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line)
+ matches = re.findall(r"^:(.+?)(?:!(.+?)@(.+?))?\s+(.+?)(?:\s+(.+?)(?:\s+(.+?))??)??(?:\s+:(.*))?$", line)
### We have a match!
if len(matches):
- parsed = (origin, ident, host, cmd, target, params, extinfo) = matches[0]
+ parsed = (origin, username, host, cmd, target, params, extinfo) = matches[0]
+ unhandled = []
+
if re.match("^\\d+$", cmd):
cmd = int(cmd) # Code is a numerical response
else:
@@ -255,13 +275,7 @@ class Connection(Thread):
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)
+ self.event("onRegistered", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
elif cmd == 433 and target == "*": # Server reports nick taken, so we need to try another.
trynick += 1
@@ -273,18 +287,49 @@ class Connection(Thread):
if not self.registered: # Registration is not yet complete
continue
+ if username and host:
+ nickname = origin
+ origin = self.user(origin)
+ if origin.nick != nickname:
+ ### Origin nickname has changed
+ origin.user = nickname
+ if origin.username != username:
+ ### Origin username has changed
+ origin.username = username
+ if origin.host != host:
+ ### Origin host has changed
+ origin.host = host
+
+ chanmatch = re.findall("([%s]?)([%s].+)"%(re.escape(self.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.supports.get("CHANTYPES", "#"))), target)
+ if chanmatch:
+ targetprefix, channame = chanmatch[0]
+ target = self.channel(channame)
+ if target.name != channame:
+ ### Target channel name has changed
+ target.name = channame
+ elif len(target) and target[0] != "$" and cmd != "NICK":
+ targetprefix = ""
+ target = self.user(target)
+
+ data = dict(origin=origin, cmd=cmd, target=target, targetprefix=targetprefix, params=params, extinfo=extinfo)
+
### Major codeblock here! Track IRC state.
- ### Send line to modules first
+ ### Send line to addons first
with self.lock:
- self.event("onRecv", self.modules, line=line, data=parsed)
+ self.event("onRecv", self.addons, line=line,
+ **data)
if cmd == 1:
+ (handled, unhandled, exceptions) = self.event("onWelcome", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
self.welcome = extinfo # Welcome message
elif cmd == 2:
+ (handled, unhandled, exceptions) = self.event("onYourHost", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
self.hostinfo = extinfo # Your Host
elif cmd == 3:
- self.servinfo = extinfo # Server Created
+ (handled, unhandled, exceptions) = self.event("onServerCreated", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
+ self.servcreated = extinfo # Server Created
elif cmd == 4:
- self.serv004 = params # What is this code?
+ (handled, unhandled, exceptions) = self.event("onServInfo", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, servinfo=params)
+ self.servinfo = params # What is this code?
elif cmd == 5: # Server Supports
support = dict(re.findall("([A-Za-z0-9]+)(?:=(\\S*))?", params))
if "CHANMODES" in support:
@@ -295,173 +340,239 @@ class Connection(Thread):
support["PREFIX"] = matches[0]
else:
del support["PREFIX"] # Might as well delete the info if it doesn't match expected pattern
+ (handled, unhandled, exceptions) = self.event("onSupports", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, supports=support)
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("+")
+ elif cmd == 8: # Snomask
+ snomask = params.lstrip("+")
+ (handled, unhandled, exceptions) = self.event("onSnoMask", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=snomask)
+ self.identity.snomask = snomask
if "s" not in self.identity.modes:
self.snomask = ""
- elif cmd == 221: # Channel Modes
- self.identity.modes = (params if params else extinfo).lstrip("+")
+ elif cmd == 221: # User Modes
+ modes = (params if params else extinfo).lstrip("+")
+ (handled, unhandled, exceptions) = self.event("onUserModes", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=modes)
+ self.identity.modes = modes
if "s" not in self.identity.modes:
self.snomask = ""
- elif cmd == 251:
+ elif cmd == 251: # Net Stats
+ (handled, unhandled, exceptions) = self.event("onNetStats", self.addons, origin=origin, netstats=extinfo)
self.netstats = extinfo
elif cmd == 252:
- self.opcount = int(params)
+ opcount = int(params)
+ (handled, unhandled, exceptions) = self.event("onOpCount", self.addons, origin=origin, opcount=opcount)
+ self.opcount = opcount
elif cmd == 254:
- self.chancount = int(params)
- elif cmd == 311: # WHOIS data
- pass
+ chancount = int(params)
+ (handled, unhandled, exceptions) = self.event("onChanCount", self.addons, origin=origin, chancount=chancount)
+ self.chancount = chancount
+
+ elif cmd == 305: # Returned from away status
+ (handled, unhandled, exceptions) = self.event("onReturn", self.addons, origin=origin, msg=extinfo)
+ self.identity.away = False
+
+ elif cmd == 306: # Entered away status
+ (handled, unhandled, exceptions) = self.event("onAway", self.addons, origin=origin, msg=extinfo)
+ self.identity.away = True
+
+ elif cmd == 311: # Start of WHOIS data
+ nickname, username, host, star = params.split()
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisStart", self.addons, origin=origin, user=user, nickname=nickname, username=username, host=host, realname=extinfo)
+ user.nick = nickname
+ user.username = username
+ user.host = host
+ elif cmd == 301: # Away Message
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisAway", self.addons, origin=origin, user=user, nickname=params, awaymsg=extinfo)
+ user.away = True
+ user.awaymsg = extinfo
+ elif cmd == 307: # Is a registered nick
+ (handled, unhandled, exceptions) = self.event("onWhoisRegisteredNick", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 378: # Connecting From
+ (handled, unhandled, exceptions) = self.event("onWhoisConnectingFrom", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 319: # Channels
+ (handled, unhandled, exceptions) = self.event("onWhoisChannels", self.addons, origin=origin, user=self.user(params), nickname=params, chanlist=extinfo.split(" "))
+ elif cmd == 310: # Availability
+ (handled, unhandled, exceptions) = self.event("onWhoisAvailability", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 312: # Server
+ nickname, server = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisServer", self.addons, origin=origin, user=user, nickname=nickname, server=server, servername=extinfo)
+ user.server = server
+ elif cmd == 313: # IRC Op
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisOp", self.addons, origin=origin, user=user, nickname=params, msg=extinfo)
+ user.ircop = True
+ user.ircopmsg = extinfo
+ elif cmd == 317: # Idle and Signon times
+ nickname, idletime, signontime = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisTimes", self.addons, origin=origin, user=user, nickname=nickname, idletime=int(idletime), signontime=int(signontime), msg=extinfo)
+ user.idlesince = int(time.time())-int(idletime)
+ user.signontime = int(signontime)
+ elif cmd == 671: # SSL
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisSSL", self.addons, origin=origin, user=user, nickname=params, msg=extinfo)
+ user.ssl = True
+ elif cmd == 379: # User modes
+ (handled, unhandled, exceptions) = self.event("onWhoisModes", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 330: # Logged in as
+ nickname, loggedinas = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisLoggedInAs", self.addons, origin=origin, user=user, nickname=nickname, loggedinas=loggedinas, msg=extinfo)
+ user.loggedinas = loggedinas
+ elif cmd == 318: # End of WHOIS
+ (handled, unhandled, exceptions) = self.event("onWhoisEnd", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+
elif cmd == 321: # Start LIST
- self.chanlistbegin = (params, extinfo)
- self.chanlist = {}
+ (handled, unhandled, exceptions) = self.event("onListStart", self.addons, origin=origin, params=params, extinfo=extinfo)
elif cmd == 322: # LIST item
(chan, pop) = params.split(" ", 1)
- self.chanlist[chan] = (pop, extinfo)
- elif cmd == 323:
- self.chanlistend = extinfo # End of LIST
+ (handled, unhandled, exceptions) = self.event("onListEntry", self.addons, origin=origin, channel=self.channel(chan), population=int(pop), extinfo=extinfo)
+ elif cmd == 323: # End of LIST
+ (handled, unhandled, exceptions) = self.event("onListEnd", self.addons, origin=origin, endmsg=extinfo)
+
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)
+ self.event("onRecv", channel.addons, line=line, **data)
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)
+ modedelta = []
for mode in setmodes:
if mode == "+":
continue
elif mode in self.supports["CHANMODES"][2]:
param = modeparams.pop(0)
+ modedelta.append(("+%s"%mode, param))
+ elif mode in self.supports["CHANMODES"][3]:
+ modedelta.append(("+%s"%mode, None))
+ (handled, unhandled, exceptions) = self.event("onChannelModes", self.addons+channel.addons, channel=channel, modedelta=modedelta)
+ for ((modeset, mode), param) in modedelta:
+ if mode in self.supports["CHANMODES"][2]:
channel.modes[mode] = param
elif mode in self.supports["CHANMODES"][3]:
channel.modes[mode] = True
+
elif cmd == 329: # Channel created
channame, created = params.split()
+ created = int(created)
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
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onChanCreated", self.addons+channel.addons, channel=channel, created=created)
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
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopic", self.addons+channel.addons, origin=origin, channel=channel, topic=extinfo)
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
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopicInfo", self.addons+channel.addons, origin=origin, channel=channel, topicsetby=nick, topictime=int(dt))
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))]
+ channel.topictime = int(dt)
+
elif cmd == 352: # WHO reply
- (channame, ident, host, serv, nick, flags) = params.split()
+ (channame, username, host, serv, nick, flags) = params.split()
try:
(hops, realname) = extinfo.split(" ", 1)
except ValueError:
hops = extinfo
realname = None
- 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 channame[0] in self.supports.get("CHANTYPES", "#"):
+ channel = self.channel(channame)
+ else:
+ channel = None
+
user = self.user(nick)
- if user.nick != nick:
- user.nick = nick
+
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onWhoEntry", self.addons+channel.addons, origin=origin, channel=channel, user=user, channame=channame, username=username, host=host, serv=serv, nick=nick, flags=flags, hops=int(hops), realname=realname)
user.hops = hops
user.realname = realname
- user.idnt = ident
+ user.username = username
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]
+ if type(channel) == Channel:
+ 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 == 315: # End of WHO reply
+ (handled, unhandled, exceptions) = self.event("onWhoEnd", self.addons+channel.addons, origin=origin, param=params, endmsg=extinfo)
+
elif cmd == 353: # NAMES reply
- (devnull, channame) = params.split()
+ (flag, 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
+ self.event("onRecv", channel.addons, line=line, **data)
+
if "PREFIX" in self.supports:
- names = re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)", extinfo)
+ names = re.findall(r"([%s]*)([^@!\s]+)(?:!(\S+)@(\S+))?"%re.escape(self.supports["PREFIX"][1]), 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:
+ names = re.findall(r"()([^@!\s]+)(?:!(\S+)@(\S+))?", extinfo) # Still put it into tuple form for compatibility in the next structure
+ (handled, unhandled, exceptions) = self.event("onNames", self.addons+channel.addons, origin=origin, channel=channel, flag=flag, channame=channame, nameslist=names)
+
+ for (symbs, nick, username, host) 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
+ if username and user.username != username:
+ user.username = username
+ if host and user.host != host:
+ user.host = host
+ with channel.lock:
+ 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 == 366: # End of NAMES reply
+ channel = self.channel(params)
+ (handled, unhandled, exceptions) = self.event("onNamesEnd", self.addons+channel.addons, origin=origin, channel=channel, channame=params, endmsg=extinfo)
+
+ elif cmd == 372: # MOTD line
+ (handled, unhandled, exceptions) = self.event("onMOTDLine", self.addons, origin=origin, motdline=extinfo)
+ self.motd.append(extinfo)
elif cmd == 375: # Begin MOTD
+ (handled, unhandled, exceptions) = self.event("onMOTDStart", self.addons, origin=origin, motdgreet=extinfo)
self.motdgreet = extinfo
self.motd = []
elif cmd == 376:
+ (handled, unhandled, exceptions) = self.event("onMOTDEnd", self.addons, origin=origin, motdend=extinfo)
self.motdend = extinfo # End of MOTD
- elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
- (channame, admin) = params.split()
+
+ elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Owner (Unreal)
+ (channame, owner) = params.split()
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ self.event("onRecv", channel.addons, line=line, **data)
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)
@@ -472,10 +583,11 @@ class Connection(Thread):
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)
+ self.event("onRecv", channel.addons, line=line, **data)
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)
@@ -486,220 +598,189 @@ class Connection(Thread):
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
+
+ addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], [])
+ self.event("onRecv", addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onNickChange", self.addons+addons, user=origin, newnick=newnick)
+ if origin == self.identity:
+ (handled, unhandled, exceptions) = self.event("onMeNickChange", self.addons+addons, newnick=newnick)
+
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)
+ self.users.remove(u) # Nick collision, safe to assume this orphaned user is offline, so we shall remove the old instance.
for channel in self.channels:
+ ### If for some odd reason, the old user still appears common channels, then we will remove the user anyway.
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
+ origin.nick = newnick
- 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
+ elif cmd == "JOIN":
+ channel = target if type(target) == Channel else self.channel(extinfo)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onJoin", self.addons+channel.addons, user=origin, channel=channel)
- if user == self.identity: # This means the bot is entering the room,
+ if origin == 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
+ self.event("onMeJoin", self.addons+channel.addons, channel=channel)
+ self.raw("MODE %s" % channel.name)
+ self.raw("WHO %s" % channel.name)
+ if "CHANMODES" in self.supports.keys():
+ self.raw("MODE %s :%s" % (channel.name, self.supports["CHANMODES"][0]))
+ if channel not in origin.channels:
+ origin.channels.append(channel)
+ if origin not in channel.users:
+ channel.users.append(origin)
+
+ elif cmd == "KICK":
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)
+ self.event("onRecv", target.addons, line=line, **data)
+ if origin == self.identity:
+ self.event("onMeKick", self.addons+target.addons, channel=target, kicked=kicked, kickmsg=extinfo)
+ if kicked == self.identity:
+ self.event("onMeKicked", self.addons+target.addons, kicker=origin, channel=target, kickmsg=extinfo)
+ (handled, unhandled, exceptions) = self.event("onKick", self.addons+target.addons, kicker=origin, channel=target, kicked=kicked, kickmsg=extinfo)
+
+ if target in kicked.channels:
+ kicked.channels.remove(target)
+ if kicked in target.users:
+ target.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()
+ if mode in target.modes and kicked in target.modes[mode]:
+ target.modes[mode].remove(kicked)
+
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)
+ self.event("onRecv", target.addons, line=line, **data)
+ if origin == self.identity:
+ self.event("onMePart", self.addons+target.addons, channel=target, partmsg=extinfo)
+ (handled, unhandled, exceptions) = self.event("onPart", self.addons+target.addons, user=origin, channel=target, partmsg=extinfo)
+
+ if target in origin.channels:
+ origin.channels.remove(target)
+ if origin in target.users:
+ target.users.remove(origin)
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
+ if mode in target.modes and origin in target.modes[mode]:
+ target.modes[mode].remove(origin)
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ elif cmd == "QUIT":
+ channels = list(origin.channels)
+ addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], [])
+ self.event("onRecv", addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuit", self.addons+addons, user=origin, quitmsg=extinfo)
+ for channel in origin.channels:
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 origin in channel.users:
+ channel.users.remove(origin)
+ if "PREFIX" in self.supports:
+ for mode in self.supports["PREFIX"][0]:
+ if mode in channel.modes and origin in channel.modes[mode]:
+ channel.modes[mode].remove(origin)
+ origin.channels = []
+
+ elif cmd == "MODE":
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
+ modedelta = []
+ 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]+self.supports["CHANMODES"][1]:
+ param = modeparams.pop(0)
+ modedelta.append(("%s%s"%(modeset, mode), param))
+ if mode in maskmodeeventnames.keys():
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]:
+ eventname = maskmodeeventnames[mode][0]
+ if modeset == "-":
+ eventname = maskmodeeventnames[mode][1]
+ matchesbot = glob.fnmatch.fnmatch("%s!%s@%s".lower()%(self.identity.nick, self.identity.username, self.identity.host), param.lower())
+ self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param)
+ if matchesbot:
+ self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param)
+ elif mode in self.supports["CHANMODES"][2]:
+ if modeset == "+":
param = modeparams.pop(0)
+ modedelta.append(("%s%s"%(modeset, mode), param))
+ else:
+ modedelta.append(("%s%s"%(modeset, mode), None))
+ elif mode in self.supports["CHANMODES"][3]:
+ modedelta.append(("%s%s"%(modeset, mode), None))
+ elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
+ modenick = modeparams.pop(0)
+ modeuser = self.user(modenick)
+ if mode in privmodeeventnames.keys():
if modeset == "+":
- channel.modes[mode] = param
+ eventname = privmodeeventnames[mode][0]
+ if modeset == "-":
+ eventname = privmodeeventnames[mode][1]
+ self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, modeuser=modeuser)
+ if modeuser == self.identity:
+ self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target)
+ modedelta.append(("%s%s"%(modeset, mode), modeuser))
+ (handled, unhandled, exceptions) = self.event("onChanModeSet", self.addons+target.addons, user=origin, channel=target, modedelta=modedelta)
+ with target.lock:
+ for ((modeset, mode), param) in modedelta:
+ if mode in self.supports["CHANMODES"][0]:
+ if modeset == "+":
+ if mode in target.modes:
+ if param.lower() not in [mask.lower() for (mask, setby, settime) in target.modes[mode]]:
+ target.modes[mode].append((param, origin, int(time.time())))
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)
+ target.modes[mode] = [(param, origin, int(time.time()))]
+ else:
+ if mode in target.modes.keys():
+ if mode == "b": # Inspircd mode is case insentive when unsetting the mode
+ masks = [mask.lower() for (mask, setby, settime) in target.modes[mode]]
+ if param.lower() in masks:
+ index = masks.index(param.lower())
+ #print "Index: %d"%index
+ del target.modes[mode][index]
+ else:
+ masks = [mask for (mask, setby, settime) in target.modes[mode]]
+ if param in masks:
+ index = masks.index(param)
+ del target.modes[mode][index]
+ elif mode in self.supports["CHANMODES"][1]:
+ if modeset == "+":
+ target.modes[mode] = param
+ else:
+ target.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][2]:
+ if modeset == "+":
+ target.modes[mode] = param
+ else:
+ target.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][3]:
+ if modeset == "+":
+ target.modes[mode] = True
+ else:
+ target.modes[mode] = False
+ elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
+ if modeset == "+":
+ if mode in target.modes and param not in target.modes[mode]:
+ target.modes[mode].append(param)
+ if mode not in target.modes:
+ target.modes[mode] = [param]
+ elif mode in target.modes and param in target.modes[mode]:
+ target.modes[mode].remove(param)
+ elif type(target) == User:
modeparams = (params if params else extinfo).split()
setmodes = modeparams.pop(0)
modeset = "+"
@@ -708,126 +789,287 @@ class Connection(Thread):
modeset = mode
continue
if modeset == "+":
- if mode not in user.modes:
- user.modes += mode
+ if mode not in target.modes:
+ target.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 snomodeset == "+" and snomode not in target.snomask:
+ target.snomask += snomode
+ if snomodeset == "-" and snomode in target.snomask:
+ target.snomask = target.snomask.replace(snomode, "")
if modeset == "-":
- if mode in user.modes:
- user.modes = user.modes.replace(mode, "")
+ if mode in target.modes:
+ target.modes = target.modes.replace(mode, "")
if mode == "s":
- user.snomask = ""
+ target.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
+ self.event("onRecv", target.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopicSet", self.addons+target.addons, user=origin, channel=target, topic=extinfo)
+
+ with target.lock:
+ target.topic = extinfo
+ target.topicsetby = origin
+ target.topictime = int(time.time())
+
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
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
### CTCP handling
ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
if ctcp:
(ctcptype, ext) = ctcp[0]
+ if ctcptype.upper() == "ACTION":
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanAction", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, action=ext)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivAction", self.addons, user=origin, action=ext)
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanCTCP", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivCTCP", self.addons, user=origin, ctcptype=ctcptype, params=ext)
if ctcptype.upper() == "VERSION":
- user.ctcpreply("VERSION", self.ctcpversion())
+ origin.ctcpreply("VERSION", self.ctcpversion())
if ctcptype.upper() == "TIME":
tformat = time.ctime()
tz = time.tzname[0]
- user.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars())
+ origin.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars())
if ctcptype.upper() == "PING":
- user.ctcpreply("PING", "%(ext)s" % vars())
+ origin.ctcpreply("PING", "%(ext)s" % vars())
if ctcptype.upper() == "FINGER":
- user.ctcpreply("FINGER", "%(ext)s" % vars())
+ origin.ctcpreply("FINGER", "%(ext)s" % vars())
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanMsg", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivMsg", self.addons, user=origin, msg=extinfo)
+
+ elif cmd == "NOTICE":
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
+
+ ### CTCP handling
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
+ if ctcp and target == self.identity:
+ (ctcptype, ext) = ctcp[0]
+ (handled, unhandled, exceptions) = self.event("onCTCPReply", self.addons, origin=origin, ctcptype=ctcptype, params=ext)
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanNotice", self.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivNotice", self.addons, origin=origin, msg=extinfo)
+
+ elif cmd == 367: # Channel Ban list
+ if 367 in lists.items():
+ banlist = lists[367]
+ else:
+ banlist = lists[367] = []
+ (channame, mask, setby, settime) = params.split()
+ channel = self.channel(channame)
+ banlist.append((self.channel(channame), mask, setby, int(settime)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 368:
+ if 367 in lists.items():
+ banlist = lists[367]
+ else:
+ banlist = lists[367] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in banlist:
+ if "b" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]:
+ channel.modes["b"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["b"] = [(mask, setby, int(settime))]
+ lists[367] = []
+
+ elif cmd == 346: # Channel Invite list
+ if 346 in lists.items():
+ invitelist = lists[346]
+ else:
+ invitelist = lists[346] = []
+ (channame, mask, setby, settime) = params.split()
+ invitelist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onInviteListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 347:
+ if 346 in lists.items():
+ invitelist = lists[346]
+ else:
+ invitelist = lists[346] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onInviteListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in invitelist:
+ if "I" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["I"]]:
+ channel.modes["I"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["I"] = [(mask, setby, int(settime))]
+ lists[346] = []
+
+ elif cmd == 348: # Channel Ban Exception list
+ if 348 in lists.items():
+ banexceptlist = lists[348]
+ else:
+ banexceptlist = lists[348] = []
+ (channame, mask, setby, settime) = params.split()
+ banexceptlist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanExceptListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 349:
+ if 348 in lists.items():
+ banexceptlist = lists[348]
+ else:
+ banexceptlist = lists[348] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanExceptListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in banexceptlist:
+ if "e" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["e"]]:
+ channel.modes["e"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["e"] = [(mask, setby, int(settime))]
+ lists[348] = []
+
elif cmd == 910: # Channel Access List
+ if 910 in lists.items():
+ accesslist = lists[910]
+ else:
+ accesslist = lists[910] = []
(channame, mask, setby, settime) = params.split()
+ accesslist.append((self.channel(channame), mask, setby, int(settime)))
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)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onAccessListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 911:
+ if 911 in lists.items():
+ accesslist = lists[910]
else:
- channel.modes["w"] = [(mask, setby, int(settime))]
- elif cmd == 941: # Channel spamfilter List
+ accesslist = lists[910] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onAccessListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in accesslist:
+ if "w" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]:
+ channel.modes["w"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["w"] = [(mask, setby, int(settime))]
+ lists[910] = []
+
+ elif cmd == 941: # Spam Filter list
+ if 941 in lists.items():
+ spamfilterlist = lists[941]
+ else:
+ spamfilterlist = lists[941] = []
(channame, mask, setby, settime) = params.split()
+ spamfilterlist.append((self.channel(channame), mask, setby, int(settime)))
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)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onSpamfilterListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 940:
+ if 941 in lists.items():
+ spamfilterlist = lists[941]
else:
- channel.modes["g"] = [(mask, setby, int(settime))]
- elif cmd == 954: # Channel spamfilter List
+ spamfilterlist = lists[941] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onSpamfilterListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in spamfilterlist:
+ if "g" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["g"]]:
+ channel.modes["g"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["g"] = [(mask, setby, int(settime))]
+ lists[941] = []
+
+ elif cmd == 954: # Channel exemptchanops list
+ if 954 in lists.items():
+ exemptchanopslist = lists[954]
+ else:
+ exemptchanopslist = lists[954] = []
(channame, mask, setby, settime) = params.split()
+ exemptchanopslist.append((self.channel(channame), mask, setby, int(settime)))
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)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 953:
+ if 954 in lists.items():
+ exemptchanopslist = lists[954]
+ else:
+ exemptchanopslist = lists[954] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in exemptchanopslist:
+ if "X" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["X"]]:
+ channel.modes["X"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["X"] = [(mask, setby, int(settime))]
+ lists[954] = []
+
+ elif cmd == 728: # Channel quiet list
+ if 728 in lists.items():
+ quietlist = lists[728]
else:
- channel.modes["X"] = [(mask, setby, int(settime))]
+ quietlist = lists[728] = []
+ (channame, modechar, mask, setby, settime) = params.split()
+ quietlist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuietListEntry", self.addons+channel.addons, origin=origin, channel=channel, modechar=modechar, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 729:
+ if 728 in lists.items():
+ quietlist = lists[728]
+ else:
+ quietlist = lists[728] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuietListEnd", self.addons+channel.addons, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in quietlist:
+ if "q" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["q"]]:
+ channel.modes["q"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["q"] = [(mask, setby, int(settime))]
+ lists[728] = []
+
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)
+ self.event("onRecv", channel.addons, line=line, **data)
+
+ elif type(cmd) == int:
+ (handled, unhandled, exceptions) = self.event("on%03d"%cmd, self.addons, line=line, origin=origin, target=target, params=params, extinfo=extinfo)
+ else:
+ (handled, unhandled, exceptions) = self.event("on%s"%cmd, self.addons, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo)
+
+ self.event("onUnhandled", unhandled, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo)
- else: # Line does NOT match ":src cmd target params :extinfo"
- self.event("onRecv", self.modules, line=line,
- data=None)
+ else: # Line does NOT match ":origin cmd target params :extinfo"
+ self.event("onRecv", self.addons, line=line)
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()
+ exc, excmsg, tb = sys.exc_info()
+ self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars())
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.
+ self.quitexpected = True
try:
self.quit("%s" % traceback.format_exc()
.rstrip().split("\n")[-1])
@@ -836,34 +1078,25 @@ class Connection(Thread):
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)
+ (handled, unhandled, exceptions) = self.event("onDisconnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), expected=self.quitexpected)
+ self.connected = False
+ self.registered = False
+ self.identity = None
### Tell outgoing thread to quit.
- self.outgoing.put("quit")
+ self.outgoing.interrupt()
### Wait until the outgoing thread dies.
- if outgoingthread:
+ if outgoingthread and outgoingthread.isAlive():
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()
+ self.logwrite("*** Connection Terminated.")
if self.quitexpected or not self.autoreconnect:
sys.exit()
@@ -875,23 +1108,14 @@ class Connection(Thread):
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()
+ self.logwrite(*["!!! FATAL Exception"]+["!!! %s"%line for line in traceback.format_exc().split("\n")])
+ print >>sys.stderr, "FATAL Exception" % vars()
+ print >>sys.stderr, traceback.format_exc()
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()
-
+ self.logwrite("### Log session ended")
+ (handled, unhandled, exceptions) = self.event("onSessionClose", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
Thread.__init__(self) # Makes thread restartable
def __repr__(self):
@@ -901,11 +1125,11 @@ class Connection(Thread):
port = self.port
if self.identity:
nick = self.identity.nick
- ident = self.identity.idnt if self.identity.idnt else "*"
+ user = self.identity.username if self.identity.username else "*"
host = self.identity.host if self.identity.host else "*"
else:
nick = "*"
- ident = "*"
+ user = "*"
host = "*"
if self.ssl and self.ipv6:
protocol = "ircs6"
@@ -915,12 +1139,11 @@ class Connection(Thread):
protocol = "irc6"
else:
protocol = "irc"
- return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
+ return "<IRC Context: %(nick)s!%(user)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)
@@ -929,7 +1152,7 @@ class Connection(Thread):
def ctcpversion(self):
reply = []
- ### Prepare reply for module
+ ### Prepare reply for addon
reply.append("%(__name__)s %(__version__)s, %(__author__)s" %
vars(self))
@@ -938,11 +1161,11 @@ class Connection(Thread):
pyver[0] = "Python "+pyver[0]
reply.extend(pyver)
reply.extend(platform.platform().split("\n"))
- ### Prepare reply for extension modules
- for module in self.modules:
+ ### Prepare reply for extension addons
+ for addon in self.addons:
try:
- r = "%(__name__)s %(__version__)s" % vars(module)
- if "__extinfo__" in vars(module):
+ r = "%(__name__)s %(__version__)s" % vars(addon)
+ if "__extinfo__" in vars(addon):
r += ", %(__extinfo__)s" % vars()
reply.append(r)
except:
@@ -950,7 +1173,11 @@ class Connection(Thread):
return reduce(lambda x, y: "%s; %s" % (x, y), reply)
def raw(self, line, origin=None):
- self.outgoing.put((line, origin))
+ try:
+ self.outgoing.put((line, origin))
+ except:
+ print (line, origin)
+ raise
def user(self, nick):
users = [user for user in self.users if user.nick.lower(
@@ -962,9 +1189,9 @@ class Connection(Thread):
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()
+ #with self.loglock:
+ # print >>self.log, "%s *** User %s created."%(timestamp, nick)
+ # self.log.flush()
return user
def channel(self, name):
@@ -977,10 +1204,9 @@ class Connection(Thread):
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()
+ #with self.loglock:
+ # print >>self.log, "%s *** Channel %s created."%(timestamp, name)
+ # self.log.flush()
return chan
@@ -988,7 +1214,7 @@ class Channel(object):
def __init__(self, name, context):
self.name = name
self.context = context
- self.modules = []
+ self.addons = []
self.topic = ""
self.topicsetby = ""
self.topictime = ()
@@ -1055,7 +1281,7 @@ class Channel(object):
class User(object):
def __init__(self, nick, context):
self.nick = nick
- self.idnt = ""
+ self.username = ""
self.host = ""
self.channels = []
self.context = context
@@ -1064,9 +1290,14 @@ class User(object):
self.server = None
self.hops = None
self.ircop = False
+ self.ircopmsg = ""
+ self.idlesince = None
+ self.signontime = None
+ self.ssl = None
+ self.away = None
def __repr__(self):
- return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self)
+ return "<User: %(nick)s!%(username)s@%(host)s>" % vars(self)
def msg(self, msg, origin=None):
nick = self.nick
@@ -1092,20 +1323,6 @@ class User(object):
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
@@ -1116,118 +1333,158 @@ class Outgoing(Thread):
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:
+ throttled = False
+ timestamps = []
+ while True:
try:
- self.IRC.connection.send("%(line)s\n" % vars())
- except socket.error:
+ q = self.IRC.outgoing.get()
+ except Queue.Interrupted:
+ break
+ 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]
+ 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.shutdown(0)
- except:
- pass
- raise
+ 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":
+ ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy addons
+ #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() 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":
+ 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() 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)
+ 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)
+
+ chanmatch = re.findall("([%s]?)([%s].+)"%(re.escape(self.IRC.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.IRC.supports.get("CHANTYPES", "#"))), target)
+ if chanmatch:
+ targetprefix, channame = chanmatch[0]
+ target = self.IRC.channel(channame)
+ if target.name != channame:
+ ### Target channel name has changed
+ target.name = channame
+ elif len(target) and target[0] != "$" and cmd != "NICK":
+ targetprefix = ""
+ target = self.IRC.user(target)
+
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",
+ extinfo)
+ if ctcp:
+ (ctcptype, ext) = ctcp[0]
+ if ctcptype.upper() == "ACTION":
+ if type(target) == Channel:
+ self.IRC.event("onSendChanAction", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, action=ext)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivAction", self.IRC.addons, origin=origin, user=target, action=ext)
+ else:
+ if type(target) == Channel:
+ self.IRC.event("onSendChanCTCP", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivCTCP", self.IRC.addons, origin=origin, user=target, ctcptype=ctcptype, params=ext)
+ else:
+ if type(target) == Channel:
+ self.IRC.event("onSendChanMsg", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivMsg", self.IRC.addons, origin=origin, user=target, msg=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 target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- params = ""
+ elif cmd.upper() == "PASS":
+ extinfo = "********"
+ target = ""
+ line = "%s :%s"%(cmd, extinfo)
+ elif cmd.upper() == "IDENTIFY":
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))
+ self.IRC.event("onSend", self.IRC.addons, origin=origin, line=line, cmd=cmd, target=target, params=params, extinfo=extinfo)
+ self.IRC.logwrite(">>> %(line)s" % vars())
+ if cmd.upper() == "QUIT":
+ self.IRC.quitexpected = True
+ 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))
+ except:
+ self.IRC.connection.send("QUIT :%s\n" %
+ traceback.format_exc().rstrip().split("\n")[-1])
+ self.IRC.connection.close()
+ self.IRC.connection.shutdown(0)
class Pinger(Thread):
diff --git a/logger.py b/logger.py
index 9df6020..01f2bc1 100644
--- a/logger.py
+++ b/logger.py
@@ -12,80 +12,75 @@ import traceback
import Queue
import ssl
import urllib2
+import irc
+modemapping = dict(
+ Y="ircop", q="owner", a="admin", o="op", h="halfop", v="voice")
-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
-
-class Logger(object):
+class Logger(Thread):
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.logs = {}
self.labels = {}
self.rotatelock = Lock()
- self.logrotate = None
- def onModuleAdd(self, IRC, label):
+ Thread.__init__(self)
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ try:
+ 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)))
+ 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.rotatelock:
+ if all([not log or log.closed for log in self.logs.values()]):
+ break
+ Y, M, D, h, m, s, w, d, dst = now = time.localtime()
+ for IRC in self.labels.keys():
+ if IRC.connected:
+ with IRC.lock:
+ try:
+ self.rotateLog(IRC)
+ except:
+ exc, excmsg, tb = sys.exc_info()
+ IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")])
+ if IRC.identity:
+ for channel in IRC.identity.channels:
+ try:
+ self.rotateLog(channel)
+ except:
+ exc, excmsg, tb = sys.exc_info()
+ IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")])
+ for user in IRC.users:
+ if user in self.logs.keys():
+ try:
+ self.closeLog(user)
+ except:
+ exc, excmsg, tb = sys.exc_info()
+ IRC.logwrite(*["!!! [Logger] Exception in module %(module)s" % vars()]+["!!! [Logger] %s" % tbline for tbline in traceback.format_exc().split("\n")])
+ IRC.logopen(os.path.join(self.logroot, self.labels[IRC], "rawdata-%04d.%02d.%02d.log"%now[:3]))
+ nextrotate = int(time.mktime((Y, M, D+1, 0, 0, 0, 0, 0, -1)))
+ finally:
+ Thread.__init__(self)
+
+ def onAddonAdd(self, IRC, label):
if label in self.labels.values():
raise BaseException("Label already exists")
if IRC in self.labels.keys():
@@ -94,289 +89,514 @@ class Logger(object):
os.mkdir(os.path.join(self.logroot, label))
self.labels[IRC] = label
if IRC.connected:
- self.openConsoleLog(IRC)
+ self.openLog(IRC)
if IRC.identity:
for channel in IRC.identity.channels:
- self.openChannelLog(channel)
+ self.openLog(channel)
+ now = time.localtime()
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
+ "0") for t in now[0:6]])
+ IRC.logopen(os.path.join(self.logroot, self.labels[IRC],
+ "rawdata-%04d.%02d.%02d.log"%now[:3]))
- def onModuleRem(self, IRC):
+ def onAddonRem(self, IRC):
if IRC.connected:
- for channel in self.channellogs.keys():
+ for channel in self.logs.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)
+ if not self.logs[channel].closed:
+ self.closeLog(channel)
+ for user in self.logs.keys():
+ if user in IRC.users:
+ if not self.logs[user].closed:
+ self.closeLog(user)
+ if not self.logs[IRC].closed:
+ self.closeLog(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):
+ def openLog(self, window):
with self.rotatelock:
- if not self.logrotate or not self.logrotate.isAlive():
- self.logrotate = LogRotate(self)
- self.logrotate.daemon = True
- self.logrotate.start()
+ if not self.isAlive():
+ self.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]
+ if type(window) == irc.Connection:
+ log = self.logs[window] = open(os.path.join(self.logroot, self.labels[window], "console-%04d.%02d.%02d.log"%now[:3]), "a")
+ print >>log, "%s ### Log file opened" % (irc.timestamp())
+ elif type(window) == irc.Channel:
+ label = self.labels[window.context]
+ log = self.logs[window] = open(os.path.join(self.logroot, label, "channel-%s-%04d.%02d.%02d.log"%((urllib2.quote(window.name.lower()).replace("/", "%2f"),)+now[:3])), "a")
+ print >>log, "%s ### Log file opened" % (irc.timestamp())
+ self.logs[window].flush()
+ if window.context.identity in window.users:
+ if window.topic:
+ print >>log, "%s <<< :%s 332 %s %s :%s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topic)
+ if window.topicsetby and window.topictime:
+ print >>log, "%s <<< :%s 333 %s %s %s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.topicsetby, window.topictime)
+ if window.users:
+ secret = "s" in window.modes.keys() and window.modes["s"]
+ private = "p" in window.modes.keys() and window.modes["p"]
+ namesusers = []
+ modes, symbols = window.context.supports["PREFIX"]
+ print >>log, "%s <<< :%s 353 %s %s %s :%s" % (irc.timestamp(),
+ window.context.serv,
+ window.context.identity.nick,
+ "@" if secret else ("*" if private else "="),
+ window.name,
+ " ".join(["".join([symbols[k] if modes[k] in window.modes.keys() and user in window.modes[modes[k]] else "" for k in xrange(len(modes))])+user.nick for user in window.users]))
+ if window.modes:
+ modes = window.modes.keys()
+ modestr = "".join([mode for mode in modes if mode not in window.context.supports["CHANMODES"][0]+window.context.supports["PREFIX"][0] and window.modes[mode]])
+ params = " ".join([window.modes[mode] for mode in modes if mode in window.context.supports["CHANMODES"][1]+window.context.supports["CHANMODES"][2] and window.modes[mode]])
+ print >>log, "%s <<< :%s 324 %s %s +%s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, modestr, params)
+ if window.created:
+ print >>log, "%s <<< :%s 329 %s %s %s" % (irc.timestamp(), window.context.serv, window.context.identity.nick, window.name, window.created)
+ if type(window) == irc.User:
+ logname = os.path.join(self.logroot, self.labels[window.context], "query-%s-%04d.%02d.%02d.log"%((urllib2.quote(window.nick.lower()).replace("/", "%2f"),)+now[:3]))
+ for (other, log) in self.logs.items():
+ if other == window:
+ continue
+ if log.name == logname:
+ print >>log, "%s ### Log file closed" % (irc.timestamp())
+ del self.logs[other]
+ self.logs[window] = log
+ if window not in self.logs.keys():
+ log = self.logs[window] = open(logname, "a")
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]
+ log = self.logs[window]
+ print >>log, "%s ### Log file opened" % (irc.timestamp())
+ log.flush()
+
+ def closeLog(self, window):
+ if window in self.logs.keys() and type(self.logs[window]) == file and not self.logs[window].closed:
+ print >>self.logs[window], "%s ### Log file closed" % (
+ irc.timestamp())
+ self.logs[window].close()
+ if window in self.logs.keys():
+ del self.logs[window]
+
+ def rotateLog(self, window):
+ self.closeLog(window)
+ self.openLog(window)
+
+ def onConnectAttempt(self, IRC):
+ if IRC not in self.logs.keys() or (not self.logs[IRC]) or self.logs[IRC].closed:
+ self.openLog(IRC)
+ ts = irc.timestamp()
+ print >>self.logs[IRC], "%s *** Attempting connection to %s:%s." % (
+ ts, IRC.server, IRC.port)
+
+ def onConnect(self, IRC):
+ if IRC not in self.logs.keys() or (not self.logs[IRC]) or self.logs[IRC].closed:
+ self.openLog(IRC)
+ ts = irc.timestamp()
+ print >>self.logs[IRC], "%s *** Connection to %s:%s established." % (
+ ts, IRC.server, IRC.port)
+
+ def onConnectFail(self, IRC, exc, excmsg, tb):
+ ### Called when a connection attempt fails.
+ if IRC not in self.logs.keys() or (not self.logs[IRC]) or self.logs[IRC].closed:
+ self.openLog(IRC)
+ ts = irc.timestamp()
+ print >>self.logs[IRC], "%s *** Connection to %s:%s failed: %s." % (
+ ts, IRC.server, IRC.port, excmsg)
+
+ def onDisconnect(self, IRC, expected=False):
+ ts = irc.timestamp()
+ for window in self.logs.keys():
+ if type(window) in (irc.Channel, irc.User) and window.context == IRC:
+ print >>self.logs[window], "%s *** Connection to %s:%s terminated." % (ts, IRC.server, IRC.port)
+ self.logs[window].flush()
+ self.closeLog(window)
+ print >>self.logs[IRC], "%s *** Connection %s:%s terminated." % (
+ ts, IRC.server, IRC.port)
+ self.logs[IRC].flush()
+ self.closeLog(IRC)
+
+ def onJoin(self, IRC, user, channel):
+ ### Called when somebody joins a channel, includes bot.
+ ts = irc.timestamp()
+ if user == IRC.identity:
+ self.openLog(channel)
+ print >>self.logs[channel], "%s <<< :%s!%s@%s JOIN %s" % (
+ ts, user.nick, user.username, user.host, channel.name)
+ self.logs[channel].flush()
+
+ def onChanMsg(self, IRC, user, channel, targetprefix, msg):
+ ### Called when someone sends a PRIVMSG to channel.
+ ts = irc.timestamp()
+ if type(user) == irc.User:
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and user in channel.modes[mode]])
+ if classes:
+ print >>self.logs[channel], "%s %s <<< :%s!%s@%s PRIVMSG %s%s :%s" % (ts, classes, user.nick, user.username, user.host, targetprefix, channel.name, msg)
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()
+ print >>self.logs[channel], "%s <<< :%s!%s@%s PRIVMSG %s%s :%s" % (ts, user.nick, user.username, user.host, targetprefix, channel.name, msg)
+ elif type(user) in (str, unicode):
+ classes = "server"
+ print >>self.logs[channel], "%s %s <<< :%s PRIVMSG %s%s :%s" % (ts,
+ classes, user, targetprefix, channel.name, msg)
+ self.logs[channel].flush()
+
+ def onChanAction(self, IRC, user, channel, targetprefix, action):
+ self.onChanMsg(IRC, user, channel, targetprefix,
+ "\x01ACTION %s\x01"%action)
+
+ def onChanNotice(self, IRC, origin, channel, targetprefix, msg):
+ ### Called when someone sends a NOTICE to channel.
+ ts = irc.timestamp()
+ if type(origin) == irc.User:
+ classes = " ".join([modemapping[mode] for mode in IRC.supports["PREFIX"][0] if mode in channel.modes.keys() and origin in channel.modes[mode]])
+ if classes:
+ print >>self.logs[channel], "%s %s <<< :%s!%s@%s NOTICE %s%s :%s" % (ts, classes, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg)
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()
+ print >>self.logs[channel], "%s <<< :%s!%s@%s NOTICE %s%s :%s" % (ts, origin.nick, origin.username, origin.host, targetprefix, channel.name, msg)
+ elif type(origin) in (str, unicode):
+ classes = "server"
+ print >>self.logs[channel], "%s %s <<< :%s NOTICE %s%s :%s" % (ts,
+ classes, origin, targetprefix, channel.name, msg)
+ self.logs[channel].flush()
+
+ def onPart(self, IRC, user, channel, partmsg):
+ ### Called when somebody parts the channel, includes bot.
+ ts = irc.timestamp()
+ if partmsg:
+ print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s :%s" % (ts, user.nick, user.username, user.host, channel.name, partmsg)
else:
- print >>self.consolelogs[IRC], "%s %s <<< %s" % (
- timestamp, time.tzname[now[-1]], line)
- self.consolelogs[IRC].flush()
+ print >>self.logs[channel], "%s <<< :%s!%s@%s PART %s" % (ts,
+ user.nick, user.username, user.host, channel.name)
+ self.logs[channel].flush()
+ if user == IRC.identity:
+ self.closeLog(channel)
- 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 onKick(self, IRC, kicker, channel, kicked, kickmsg):
+ ### Called when somebody is kicked from the channel, includes bot.
+ ts = irc.timestamp()
+ if kickmsg:
+ print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s :%s" % (ts, kicker.nick, kicker.username, kicker.host, channel.name, kicked.nick, kickmsg)
+ else:
+ print >>self.logs[channel], "%s <<< :%s!%s@%s KICK %s %s" % (ts, user.nick, user.username, user.host, channel.name, kicked.nick)
+ self.logs[channel].flush()
+ if user == IRC.identity:
+ self.closeLog(channel)
- 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 onSendChanMsg(self, IRC, origin, channel, targetprefix, msg):
+ ### Called when bot sends a PRIVMSG to channel.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ ts = irc.timestamp()
+ 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.logs[channel], "%s %s >>> :%s!%s@%s PRIVMSG %s%s :%s" % (ts, classes, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)
+ else:
+ print >>self.logs[channel], "%s >>> :%s!%s@%s PRIVMSG %s%s :%s" % (ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, targetprefix, channel.name, msg)
+ self.logs[channel].flush()
- 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()
+ def onSendChanAction(self, IRC, origin, channel, targetprefix, action):
+ ### origin is the source of the channel message
+ ### Called when bot sends an action (/me) to channel.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ self.onSendChanMsg(IRC, origin, channel, targetprefix,
+ "\x01ACTION %s\x01"%action)
+
+ def onPrivMsg(self, IRC, user, msg):
+ ### Called when someone sends a PRIVMSG to the bot.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ ts = irc.timestamp()
+ print >>self.logs[user], "%s <<< :%s!%s@%s PRIVMSG %s :%s" % (ts, user.nick, user.username, user.host, IRC.identity.nick, msg)
+ self.logs[user].flush()
+
+ def onPrivNotice(self, IRC, origin, msg):
+ ### Called when someone sends a NOTICE to the bot.
+ ts = irc.timestamp()
+ if type(origin) == irc.User:
+ if origin not in self.logs.keys():
+ self.openLog(origin)
+ print >>self.logs[origin], "%s <<< :%s!%s@%s NOTICE %s :%s" % (ts, origin.nick, origin.username, origin.host, IRC.identity.nick, msg)
+ self.logs[origin].flush()
+ else:
+ print >>self.logs[IRC], "%s <<< :%s NOTICE %s :%s" % (
+ ts, origin, IRC.identity.nick, msg)
+ self.logs[IRC].flush()
+
+ def onPrivAction(self, IRC, user, action):
+ ### Called when someone sends an action (/me) to the bot.
+ self.onPrivMsg(IRC, user, "\x01ACTION %s\x01"%action)
+
+ def onSendPrivMsg(self, IRC, origin, user, msg):
+ ### Called when bot sends a PRIVMSG to a user.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ ts = irc.timestamp()
+ print >>self.logs[user], "%s >>> :%s!%s@%s PRIVMSG %s :%s" % (ts, IRC.identity.nick, IRC.identity.username, IRC.identity.host, user.nick, msg)
+ self.logs[user].flush()
+
+ def onSendPrivAction(self, IRC, origin, user, action):
+ ### Called when bot sends an action (/me) to a user.
+ ### The variable origin refers to a class instance voluntarily identifying itself as that which requested data be sent.
+ self.onSendPrivMsg(IRC, origin, user, "\x01ACTION %s\x01"%action)
+
+ def onNickChange(self, IRC, user, newnick):
+ ### Called when somebody changes nickname.
+ ts = irc.timestamp()
+ line = "%s <<< :%s!%s@%s NICK %s" % (
+ ts, user.nick, user.username, user.host, newnick)
+
+ ### Print nick change in each channel the user is in.
+ for channel in user.channels:
+ print >>self.logs[channel], line
+ self.logs[channel].flush()
+
+ ### And in the query if open.
+ if user in self.logs.keys():
+ print >>self.logs[user], line
+ self.logs[user].flush()
+
+ def onMeNickChange(self, IRC, newnick):
+ ### Called when the bot changes nickname.
+
+ ### Print nick change to all open queries, except for query with self (already done with onNickChange).
+ ts = irc.timestamp()
+ line = "%s <<< :%s!%s@%s NICK %s" % (ts, IRC.identity.nick,
+ IRC.identity.username, IRC.identity.host, newnick)
+ for (window, log) in self.logs.items():
+ if type(window) == irc.User and window != IRC.identity:
+ print >>log, line
+ log.flush()
+
+ def onQuit(self, IRC, user, quitmsg):
+ ### Called when somebody quits IRC.
+ ts = irc.timestamp()
+ if quitmsg:
+ line = "%s <<< :%s!%s@%s QUIT :%s" % (
+ ts, user.nick, user.username, user.host, quitmsg)
+ else:
+ line = "%s <<< :%s!%s@%s QUIT" % (
+ ts, user.nick, user.username, user.host)
+
+ ### Print quit in each channel the user was in.
+ for channel in user.channels:
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ print >>self.logs[channel], line
+ self.logs[channel].flush()
+
+ ### And in the query if open.
+ if user in self.logs.keys():
+ print >>self.logs[user], line
+ self.logs[user].flush()
+ self.closeLog(user)
+
+ def onNames(self, IRC, origin, channel, flag, channame, nameslist):
+ ### Called when a NAMES list is received.
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ ts = irc.timestamp()
+
+ secret = "s" in channel.modes.keys() and channel.modes["s"]
+ private = "p" in channel.modes.keys() and channel.modes["p"]
+ modes, symbols = channel.context.supports["PREFIX"]
+ print >>log, "%s <<< :%s 353 %s %s %s :%s" % (ts, origin, IRC.identity.nick, flag, channame,
+ " ".join(["%s%s!%s@%s"%(prefix, nick, username, host) if username and host else "%s%s"%(prefix, nick) for (prefix, nick, username, host) in nameslist]))
+ log.flush()
+
+ def onNamesEnd(self, IRC, origin, channel, channame, endmsg):
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ ts = irc.timestamp()
+ print >>log, "%s <<< :%s 366 %s %s :%s" % (
+ ts, origin, IRC.identity.nick, channame, endmsg)
+ log.flush()
+
+ def onWhoisStart(self, IRC, origin, user, nickname, username, host, realname):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 311 %s %s %s %s * :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, username, host, realname)
+
+ def onWhoisRegisteredNick(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 307 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisAway(self, IRC, origin, user, nickname, awaymsg):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 301 %s %s :%s" % (irc.timestamp(
+ ), origin, IRC.identity.nick, nickname, awaymsg)
+
+ def onWhoisConnectingFrom(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 378 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisChannels(self, IRC, origin, user, nickname, chanlist):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 319 %s %s :%s" % (irc.timestamp(),
+ origin, IRC.identity.nick, nickname, " ".join(chanlist))
+
+ def onWhoisAvailability(self, IRC, origin, user, nickname, msg):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 310 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisServer(self, IRC, origin, user, nickname, server, servername):
+ ### Called when a WHOIS reply is received.
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 312 %s %s %s :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, server, servername)
+
+ def onWhoisOp(self, IRC, origin, user, nickname, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 313 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisTimes(self, IRC, origin, user, nickname, idletime, signontime, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 317 %s %s %d %d :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, idletime, signontime, msg)
+
+ def onWhoisSSL(self, IRC, origin, user, nickname, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 671 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisModes(self, IRC, origin, user, nickname, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 339 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+
+ def onWhoisLoggedInAs(self, IRC, origin, user, nickname, loggedinas, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 330 %s %s %s :%s" % (irc.timestamp(), origin, IRC.identity.nick, nickname, loggedinas, msg)
+
+ def onWhoisEnd(self, IRC, origin, user, nickname, msg):
+ if user not in self.logs.keys():
+ self.openLog(user)
+ print >>self.logs[user], "%s <<< :%s 318 %s %s :%s" % (
+ irc.timestamp(), origin, IRC.identity.nick, nickname, msg)
+ self.logs[user].flush()
+
+ def onWhoEntry(self, IRC, **kwargs):
+ ### Called when a WHO list is received.
+ pass
+
+ def onWhoEnd(self, IRC, **kwargs):
+ ### Called when a WHO list is received.
+ pass
+
+ def onList(self, IRC, chanlistbegin, chanlist, endmsg):
+ ### Called when a channel list is received.
+ pass
+
+ def onTopic(self, IRC, origin, channel, topic):
+ ### Called when channel topic is received via 332 response.
+ ts = irc.timestamp()
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ print >>log, "%s <<< :%s 332 %s %s :%s" % (
+ ts, origin, IRC.identity.nick, channel.name, topic)
+ log.flush()
+
+ def onTopicInfo(self, IRC, origin, channel, topicsetby, topictime):
+ ### Called when channel topic info is received via 333 response.
+ ts = irc.timestamp()
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ print >>log, "%s <<< :%s 333 %s %s %s %d" % (ts, origin,
+ IRC.identity.nick, channel.name, topicsetby, topictime)
+ log.flush()
+
+ def onTopicSet(self, IRC, user, channel, topic):
+ ### Called when channel topic is changed.
+ ts = irc.timestamp()
+ print >>self.logs[channel], "%s <<< :%s!%s@%s TOPIC %s :%s" % (ts,
+ user.nick, user.username, user.host, channel.name, topic)
+ self.logs[channel].flush()
+
+ def onChanModeSet(self, IRC, user, channel, modedelta):
+ ### Called when channel modes are changed.
+ ### modedelta is a list of tuples of the format ("+x", parameter), ("+x", None) if no parameter is provided.
+ ts = irc.timestamp()
+ modestr = ""
+ params = []
+ sign = ""
+ for (sgn, modechar), param in modedelta:
+ if sgn != sign:
+ modestr += sgn
+ sign = sgn
+ modestr += modechar
+ if param is not None:
+ params.append(param.nick if type(param) == irc.User else param)
+ if len(params):
+ if type(user) == irc.User:
+ print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s %s" % (ts, user.nick, user.username, user.host, channel.name, modestr, " ".join(params))
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()
+ print >>self.logs[channel], "%s <<< :%s MODE %s %s %s" % (ts, user, channel.name, modestr, " ".join(params))
+ else:
+ if type(user) == irc.User:
+ print >>self.logs[channel], "%s <<< :%s!%s@%s MODE %s %s" % (ts, user.nick, user.username, user.host, channel.name, modestr)
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()
+ print >>self.logs[channel], "%s <<< :%s MODE %s %s" % (
+ ts, user, channel.name, modestr)
+ self.logs[channel].flush()
+
+ def onChannelModes(self, IRC, channel, modedelta):
+ ### Called when channel modes are received via 324 response.
+ ts = irc.timestamp()
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ modestr = ""
+ params = []
+ sign = ""
+ for (sgn, modechar), param in modedelta:
+ if sgn != sign:
+ modestr += sgn
+ sign = sgn
+ modestr += modechar
+ if param is not None:
+ params.append(param)
+ if len(params):
+ print >>log, "%s <<< :%s 324 %s %s %s %s" % (ts, IRC.serv, IRC.identity.nick, channel.name, modestr, " ".join(params))
+ else:
+ print >>log, "%s <<< :%s 324 %s %s %s" % (ts, IRC.serv,
+ IRC.identity.nick, channel.name, modestr)
+ log.flush()
+
+ def onChanCreated(self, IRC, channel, created):
+ ### Called when a 329 response is received.
+ ts = irc.timestamp()
+ if channel in self.logs.keys() and not self.logs[channel].closed:
+ log = self.logs[channel]
+ else:
+ log = self.logs[IRC]
+ print >>log, "%s <<< :%s 329 %s %s %d" % (
+ ts, IRC.serv, IRC.identity.nick, channel.name, created)
+ log.flush()
+
+ def onUnhandled(self, IRC, line, origin, cmd, target, params, extinfo):
+ ts = irc.timestamp()
+ print >>self.logs[IRC], "%s <<< %s" % (ts, line)
+ self.logs[IRC].flush()
diff --git a/poker.py b/poker.py
deleted file mode 100644
index 9255548..0000000
--- a/poker.py
+++ /dev/null
@@ -1,218 +0,0 @@
-#!/usr/bin/python
-import re
-import os
-import random
-import string
-import 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 is 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
index 9cbffb7..a3817bc 100644
--- a/sedbot.py
+++ b/sedbot.py
@@ -7,72 +7,53 @@ import time
class SED(object):
def __init__(self, expiry=1800):
self.__name__ = "SED Bot"
- self.__version__ = "0.0.1"
+ self.__version__ = "0.0.2"
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 onChanMsg(self, IRC, user, channel, targetprefix, msg):
+ matches = re.findall(self.pattern, msg)
+ if matches:
+ separator, find, replace, flags = matches[0]
+ find = re.sub("\\\\([,/#\\\\])", "\\1", find)
+ replace = re.sub("\\\\(,/#\\\\)", "\\1", replace)
+ match = False
+ for t, IRC2, user2, channel2, msg2, isaction in self.history.__reversed__():
+ if channel != channel2:
+ continue
+ try:
+ if re.findall(find, msg2):
+ sub = re.sub(find, replace, msg2, flags=re.I if "i" in flags else 0)
+ match = True
+ else:
+ continue
+ except:
+ channel.msg("%s: Invalid syntax" % user.nick, origin=self)
+ raise
+ if isaction:
+ channel.msg("What %s really meant was: *%s %s" % (user2.nick, user2.nick, sub), origin=self)
+ else:
+ channel.msg("What %s really meant to say was: %s" %
+ (user2.nick, sub), origin=self)
+ break
+ if not match:
+ channel.msg("%s: I tried. I really tried! But I could not find the pattern: %s" % (user.nick, find), origin=self)
+ else:
+ self.history.append((time.time(), IRC, user, channel, msg, False))
+ 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 IRC.identity:
- self.replace(IRC, IRC.identity.nick,
- IRC.identity.idnt, IRC.identity.host, *data)
+ def onSendChanMsg(self, IRC, origin, channel, targetprefix, msg):
+ if origin != self: # Ignore messages sent from THIS addon.
+ self.onChanMsg(IRC, IRC.identity, channel, targetprefix, msg)
- 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:
+ def onChanAction(self, IRC, user, channel, targetprefix, action):
+ self.history.append((time.time(
+ ), IRC, user, channel, targetprefix, action, True))
+ while len(self.history) and self.history[0][0] < time.time()-1800:
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 onSendChanAction(self, IRC, origin, channel, targetprefix, action):
+ if origin != self: # Ignore messages sent from THIS addon.
+ self.onChanAction(IRC, IRC.identity, channel, targetprefix, action)
diff --git a/startirc.py b/startirc.py
index 554c6b3..c3a6cec 100755
--- a/startirc.py
+++ b/startirc.py
@@ -20,21 +20,21 @@ networks = {}
def quit(quitmsg="Goodbye!"):
global networks
- modules = []
+ addons = []
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():
+ for addon in list(IRC.addons):
+ IRC.rmAddon(addon)
+ if addon not in addons:
+ addons.append(addon)
+ for addon in addons:
+ if "stop" in dir(addon) and callable(addon.stop) and "isAlive" in dir(addon) and callable(addon.isAlive) and addon.isAlive():
try:
- module.stop()
+ addon.stop()
except:
pass
print "Goodbye!"
@@ -53,21 +53,22 @@ signal.signal(signal.SIGTERM, sigterm)
logroot = os.path.join(os.environ["HOME"], "IRC")
-insomnialog = open(os.path.join(logroot, "insomnia.log"), "a")
InsomniaIRC = networks["InsomniaIRC"] = irc.Connection(
- server="perseus.insomniairc.net", ipv6=False, ssl=True, log=insomnialog)
+ server="perseus.insomniairc.net", ipv6=False, ssl=True, log=open("/dev/null", "w"))
ax = autoexec.Autoexec()
log = logger.Logger(logroot)
+
+### Be sure to generate your own cert.pem and key.pem files!
BNC = bouncer.Bouncer(
- "", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem")
+ "", 16698, ssl=True, certfile="cert.pem", keyfile="key.pem", autoaway="I'm off to see the wizard!")
for (label, IRC) in networks.items():
- IRC.addModule(log, label=label)
+ IRC.addAddon(log, label=label)
### The password is 'hunter2'
- IRC.addModule(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512")
+ IRC.addAddon(BNC, label=label, passwd="6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22", hashtype="sha512")
-InsomniaIRC.addModule(ax, label="InsomniaIRC", autojoin=["#chat"])
+InsomniaIRC.addAddon(ax, label="InsomniaIRC", autojoin=["#chat"])
for (label, IRC) in networks.items():
- IRC.start()
+ IRC.start() \ No newline at end of file