3
0
mirror of https://github.com/jlu5/PyLink.git synced 2025-02-17 14:01:03 +01:00

Merge branch 'devel'

Closes #71.
Closes #91.
Closes #92.
Closes #93.
This commit is contained in:
James Lu 2015-08-17 03:48:35 -07:00
commit 8b77956ab9
5 changed files with 165 additions and 117 deletions

View File

@ -186,7 +186,7 @@ def showchan(irc, source, args):
def mode(irc, source, args): def mode(irc, source, args):
"""<source> <target> <modes> """<source> <target> <modes>
Admin-only. Sets modes <modes> on <target> from <source>, where <source> is the nick of a PyLink client.""" Admin-only. Sets modes <modes> on <target> from <source>, where <source> is either the nick of a PyLink client, or the SID of a PyLink server."""
checkauthenticated(irc, source) checkauthenticated(irc, source)
try: try:
modesource, target, modes = args[0], args[1], args[2:] modesource, target, modes = args[0], args[1], args[2:]
@ -208,3 +208,30 @@ def mode(irc, source, args):
else: else:
sourceuid = utils.nickToUid(irc, modesource) sourceuid = utils.nickToUid(irc, modesource)
irc.proto.modeClient(irc, sourceuid, target, parsedmodes) irc.proto.modeClient(irc, sourceuid, target, parsedmodes)
@utils.add_cmd
def msg(irc, source, args):
"""<source> <target> <text>
Admin-only. Sends message <text> from <source>, where <source> is the nick of a PyLink client."""
checkauthenticated(irc, source)
try:
msgsource, target, text = args[0], args[1], ' '.join(args[2:])
except IndexError:
utils.msg(irc, source, 'Error: not enough arguments. Needs 3: source nick, target, text.')
return
sourceuid = utils.nickToUid(irc, msgsource)
if not sourceuid:
utils.msg(irc, source, 'Error: unknown user %r' % msgsource)
return
if not utils.isChannel(target):
real_target = utils.nickToUid(irc, target)
if real_target is None:
utils.msg(irc, source, 'Error: unknown user %r' % target)
return
else:
real_target = target
if not text:
utils.msg(irc, source, 'Error: no text given.')
return
irc.proto.messageClient(irc, sourceuid, real_target, text)

View File

