3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-30 14:49:28 +01:00

unreal: add joinClient + sjoinServer, support +qaohv properly

This commit is contained in:
James Lu 2015-10-12 19:38:38 -07:00
parent f0ff14fda9
commit dbe24973d5

View File

@ -23,6 +23,7 @@ class UnrealProtocol(TS6BaseProtocol):
self.uidgen = {}
self.caps = {}
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
self._unrealCmodes = {'l': 'limit', 'c': 'blockcolor', 'G': 'censor',
'D': 'delayjoin', 'n': 'noextmsg', 's': 'secret',
'T': 'nonotice', 'z': 'sslonly', 'b': 'ban', 'V': 'noinvite',
@ -65,7 +66,75 @@ class UnrealProtocol(TS6BaseProtocol):
return u
def joinClient(self, client, channel):
pass
"""Joins a PyLink client to a channel."""
channel = utils.toLower(self.irc, channel)
if not utils.isInternalClient(self.irc, client):
raise LookupError('No such PyLink client exists.')
self._send(client, "JOIN %s" % channel)
self.irc.channels[channel].users.add(client)
self.irc.users[client].channels.add(channel)
def sjoinServer(self, server, channel, users, ts=None):
"""Sends an SJOIN for a group of users to a channel.
The sender should always be a Server ID (SID). TS is optional, and defaults
to the one we've stored in the channel state if not given.
<users> is a list of (prefix mode, UID) pairs:
Example uses:
sjoinServer('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
sjoinServer(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
Note that for UnrealIRCd, no mode data is sent in an SJOIN command, only
The channel name, TS, and user list.
"""
# <- :001 SJOIN 1444361345 #endlessvoid :001DJ1O02
# The nicklist consists of users joining the channel, with status prefixes for
# their status ('@+', '@', '+' or ''), for example:
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'.
channel = utils.toLower(self.irc, channel)
server = server or self.irc.sid
assert users, "sjoinServer: No users sent?"
if not server:
raise LookupError('No such PyLink server exists.')
orig_ts = self.irc.channels[channel].ts
ts = ts or orig_ts
if ts < orig_ts:
# If the TS we're sending is lower than the one that existing, clear the
# mode lists from our channel state and reset the timestamp.
log.debug('(%s) sjoinServer: resetting TS of %r from %s to %s (clearing modes)',
self.irc.name, channel, orig_ts, ts)
self.irc.channels[channel].ts = ts
self.irc.channels[channel].modes.clear()
for p in self.irc.channels[channel].prefixmodes.values():
p.clear()
changedmodes = []
uids = []
namelist = []
for userpair in users:
assert len(userpair) == 2, "Incorrect format of userpair: %r" % userpair
prefixes, user = userpair
# Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~,
# and +a is ~ instead of &.
# &, ", and ' are used for bursting bans.
sjoin_prefixes = {'q': '*', 'a': '~', 'o': '@', 'h': '%', 'v': '+'}
prefixchars = ''.join([sjoin_prefixes.get(prefix, '') for prefix in prefixes])
if prefixchars:
changedmodes + [('+%s' % prefix, user) for prefix in prefixes]
namelist.append(prefixchars+user)
uids.append(user)
try:
self.irc.users[user].channels.add(channel)
except KeyError: # Not initialized yet?
log.debug("(%s) sjoinServer: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
namelist = ' '.join(namelist)
self._send(server, "SJOIN {ts} {channel} :{users}".format(
ts=ts, users=namelist, channel=channel))
self.irc.channels[channel].users.update(uids)
if ts <= orig_ts:
# Only save our prefix modes in the channel state if our TS is lower than or equal to theirs.
utils.applyModes(self.irc, channel, changedmodes)
def pingServer(self, source=None, target=None):
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
@ -223,12 +292,14 @@ class UnrealProtocol(TS6BaseProtocol):
self.caps['NOQUIT'] = True
elif cap == 'SJ3':
self.caps['SJ3'] = True
self.irc.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
'op': 'o', 'voice': 'v'})
def _getNick(self, target):
"""Converts a nick argument to its matching UID. This differs from utils.nickToUid()
in that it returns the original text instead of None, if no matching nick is found."""
target = utils.nickToUid(self.irc, target) or target
if target not in self.irc.users:
if target not in self.irc.users and not utils.isChannel(target):
log.warning("(%s) Possible desync? Got command target %s, who "
"isn't in our user list!", self.irc.name, target)
return target
@ -324,9 +395,15 @@ class UnrealProtocol(TS6BaseProtocol):
namelist = []
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
for userpair in userlist:
if userpair.startswith("&\"'"): # TODO: handle ban bursts too
# &, ", and ' entries are used for bursting bans:
# https://www.unrealircd.org/files/docs/technical/serverprotocol.html#S5_1
break
r = re.search(r'([^\d]*)(.*)', userpair)
user = r.group(2)
modeprefix = r.group(1) or ''
# Unreal uses slightly different prefixes in SJOIN. +q is * instead of ~,
# and +a is ~ instead of &.
modeprefix = (r.group(1) or '').replace("~", "&").replace("*", "~")
finalprefix = ''
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)