mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-27 21:19:31 +01:00
hybrid: inherit from proto/ts6, drastically reducing the module size
This commit is contained in:
parent
d585b60507
commit
3ae9155e78
@ -9,17 +9,15 @@ import utils
|
||||
from log import log
|
||||
|
||||
from classes import *
|
||||
from ts6_common import TS6BaseProtocol
|
||||
from ts6 import TS6Protocol
|
||||
|
||||
class HybridProtocol(TS6BaseProtocol):
|
||||
class HybridProtocol(TS6Protocol):
|
||||
def __init__(self, irc):
|
||||
super(HybridProtocol, self).__init__(irc)
|
||||
# This protocol module inherits from the TS6 protocol.
|
||||
super().__init__(irc)
|
||||
|
||||
self.casemapping = 'ascii'
|
||||
self.sidgen = utils.TS6SIDGenerator(self.irc)
|
||||
self.uidgen = {}
|
||||
|
||||
self.caps = {}
|
||||
|
||||
self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC'}
|
||||
|
||||
def connect(self):
|
||||
@ -105,7 +103,6 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
||||
manipulatable=False):
|
||||
"""Spawns a client with nick <nick> on the given IRC connection.
|
||||
|
||||
Note: No nick collision / valid nickname checks are done here; it is
|
||||
up to plugins to make sure they don't introduce anything invalid."""
|
||||
server = server or self.irc.sid
|
||||
@ -146,119 +143,7 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
else:
|
||||
raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)
|
||||
|
||||
def join(self, client, channel):
|
||||
"""Joins a PyLink client to a channel."""
|
||||
channel = utils.toLower(self.irc, channel)
|
||||
if not self.irc.isInternalClient(client):
|
||||
raise LookupError('No such PyLink client exists.')
|
||||
self._send(client, "JOIN 0 %s +" % channel)
|
||||
self.irc.channels[channel].users.add(client)
|
||||
self.irc.users[client].channels.add(channel)
|
||||
|
||||
def invite(self, numeric, target, channel):
|
||||
"""Sends an INVITE from a PyLink client.."""
|
||||
if not self.irc.isInternalClient(numeric):
|
||||
raise LookupError('No such PyLink client exists.')
|
||||
self._send(numeric, 'INVITE %s %s %d' % (target, channel, int(time.time())))
|
||||
|
||||
def mode(self, numeric, target, modes, ts=None):
|
||||
"""Sends mode changes from a PyLink client/server."""
|
||||
# c <- :0UYAAAAAA TMODE 0 #a +o 0T4AAAAAC
|
||||
# u <- :0UYAAAAAA MODE 0UYAAAAAA :-Facdefklnou
|
||||
|
||||
if (not self.irc.isInternalClient(numeric)) and \
|
||||
(not self.irc.isInternalServer(numeric)):
|
||||
raise LookupError('No such PyLink client/server exists.')
|
||||
|
||||
utils.applyModes(self.irc, target, modes)
|
||||
modes = list(modes)
|
||||
|
||||
if utils.isChannel(target):
|
||||
ts = ts or self.irc.channels[utils.toLower(self.irc, target)].ts
|
||||
# TMODE:
|
||||
# parameters: channelTS, channel, cmode changes, opt. cmode parameters...
|
||||
|
||||
# On output, at most ten cmode parameters should be sent; if there are more,
|
||||
# multiple TMODE messages should be sent.
|
||||
# TODO(dan): this may not be required on Hybrid?
|
||||
while modes[:9]:
|
||||
# Seriously, though. If you send more than 10 mode parameters in
|
||||
# a line, charybdis will silently REJECT the entire command!
|
||||
joinedmodes = utils.joinModes(modes = [m for m in modes[:9] if m[0] not in self.irc.cmodes['*A']])
|
||||
modes = modes[9:]
|
||||
self._send(numeric, 'TMODE %s %s %s' % (ts, target, joinedmodes))
|
||||
else:
|
||||
joinedmodes = utils.joinModes(modes)
|
||||
self._send(numeric, 'MODE %s %s' % (target, joinedmodes))
|
||||
|
||||
def ping(self, source=None, target=None):
|
||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||
source = source or self.irc.sid
|
||||
target = target or self.irc.uplink
|
||||
if not (target is None or source is None):
|
||||
self._send(source, 'PING :%s' % (source,))
|
||||
|
||||
def handle_events(self, data):
|
||||
"""Event handler for the Hybrid protocol.
|
||||
|
||||
This passes most commands to the various handle_ABCD() functions
|
||||
elsewhere defined protocol modules, coersing various sender prefixes
|
||||
from nicks and server names to UIDs and SIDs respectively,
|
||||
whenever possible.
|
||||
|
||||
Commands sent without an explicit sender prefix will have them set to
|
||||
the SID of the uplink server.
|
||||
"""
|
||||
data = data.split(" ")
|
||||
try: # Message starts with a SID/UID prefix.
|
||||
args = self.parseTS6Args(data)
|
||||
sender = args[0]
|
||||
command = args[1]
|
||||
args = args[2:]
|
||||
# If the sender isn't in UID format, try to convert it automatically.
|
||||
# Unreal's protocol, for example, isn't quite consistent with this yet!
|
||||
sender_server = self._getSid(sender)
|
||||
if sender_server in self.irc.servers:
|
||||
# Sender is a server when converted from name to SID.
|
||||
numeric = sender_server
|
||||
else:
|
||||
# Sender is a user.
|
||||
numeric = self._getNick(sender)
|
||||
|
||||
# parseTS6Args() will raise IndexError if the TS6 sender prefix is missing.
|
||||
except IndexError:
|
||||
# Raw command without an explicit sender; assume it's being sent by our uplink.
|
||||
args = self.parseArgs(data)
|
||||
numeric = self.irc.uplink
|
||||
command = args[0]
|
||||
args = args[1:]
|
||||
|
||||
try:
|
||||
command = self.hook_map.get(command.upper(), command)
|
||||
func = getattr(self, 'handle_'+command.lower())
|
||||
except AttributeError: # unhandled command
|
||||
# self._send(self.irc.sid, 'ERROR', 'Unknown Command')
|
||||
print('Unknown command.\nOffending line: {}'.format(data))
|
||||
exit(1)
|
||||
else:
|
||||
log.debug('(%s) Handling event %s - %s - %s', self.irc.name, numeric, command, str(args))
|
||||
parsed_args = func(numeric, command, args)
|
||||
if parsed_args is not None:
|
||||
return [numeric, command, parsed_args]
|
||||
|
||||
# command handlers
|
||||
def handle_pass(self, numeric, command, args):
|
||||
# <- PASS $somepassword TS 6 42X
|
||||
if args[0] != self.irc.serverdata['recvpass']:
|
||||
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
||||
ver = args[args.index('TS') + 1]
|
||||
if ver != '6':
|
||||
raise ProtocolError("Remote protocol version {} is too old! Is this even TS6?".format())
|
||||
numeric = args[3]
|
||||
log.debug('(%s) Found uplink SID as %r', self.irc.name, numeric)
|
||||
self.irc.servers[numeric] = IrcServer(None, 'unknown')
|
||||
self.irc.uplink = numeric
|
||||
|
||||
def handle_capab(self, numeric, command, args):
|
||||
# We only get a list of keywords here. Hybrid obviously assumes that
|
||||
@ -272,16 +157,6 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
||||
self.irc.connected.set()
|
||||
|
||||
def handle_server(self, numeric, command, args):
|
||||
# <- SERVER charybdis.midnight.vpn 1 :charybdis test server
|
||||
sname = args[0].lower()
|
||||
log.debug('(%s) Found uplink server name as %r', self.irc.name, sname)
|
||||
self.irc.servers[self.irc.uplink].name = sname
|
||||
self.irc.servers[self.irc.uplink].desc = args[2]
|
||||
# According to the TS6 protocol documentation, we should send SVINFO
|
||||
# when we get our uplink's SERVER command.
|
||||
self.irc.send('SVINFO 6 6 0 :%s' % int(time.time()))
|
||||
|
||||
def handle_uid(self, numeric, command, args):
|
||||
"""Handles incoming UID commands (user introduction)."""
|
||||
# <- :0UY UID dan 1 1451041551 +Facdeiklosuw ~ident localhost 127.0.0.1 0UYAAAAAB * :realname
|
||||
@ -303,66 +178,6 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])
|
||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}
|
||||
|
||||
def handle_join(self, numeric, command, args):
|
||||
"""Handles incoming channel JOINs."""
|
||||
# parameters: channelTS, channel, '+' (a plus sign)
|
||||
# <- :0UYAAAAAF JOIN 0 #channel +
|
||||
ts = int(args[0])
|
||||
uid = numeric
|
||||
channel = args[1]
|
||||
if channel == '0':
|
||||
# /join 0; part the user from all channels
|
||||
oldchans = self.irc.users[uid].channels.copy()
|
||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||
self.irc.name, uid, oldchans)
|
||||
for channel in oldchans:
|
||||
self.irc.channels[channel].users.discard(uid)
|
||||
self.irc.users[uid].channels.discard(channel)
|
||||
else:
|
||||
channel = utils.toLower(self.irc, args[1])
|
||||
self.updateTS(channel, ts)
|
||||
return {'channel': channel, 'users': [uid], 'modes':
|
||||
self.irc.channels[channel].modes, 'ts': ts}
|
||||
|
||||
def handle_sjoin(self, numeric, command, args):
|
||||
"""Handles incoming channel SJOINs."""
|
||||
# parameters: channelTS, channel, modes, prefixed uids
|
||||
# <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB
|
||||
ts = int(args[0])
|
||||
uids = args[3].split()
|
||||
users = {}
|
||||
umodes = ['+']
|
||||
modeprefixes = {v: k for k, v in self.irc.prefixmodes.items()}
|
||||
for uid in uids:
|
||||
modes = ''
|
||||
while uid and uid[0] in modeprefixes:
|
||||
modes += uid[0]
|
||||
uid = uid[1:]
|
||||
for mode in modes:
|
||||
umodes[0] += modeprefixes[mode]
|
||||
umodes.append(uid)
|
||||
if args[1] == '0':
|
||||
# /join 0; part the user from all channels
|
||||
for uid in users:
|
||||
oldchans = self.irc.users[uid].channels.copy()
|
||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||
self.irc.name, uid, oldchans)
|
||||
for channel in oldchans:
|
||||
self.irc.channels[channel].users.discard(uid)
|
||||
self.irc.users[uid].channels.discard(channel)
|
||||
# return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||
else:
|
||||
channel = utils.toLower(self.irc, args[1])
|
||||
self.updateTS(channel, ts)
|
||||
parsedmodes = utils.parseModes(self.irc, channel, [args[2]])
|
||||
utils.applyModes(self.irc, channel, parsedmodes)
|
||||
parsedmodes = utils.parseModes(self.irc, channel, umodes)
|
||||
utils.applyModes(self.irc, channel, parsedmodes)
|
||||
# We send users and modes here because SJOIN and JOIN both use one hook,
|
||||
# for simplicity's sake (with plugins).
|
||||
return {'channel': channel, 'users': uids, 'modes':
|
||||
self.irc.channels[channel].modes, 'ts': ts}
|
||||
|
||||
def handle_tburst(self, numeric, command, args):
|
||||
"""Handles incoming topic burst (TBURST) commands."""
|
||||
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
||||
@ -374,77 +189,6 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
self.irc.channels[channel].topicset = True
|
||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||
|
||||
def handle_whois(self, numeric, command, args):
|
||||
"""Handles incoming WHOIS commands.
|
||||
|
||||
Note: The core of WHOIS handling is done by coreplugin.py
|
||||
(IRCd-independent), and not here."""
|
||||
# <- :0UYAAAAAH WHOIS 0T4AAAAAA :pylink
|
||||
return {'target': args[0]}
|
||||
|
||||
def handle_ping(self, source, command, args):
|
||||
"""Handles incoming PING commands."""
|
||||
# PING:
|
||||
# source: any
|
||||
# parameters: origin, opt. destination server
|
||||
# PONG:
|
||||
# source: server
|
||||
# parameters: origin, destination
|
||||
|
||||
# Sends a PING to the destination server, which will reply with a PONG. If the
|
||||
# destination server parameter is not present, the server receiving the message
|
||||
# must reply.
|
||||
try:
|
||||
destination = args[1]
|
||||
except IndexError:
|
||||
destination = self.irc.sid
|
||||
if self.irc.isInternalServer(destination):
|
||||
self._send(destination, 'PONG %s :%s' % (self.irc.servers[destination].name, source))
|
||||
|
||||
def handle_pong(self, source, command, args):
|
||||
"""Handles incoming PONG commands."""
|
||||
log.debug('(%s) Ping received from %s for %s.', self.irc.name, source, args[1])
|
||||
if source == self.irc.uplink and args[1] == self.irc.sid:
|
||||
log.debug('(%s) Set self.irc.lastping.', self.irc.name)
|
||||
self.irc.lastping = time.time()
|
||||
|
||||
def handle_mode(self, numeric, command, args):
|
||||
# <- :0UYAAAAAD MODE 0UYAAAAAD :-i
|
||||
target = args[0]
|
||||
modestrings = args[1:]
|
||||
changedmodes = utils.parseModes(self.irc, target, modestrings)
|
||||
utils.applyModes(self.irc, target, changedmodes)
|
||||
# Call the OPERED UP hook if +o is being set.
|
||||
if ('+o', None) in changedmodes:
|
||||
otype = 'IRC Operator'
|
||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': otype}])
|
||||
return {'target': target, 'modes': changedmodes}
|
||||
|
||||
def handle_tmode(self, numeric, command, args):
|
||||
# <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD
|
||||
channel = utils.toLower(self.irc, args[1])
|
||||
oldobj = self.irc.channels[channel].deepcopy()
|
||||
modes = args[2:]
|
||||
changedmodes = utils.parseModes(self.irc, channel, modes)
|
||||
utils.applyModes(self.irc, channel, changedmodes)
|
||||
ts = int(args[0])
|
||||
if ts > 0:
|
||||
self.updateTS(channel, ts)
|
||||
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
||||
'oldchan': oldobj}
|
||||
|
||||
def handle_invite(self, numeric, command, args):
|
||||
"""Handles incoming INVITEs."""
|
||||
# <- :0UYAAAAAA INVITE 0T4AAAAAA #testchan 1459316899
|
||||
target = args[0]
|
||||
channel = args[1].lower()
|
||||
try:
|
||||
ts = args[3]
|
||||
except IndexError:
|
||||
ts = int(time.time())
|
||||
# We don't actually need to process this; it's just something plugins/hooks can use
|
||||
return {'target': target, 'channel': channel, 'ts': ts}
|
||||
|
||||
def handle_svstag(self, numeric, command, args):
|
||||
tag = args[2]
|
||||
if tag in ['313']:
|
||||
@ -455,12 +199,5 @@ class HybridProtocol(TS6BaseProtocol):
|
||||
log.debug('(%s) end of burst received', self.irc.name)
|
||||
return {}
|
||||
|
||||
# empty handlers
|
||||
# TODO: there's a better way to do this
|
||||
def handle_globops(self, numeric, command, args):
|
||||
pass
|
||||
|
||||
def handle_svinfo(self, numeric, command, args):
|
||||
pass
|
||||
|
||||
Class = HybridProtocol
|
||||
|
Loading…
Reference in New Issue
Block a user