@ -16,7 +16,9 @@ dbname = "pylinkrelay"
if confname != 'pylink': if confname != 'pylink':
dbname += '-%s' % confname dbname += '-%s' % confname
dbname += '.db' dbname += '.db'
relayusers = defaultdict(dict) relayusers = defaultdict(dict)
spawnlocks = defaultdict(threading.Lock)
def relayWhoisHandlers(irc, target): def relayWhoisHandlers(irc, target):
user = irc.users[target] user = irc.users[target]
@ -124,6 +126,7 @@ def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
return remoteirc.pseudoclient.uid return remoteirc.pseudoclient.uid
except AttributeError: # Network hasn't been initialized yet? except AttributeError: # Network hasn't been initialized yet?
pass pass
with spawnlocks[irc.name]:
try: try:
u = relayusers[(irc.name, user)][remoteirc.name] u = relayusers[(irc.name, user)][remoteirc.name]
except KeyError: except KeyError:
@ -142,7 +145,7 @@ def getRemoteUser(irc, remoteirc, user, spawnIfMissing=True):
u = remoteirc.proto.spawnClient(remoteirc, nick, ident=ident, u = remoteirc.proto.spawnClient(remoteirc, nick, ident=ident,
host=host, realname=realname, host=host, realname=realname,
modes=modes, ts=userobj.ts).uid modes=modes, ts=userobj.ts).uid
remoteirc.users[u].remote = irc.name remoteirc.users[u].remote = (irc.name, user)
away = userobj.away away = userobj.away
if away: if away:
remoteirc.proto.awayClient(remoteirc, u, away) remoteirc.proto.awayClient(remoteirc, u, away)
@ -160,21 +163,10 @@ def getLocalUser(irc, user, targetirc=None):
representing the original user on the target network, similar to what representing the original user on the target network, similar to what
getRemoteUser() does.""" getRemoteUser() does."""
# First, iterate over everyone! # First, iterate over everyone!
try:
remoteuser = irc.users[user].remote
except (AttributeError, KeyError):
remoteuser = None remoteuser = None
for k, v in relayusers.items():
if k[0] == irc.name:
# We don't need to do anything if the target users is on
# the same network as us.
continue
if v.get(irc.name) == user:
# If the stored pseudoclient UID for the kicked user on
# this network matches the target we have, set that user
# as the one we're kicking! It's a handful, but remember
# we're mapping (home network, UID) pairs to their
# respective relay pseudoclients on other networks.
remoteuser = k
log.debug('(%s) getLocalUser: found %s to correspond to %s.', irc.name, v, k)
break
log.debug('(%s) getLocalUser: remoteuser set to %r (looking up %s/%s).', irc.name, remoteuser, user, irc.name) log.debug('(%s) getLocalUser: remoteuser set to %r (looking up %s/%s).', irc.name, remoteuser, user, irc.name)
if remoteuser: if remoteuser:
# If targetirc is given, we'll return simply the UID of the user on the # If targetirc is given, we'll return simply the UID of the user on the
@ -223,6 +215,11 @@ def initializeChannel(irc, channel):
log.debug('(%s) initializeChannel: relay pair found to be %s', irc.name, relay) log.debug('(%s) initializeChannel: relay pair found to be %s', irc.name, relay)
queued_users = [] queued_users = []
if relay: if relay:
# Send our users and channel modes to the other nets
log.debug('(%s) initializeChannel: joining our users: %s', irc.name, c.users)
relayJoins(irc, channel, c.users, c.ts)
irc.proto.joinClient(irc, irc.pseudoclient.uid, channel)
all_links = db[relay]['links'].copy() all_links = db[relay]['links'].copy()
all_links.update((relay,)) all_links.update((relay,))
log.debug('(%s) initializeChannel: all_links: %s', irc.name, all_links) log.debug('(%s) initializeChannel: all_links: %s', irc.name, all_links)
@ -239,29 +236,23 @@ def initializeChannel(irc, channel):
if not (remoteirc.connected.is_set() and findRemoteChan(remoteirc, irc, remotechan)): if not (remoteirc.connected.is_set() and findRemoteChan(remoteirc, irc, remotechan)):
continue # They aren't connected, don't bother! continue # They aren't connected, don't bother!
# Join their (remote) users and set their modes. # Join their (remote) users and set their modes.
relayJoins(remoteirc, remotechan, rc.users, relayJoins(remoteirc, remotechan, rc.users, rc.ts)
rc.ts, rc.modes) relayModes(remoteirc, irc, remoteirc.sid, remotechan, rc.modes)
relayModes(irc, remoteirc, irc.sid, channel) relayModes(irc, remoteirc, irc.sid, channel, modes)
topic = remoteirc.channels[relay[1]].topic topic = remoteirc.channels[remotechan].topic
# Only update the topic if it's different from what we already have, # Only update the topic if it's different from what we already have,
# and topic bursting is complete. # and topic bursting is complete.
if remoteirc.channels[channel].topicset and topic != irc.channels[channel].topic: if remoteirc.channels[remotechan].topicset and topic != irc.channels[channel].topic:
irc.proto.topicServer(irc, irc.sid, channel, topic) irc.proto.topicServer(irc, irc.sid, channel, topic)
log.debug('(%s) initializeChannel: joining our users: %s', irc.name, c.users)
# After that's done, we'll send our users to them.
relayJoins(irc, channel, c.users, c.ts, c.modes)
irc.proto.joinClient(irc, irc.pseudoclient.uid, channel)
def handle_join(irc, numeric, command, args): def handle_join(irc, numeric, command, args):
channel = args['channel'] channel = args['channel']
if not findRelay((irc.name, channel)): if not findRelay((irc.name, channel)):
# No relay here, return. # No relay here, return.
return return
modes = args['modes']
ts = args['ts'] ts = args['ts']
users = set(args['users']) users = set(args['users'])
relayJoins(irc, channel, users, ts, modes) relayJoins(irc, channel, users, ts)
utils.add_hook(handle_join, 'JOIN') utils.add_hook(handle_join, 'JOIN')
def handle_quit(irc, numeric, command, args): def handle_quit(irc, numeric, command, args):
@ -308,20 +299,8 @@ def handle_privmsg(irc, numeric, command, args):
text = args['text'] text = args['text']
if target == irc.pseudoclient.uid: if target == irc.pseudoclient.uid:
return return
sent = 0
relay = findRelay((irc.name, target)) relay = findRelay((irc.name, target))
# Don't send any "you must be in common channels" if we're not part remoteusers = relayusers[(irc.name, numeric)]
# of a relay, or we are but there are no links!
remoteusers = relayusers[(irc.name, numeric)].items()
'''
if utils.isChannel(target) and ((relay and not db[relay]['links']) or \
relay is None):
return
'''
if not remoteusers:
return
for netname, user in relayusers[(irc.name, numeric)].items():
remoteirc = utils.networkobjects[netname]
# HACK: Don't break on sending to @#channel or similar. # HACK: Don't break on sending to @#channel or similar.
try: try:
prefix, target = target.split('#', 1) prefix, target = target.split('#', 1)
@ -329,36 +308,46 @@ def handle_privmsg(irc, numeric, command, args):
prefix = '' prefix = ''
else: else:
target = '#' + target target = '#' + target
if utils.isChannel(target):
log.debug('(%s) relay privmsg: prefix is %r, target is %r', irc.name, prefix, target) log.debug('(%s) relay privmsg: prefix is %r, target is %r', irc.name, prefix, target)
if utils.isChannel(target) and relay and numeric not in irc.channels[target].users:
# The sender must be in the target channel to send messages over the relay;
# it's the only way we can make sure they have a spawned client on ALL
# of the linked networks. This affects -n channels too; see
# https://github.com/GLolol/PyLink/issues/91 for an explanation of why.
utils.msg(irc, numeric, 'Error: You must be in %r in order to send '
'messages over the relay.' % target, notice=True)
return
if utils.isChannel(target):
for netname, user in relayusers[(irc.name, numeric)].items():
remoteirc = utils.networkobjects[netname]
real_target = findRemoteChan(irc, remoteirc, target) real_target = findRemoteChan(irc, remoteirc, target)
if not real_target: if not real_target:
continue continue
real_target = prefix + real_target real_target = prefix + real_target
else:
remoteuser = getLocalUser(irc, target)
if remoteuser is None:
continue
real_target = remoteuser[1]
if notice: if notice:
remoteirc.proto.noticeClient(remoteirc, user, real_target, text) remoteirc.proto.noticeClient(remoteirc, user, real_target, text)
else: else:
remoteirc.proto.messageClient(remoteirc, user, real_target, text) remoteirc.proto.messageClient(remoteirc, user, real_target, text)
sent += 1
'''
if not sent:
# We must be on a common channel with the target. Otherwise, the sender
# doesn't have a client representing them on the remote network,
# and we won't have anywhere to send our messages from.
# In this case, we've iterated over all networks where the sender
# has pseudoclients, and found no suitable targets to send to.
if target in irc.users:
target_s = 'a common channel with %r' % irc.users[target].nick
else: else:
target_s = repr(target) remoteuser = getLocalUser(irc, target)
utils.msg(irc, numeric, 'Error: You must be in %s in order to send messages.' % \ if remoteuser is None:
target_s, notice=True) return
''' homenet, real_target = remoteuser
# For PMs, we must be on a common channel with the target.
# Otherwise, the sender doesn't have a client representing them
# on the remote network, and we won't have anything to send our
# messages from.
if homenet not in remoteusers.keys():
utils.msg(irc, numeric, 'Error: you must be in a common channel '
'with %r in order to send messages.' % \
irc.users[target].nick, notice=True)
return
remoteirc = utils.networkobjects[homenet]
user = getRemoteUser(irc, remoteirc, numeric, spawnIfMissing=False)
if notice:
remoteirc.proto.noticeClient(remoteirc, user, real_target, text)
else:
remoteirc.proto.messageClient(remoteirc, user, real_target, text)
utils.add_hook(handle_privmsg, 'PRIVMSG') utils.add_hook(handle_privmsg, 'PRIVMSG')
utils.add_hook(handle_privmsg, 'NOTICE') utils.add_hook(handle_privmsg, 'NOTICE')
@ -386,7 +375,7 @@ def handle_kick(irc, source, command, args):
# they originate from the same network. We won't have # they originate from the same network. We won't have
# to filter this; the uplink IRCd will handle it appropriately, # to filter this; the uplink IRCd will handle it appropriately,
# and we'll just follow. # and we'll just follow.
real_target = getRemoteUser(irc, remoteirc, target) real_target = getRemoteUser(irc, remoteirc, target, spawnIfMissing=False)
log.debug('(%s) Relay kick: real target for %s is %s', irc.name, target, real_target) log.debug('(%s) Relay kick: real target for %s is %s', irc.name, target, real_target)
else: else:
log.debug('(%s) Relay kick: target %s is an internal client, going to look up the real user', irc.name, target) log.debug('(%s) Relay kick: target %s is an internal client, going to look up the real user', irc.name, target)
@ -429,9 +418,9 @@ def handle_kick(irc, source, command, args):
remotechan, real_target, text) remotechan, real_target, text)
if target != irc.pseudoclient.uid and not irc.users[target].channels: if target != irc.pseudoclient.uid and not irc.users[target].channels:
irc.proto.quitClient(irc, target, 'Left all shared channels.')
remoteuser = getLocalUser(irc, target) remoteuser = getLocalUser(irc, target)
del relayusers[remoteuser][irc.name] del relayusers[remoteuser][irc.name]
irc.proto.quitClient(irc, target, 'Left all shared channels.')
utils.add_hook(handle_kick, 'KICK') utils.add_hook(handle_kick, 'KICK')
@ -643,7 +632,7 @@ def handle_kill(irc, numeric, command, args):
utils.add_hook(handle_kill, 'KILL') utils.add_hook(handle_kill, 'KILL')
def relayJoins(irc, channel, users, ts, modes): def relayJoins(irc, channel, users, ts):
for name, remoteirc in utils.networkobjects.items(): for name, remoteirc in utils.networkobjects.items():
queued_users = [] queued_users = []
if name == irc.name or not remoteirc.connected.is_set(): if name == irc.name or not remoteirc.connected.is_set():
@ -672,7 +661,7 @@ def relayJoins(irc, channel, users, ts, modes):
u = getRemoteUser(irc, remoteirc, user) u = getRemoteUser(irc, remoteirc, user)
# Only join users if they aren't already joined. This prevents op floods # Only join users if they aren't already joined. This prevents op floods
# on charybdis from all the SJOINing. # on charybdis from all the SJOINing.
if u not in remoteirc.channels[channel].users: if u not in remoteirc.channels[remotechan].users:
ts = irc.channels[channel].ts ts = irc.channels[channel].ts
prefixes = getPrefixModes(irc, remoteirc, channel, user) prefixes = getPrefixModes(irc, remoteirc, channel, user)
userpair = (prefixes, u) userpair = (prefixes, u)
@ -683,7 +672,6 @@ def relayJoins(irc, channel, users, ts, modes):
u, remoteirc.name, remotechan) u, remoteirc.name, remotechan)
if queued_users: if queued_users:
remoteirc.proto.sjoinServer(remoteirc, remoteirc.sid, remotechan, queued_users, ts=ts) remoteirc.proto.sjoinServer(remoteirc, remoteirc.sid, remotechan, queued_users, ts=ts)
relayModes(irc, remoteirc, irc.sid, channel, modes)
def relayPart(irc, channel, user): def relayPart(irc, channel, user):
for name, remoteirc in utils.networkobjects.items(): for name, remoteirc in utils.networkobjects.items():

