diff options
| -rw-r--r-- | autoexec.py | 55 | ||||
| -rw-r--r-- | bouncer.py | 513 | ||||
| -rw-r--r-- | cannon.py | 56 | ||||
| -rw-r--r-- | figlet.py | 33 | ||||
| -rw-r--r-- | iqueue.py | 287 | ||||
| -rw-r--r-- | irc.py | 1497 | ||||
| -rw-r--r-- | logger.py | 864 | ||||
| -rw-r--r-- | poker.py | 218 | ||||
| -rw-r--r-- | sedbot.py | 105 | ||||
| -rwxr-xr-x | startirc.py | 31 | 
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) @@ -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 @@ -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) @@ -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() @@ -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): @@ -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) @@ -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  | 
