summaryrefslogtreecommitdiff
path: root/irc.py
diff options
context:
space:
mode:
Diffstat (limited to 'irc.py')
-rw-r--r--irc.py1497
1 files changed, 877 insertions, 620 deletions
diff --git a/irc.py b/irc.py
index 7ed9d1a..13f1922 100644
--- a/irc.py
+++ b/irc.py
@@ -8,16 +8,26 @@ import socket
import os
import platform
import traceback
-import Queue
import ssl
+import glob
+import iqueue as Queue
+
+
+def timestamp():
+ t = time.time()
+ ms = 1000*t%1000
+ ymdhms = time.localtime(t)
+ tz = time.altzone if ymdhms.tm_isdst else time.timezone
+ sgn = "-" if tz >= 0 else "+"
+ return "%04d-%02d-%02d %02d:%02d:%02d.%03d%s%02d:%02d"%(ymdhms[:6]+(1000*t%1000, sgn, abs(tz)/3600, abs(tz)/60%60))
class Connection(Thread):
- def __init__(self, server, nick="ircbot", ident="python", realname="Python IRC Library", passwd=None, port=None, ipv6=False, ssl=False, autoreconnect=True, log=sys.stderr, timeout=300, retrysleep=5, maxretries=15, onlogin=None):
+ def __init__(self, server, nick="ircbot", username="python", realname="Python IRC Library", passwd=None, port=None, ipv6=False, ssl=False, autoreconnect=True, log=sys.stderr, timeout=300, retrysleep=5, maxretries=15, onlogin=None):
self.__name__ = "pyIRC"
- self.__version__ = "1.0.0rc2"
+ self.__version__ = "1.1"
self.__author__ = "Brian Sherson"
- self.__date__ = "August 26, 2013"
+ self.__date__ = "December 1, 2013"
if port is None:
self.port = 6667 if not ssl else 6697
@@ -30,7 +40,7 @@ class Connection(Thread):
self.nick = nick
self.realname = realname
- self.idnt = ident
+ self.username = username
self.passwd = passwd
self.server = server
self.ssl = ssl
@@ -48,12 +58,13 @@ class Connection(Thread):
self.quitexpected = False
self.log = log
- self.modules = []
+ self.addons = []
self.trusted = []
### Initialize IRC environment variables
- self.motdgreet = ""
- self.motd = []
+ self.motdgreet = None
+ self.motd = None
+ self.motdend = None
self.identity = None
self.users = []
self.channels = []
@@ -66,83 +77,104 @@ class Connection(Thread):
Thread.__init__(self)
+ def logwrite(self, *lines):
+ with self.loglock:
+ ts = timestamp()
+ for line in lines:
+ print >>self.log, "%s %s"%(ts, line)
+ self.log.flush()
+
+ def logopen(self, filename):
+ with self.loglock:
+ ts = timestamp()
+ newlog = open(filename, "a")
+ if type(self.log) == file and not self.log.closed:
+ print >>self.log, "%s ### Log file closed" % (ts)
+ if self.log not in (sys.stdout, sys.stderr):
+ self.log.close()
+ self.log = newlog
+ print >>self.log, "%s ### Log file opened" % (ts)
+ self.log.flush()
+
def event(self, method, modlist, exceptions=False, **params):
- #print method, modlist
timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2,
"0") for t in time.localtime()[0:6]])
- for module in modlist:
- #print module, dir(module)
- #if modlist is not self.modules and module in self.modules: continue
- if method in dir(module) and callable(getattr(module, method)):
+ handled = []
+ unhandled = []
+ errors = []
+ for k, addon in enumerate(modlist):
+ if modlist.index(addon) < k:
+ continue
+ if method in dir(addon) and callable(getattr(addon, method)):
try:
- getattr(module, method)(self, **params)
+ getattr(addon, method)(self, **params)
except:
- with self.loglock:
- exc, excmsg, tb = sys.exc_info()
- print >>self.log, "%(timestamp)s !!! Exception in module %(module)s" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
- self.log.flush()
+ exc, excmsg, tb = sys.exc_info()
+ errors.append((addon, exc, excmsg, tb))
+ self.logwrite(*["!!! Exception in addon %(addon)s" % vars()]+["!!! %s"%line for line in traceback.format_exc().split("\n")])
+ print >>sys.stderr, "Exception in addon %(addon)s" % vars()
+ print >>sys.stderr, traceback.format_exc()
if exceptions: # If set to true, we raise the exception.
raise
+ else:
+ handled.append(addon)
+ else:
+ unhandled.append(addon)
+ return (handled, unhandled, errors)
- def addModule(self, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException("Module already added.")
+ def addAddon(self, addon, trusted=False, **params):
+ if addon in self.addons:
+ raise BaseException("Addon already added.")
with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.append(module)
+ self.event("onAddonAdd", [addon], exceptions=True, **params)
+ self.addons.append(addon)
if trusted:
- self.trusted.append(module)
+ self.trusted.append(addon)
- def insertModule(self, index, module, trusted=False, **params):
- if module in self.modules:
- raise BaseException("Module already added.")
+ def insertAddon(self, index, addon, trusted=False, **params):
+ if addon in self.addons:
+ raise BaseException("Addon already added.")
with self.lock:
- self.event("onModuleAdd", [module], exceptions=True, **params)
- self.modules.insert(index, module)
+ self.event("onAddonAdd", [addon], exceptions=True, **params)
+ self.addons.insert(index, addon)
if trusted:
- self.trusted.append(module)
+ self.trusted.append(addon)
- def rmModule(self, module, **params):
+ def rmAddon(self, addon, **params):
with self.lock:
- self.modules.remove(module)
- self.event("onModuleRem", [module], exceptions=True, **params)
- if module in self.trusted:
- self.trusted.remove(module)
+ self.addons.remove(addon)
+ self.event("onAddonRem", [addon], exceptions=True, **params)
+ if addon in self.trusted:
+ self.trusted.remove(addon)
def run(self):
+ privmodeeventnames = dict(q=("Owner", "Deowner"), a=("Admin", "Deadmin"), o=("Op", "Deop"), h=("Halfop", "Dehalfop"), v=("Voice", "Devoice"))
+ maskmodeeventnames = dict(b=("Ban", "Unban"), e=(
+ "BanExcept", "UnbanExcept"), I=("Invite", "Uninvite"))
self.quitexpected = False
outgoingthread = None
+ whoisstarted = False
+ nameslist = []
+ wholist = []
+ lists = {}
+ nameschan = None
server = self.server
if self.ipv6 and ":" in server:
server = "[%s]"%server
port = self.port
try:
- modules = list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
with self.lock:
- self.event("onLogOpen", modules)
+ self.event("onSessionOpen", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
- 2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s ### Log session started" % vars()
- self.log.flush()
+ self.logwrite("### Log session started")
attempt = 1
while True: # An entire connection lives within this while loop. When the connection fails, will try to reestablish, unless self.autoreconnect is set to False.
while True: # Enter retry loop
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Attempting connection to %(server)s:%(port)s." % vars()
- self.log.flush()
+ self.logwrite("*** Attempting connection to %(server)s:%(port)s." % vars())
with self.lock:
- self.event("onConnectAttempt", self.modules)
+ self.event("onConnectAttempt", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
try:
if self.ssl:
@@ -153,31 +185,22 @@ class Connection(Thread):
self.connection = socket.socket(socket.AF_INET6 if self.ipv6 else socket.AF_INET, socket.SOCK_STREAM)
self.connection.connect((self.server, self.port, 0, 0) if self.ipv6 else (self.server, self.port))
except socket.error:
- with self.loglock:
- exc, excmsg, tb = sys.exc_info()
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
- self.log.flush()
+ exc, excmsg, tb = sys.exc_info()
+ self.event("onConnectFail", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), exc=exc, excmsg=excmsg, tb=tb)
+ self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars())
else:
self.connected = True
self.connection.settimeout(self.timeout)
### Setting up thread responsible for sending data back to IRC server.
+ self.outgoing._interrupted = False
outgoingthread = Outgoing(self)
outgoingthread.daemon = True
outgoingthread.start()
- ### Run onConnect on all modules to signal connection was established.
- modules = list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
- self.event("onConnect", modules)
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s established." % vars()
- self.log.flush()
+ ### Run onConnect on all addons to signal connection was established.
+ self.event("onConnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
+ self.logwrite("*** Connection to %(server)s:%(port)s established." % vars())
break
if self.quitexpected:
@@ -188,11 +211,8 @@ class Connection(Thread):
sys.exit()
attempt += 1
else:
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars()
- self.log.flush()
- break
+ self.logwrite("*** Maximum number of attempts reached. Giving up. (%(server)s:%(port)s)" % vars())
+ sys.exit()
### Connection succeeded
try:
@@ -202,7 +222,8 @@ class Connection(Thread):
if self.passwd:
self.raw("PASS :%(passwd)s" % vars(self))
self.raw("NICK :%(nick)s" % vars())
- self.raw("USER %(idnt)s * * :%(realname)s" % vars(self))
+ self.raw("USER %(username)s * * :%(realname)s" %
+ vars(self))
### Initialize buffers
linebuf = []
@@ -233,17 +254,16 @@ class Connection(Thread):
self.connection.send("PONG :%s\n" % ping[0])
continue
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >> self.log, "%(timestamp)s <<< %(line)s" % vars()
- self.log.flush()
+ self.logwrite("<<< %(line)s" % vars())
### Attempts to match against pattern ":src cmd target params :extinfo"
- matches = re.findall("^:(.+?)(?:!(.+?)@(.+?))?\\s+(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line)
+ matches = re.findall(r"^:(.+?)(?:!(.+?)@(.+?))?\s+(.+?)(?:\s+(.+?)(?:\s+(.+?))??)??(?:\s+:(.*))?$", line)
### We have a match!
if len(matches):
- parsed = (origin, ident, host, cmd, target, params, extinfo) = matches[0]
+ parsed = (origin, username, host, cmd, target, params, extinfo) = matches[0]
+ unhandled = []
+
if re.match("^\\d+$", cmd):
cmd = int(cmd) # Code is a numerical response
else:
@@ -255,13 +275,7 @@ class Connection(Thread):
self.registered = True
self.identity = self.user(target)
self.serv = origin
-
- modules = list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
- self.event("onRegistered", modules)
+ self.event("onRegistered", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
elif cmd == 433 and target == "*": # Server reports nick taken, so we need to try another.
trynick += 1
@@ -273,18 +287,49 @@ class Connection(Thread):
if not self.registered: # Registration is not yet complete
continue
+ if username and host:
+ nickname = origin
+ origin = self.user(origin)
+ if origin.nick != nickname:
+ ### Origin nickname has changed
+ origin.user = nickname
+ if origin.username != username:
+ ### Origin username has changed
+ origin.username = username
+ if origin.host != host:
+ ### Origin host has changed
+ origin.host = host
+
+ chanmatch = re.findall("([%s]?)([%s].+)"%(re.escape(self.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.supports.get("CHANTYPES", "#"))), target)
+ if chanmatch:
+ targetprefix, channame = chanmatch[0]
+ target = self.channel(channame)
+ if target.name != channame:
+ ### Target channel name has changed
+ target.name = channame
+ elif len(target) and target[0] != "$" and cmd != "NICK":
+ targetprefix = ""
+ target = self.user(target)
+
+ data = dict(origin=origin, cmd=cmd, target=target, targetprefix=targetprefix, params=params, extinfo=extinfo)
+
### Major codeblock here! Track IRC state.
- ### Send line to modules first
+ ### Send line to addons first
with self.lock:
- self.event("onRecv", self.modules, line=line, data=parsed)
+ self.event("onRecv", self.addons, line=line,
+ **data)
if cmd == 1:
+ (handled, unhandled, exceptions) = self.event("onWelcome", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
self.welcome = extinfo # Welcome message
elif cmd == 2:
+ (handled, unhandled, exceptions) = self.event("onYourHost", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
self.hostinfo = extinfo # Your Host
elif cmd == 3:
- self.servinfo = extinfo # Server Created
+ (handled, unhandled, exceptions) = self.event("onServerCreated", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, msg=extinfo)
+ self.servcreated = extinfo # Server Created
elif cmd == 4:
- self.serv004 = params # What is this code?
+ (handled, unhandled, exceptions) = self.event("onServInfo", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, servinfo=params)
+ self.servinfo = params # What is this code?
elif cmd == 5: # Server Supports
support = dict(re.findall("([A-Za-z0-9]+)(?:=(\\S*))?", params))
if "CHANMODES" in support:
@@ -295,173 +340,239 @@ class Connection(Thread):
support["PREFIX"] = matches[0]
else:
del support["PREFIX"] # Might as well delete the info if it doesn't match expected pattern
+ (handled, unhandled, exceptions) = self.event("onSupports", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, supports=support)
self.supports.update(support)
if "serv005" in dir(self) and type(self.serv005) == list:
self.serv005.append(params)
else:
self.serv005 = [params]
- elif cmd == 8: # Channel Modes
- self.identity.snomask = params.lstrip("+")
+ elif cmd == 8: # Snomask
+ snomask = params.lstrip("+")
+ (handled, unhandled, exceptions) = self.event("onSnoMask", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=snomask)
+ self.identity.snomask = snomask
if "s" not in self.identity.modes:
self.snomask = ""
- elif cmd == 221: # Channel Modes
- self.identity.modes = (params if params else extinfo).lstrip("+")
+ elif cmd == 221: # User Modes
+ modes = (params if params else extinfo).lstrip("+")
+ (handled, unhandled, exceptions) = self.event("onUserModes", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), origin=origin, snomask=modes)
+ self.identity.modes = modes
if "s" not in self.identity.modes:
self.snomask = ""
- elif cmd == 251:
+ elif cmd == 251: # Net Stats
+ (handled, unhandled, exceptions) = self.event("onNetStats", self.addons, origin=origin, netstats=extinfo)
self.netstats = extinfo
elif cmd == 252:
- self.opcount = int(params)
+ opcount = int(params)
+ (handled, unhandled, exceptions) = self.event("onOpCount", self.addons, origin=origin, opcount=opcount)
+ self.opcount = opcount
elif cmd == 254:
- self.chancount = int(params)
- elif cmd == 311: # WHOIS data
- pass
+ chancount = int(params)
+ (handled, unhandled, exceptions) = self.event("onChanCount", self.addons, origin=origin, chancount=chancount)
+ self.chancount = chancount
+
+ elif cmd == 305: # Returned from away status
+ (handled, unhandled, exceptions) = self.event("onReturn", self.addons, origin=origin, msg=extinfo)
+ self.identity.away = False
+
+ elif cmd == 306: # Entered away status
+ (handled, unhandled, exceptions) = self.event("onAway", self.addons, origin=origin, msg=extinfo)
+ self.identity.away = True
+
+ elif cmd == 311: # Start of WHOIS data
+ nickname, username, host, star = params.split()
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisStart", self.addons, origin=origin, user=user, nickname=nickname, username=username, host=host, realname=extinfo)
+ user.nick = nickname
+ user.username = username
+ user.host = host
+ elif cmd == 301: # Away Message
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisAway", self.addons, origin=origin, user=user, nickname=params, awaymsg=extinfo)
+ user.away = True
+ user.awaymsg = extinfo
+ elif cmd == 307: # Is a registered nick
+ (handled, unhandled, exceptions) = self.event("onWhoisRegisteredNick", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 378: # Connecting From
+ (handled, unhandled, exceptions) = self.event("onWhoisConnectingFrom", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 319: # Channels
+ (handled, unhandled, exceptions) = self.event("onWhoisChannels", self.addons, origin=origin, user=self.user(params), nickname=params, chanlist=extinfo.split(" "))
+ elif cmd == 310: # Availability
+ (handled, unhandled, exceptions) = self.event("onWhoisAvailability", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 312: # Server
+ nickname, server = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisServer", self.addons, origin=origin, user=user, nickname=nickname, server=server, servername=extinfo)
+ user.server = server
+ elif cmd == 313: # IRC Op
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisOp", self.addons, origin=origin, user=user, nickname=params, msg=extinfo)
+ user.ircop = True
+ user.ircopmsg = extinfo
+ elif cmd == 317: # Idle and Signon times
+ nickname, idletime, signontime = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisTimes", self.addons, origin=origin, user=user, nickname=nickname, idletime=int(idletime), signontime=int(signontime), msg=extinfo)
+ user.idlesince = int(time.time())-int(idletime)
+ user.signontime = int(signontime)
+ elif cmd == 671: # SSL
+ user = self.user(params)
+ (handled, unhandled, exceptions) = self.event("onWhoisSSL", self.addons, origin=origin, user=user, nickname=params, msg=extinfo)
+ user.ssl = True
+ elif cmd == 379: # User modes
+ (handled, unhandled, exceptions) = self.event("onWhoisModes", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+ elif cmd == 330: # Logged in as
+ nickname, loggedinas = params.split(" ")
+ user = self.user(nickname)
+ (handled, unhandled, exceptions) = self.event("onWhoisLoggedInAs", self.addons, origin=origin, user=user, nickname=nickname, loggedinas=loggedinas, msg=extinfo)
+ user.loggedinas = loggedinas
+ elif cmd == 318: # End of WHOIS
+ (handled, unhandled, exceptions) = self.event("onWhoisEnd", self.addons, origin=origin, user=self.user(params), nickname=params, msg=extinfo)
+
elif cmd == 321: # Start LIST
- self.chanlistbegin = (params, extinfo)
- self.chanlist = {}
+ (handled, unhandled, exceptions) = self.event("onListStart", self.addons, origin=origin, params=params, extinfo=extinfo)
elif cmd == 322: # LIST item
(chan, pop) = params.split(" ", 1)
- self.chanlist[chan] = (pop, extinfo)
- elif cmd == 323:
- self.chanlistend = extinfo # End of LIST
+ (handled, unhandled, exceptions) = self.event("onListEntry", self.addons, origin=origin, channel=self.channel(chan), population=int(pop), extinfo=extinfo)
+ elif cmd == 323: # End of LIST
+ (handled, unhandled, exceptions) = self.event("onListEnd", self.addons, origin=origin, endmsg=extinfo)
+
elif cmd == 324: # Channel Modes
modeparams = params.split()
channame = modeparams.pop(0)
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ self.event("onRecv", channel.addons, line=line, **data)
if channel.name != channame:
channel.name = channame # Server seems to have changed the idea of the case of the channel name
setmodes = modeparams.pop(0)
+ modedelta = []
for mode in setmodes:
if mode == "+":
continue
elif mode in self.supports["CHANMODES"][2]:
param = modeparams.pop(0)
+ modedelta.append(("+%s"%mode, param))
+ elif mode in self.supports["CHANMODES"][3]:
+ modedelta.append(("+%s"%mode, None))
+ (handled, unhandled, exceptions) = self.event("onChannelModes", self.addons+channel.addons, channel=channel, modedelta=modedelta)
+ for ((modeset, mode), param) in modedelta:
+ if mode in self.supports["CHANMODES"][2]:
channel.modes[mode] = param
elif mode in self.supports["CHANMODES"][3]:
channel.modes[mode] = True
+
elif cmd == 329: # Channel created
channame, created = params.split()
+ created = int(created)
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onChanCreated", self.addons+channel.addons, channel=channel, created=created)
channel.created = int(created)
+
elif cmd == 332: # Channel Topic
channame = params
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopic", self.addons+channel.addons, origin=origin, channel=channel, topic=extinfo)
channel.topic = extinfo
+
elif cmd == 333: # Channel Topic info
(channame, nick, dt) = params.split()
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopicInfo", self.addons+channel.addons, origin=origin, channel=channel, topicsetby=nick, topictime=int(dt))
channel.topicsetby = nick
- channel.topictime = dt
- elif cmd == 346: # Invite
- (channame, invite, nick, invtime) = params.split()
- channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "I" in channel.modes:
- if invite.lower() not in [m.lower() for (m, b, t) in channel.modes["I"]]:
- channel.modes["I"].append((invite, nick, int(invtime)))
- else:
- channel.modes["I"] = [(invite, nick, int(invtime))]
- elif cmd == 348: # Ban Exception
- (channame, exception, nick, exctime) = params.split()
- channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "e" in channel.modes:
- if exception.lower() not in [m.lower() for (m, b, t) in channel.modes["e"]]:
- channel.modes["e"].append((exception, nick, int(exctime)))
- else:
- channel.modes["e"] = [(exception, nick, int(exctime))]
+ channel.topictime = int(dt)
+
elif cmd == 352: # WHO reply
- (channame, ident, host, serv, nick, flags) = params.split()
+ (channame, username, host, serv, nick, flags) = params.split()
try:
(hops, realname) = extinfo.split(" ", 1)
except ValueError:
hops = extinfo
realname = None
- channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+
+ if channame[0] in self.supports.get("CHANTYPES", "#"):
+ channel = self.channel(channame)
+ else:
+ channel = None
+
user = self.user(nick)
- if user.nick != nick:
- user.nick = nick
+
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onWhoEntry", self.addons+channel.addons, origin=origin, channel=channel, user=user, channame=channame, username=username, host=host, serv=serv, nick=nick, flags=flags, hops=int(hops), realname=realname)
user.hops = hops
user.realname = realname
- user.idnt = ident
+ user.username = username
user.host = host
user.server = serv
user.away = "G" in flags
user.ircop = "*" in flags
- if user not in channel.users:
- channel.users.append(user)
- if channel not in user.channels:
- user.channels.append(channel)
- for (mode, prefix) in zip(*self.supports["PREFIX"]):
- if prefix in flags:
- if mode in channel.modes.keys() and user not in channel.modes[mode]:
- channel.modes[mode].append(user)
- elif mode not in channel.modes.keys():
- channel.modes[mode] = [user]
+ if type(channel) == Channel:
+ if user not in channel.users:
+ channel.users.append(user)
+ if channel not in user.channels:
+ user.channels.append(channel)
+ for (mode, prefix) in zip(*self.supports["PREFIX"]):
+ if prefix in flags:
+ if mode in channel.modes.keys() and user not in channel.modes[mode]:
+ channel.modes[mode].append(user)
+ elif mode not in channel.modes.keys():
+ channel.modes[mode] = [user]
+
+ elif cmd == 315: # End of WHO reply
+ (handled, unhandled, exceptions) = self.event("onWhoEnd", self.addons+channel.addons, origin=origin, param=params, endmsg=extinfo)
+
elif cmd == 353: # NAMES reply
- (devnull, channame) = params.split()
+ (flag, channame) = params.split()
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ self.event("onRecv", channel.addons, line=line, **data)
+
if "PREFIX" in self.supports:
- names = re.findall("(["+re.escape(self.supports["PREFIX"][1])+"]*)(\\S+)", extinfo)
+ names = re.findall(r"([%s]*)([^@!\s]+)(?:!(\S+)@(\S+))?"%re.escape(self.supports["PREFIX"][1]), extinfo)
else:
- names = [("", name) for name in extinfo.split()] # Still put it into tuple form for compatibility in the next structure
- for (symbs, nick) in names:
+ names = re.findall(r"()([^@!\s]+)(?:!(\S+)@(\S+))?", extinfo) # Still put it into tuple form for compatibility in the next structure
+ (handled, unhandled, exceptions) = self.event("onNames", self.addons+channel.addons, origin=origin, channel=channel, flag=flag, channame=channame, nameslist=names)
+
+ for (symbs, nick, username, host) in names:
user = self.user(nick)
if user.nick != nick:
user.nick = nick
- if channel not in user.channels:
- user.channels.append(channel)
- if user not in channel.users:
- channel.users.append(user)
- if "PREFIX" in self.supports:
- for symb in symbs:
- mode = self.supports["PREFIX"][0][self.supports["PREFIX"][1].index(symb)]
- if mode not in channel.modes:
- channel.modes[mode] = [user]
- elif user not in channel.modes[mode]:
- channel.modes[mode].append(user)
- elif cmd == 367: # Channel Ban
- (channame, ban, nick, bantime) = params.split()
- channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "b" in channel.modes.keys():
- if ban.lower() not in [m.lower() for (m, b, t) in channel.modes["b"]]:
- channel.modes["b"].append((ban, nick, int(bantime)))
- else:
- channel.modes["b"] = [(ban, nick, int(bantime))]
- elif cmd == 372:
- self.motd.append(extinfo) # MOTD item
+ if username and user.username != username:
+ user.username = username
+ if host and user.host != host:
+ user.host = host
+ with channel.lock:
+ if channel not in user.channels:
+ user.channels.append(channel)
+ if user not in channel.users:
+ channel.users.append(user)
+ if "PREFIX" in self.supports:
+ for symb in symbs:
+ mode = self.supports["PREFIX"][0][self.supports["PREFIX"][1].index(symb)]
+ if mode not in channel.modes:
+ channel.modes[mode] = [user]
+ elif user not in channel.modes[mode]:
+ channel.modes[mode].append(user)
+
+ elif cmd == 366: # End of NAMES reply
+ channel = self.channel(params)
+ (handled, unhandled, exceptions) = self.event("onNamesEnd", self.addons+channel.addons, origin=origin, channel=channel, channame=params, endmsg=extinfo)
+
+ elif cmd == 372: # MOTD line
+ (handled, unhandled, exceptions) = self.event("onMOTDLine", self.addons, origin=origin, motdline=extinfo)
+ self.motd.append(extinfo)
elif cmd == 375: # Begin MOTD
+ (handled, unhandled, exceptions) = self.event("onMOTDStart", self.addons, origin=origin, motdgreet=extinfo)
self.motdgreet = extinfo
self.motd = []
elif cmd == 376:
+ (handled, unhandled, exceptions) = self.event("onMOTDEnd", self.addons, origin=origin, motdend=extinfo)
self.motdend = extinfo # End of MOTD
- elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
- (channame, admin) = params.split()
+
+ elif cmd == 386 and "q" in self.supports["PREFIX"][0]: # Channel Owner (Unreal)
+ (channame, owner) = params.split()
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ self.event("onRecv", channel.addons, line=line, **data)
if channel.name != channame:
channel.name = channame # Server seems to have changed the idea of the case of the channel name
user = self.user(owner)
@@ -472,10 +583,11 @@ class Connection(Thread):
channel.modes["q"].append(user)
else:
channel.modes["q"] = [user]
+
elif cmd == 388 and "a" in self.supports["PREFIX"][0]: # Channel Admin (Unreal)
(channame, admin) = params.split()
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ self.event("onRecv", channel.addons, line=line, **data)
if channel.name != channame:
channel.name = channame # Server seems to have changed the idea of the case of the channel name
user = self.user(admin)
@@ -486,220 +598,189 @@ class Connection(Thread):
channel.modes["a"].append(user)
else:
channel.modes["a"] = [user]
+
elif cmd == "NICK":
- user = self.user(origin)
- modules = []
- for channel in user.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
- self.event(modules, line, parsed)
newnick = extinfo if len(extinfo) else target
- #print user, newnick
- ### Need to check if newnick is still listed
+
+ addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], [])
+ self.event("onRecv", addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onNickChange", self.addons+addons, user=origin, newnick=newnick)
+ if origin == self.identity:
+ (handled, unhandled, exceptions) = self.event("onMeNickChange", self.addons+addons, newnick=newnick)
+
for u in self.users:
- #print u
- #print [u.nick, newnick, u.nick.lower(), newnick.lower()]
if u.nick.lower() == newnick.lower():
- with self.loglock:
- print >>self.log, "%s *** Orphaned user %s!%s@%s was removed when %s!%s@%s changed his/her nick to %s."%(timestamp, u.nick, u.idnt, u.host, user.nick, user.idnt, user.host, newnick)
- self.log.flush()
- self.users.remove(u)
+ self.users.remove(u) # Nick collision, safe to assume this orphaned user is offline, so we shall remove the old instance.
for channel in self.channels:
+ ### If for some odd reason, the old user still appears common channels, then we will remove the user anyway.
if u in channel.users:
channel.users.remove(u)
- user.nick = newnick
- elif cmd == "JOIN":
- channame = target if len(target) else extinfo
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
+ origin.nick = newnick
- channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
+ elif cmd == "JOIN":
+ channel = target if type(target) == Channel else self.channel(extinfo)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onJoin", self.addons+channel.addons, user=origin, channel=channel)
- if user == self.identity: # This means the bot is entering the room,
+ if origin == self.identity: # This means the bot is entering the room,
# and will reset all the channel data, on the assumption that such data may have changed.
# Also, the bot must request modes
channel.topic = ""
channel.topicmod = ""
channel.modes = {}
channel.users = []
- self.raw("MODE %(channame)s" % vars())
- self.raw("WHO %(channame)s" % vars())
- self.raw("MODE %s :%s" % (channame, self.supports["CHANMODES"][0]))
- if channel not in user.channels:
- user.channels.append(channel)
- if user not in channel.users:
- channel.users.append(user)
- elif cmd == "KICK":
- kicker = self.user(origin)
- if kicker.nick != origin:
- kicker.nick = origin
- if kicker.idnt != ident:
- kicker.idnt = ident
- if kicker.host != host:
- kicker.host = host
+ self.event("onMeJoin", self.addons+channel.addons, channel=channel)
+ self.raw("MODE %s" % channel.name)
+ self.raw("WHO %s" % channel.name)
+ if "CHANMODES" in self.supports.keys():
+ self.raw("MODE %s :%s" % (channel.name, self.supports["CHANMODES"][0]))
+ if channel not in origin.channels:
+ origin.channels.append(channel)
+ if origin not in channel.users:
+ channel.users.append(origin)
+
+ elif cmd == "KICK":
kicked = self.user(params)
if kicked.nick != params:
kicked.nick = params
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != target:
- channel.name = target # Server seems to have changed the idea of the case of the channel name
- if channel in kicked.channels:
- kicked.channels.remove(channel)
- if kicked in channel.users:
- channel.users.remove(kicked)
+ self.event("onRecv", target.addons, line=line, **data)
+ if origin == self.identity:
+ self.event("onMeKick", self.addons+target.addons, channel=target, kicked=kicked, kickmsg=extinfo)
+ if kicked == self.identity:
+ self.event("onMeKicked", self.addons+target.addons, kicker=origin, channel=target, kickmsg=extinfo)
+ (handled, unhandled, exceptions) = self.event("onKick", self.addons+target.addons, kicker=origin, channel=target, kicked=kicked, kickmsg=extinfo)
+
+ if target in kicked.channels:
+ kicked.channels.remove(target)
+ if kicked in target.users:
+ target.users.remove(kicked)
if "PREFIX" in self.supports:
for mode in self.supports["PREFIX"][0]:
- if mode in channel.modes and kicked in channel.modes[mode]:
- channel.modes[mode].remove(kicked)
- #if not len(chanobj.users): #Let's remove this channel
- # del self.channels[target.lower()]
- if all([kicked not in c.users for c in self.identity.channels]):
- with self.loglock:
- print >>self.log, "%s *** User %s!%s@%s was orphaned when being kicked from %s."%(timestamp, kicked.nick, kicked.idnt, kicked.host, channel.name)
- self.log.flush()
+ if mode in target.modes and kicked in target.modes[mode]:
+ target.modes[mode].remove(kicked)
+
elif cmd == "PART":
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
-
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != target:
- channel.name = target # Server seems to have changed the idea of the case of the channel name
-
- if channel in user.channels:
- user.channels.remove(channel)
- if user in channel.users:
- channel.users.remove(user)
+ self.event("onRecv", target.addons, line=line, **data)
+ if origin == self.identity:
+ self.event("onMePart", self.addons+target.addons, channel=target, partmsg=extinfo)
+ (handled, unhandled, exceptions) = self.event("onPart", self.addons+target.addons, user=origin, channel=target, partmsg=extinfo)
+
+ if target in origin.channels:
+ origin.channels.remove(target)
+ if origin in target.users:
+ target.users.remove(origin)
if "PREFIX" in self.supports:
for mode in self.supports["PREFIX"][0]:
- if mode in channel.modes and user in channel.modes[mode]:
- channel.modes[mode].remove(user)
- if all([user not in c.users for c in self.identity.channels]):
- with self.loglock:
- print >>self.log, "%s *** User %s!%s@%s was orphaned when parting %s."%(timestamp, user.nick, user.idnt, user.host, channel.name)
- self.log.flush()
- elif cmd == "QUIT":
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
- channels = list(user.channels)
- for channel in user.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
- self.event(modules, line, parsed)
- for channel in channels:
- channel.lock.acquire(True)
- for channel in user.channels:
- if user in channel.users:
- channel.users.remove(user)
- if "PREFIX" in self.supports:
- for mode in self.supports["PREFIX"][0]:
- if mode in channel.modes and user in channel.modes[mode]:
- channel.modes[mode].remove(user)
- #if not len(chanobj.users): #Let's remove this channel
- # del self.channels[chan]
- # On second thought, no, since we may want to save certain info
- user.channels = []
- for channel in channels:
- channel.lock.release()
- print >>self.log, "%s *** User %s!%s@%s was orphaned when quitting IRC."%(timestamp, user.nick, user.idnt, user.host)
- elif cmd == "MODE":
- if target[0] in self.supports["CHANTYPES"]:
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
+ if mode in target.modes and origin in target.modes[mode]:
+ target.modes[mode].remove(origin)
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ elif cmd == "QUIT":
+ channels = list(origin.channels)
+ addons = reduce(lambda x, y: x+y, [chan.addons for chan in origin.channels], [])
+ self.event("onRecv", addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuit", self.addons+addons, user=origin, quitmsg=extinfo)
+ for channel in origin.channels:
with channel.lock:
- if channel.name != target:
- channel.name = target # Server seems to have changed the idea of the case of the channel name
-
- modeparams = params.split()
- setmodes = modeparams.pop(0)
- modeset = "+"
- for mode in setmodes:
- if mode in "+-":
- modeset = mode
- else:
- if mode in self.supports["CHANMODES"][0]:
- param = modeparams.pop(0)
+ if origin in channel.users:
+ channel.users.remove(origin)
+ if "PREFIX" in self.supports:
+ for mode in self.supports["PREFIX"][0]:
+ if mode in channel.modes and origin in channel.modes[mode]:
+ channel.modes[mode].remove(origin)
+ origin.channels = []
+
+ elif cmd == "MODE":
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
+ modedelta = []
+ modeparams = params.split()
+ setmodes = modeparams.pop(0)
+ modeset = "+"
+ for mode in setmodes:
+ if mode in "+-":
+ modeset = mode
+ else:
+ if mode in self.supports["CHANMODES"][0]+self.supports["CHANMODES"][1]:
+ param = modeparams.pop(0)
+ modedelta.append(("%s%s"%(modeset, mode), param))
+ if mode in maskmodeeventnames.keys():
if modeset == "+":
- if mode in channel.modes:
- if param.lower() not in [mask.lower() for (mask, setby, settime) in channel.modes[mode]]:
- channel.modes[mode].append((param, origin, int(time.time())))
- else:
- channel.modes[mode] = [(param, origin, int(time.time()))]
- else:
- if mode in channel.modes.keys():
- if mode == "b": # Inspircd mode is case insentive when unsetting the mode
- masks = [mask.lower() for (mask, setby, settime) in channel.modes[mode]]
- if param.lower() in masks:
- index = masks.index(param.lower())
- #print "Index: %d"%index
- del channel.modes[mode][index]
- else:
- masks = [mask for (mask, setby, settime) in channel.modes[mode]]
- if param in masks:
- index = masks.index(param)
- del channel.modes[mode][index]
- elif mode in self.supports["CHANMODES"][1]:
+ eventname = maskmodeeventnames[mode][0]
+ if modeset == "-":
+ eventname = maskmodeeventnames[mode][1]
+ matchesbot = glob.fnmatch.fnmatch("%s!%s@%s".lower()%(self.identity.nick, self.identity.username, self.identity.host), param.lower())
+ self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param)
+ if matchesbot:
+ self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target, banmask=param)
+ elif mode in self.supports["CHANMODES"][2]:
+ if modeset == "+":
param = modeparams.pop(0)
+ modedelta.append(("%s%s"%(modeset, mode), param))
+ else:
+ modedelta.append(("%s%s"%(modeset, mode), None))
+ elif mode in self.supports["CHANMODES"][3]:
+ modedelta.append(("%s%s"%(modeset, mode), None))
+ elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
+ modenick = modeparams.pop(0)
+ modeuser = self.user(modenick)
+ if mode in privmodeeventnames.keys():
if modeset == "+":
- channel.modes[mode] = param
+ eventname = privmodeeventnames[mode][0]
+ if modeset == "-":
+ eventname = privmodeeventnames[mode][1]
+ self.event("on%s"%eventname, self.addons+target.addons, user=origin, channel=target, modeuser=modeuser)
+ if modeuser == self.identity:
+ self.event("onMe%s"%eventname, self.addons+target.addons, user=origin, channel=target)
+ modedelta.append(("%s%s"%(modeset, mode), modeuser))
+ (handled, unhandled, exceptions) = self.event("onChanModeSet", self.addons+target.addons, user=origin, channel=target, modedelta=modedelta)
+ with target.lock:
+ for ((modeset, mode), param) in modedelta:
+ if mode in self.supports["CHANMODES"][0]:
+ if modeset == "+":
+ if mode in target.modes:
+ if param.lower() not in [mask.lower() for (mask, setby, settime) in target.modes[mode]]:
+ target.modes[mode].append((param, origin, int(time.time())))
else:
- channel.modes[mode] = None
- elif mode in self.supports["CHANMODES"][2]:
- if modeset == "+":
- param = modeparams.pop(0)
- channel.modes[mode] = param
- else:
- channel.modes[mode] = None
- elif mode in self.supports["CHANMODES"][3]:
- if modeset == "+":
- channel.modes[mode] = True
- else:
- channel.modes[mode] = False
- elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
- modenick = modeparams.pop(0)
- modeuser = self.user(modenick)
- if modeuser.nick != modenick:
- modeuser.nick = modenick
- if modeset == "+":
- if mode in channel.modes and modeuser not in channel.modes[mode]:
- channel.modes[mode].append(modeuser)
- if mode not in channel.modes:
- channel.modes[mode] = [modeuser]
- elif mode in channel.modes and modeuser in channel.modes[mode]:
- channel.modes[mode].remove(modeuser)
- else:
- user = self.user(target)
+ target.modes[mode] = [(param, origin, int(time.time()))]
+ else:
+ if mode in target.modes.keys():
+ if mode == "b": # Inspircd mode is case insentive when unsetting the mode
+ masks = [mask.lower() for (mask, setby, settime) in target.modes[mode]]
+ if param.lower() in masks:
+ index = masks.index(param.lower())
+ #print "Index: %d"%index
+ del target.modes[mode][index]
+ else:
+ masks = [mask for (mask, setby, settime) in target.modes[mode]]
+ if param in masks:
+ index = masks.index(param)
+ del target.modes[mode][index]
+ elif mode in self.supports["CHANMODES"][1]:
+ if modeset == "+":
+ target.modes[mode] = param
+ else:
+ target.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][2]:
+ if modeset == "+":
+ target.modes[mode] = param
+ else:
+ target.modes[mode] = None
+ elif mode in self.supports["CHANMODES"][3]:
+ if modeset == "+":
+ target.modes[mode] = True
+ else:
+ target.modes[mode] = False
+ elif "PREFIX" in self.supports and mode in self.supports["PREFIX"][0]:
+ if modeset == "+":
+ if mode in target.modes and param not in target.modes[mode]:
+ target.modes[mode].append(param)
+ if mode not in target.modes:
+ target.modes[mode] = [param]
+ elif mode in target.modes and param in target.modes[mode]:
+ target.modes[mode].remove(param)
+ elif type(target) == User:
modeparams = (params if params else extinfo).split()
setmodes = modeparams.pop(0)
modeset = "+"
@@ -708,126 +789,287 @@ class Connection(Thread):
modeset = mode
continue
if modeset == "+":
- if mode not in user.modes:
- user.modes += mode
+ if mode not in target.modes:
+ target.modes += mode
if mode == "s" and len(modeparams):
snomask = modeparams.pop(0)
for snomode in snomask:
if snomode in "+-":
snomodeset = snomode
continue
- if snomodeset == "+" and snomode not in user.snomask:
- user.snomask += snomode
- if snomodeset == "-" and snomode in user.snomask:
- user.snomask = user.snomask.replace(snomode, "")
+ if snomodeset == "+" and snomode not in target.snomask:
+ target.snomask += snomode
+ if snomodeset == "-" and snomode in target.snomask:
+ target.snomask = target.snomask.replace(snomode, "")
if modeset == "-":
- if mode in user.modes:
- user.modes = user.modes.replace(mode, "")
+ if mode in target.modes:
+ target.modes = target.modes.replace(mode, "")
if mode == "s":
- user.snomask = ""
+ target.snomask = ""
+
elif cmd == "TOPIC":
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
-
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
-
- with channel.lock:
- if channel.name != target:
- channel.name = target # Server seems to have changed the idea of the case of the channel name
- channel.topic = extinfo
+ self.event("onRecv", target.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onTopicSet", self.addons+target.addons, user=origin, channel=target, topic=extinfo)
+
+ with target.lock:
+ target.topic = extinfo
+ target.topicsetby = origin
+ target.topictime = int(time.time())
+
elif cmd == "PRIVMSG":
- user = self.user(origin)
- if user.nick != origin:
- user.nick = origin
- if user.idnt != ident:
- user.idnt = ident
- if user.host != host:
- user.host = host
-
- if target[0] in self.supports["CHANTYPES"]:
- channel = self.channel(target)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != target:
- channel.name = target # Server seems to have changed the idea of the case of the channel name
- elif target[0] == "$":
- pass # Server message -- Not implemented
- else:
- targetuser = self.user(target)
- if targetuser.nick != target:
- targetuser.nick = target # Server seems to have changed the idea of the case of the nickname
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
### CTCP handling
ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
if ctcp:
(ctcptype, ext) = ctcp[0]
+ if ctcptype.upper() == "ACTION":
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanAction", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, action=ext)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivAction", self.addons, user=origin, action=ext)
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanCTCP", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivCTCP", self.addons, user=origin, ctcptype=ctcptype, params=ext)
if ctcptype.upper() == "VERSION":
- user.ctcpreply("VERSION", self.ctcpversion())
+ origin.ctcpreply("VERSION", self.ctcpversion())
if ctcptype.upper() == "TIME":
tformat = time.ctime()
tz = time.tzname[0]
- user.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars())
+ origin.ctcpreply("TIME", "%(tformat)s %(tz)s" % vars())
if ctcptype.upper() == "PING":
- user.ctcpreply("PING", "%(ext)s" % vars())
+ origin.ctcpreply("PING", "%(ext)s" % vars())
if ctcptype.upper() == "FINGER":
- user.ctcpreply("FINGER", "%(ext)s" % vars())
+ origin.ctcpreply("FINGER", "%(ext)s" % vars())
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanMsg", self.addons+target.addons, user=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivMsg", self.addons, user=origin, msg=extinfo)
+
+ elif cmd == "NOTICE":
+ if type(target) == Channel:
+ self.event("onRecv", target.addons, line=line, **data)
+
+ ### CTCP handling
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$", extinfo)
+ if ctcp and target == self.identity:
+ (ctcptype, ext) = ctcp[0]
+ (handled, unhandled, exceptions) = self.event("onCTCPReply", self.addons, origin=origin, ctcptype=ctcptype, params=ext)
+ else:
+ if type(target) == Channel:
+ (handled, unhandled, exceptions) = self.event("onChanNotice", self.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif target == self.identity:
+ (handled, unhandled, exceptions) = self.event("onPrivNotice", self.addons, origin=origin, msg=extinfo)
+
+ elif cmd == 367: # Channel Ban list
+ if 367 in lists.items():
+ banlist = lists[367]
+ else:
+ banlist = lists[367] = []
+ (channame, mask, setby, settime) = params.split()
+ channel = self.channel(channame)
+ banlist.append((self.channel(channame), mask, setby, int(settime)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 368:
+ if 367 in lists.items():
+ banlist = lists[367]
+ else:
+ banlist = lists[367] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in banlist:
+ if "b" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]:
+ channel.modes["b"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["b"] = [(mask, setby, int(settime))]
+ lists[367] = []
+
+ elif cmd == 346: # Channel Invite list
+ if 346 in lists.items():
+ invitelist = lists[346]
+ else:
+ invitelist = lists[346] = []
+ (channame, mask, setby, settime) = params.split()
+ invitelist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onInviteListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 347:
+ if 346 in lists.items():
+ invitelist = lists[346]
+ else:
+ invitelist = lists[346] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onInviteListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in invitelist:
+ if "I" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["I"]]:
+ channel.modes["I"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["I"] = [(mask, setby, int(settime))]
+ lists[346] = []
+
+ elif cmd == 348: # Channel Ban Exception list
+ if 348 in lists.items():
+ banexceptlist = lists[348]
+ else:
+ banexceptlist = lists[348] = []
+ (channame, mask, setby, settime) = params.split()
+ banexceptlist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanExceptListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 349:
+ if 348 in lists.items():
+ banexceptlist = lists[348]
+ else:
+ banexceptlist = lists[348] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onBanExceptListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in banexceptlist:
+ if "e" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["e"]]:
+ channel.modes["e"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["e"] = [(mask, setby, int(settime))]
+ lists[348] = []
+
elif cmd == 910: # Channel Access List
+ if 910 in lists.items():
+ accesslist = lists[910]
+ else:
+ accesslist = lists[910] = []
(channame, mask, setby, settime) = params.split()
+ accesslist.append((self.channel(channame), mask, setby, int(settime)))
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "w" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["w"]]:
- channel.modes["w"].append((mask, setby, int(settime)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onAccessListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 911:
+ if 911 in lists.items():
+ accesslist = lists[910]
else:
- channel.modes["w"] = [(mask, setby, int(settime))]
- elif cmd == 941: # Channel spamfilter List
+ accesslist = lists[910] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onAccessListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in accesslist:
+ if "w" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["b"]]:
+ channel.modes["w"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["w"] = [(mask, setby, int(settime))]
+ lists[910] = []
+
+ elif cmd == 941: # Spam Filter list
+ if 941 in lists.items():
+ spamfilterlist = lists[941]
+ else:
+ spamfilterlist = lists[941] = []
(channame, mask, setby, settime) = params.split()
+ spamfilterlist.append((self.channel(channame), mask, setby, int(settime)))
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "g" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["g"]]:
- channel.modes["g"].append((mask, setby, int(settime)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onSpamfilterListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 940:
+ if 941 in lists.items():
+ spamfilterlist = lists[941]
else:
- channel.modes["g"] = [(mask, setby, int(settime))]
- elif cmd == 954: # Channel spamfilter List
+ spamfilterlist = lists[941] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onSpamfilterListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in spamfilterlist:
+ if "g" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["g"]]:
+ channel.modes["g"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["g"] = [(mask, setby, int(settime))]
+ lists[941] = []
+
+ elif cmd == 954: # Channel exemptchanops list
+ if 954 in lists.items():
+ exemptchanopslist = lists[954]
+ else:
+ exemptchanopslist = lists[954] = []
(channame, mask, setby, settime) = params.split()
+ exemptchanopslist.append((self.channel(channame), mask, setby, int(settime)))
channel = self.channel(channame)
- self.event("onRecv", channel.modules, line=line, data=parsed)
- if channel.name != channame:
- channel.name = channame # Server seems to have changed the idea of the case of the channel name
- if "X" in channel.modes.keys():
- if mask not in [m for (m, b, t) in channel.modes["X"]]:
- channel.modes["X"].append((mask, setby, int(settime)))
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEntry", self.addons+channel.addons, origin=origin, channel=channel, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 953:
+ if 954 in lists.items():
+ exemptchanopslist = lists[954]
+ else:
+ exemptchanopslist = lists[954] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onExemptChanOpsListEnd", self.addons+channel.addons, origin=origin, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in exemptchanopslist:
+ if "X" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["X"]]:
+ channel.modes["X"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["X"] = [(mask, setby, int(settime))]
+ lists[954] = []
+
+ elif cmd == 728: # Channel quiet list
+ if 728 in lists.items():
+ quietlist = lists[728]
else:
- channel.modes["X"] = [(mask, setby, int(settime))]
+ quietlist = lists[728] = []
+ (channame, modechar, mask, setby, settime) = params.split()
+ quietlist.append((self.channel(channame), mask, setby, int(settime)))
+ channel = self.channel(channame)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuietListEntry", self.addons+channel.addons, origin=origin, channel=channel, modechar=modechar, mask=mask, setby=setby, settime=int(settime))
+ elif cmd == 729:
+ if 728 in lists.items():
+ quietlist = lists[728]
+ else:
+ quietlist = lists[728] = []
+ channel = self.channel(params)
+ self.event("onRecv", channel.addons, line=line, **data)
+ (handled, unhandled, exceptions) = self.event("onQuietListEnd", self.addons+channel.addons, channel=channel, endmsg=extinfo)
+ for (channame, mask, setby, settime) in quietlist:
+ if "q" in channel.modes.keys():
+ if mask.lower() not in [m.lower() for (m, s, t) in channel.modes["q"]]:
+ channel.modes["q"].append((mask, setby, int(settime)))
+ else:
+ channel.modes["q"] = [(mask, setby, int(settime))]
+ lists[728] = []
+
elif cmd in (495, 384, 385, 386, 468, 470, 366, 315, 482, 484, 953, 368, 482, 349, 940, 911, 489, 490, 492, 520, 530): # Channels which appear in params
for param in params.split():
if len(param) and param[0] in self.supports["CHANTYPES"]:
channel = self.channel(param)
- self.event("onRecv", channel.modules, line=line, data=parsed)
+ self.event("onRecv", channel.addons, line=line, **data)
+
+ elif type(cmd) == int:
+ (handled, unhandled, exceptions) = self.event("on%03d"%cmd, self.addons, line=line, origin=origin, target=target, params=params, extinfo=extinfo)
+ else:
+ (handled, unhandled, exceptions) = self.event("on%s"%cmd, self.addons, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo)
+
+ self.event("onUnhandled", unhandled, line=line, origin=origin, cmd=cmd, target=target, params=params, extinfo=extinfo)
- else: # Line does NOT match ":src cmd target params :extinfo"
- self.event("onRecv", self.modules, line=line,
- data=None)
+ else: # Line does NOT match ":origin cmd target params :extinfo"
+ self.event("onRecv", self.addons, line=line)
except SystemExit: # Connection lost normally.
pass
except socket.error: # Connection lost due to either ping timeout or connection reset by peer. Not a fatal error.
- with self.loglock:
- exc, excmsg, tb = sys.exc_info()
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars()
- self.log.flush()
+ exc, excmsg, tb = sys.exc_info()
+ self.logwrite("*** Connection to %(server)s:%(port)s failed: %(excmsg)s." % vars())
except: # Unknown exception, treated as FATAL. Try to quit IRC and terminate thread with exception.
### Quit with a (hopefully) useful quit message, or die trying.
+ self.quitexpected = True
try:
self.quit("%s" % traceback.format_exc()
.rstrip().split("\n")[-1])
@@ -836,34 +1078,25 @@ class Connection(Thread):
raise
finally: # Post-connection operations after connection is lost, and must be executed, even if exception occurred.
with self.lock:
- modules = list(self.modules)
- for channel in self.channels:
- for module in channel.modules:
- if module not in modules:
- modules.append(module)
- self.event("onDisconnect", self.modules)
+ (handled, unhandled, exceptions) = self.event("onDisconnect", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []), expected=self.quitexpected)
+ self.connected = False
+ self.registered = False
+ self.identity = None
### Tell outgoing thread to quit.
- self.outgoing.put("quit")
+ self.outgoing.interrupt()
### Wait until the outgoing thread dies.
- if outgoingthread:
+ if outgoingthread and outgoingthread.isAlive():
outgoingthread.join()
outgoingthread = None
- self.connected = False
- self.registered = False
- self.identity = None
-
try:
self.connection.close()
except:
pass
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s *** Connection Terminated." % vars()
- self.log.flush()
+ self.logwrite("*** Connection Terminated.")
if self.quitexpected or not self.autoreconnect:
sys.exit()
@@ -875,23 +1108,14 @@ class Connection(Thread):
pass
except: # Print exception to log file
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
- 2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s !!! Fatal Exception" % vars()
- for tbline in traceback.format_exc().split("\n"):
- print >>self.log, "%(timestamp)s !!! %(tbline)s" % vars()
- self.log.flush()
+ self.logwrite(*["!!! FATAL Exception"]+["!!! %s"%line for line in traceback.format_exc().split("\n")])
+ print >>sys.stderr, "FATAL Exception" % vars()
+ print >>sys.stderr, traceback.format_exc()
sys.exit()
finally:
- with self.loglock:
- timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
- 2, "0") for t in time.localtime()[0:6]])
- print >>self.log, "%(timestamp)s ### Log session ended" % vars(
- )
- self.log.flush()
-
+ self.logwrite("### Log session ended")
+ (handled, unhandled, exceptions) = self.event("onSessionClose", self.addons+reduce(lambda x, y: x+y, [chan.addons for chan in self.channels], []))
Thread.__init__(self) # Makes thread restartable
def __repr__(self):
@@ -901,11 +1125,11 @@ class Connection(Thread):
port = self.port
if self.identity:
nick = self.identity.nick
- ident = self.identity.idnt if self.identity.idnt else "*"
+ user = self.identity.username if self.identity.username else "*"
host = self.identity.host if self.identity.host else "*"
else:
nick = "*"
- ident = "*"
+ user = "*"
host = "*"
if self.ssl and self.ipv6:
protocol = "ircs6"
@@ -915,12 +1139,11 @@ class Connection(Thread):
protocol = "irc6"
else:
protocol = "irc"
- return "<IRC Context: %(nick)s!%(ident)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
+ return "<IRC Context: %(nick)s!%(user)s@%(host)s on %(protocol)s://%(server)s:%(port)s>" % locals()
#else: return "<IRC Context: irc%(ssl)s://%(server)s:%(port)s>" % locals()
def quit(self, msg="", origin=None):
with self.lock:
- self.quitexpected = True
if self.connected:
if len(msg):
self.raw("QUIT :%(msg)s" % vars(), origin=origin)
@@ -929,7 +1152,7 @@ class Connection(Thread):
def ctcpversion(self):
reply = []
- ### Prepare reply for module
+ ### Prepare reply for addon
reply.append("%(__name__)s %(__version__)s, %(__author__)s" %
vars(self))
@@ -938,11 +1161,11 @@ class Connection(Thread):
pyver[0] = "Python "+pyver[0]
reply.extend(pyver)
reply.extend(platform.platform().split("\n"))
- ### Prepare reply for extension modules
- for module in self.modules:
+ ### Prepare reply for extension addons
+ for addon in self.addons:
try:
- r = "%(__name__)s %(__version__)s" % vars(module)
- if "__extinfo__" in vars(module):
+ r = "%(__name__)s %(__version__)s" % vars(addon)
+ if "__extinfo__" in vars(addon):
r += ", %(__extinfo__)s" % vars()
reply.append(r)
except:
@@ -950,7 +1173,11 @@ class Connection(Thread):
return reduce(lambda x, y: "%s; %s" % (x, y), reply)
def raw(self, line, origin=None):
- self.outgoing.put((line, origin))
+ try:
+ self.outgoing.put((line, origin))
+ except:
+ print (line, origin)
+ raise
def user(self, nick):
users = [user for user in self.users if user.nick.lower(
@@ -962,9 +1189,9 @@ class Connection(Thread):
self.users.append(user)
timestamp = reduce(lambda x, y: x+":"+y, [str(
t).rjust(2, "0") for t in time.localtime()[0:6]])
- with self.loglock:
- print >>self.log, "%s *** User %s created."%(timestamp, nick)
- self.log.flush()
+ #with self.loglock:
+ # print >>self.log, "%s *** User %s created."%(timestamp, nick)
+ # self.log.flush()
return user
def channel(self, name):
@@ -977,10 +1204,9 @@ class Connection(Thread):
t).rjust(2, "0") for t in time.localtime()[0:6]])
chan = Channel(name, self)
self.channels.append(chan)
- with self.loglock:
- print >>self.log, "%s *** Channel %s created."%(
- timestamp, name)
- self.log.flush()
+ #with self.loglock:
+ # print >>self.log, "%s *** Channel %s created."%(timestamp, name)
+ # self.log.flush()
return chan
@@ -988,7 +1214,7 @@ class Channel(object):
def __init__(self, name, context):
self.name = name
self.context = context
- self.modules = []
+ self.addons = []
self.topic = ""
self.topicsetby = ""
self.topictime = ()
@@ -1055,7 +1281,7 @@ class Channel(object):
class User(object):
def __init__(self, nick, context):
self.nick = nick
- self.idnt = ""
+ self.username = ""
self.host = ""
self.channels = []
self.context = context
@@ -1064,9 +1290,14 @@ class User(object):
self.server = None
self.hops = None
self.ircop = False
+ self.ircopmsg = ""
+ self.idlesince = None
+ self.signontime = None
+ self.ssl = None
+ self.away = None
def __repr__(self):
- return "<User: %(nick)s!%(idnt)s@%(host)s>" % vars(self)
+ return "<User: %(nick)s!%(username)s@%(host)s>" % vars(self)
def msg(self, msg, origin=None):
nick = self.nick
@@ -1092,20 +1323,6 @@ class User(object):
self.ctcp("ACTION", msg, origin=origin)
-class SendLines(Thread):
- def __init__(self, connection, lines, origin=None):
- self.connection = connection
- self.lines = lines
- self.origin = origin
- Thread.__init__(self)
-
- def run(self):
- for line in self.lines:
- self.connection.raw(reply, origin=self.origin)
- self.connection.log.flush()
- time.sleep(2)
-
-
class Outgoing(Thread):
def __init__(self, IRC, throttle=0.25, lines=40, t=5):
self.IRC = IRC
@@ -1116,118 +1333,158 @@ class Outgoing(Thread):
Thread.__init__(self)
def run(self):
- throttled = False
- timestamps = []
- while True:
- q = self.IRC.outgoing.get()
- if q == "quit" or not self.IRC.connected:
- break
- line, origin = q
- match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
- (cmd, target, params, extinfo) = match[0]
- if cmd.upper() == "QUIT":
- self.IRC.quitexpected = True
- timestamp = reduce(lambda x, y: x+":"+y, [str(
- t).rjust(2, "0") for t in time.localtime()[0:6]])
- with self.IRC.lock:
+ try:
+ throttled = False
+ timestamps = []
+ while True:
try:
- self.IRC.connection.send("%(line)s\n" % vars())
- except socket.error:
+ q = self.IRC.outgoing.get()
+ except Queue.Interrupted:
+ break
+ if q == "quit" or not self.IRC.connected:
+ break
+ line, origin = q
+ match = re.findall("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I)
+ (cmd, target, params, extinfo) = match[0]
+ timestamp = reduce(lambda x, y: x+":"+y, [str(t).rjust(
+ 2, "0") for t in time.localtime()[0:6]])
+ with self.IRC.lock:
try:
- self.IRC.connection.shutdown(0)
- except:
- pass
- raise
+ self.IRC.connection.send("%(line)s\n" % vars())
+ except socket.error:
+ try:
+ self.IRC.connection.shutdown(0)
+ except:
+ pass
+ raise
- ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy modules
- #if re.match("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I):
- if cmd.upper() == "PRIVMSG":
- if target.upper() == "NICKSERV":
- nscmd = re.findall(r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
- if nscmd:
- nscmd = nscmd[0]
- if nscmd[0].upper() in ("IDENTIFY", "REGISTER"):
- extinfo = "%s ********"%nscmd[0]
- line = "%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- extinfo = "%s %s ********"%nscmd[:2]
- line = "%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() == "SET":
- if nscmd[1].upper() == "PASSWORD":
+ ### Modify line if it contains a password so that the password is not logged or sent to any potentially untrustworthy addons
+ #if re.match("^(.+?)(?:\\s+(.+?)(?:\\s+(.+?))??)??(?:\\s+:(.*))?$", line, re.I):
+ if cmd.upper() == "PRIVMSG":
+ if target.upper() == "NICKSERV":
+ nscmd = re.findall(r"^\s*(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
+ if nscmd:
+ nscmd = nscmd[0]
+ if nscmd[0].upper() in ("IDENTIFY", "REGISTER"):
+ extinfo = "%s ********"%nscmd[0]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif nscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
extinfo = "%s %s ********"%nscmd[:2]
line = "%s %s :%s"%(cmd, target, extinfo)
- elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- extinfo = "********"
- line = "%s %s :%s"%(cmd, target, extinfo)
- if target.upper() == "CHANSERV":
- cscmd = re.findall(r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
- if cscmd:
- cscmd = cscmd[0]
- if cscmd[0].upper() in ("IDENTIFY", "REGISTER"):
- extinfo = "%s %s ********"%cscmd[:2]
- line = "%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- extinfo = "%s %s %s ********"%cscmd[:3]
- line = "%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() == "SET":
- if cscmd[2].upper() == "PASSWORD":
+ elif nscmd[0].upper() == "SET":
+ if nscmd[1].upper() == "PASSWORD":
+ extinfo = "%s %s ********"%nscmd[:2]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif nscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ extinfo = "********"
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ if target.upper() == "CHANSERV":
+ cscmd = re.findall(r"^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s*(\S+)(?:\s*(.+))?)?$", extinfo, re.I)
+ if cscmd:
+ cscmd = cscmd[0]
+ if cscmd[0].upper() in ("IDENTIFY", "REGISTER"):
+ extinfo = "%s %s ********"%cscmd[:2]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif cscmd[0].upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
extinfo = "%s %s %s ********"%cscmd[:3]
line = "%s %s :%s"%(cmd, target, extinfo)
- elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- extinfo = "********"
- line = "%s %s :%s"%(cmd, target, extinfo)
- #elif target.upper()=="CHANSERV":
- #msg=extinfo.split(" ")
- #if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2:
- #msg[2]="********"
- #extinfo=" ".join(msg)
- #line="%s %s :%s"%(cmd, target, extinfo)
- elif cmd.upper() == "NS":
- if target.upper() in ("IDENTIFY", "REGISTER"):
- params = params.split(" ")
- while "" in params:
- params.remove("")
- if len(params):
- params[0] = "********"
- params = " ".join(params)
- line = "%s %s %s"%(cmd, target, params)
- elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
- params = params.split(" ")
- while "" in params:
- params.remove("")
- if len(params) > 1:
- params[1] = "********"
- params = " ".join(params)
+ elif cscmd[0].upper() == "SET":
+ if cscmd[2].upper() == "PASSWORD":
+ extinfo = "%s %s %s ********"%cscmd[:3]
+ line = "%s %s :%s"%(cmd, target, extinfo)
+ elif cscmd[0].upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ extinfo = "********"
+ line = "%s %s :%s"%(cmd, target, extinfo)
+
+ chanmatch = re.findall("([%s]?)([%s].+)"%(re.escape(self.IRC.supports.get("PREFIX", ("ohv", "@%+"))[1]), re.escape(self.IRC.supports.get("CHANTYPES", "#"))), target)
+ if chanmatch:
+ targetprefix, channame = chanmatch[0]
+ target = self.IRC.channel(channame)
+ if target.name != channame:
+ ### Target channel name has changed
+ target.name = channame
+ elif len(target) and target[0] != "$" and cmd != "NICK":
+ targetprefix = ""
+ target = self.IRC.user(target)
+
+ ctcp = re.findall("^\x01(.*?)(?:\\s+(.*?)\\s*)?\x01$",
+ extinfo)
+ if ctcp:
+ (ctcptype, ext) = ctcp[0]
+ if ctcptype.upper() == "ACTION":
+ if type(target) == Channel:
+ self.IRC.event("onSendChanAction", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, action=ext)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivAction", self.IRC.addons, origin=origin, user=target, action=ext)
+ else:
+ if type(target) == Channel:
+ self.IRC.event("onSendChanCTCP", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, ctcptype=ctcptype, params=ext)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivCTCP", self.IRC.addons, origin=origin, user=target, ctcptype=ctcptype, params=ext)
+ else:
+ if type(target) == Channel:
+ self.IRC.event("onSendChanMsg", self.IRC.addons+target.addons, origin=origin, channel=target, targetprefix=targetprefix, msg=extinfo)
+ elif type(target) == User:
+ self.IRC.event("onSendPrivMsg", self.IRC.addons, origin=origin, user=target, msg=extinfo)
+
+ #elif target.upper()=="CHANSERV":
+ #msg=extinfo.split(" ")
+ #if msg[0].upper() in ("IDENTIFY", "REGISTER") and len(msg)>2:
+ #msg[2]="********"
+ #extinfo=" ".join(msg)
+ #line="%s %s :%s"%(cmd, target, extinfo)
+ elif cmd.upper() == "NS":
+ if target.upper() in ("IDENTIFY", "REGISTER"):
+ params = params.split(" ")
+ while "" in params:
+ params.remove("")
+ if len(params):
+ params[0] = "********"
+ params = " ".join(params)
+ line = "%s %s %s"%(cmd, target, params)
+ elif target.upper() in ("GROUP", "GHOST", "RECOVER", "RELEASE"):
+ params = params.split(" ")
+ while "" in params:
+ params.remove("")
+ if len(params) > 1:
+ params[1] = "********"
+ params = " ".join(params)
+ line = "%s %s %s"%(cmd, target, params)
+ elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
+ params = ""
+ target = "********"
+ line = "%s %s"%(cmd, target)
+ elif cmd.upper() == "OPER":
+ params = "********"
line = "%s %s %s"%(cmd, target, params)
- elif target.upper() not in ("GLIST", "ACCESS", "SASET", "DROP", "SENDPASS", "ALIST", "INFO", "LIST", "LOGOUT", "STATUS", "UPDATE", "GETPASS", "FORBID", "SUSPEND", "UNSUSPEND", "OINFO"):
- params = ""
+ elif cmd.upper() == "PASS":
+ extinfo = "********"
+ target = ""
+ line = "%s :%s"%(cmd, extinfo)
+ elif cmd.upper() == "IDENTIFY":
target = "********"
line = "%s %s"%(cmd, target)
- elif cmd.upper() == "OPER":
- params = "********"
- line = "%s %s %s"%(cmd, target, params)
- elif cmd.upper() == "PASS":
- extinfo = "********"
- target = ""
- line = "%s :%s"%(cmd, extinfo)
- elif cmd.upper() == "IDENTIFY":
- target = "********"
- line = "%s %s"%(cmd, target)
- self.IRC.event("onSend", self.IRC.modules, line=line, data=(cmd, target, params, extinfo), origin=origin)
- with self.IRC.loglock:
- print >>self.IRC.log, "%(timestamp)s >>> %(line)s" % vars()
- self.IRC.log.flush()
- timestamps.append(time.time())
- while timestamps[0] < timestamps[-1]-self.time-0.1:
- del timestamps[0]
- if throttled:
- if len(timestamps) < 2:
- throttled = False
- else:
- if len(timestamps) >= self.lines:
- throttled = True
- if throttled:
- time.sleep(max(timestamps[-1]+self.throttle-time.time(), 0))
+ self.IRC.event("onSend", self.IRC.addons, origin=origin, line=line, cmd=cmd, target=target, params=params, extinfo=extinfo)
+ self.IRC.logwrite(">>> %(line)s" % vars())
+ if cmd.upper() == "QUIT":
+ self.IRC.quitexpected = True
+ timestamps.append(time.time())
+ while timestamps[0] < timestamps[-1]-self.time-0.1:
+ del timestamps[0]
+ if throttled:
+ if len(timestamps) < 2:
+ throttled = False
+ else:
+ if len(timestamps) >= self.lines:
+ throttled = True
+ if throttled:
+ time.sleep(max(timestamps[-1] +
+ self.throttle-time.time(), 0))
+ except:
+ self.IRC.connection.send("QUIT :%s\n" %
+ traceback.format_exc().rstrip().split("\n")[-1])
+ self.IRC.connection.close()
+ self.IRC.connection.shutdown(0)
class Pinger(Thread):