View File

@ -56,10 +56,11 @@ def joinClient(irc, client, channel):
if not server: if not server:
log.error('(%s) Error trying to join client %r to %r (no such pseudoclient exists)', irc.name, client, channel) log.error('(%s) Error trying to join client %r to %r (no such pseudoclient exists)', irc.name, client, channel)
raise LookupError('No such PyLink PseudoClient exists.') raise LookupError('No such PyLink PseudoClient exists.')
# One channel per line here! # Strip out list-modes, they shouldn't be ever sent in FJOIN.
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
_send(irc, server, "FJOIN {channel} {ts} {modes} :,{uid}".format( _send(irc, server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
ts=irc.channels[channel].ts, uid=client, channel=channel, ts=irc.channels[channel].ts, uid=client, channel=channel,
modes=utils.joinModes(irc.channels[channel].modes))) modes=utils.joinModes(modes)))
irc.channels[channel].users.add(client) irc.channels[channel].users.add(client)
irc.users[client].channels.add(channel) irc.users[client].channels.add(channel)
@ -70,16 +71,17 @@ def sjoinServer(irc, server, channel, users, ts=None):
log.debug('(%s) sjoinServer: got %r for users', irc.name, users) log.debug('(%s) sjoinServer: got %r for users', irc.name, users)
if not server: if not server:
raise LookupError('No such PyLink PseudoClient exists.') raise LookupError('No such PyLink PseudoClient exists.')
if ts is None: orig_ts = irc.channels[channel].ts
ts = irc.channels[channel].ts ts = ts or orig_ts
if ts < orig_ts:
log.debug('(%s) sjoinServer: resetting TS of %r from %s to %s (clearing modes)',
irc.name, channel, orig_ts, ts)
irc.channels[channel].ts = ts
irc.channels[channel].modes.clear()
for p in irc.channels[channel].prefixmodes.values():
p.clear()
log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts, log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts,
time.strftime("%c", time.localtime(ts))) time.strftime("%c", time.localtime(ts)))
''' TODO: handle this properly!
if modes is None:
modes = irc.channels[channel].modes
else:
utils.applyModes(irc, channel, modes)
'''
# Strip out list-modes, they shouldn't be ever sent in FJOIN. # Strip out list-modes, they shouldn't be ever sent in FJOIN.
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']] modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
uids = [] uids = []
@ -120,8 +122,12 @@ def removeClient(irc, numeric):
Removes a client from our internal databases, regardless Removes a client from our internal databases, regardless
of whether it's one of our pseudoclients or not.""" of whether it's one of our pseudoclients or not."""
for v in irc.channels.values(): for c, v in irc.channels.copy().items():
v.removeuser(numeric) v.removeuser(numeric)
# Clear empty non-permanent channels.
if not (irc.channels[c].users or ((irc.cmodes.get('permanent'), None) in irc.channels[c].modes)):
del irc.channels[c]
sid = numeric[:3] sid = numeric[:3]
log.debug('Removing client %s from irc.users', numeric) log.debug('Removing client %s from irc.users', numeric)
del irc.users[numeric] del irc.users[numeric]
@ -245,6 +251,8 @@ def topicClient(irc, numeric, target, text):
if not utils.isInternalClient(irc, numeric): if not utils.isInternalClient(irc, numeric):
raise LookupError('No such PyLink PseudoClient exists.') raise LookupError('No such PyLink PseudoClient exists.')
_send(irc, numeric, 'TOPIC %s :%s' % (target, text)) _send(irc, numeric, 'TOPIC %s :%s' % (target, text))
irc.channels[target].topic = text
irc.channels[target].topicset = True
def topicServer(irc, numeric, target, text): def topicServer(irc, numeric, target, text):
if not utils.isInternalServer(irc, numeric): if not utils.isInternalServer(irc, numeric):
@ -252,6 +260,8 @@ def topicServer(irc, numeric, target, text):
ts = int(time.time()) ts = int(time.time())
servername = irc.servers[numeric].name servername = irc.servers[numeric].name
_send(irc, numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text)) _send(irc, numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
irc.channels[target].topic = text
irc.channels[target].topicset = True
def inviteClient(irc, numeric, target, channel): def inviteClient(irc, numeric, target, channel):
"""<irc object> <client numeric> <text> """<irc object> <client numeric> <text>
@ -360,6 +370,9 @@ def handle_part(irc, source, command, args):
reason = args[1] reason = args[1]
except IndexError: except IndexError:
reason = '' reason = ''
# Clear empty non-permanent channels.
if not (irc.channels[channel].users or ((irc.cmodes.get('permanent'), None) in irc.channels[channel].modes)):
del irc.channels[channel]
return {'channels': channels, 'text': reason} return {'channels': channels, 'text': reason}
def handle_error(irc, numeric, command, args): def handle_error(irc, numeric, command, args):
@ -378,6 +391,9 @@ def handle_fjoin(irc, servernumeric, command, args):
log.debug('(%s) Setting channel TS of %s to %s from %s', log.debug('(%s) Setting channel TS of %s to %s from %s',
irc.name, channel, their_ts, our_ts) irc.name, channel, their_ts, our_ts)
irc.channels[channel].ts = their_ts irc.channels[channel].ts = their_ts
irc.channels[channel].modes.clear()
for p in irc.channels[channel].prefixmodes.values():
p.clear()
modestring = args[2:-1] or args[2] modestring = args[2:-1] or args[2]
parsedmodes = utils.parseModes(irc, channel, modestring) parsedmodes = utils.parseModes(irc, channel, modestring)
utils.applyModes(irc, channel, parsedmodes) utils.applyModes(irc, channel, parsedmodes)

View File

@ -77,8 +77,15 @@ def sjoinServer(irc, server, channel, users, ts=None):
log.debug('(%s) sjoinServer: got %r for users', irc.name, users) log.debug('(%s) sjoinServer: got %r for users', irc.name, users)
if not server: if not server:
raise LookupError('No such PyLink PseudoClient exists.') raise LookupError('No such PyLink PseudoClient exists.')
if ts is None: orig_ts = irc.channels[channel].ts
ts = irc.channels[channel].ts ts = ts or orig_ts
if ts < orig_ts:
log.debug('(%s) sjoinServer: resetting TS of %r from %s to %s (clearing modes)',
irc.name, channel, orig_ts, ts)
irc.channels[channel].ts = ts
irc.channels[channel].modes.clear()
for p in irc.channels[channel].prefixmodes.values():
p.clear()
log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts, log.debug("sending SJOIN to %s%s with ts %s (that's %r)", channel, irc.name, ts,
time.strftime("%c", time.localtime(ts))) time.strftime("%c", time.localtime(ts)))
modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']] modes = [m for m in irc.channels[channel].modes if m[0] not in irc.cmodes['*A']]
@ -95,6 +102,7 @@ def sjoinServer(irc, server, channel, users, ts=None):
pr = irc.prefixmodes.get(prefix) pr = irc.prefixmodes.get(prefix)
if pr: if pr:
prefixchars += pr prefixchars += pr
changedmodes.append(('+%s' % prefix, user))
namelist.append(prefixchars+user) namelist.append(prefixchars+user)
uids.append(user) uids.append(user)
try: try:
@ -186,6 +194,8 @@ def topicServer(irc, numeric, target, text):
ts = irc.channels[target].ts ts = irc.channels[target].ts
servername = irc.servers[numeric].name servername = irc.servers[numeric].name
_send(irc, numeric, 'TB %s %s %s :%s' % (target, ts, servername, text)) _send(irc, numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
irc.channels[target].topic = text
irc.channels[target].topicset = True
def inviteClient(irc, numeric, target, channel): def inviteClient(irc, numeric, target, channel):
"""<irc object> <client numeric> <text> """<irc object> <client numeric> <text>
@ -368,9 +378,10 @@ def handle_part(irc, source, command, args):
reason = args[1] reason = args[1]
except IndexError: except IndexError:
reason = '' reason = ''
if not (irc.channels[channel].users or ((irc.cmodes.get('permanent'), None) in irc.channels[channel].modes)):
del irc.channels[channel]
return {'channels': channels, 'text': reason} return {'channels': channels, 'text': reason}
def handle_sjoin(irc, servernumeric, command, args): def handle_sjoin(irc, servernumeric, command, args):
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist # parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
channel = args[1].lower() channel = args[1].lower()
@ -382,6 +393,9 @@ def handle_sjoin(irc, servernumeric, command, args):
log.debug('(%s) Setting channel TS of %s to %s from %s', log.debug('(%s) Setting channel TS of %s to %s from %s',
irc.name, channel, their_ts, our_ts) irc.name, channel, their_ts, our_ts)
irc.channels[channel].ts = their_ts irc.channels[channel].ts = their_ts
irc.channels[channel].modes.clear()
for p in irc.channels[channel].prefixmodes.values():
p.clear()
modestring = args[2:-1] or args[2] modestring = args[2:-1] or args[2]
parsedmodes = utils.parseModes(irc, channel, modestring) parsedmodes = utils.parseModes(irc, channel, modestring)
utils.applyModes(irc, channel, parsedmodes) utils.applyModes(irc, channel, parsedmodes)

View File

@ -241,7 +241,10 @@ def applyModes(irc, target, changedmodes):
log.debug('(%s) Applying modes %r on %s (initial modelist: %s)', irc.name, changedmodes, target, modelist) log.debug('(%s) Applying modes %r on %s (initial modelist: %s)', irc.name, changedmodes, target, modelist)
for mode in changedmodes: for mode in changedmodes:
# Chop off the +/- part that parseModes gives; it's meaningless for a mode list. # Chop off the +/- part that parseModes gives; it's meaningless for a mode list.
try:
real_mode = (mode[0][1], mode[1]) real_mode = (mode[0][1], mode[1])
except IndexError:
real_mode = mode
if not usermodes: if not usermodes:
pmode = '' pmode = ''
for m in ('owner', 'admin', 'op', 'halfop', 'voice'): for m in ('owner', 'admin', 'op', 'halfop', 'voice'):