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:
parent
f0ff14fda9
commit
dbe24973d5
@ -23,6 +23,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
self.uidgen = {}
|
self.uidgen = {}
|
||||||
|
|
||||||
self.caps = {}
|
self.caps = {}
|
||||||
|
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
||||||
self._unrealCmodes = {'l': 'limit', 'c': 'blockcolor', 'G': 'censor',
|
self._unrealCmodes = {'l': 'limit', 'c': 'blockcolor', 'G': 'censor',
|
||||||
'D': 'delayjoin', 'n': 'noextmsg', 's': 'secret',
|
'D': 'delayjoin', 'n': 'noextmsg', 's': 'secret',
|
||||||
'T': 'nonotice', 'z': 'sslonly', 'b': 'ban', 'V': 'noinvite',
|
'T': 'nonotice', 'z': 'sslonly', 'b': 'ban', 'V': 'noinvite',
|
||||||
@ -65,7 +66,75 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
return u
|
return u
|
||||||
|
|
||||||
def joinClient(self, client, channel):
|
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):
|
def pingServer(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""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
|
self.caps['NOQUIT'] = True
|
||||||
elif cap == 'SJ3':
|
elif cap == 'SJ3':
|
||||||
self.caps['SJ3'] = True
|
self.caps['SJ3'] = True
|
||||||
|
self.irc.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
|
||||||
|
'op': 'o', 'voice': 'v'})
|
||||||
|
|
||||||
def _getNick(self, target):
|
def _getNick(self, target):
|
||||||
"""Converts a nick argument to its matching UID. This differs from utils.nickToUid()
|
"""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."""
|
in that it returns the original text instead of None, if no matching nick is found."""
|
||||||
target = utils.nickToUid(self.irc, target) or target
|
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 "
|
log.warning("(%s) Possible desync? Got command target %s, who "
|
||||||
"isn't in our user list!", self.irc.name, target)
|
"isn't in our user list!", self.irc.name, target)
|
||||||
return target
|
return target
|
||||||
@ -324,9 +395,15 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
namelist = []
|
namelist = []
|
||||||
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
||||||
for userpair in userlist:
|
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)
|
r = re.search(r'([^\d]*)(.*)', userpair)
|
||||||
user = r.group(2)
|
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 = ''
|
finalprefix = ''
|
||||||
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
|
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)
|
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)
|
||||||
|
Loading…
Reference in New Issue
Block a user