From eac934c23727c2a2c3678073ce44bc6066f02ac7 Mon Sep 17 00:00:00 2001 From: James Lu Date: Thu, 31 Dec 2015 17:28:47 -0800 Subject: [PATCH] classes: sort code, move nickToUid, clientToServer, isInternalClient, isInternalServer into the Irc class The following BREAKING changes are made: utils.nickToUid(irc, nick) -> irc.nickToUid(nick) utils.isInternalClient(irc, uid) -> irc.isInternalClient(uid) utils.isInternalServer(irc, uid) -> irc.isInternalServer(uid) utils.clientToServer(irc, uid) -> utils.getServer(uid) --- classes.py | 209 +++++++++++++++++++++++----------------- coreplugin.py | 6 +- plugins/bots.py | 14 +-- plugins/chancmds.py | 6 +- plugins/commands.py | 5 +- plugins/fantasy.py | 2 +- plugins/relay.py | 10 +- protocols/inspircd.py | 28 +++--- protocols/ts6.py | 22 ++--- protocols/ts6_common.py | 18 ++-- protocols/unreal.py | 26 ++--- utils.py | 30 +----- 12 files changed, 189 insertions(+), 187 deletions(-) diff --git a/classes.py b/classes.py index 49bec0a..1b528fa 100644 --- a/classes.py +++ b/classes.py @@ -248,44 +248,6 @@ class Irc(): log.info('(%s) Stopping connect loop (autoconnect value %r is < 1).', self.name, autoconnect) return - def callCommand(self, source, text): - """ - Calls a PyLink bot command. source is the caller's UID, and text is the - full, unparsed text of the message. - """ - cmd_args = text.strip().split(' ') - cmd = cmd_args[0].lower() - cmd_args = cmd_args[1:] - if cmd not in world.commands: - self.msg(self.called_by or source, 'Error: Unknown command %r.' % cmd) - log.info('(%s) Received unknown command %r from %s', self.name, cmd, utils.getHostmask(self, source)) - return - log.info('(%s) Calling command %r for %s', self.name, cmd, utils.getHostmask(self, source)) - for func in world.commands[cmd]: - try: - func(self, source, cmd_args) - except utils.NotAuthenticatedError: - self.msg(self.called_by or source, 'Error: You are not authorized to perform this operation.') - except Exception as e: - log.exception('Unhandled exception caught in command %r', cmd) - self.msg(self.called_by or source, 'Uncaught exception in command %r: %s: %s' % (cmd, type(e).__name__, str(e))) - - def msg(self, target, text, notice=False, source=None): - """Handy function to send messages/notices to clients. Source - is optional, and defaults to the main PyLink client if not specified.""" - source = source or self.pseudoclient.uid - if notice: - self.proto.noticeClient(source, target, text) - cmd = 'PYLINK_SELF_NOTICE' - else: - self.proto.messageClient(source, target, text) - cmd = 'PYLINK_SELF_PRIVMSG' - self.callHooks([source, cmd, {'target': target, 'text': text}]) - - def reply(self, text, notice=False, source=None): - """Replies to the last caller in the right context (channel or PM).""" - self.msg(self.called_by, text, notice=notice, source=source) - def _disconnect(self): """Handle disconnects from the remote server.""" self.connected.clear() @@ -435,6 +397,71 @@ class Irc(): def __repr__(self): return "" % self.name + ### Utility functions + def callCommand(self, source, text): + """ + Calls a PyLink bot command. source is the caller's UID, and text is the + full, unparsed text of the message. + """ + cmd_args = text.strip().split(' ') + cmd = cmd_args[0].lower() + cmd_args = cmd_args[1:] + if cmd not in world.commands: + self.msg(self.called_by or source, 'Error: Unknown command %r.' % cmd) + log.info('(%s) Received unknown command %r from %s', self.name, cmd, utils.getHostmask(self, source)) + return + log.info('(%s) Calling command %r for %s', self.name, cmd, utils.getHostmask(self, source)) + for func in world.commands[cmd]: + try: + func(self, source, cmd_args) + except utils.NotAuthenticatedError: + self.msg(self.called_by or source, 'Error: You are not authorized to perform this operation.') + except Exception as e: + log.exception('Unhandled exception caught in command %r', cmd) + self.msg(self.called_by or source, 'Uncaught exception in command %r: %s: %s' % (cmd, type(e).__name__, str(e))) + + def msg(self, target, text, notice=False, source=None): + """Handy function to send messages/notices to clients. Source + is optional, and defaults to the main PyLink client if not specified.""" + source = source or self.pseudoclient.uid + if notice: + self.proto.noticeClient(source, target, text) + cmd = 'PYLINK_SELF_NOTICE' + else: + self.proto.messageClient(source, target, text) + cmd = 'PYLINK_SELF_PRIVMSG' + self.callHooks([source, cmd, {'target': target, 'text': text}]) + + def reply(self, text, notice=False, source=None): + """Replies to the last caller in the right context (channel or PM).""" + self.msg(self.called_by, text, notice=notice, source=source) + + def nickToUid(self, nick): + """Looks up the UID of a user with the given nick, if one is present.""" + nick = utils.toLower(self, nick) + for k, v in self.users.copy().items(): + if utils.toLower(self, v.nick) == nick: + return k + + def isInternalClient(self, numeric): + """ + Checks whether the given numeric is a PyLink Client, + returning the SID of the server it's on if so. + """ + for sid in self.servers: + if self.servers[sid].internal and numeric in self.servers[sid].users: + return sid + + def isInternalServer(self, sid): + """Returns whether the given SID is an internal PyLink server.""" + return (sid in self.servers and self.servers[sid].internal) + + def getServer(self, numeric): + """Finds the SID of the server a user is on.""" + for server in self.servers: + if numeric in self.servers[server].users: + return server + class IrcUser(): """PyLink IRC user class.""" def __init__(self, nick, ts, uid, ident='null', host='null', @@ -511,60 +538,8 @@ class IrcChannel(): """Returns a deep copy of the channel object.""" return deepcopy(self) -### FakeIRC classes, used for test cases - -class FakeIRC(Irc): - """Fake IRC object used for unit tests.""" - def connect(self): - self.messages = [] - self.hookargs = [] - self.hookmsgs = [] - self.socket = None - self.initVars() - self.spawnMain() - self.connected = threading.Event() - self.connected.set() - - def run(self, data): - """Queues a message to the fake IRC server.""" - log.debug('<- ' + data) - hook_args = self.proto.handle_events(data) - if hook_args is not None: - self.hookmsgs.append(hook_args) - self.callHooks(hook_args) - - def send(self, data): - self.messages.append(data) - log.debug('-> ' + data) - - def takeMsgs(self): - """Returns a list of messages sent by the protocol module since - the last takeMsgs() call, so we can track what has been sent.""" - msgs = self.messages - self.messages = [] - return msgs - - def takeCommands(self, msgs): - """Returns a list of commands parsed from the output of takeMsgs().""" - sidprefix = ':' + self.sid - commands = [] - for m in msgs: - args = m.split() - if m.startswith(sidprefix): - commands.append(args[1]) - else: - commands.append(args[0]) - return commands - - def takeHooks(self): - """Returns a list of hook arguments sent by the protocol module since - the last takeHooks() call.""" - hookmsgs = self.hookmsgs - self.hookmsgs = [] - return hookmsgs - class Protocol(): - # TODO: Future state-keeping things will go here + """Base Protocol module class for PyLink.""" def __init__(self, irc): self.irc = irc self.casemapping = 'rfc1459' @@ -626,6 +601,58 @@ class Protocol(): for p in self.irc.channels[channel].prefixmodes.values(): p.clear() +### FakeIRC classes, used for test cases + +class FakeIRC(Irc): + """Fake IRC object used for unit tests.""" + def connect(self): + self.messages = [] + self.hookargs = [] + self.hookmsgs = [] + self.socket = None + self.initVars() + self.spawnMain() + self.connected = threading.Event() + self.connected.set() + + def run(self, data): + """Queues a message to the fake IRC server.""" + log.debug('<- ' + data) + hook_args = self.proto.handle_events(data) + if hook_args is not None: + self.hookmsgs.append(hook_args) + self.callHooks(hook_args) + + def send(self, data): + self.messages.append(data) + log.debug('-> ' + data) + + def takeMsgs(self): + """Returns a list of messages sent by the protocol module since + the last takeMsgs() call, so we can track what has been sent.""" + msgs = self.messages + self.messages = [] + return msgs + + def takeCommands(self, msgs): + """Returns a list of commands parsed from the output of takeMsgs().""" + sidprefix = ':' + self.sid + commands = [] + for m in msgs: + args = m.split() + if m.startswith(sidprefix): + commands.append(args[1]) + else: + commands.append(args[0]) + return commands + + def takeHooks(self): + """Returns a list of hook arguments sent by the protocol module since + the last takeHooks() call.""" + hookmsgs = self.hookmsgs + self.hookmsgs = [] + return hookmsgs + class FakeProto(Protocol): """Dummy protocol module for testing purposes.""" def handle_events(self, data): diff --git a/coreplugin.py b/coreplugin.py index 9f5b9c5..dc51d32 100644 --- a/coreplugin.py +++ b/coreplugin.py @@ -25,7 +25,7 @@ utils.add_hook(handle_kick, 'KICK') def handle_commands(irc, source, command, args): """Handle commands sent to the PyLink client (PRIVMSG).""" - if args['target'] == irc.pseudoclient.uid and not utils.isInternalClient(irc, source): + if args['target'] == irc.pseudoclient.uid and not irc.isInternalClient(source): irc.called_by = source irc.callCommand(source, args['text']) @@ -40,7 +40,7 @@ def handle_whois(irc, source, command, args): 'doesn\'t exist in irc.users!', irc.name, target, source) return f = irc.proto.numericServer - server = utils.clientToServer(irc, target) or irc.sid + server = irc.getServer(target) or irc.sid nick = user.nick sourceisOper = ('o', None) in irc.users[source].modes # https://www.alien.net.au/irc/irc2numerics.html @@ -125,7 +125,7 @@ def handle_mode(irc, source, command, args): modes = args['modes'] # If the sender is not a PyLink client, and the target IS a protected # client, revert any forced deoper attempts. - if utils.isInternalClient(irc, target) and not utils.isInternalClient(irc, source): + if irc.isInternalClient(target) and not irc.isInternalClient(source): if ('-o', None) in modes and (target == irc.pseudoclient.uid or not utils.isManipulatableClient(irc, target)): irc.proto.modeServer(irc.sid, target, {('+o', None)}) utils.add_hook(handle_mode, 'MODE') diff --git a/plugins/bots.py b/plugins/bots.py index e4cc95d..ece7e61 100644 --- a/plugins/bots.py +++ b/plugins/bots.py @@ -35,10 +35,10 @@ def quit(irc, source, args): except IndexError: irc.reply("Error: Not enough arguments. Needs 1-2: nick, reason (optional).") return - if irc.pseudoclient.uid == utils.nickToUid(irc, nick): + if irc.pseudoclient.uid == irc.nickToUid(nick): irc.reply("Error: Cannot quit the main PyLink PseudoClient!") return - u = utils.nickToUid(irc, nick) + u = irc.nickToUid(nick) quitmsg = ' '.join(args[1:]) or 'Client Quit' if not utils.isManipulatableClient(irc, u): irc.reply("Error: Cannot force quit a protected PyLink services client.") @@ -59,7 +59,7 @@ def joinclient(irc, source, args): except IndexError: irc.reply("Error: Not enough arguments. Needs 2: nick, comma separated list of channels.") return - u = utils.nickToUid(irc, nick) + u = irc.nickToUid(nick) if not utils.isManipulatableClient(irc, u): irc.reply("Error: Cannot force join a protected PyLink services client.") return @@ -85,7 +85,7 @@ def nick(irc, source, args): except IndexError: irc.reply("Error: Not enough arguments. Needs 2: nick, newnick.") return - u = utils.nickToUid(irc, nick) + u = irc.nickToUid(nick) if newnick in ('0', u): newnick = u elif not utils.isNick(newnick): @@ -110,7 +110,7 @@ def part(irc, source, args): except IndexError: irc.reply("Error: Not enough arguments. Needs 2: nick, comma separated list of channels.") return - u = utils.nickToUid(irc, nick) + u = irc.nickToUid(nick) if not utils.isManipulatableClient(irc, u): irc.reply("Error: Cannot force part a protected PyLink services client.") return @@ -132,12 +132,12 @@ def msg(irc, source, args): except IndexError: irc.reply('Error: Not enough arguments. Needs 3: source nick, target, text.') return - sourceuid = utils.nickToUid(irc, msgsource) + sourceuid = irc.nickToUid(msgsource) if not sourceuid: irc.reply('Error: Unknown user %r.' % msgsource) return if not utils.isChannel(target): - real_target = utils.nickToUid(irc, target) + real_target = irc.nickToUid(target) if real_target is None: irc.reply('Error: Unknown user %r.' % target) return diff --git a/plugins/chancmds.py b/plugins/chancmds.py index d2f657a..f09dfa7 100644 --- a/plugins/chancmds.py +++ b/plugins/chancmds.py @@ -26,14 +26,14 @@ def kick(irc, source, args): return # Convert the source and target nicks to UIDs. - u = utils.nickToUid(irc, sourcenick) or sourcenick - targetu = utils.nickToUid(irc, target) + u = irc.nickToUid(sourcenick) or sourcenick + targetu = irc.nickToUid(target) if channel not in irc.channels: # KICK only works on channels that exist. irc.reply("Error: Unknown channel %r." % channel) return - if utils.isInternalServer(irc, u): + if irc.isInternalServer(u): # Send kick from server if the given kicker is a SID irc.proto.kickServer(u, channel, targetu, reason) elif u not in irc.users: diff --git a/plugins/commands.py b/plugins/commands.py index 1c15612..c79134a 100644 --- a/plugins/commands.py +++ b/plugins/commands.py @@ -79,7 +79,7 @@ def showuser(irc, source, args): except IndexError: irc.reply("Error: Not enough arguments. Needs 1: nick.") return - u = utils.nickToUid(irc, target) or target + u = irc.nickToUid(target) or target # Only show private info if the person is calling 'showuser' on themselves, # or is an oper. verbose = utils.isOper(irc, source) or u == source @@ -91,7 +91,8 @@ def showuser(irc, source, args): userobj = irc.users[u] f('Information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident, userobj.host, userobj.realname)) - sid = utils.clientToServer(irc, u) + + sid = irc.getServer(u) serverobj = irc.servers[sid] ts = userobj.ts f('\x02Home server\x02: %s (%s); \x02Signon time:\x02 %s (%s)' % \ diff --git a/plugins/fantasy.py b/plugins/fantasy.py index 1de767c..19907cd 100644 --- a/plugins/fantasy.py +++ b/plugins/fantasy.py @@ -38,7 +38,7 @@ def handle_fantasy(irc, source, command, args): # message loops). if utils.isChannel(channel) and text.startswith(prefix) and \ irc.pseudoclient.uid in irc.channels[channel].users and not \ - utils.isInternalClient(irc, source): + irc.isInternalClient(source): # Cut off the length of the prefix from the text. text = text[len(prefix):] diff --git a/plugins/relay.py b/plugins/relay.py index 1a5af8b..23367f6 100644 --- a/plugins/relay.py +++ b/plugins/relay.py @@ -111,7 +111,7 @@ def normalizeNick(irc, netname, nick, separator=None, uid=''): # somecutoffnick/net would otherwise be erroneous NICK'ed to somecutoffnic//net, # even though there would be no collision because the old and new nicks are from # the same client. - while utils.nickToUid(irc, nick) and utils.nickToUid(irc, nick) != uid: + while irc.nickToUid(nick) and irc.nickToUid(nick) != uid: new_sep = separator + separator[-1] log.debug('(%s) normalizeNick: nick %r is in use; using %r as new_sep.', irc.name, nick, new_sep) nick = normalizeNick(irc, netname, orig_nick, separator=new_sep) @@ -429,8 +429,8 @@ def checkClaim(irc, channel, sender, chanobj=None): return (not relay) or irc.name == relay[0] or not db[relay]['claim'] or \ irc.name in db[relay]['claim'] or \ any([mode in sender_modes for mode in ('y', 'q', 'a', 'o', 'h')]) \ - or utils.isInternalClient(irc, sender) or \ - utils.isInternalServer(irc, sender) + or irc.isInternalClient(sender) or \ + irc.isInternalServer(sender) def getSupportedUmodes(irc, remoteirc, modes): """Given a list of user modes, filters out all of those not supported by the @@ -713,7 +713,7 @@ def handle_messages(irc, numeric, command, args): notice = (command in ('NOTICE', 'PYLINK_SELF_NOTICE')) target = args['target'] text = args['text'] - if utils.isInternalClient(irc, numeric) and utils.isInternalClient(irc, target): + if irc.isInternalClient(numeric) and irc.isInternalClient(target): # Drop attempted PMs between internal clients (this shouldn't happen, # but whatever). return @@ -1351,7 +1351,7 @@ def showuser(irc, source, args): # No errors here; showuser from the commands plugin already does this # for us. return - u = utils.nickToUid(irc, target) + u = irc.nickToUid(target) if u: try: userpair = getOrigUser(irc, u) or (irc.name, u) diff --git a/protocols/inspircd.py b/protocols/inspircd.py index c2f8967..3c28b96 100644 --- a/protocols/inspircd.py +++ b/protocols/inspircd.py @@ -40,7 +40,7 @@ class InspIRCdProtocol(TS6BaseProtocol): 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 - if not utils.isInternalServer(self.irc, server): + if not self.irc.isInternalServer(server): raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server) # Create an UIDGenerator instance for every SID, so that each gets # distinct values. @@ -68,7 +68,7 @@ class InspIRCdProtocol(TS6BaseProtocol): # so what we're actually doing here is sending FJOIN from the server, # on behalf of the clients that are joining. channel = utils.toLower(self.irc, channel) - server = utils.isInternalClient(self.irc, client) + server = self.irc.isInternalClient(client) if not server: log.error('(%s) Error trying to join client %r to %r (no such pseudoclient exists)', self.irc.name, client, channel) raise LookupError('No such PyLink PseudoClient exists.') @@ -176,7 +176,7 @@ class InspIRCdProtocol(TS6BaseProtocol): Sends mode changes from a PyLink client. should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendModes(numeric, target, modes, ts=ts) @@ -185,7 +185,7 @@ class InspIRCdProtocol(TS6BaseProtocol): Sends mode changes from a PyLink server. should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') self._sendModes(numeric, target, modes, ts=ts) @@ -194,24 +194,24 @@ class InspIRCdProtocol(TS6BaseProtocol): # We only need to call removeClient here if the target is one of our # clients, since any remote servers will send a QUIT from # their target if the command succeeds. - if utils.isInternalClient(self.irc, target): + if self.irc.isInternalClient(target): self.removeClient(target) def killServer(self, numeric, target, reason): """Sends a kill from a PyLink server.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') self._sendKill(numeric, target, reason) def killClient(self, numeric, target, reason): """Sends a kill from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendKill(numeric, target, reason) def topicServer(self, numeric, target, text): """Sends a topic change from a PyLink server. This is usually used on burst.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') ts = int(time.time()) servername = self.irc.servers[numeric].name @@ -221,13 +221,13 @@ class InspIRCdProtocol(TS6BaseProtocol): def inviteClient(self, numeric, target, channel): """Sends an INVITE from a PyLink client..""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'INVITE %s %s' % (target, channel)) def knockClient(self, numeric, target, text): """Sends a KNOCK from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'ENCAP * KNOCK %s :%s' % (target, text)) @@ -239,7 +239,7 @@ class InspIRCdProtocol(TS6BaseProtocol): raise NotImplementedError("Changing field %r of a client is " "unsupported by this protocol." % field) - if utils.isInternalClient(self.irc, target): + if self.irc.isInternalClient(target): # It is one of our clients, use FIDENT/HOST/NAME. if field == 'IDENT': self.irc.users[target].ident = text @@ -321,7 +321,7 @@ class InspIRCdProtocol(TS6BaseProtocol): for server in self.irc.servers.values(): if name == server.name: raise ValueError('A server named %r already exists!' % name) - if not utils.isInternalServer(self.irc, uplink): + if not self.irc.isInternalServer(uplink): raise ValueError('Server %r is not a PyLink internal PseudoServer!' % uplink) if not utils.isServerName(name): raise ValueError('Invalid server name %r' % name) @@ -446,7 +446,7 @@ class InspIRCdProtocol(TS6BaseProtocol): """Handles incoming PING commands, so we don't time out.""" # <- :70M PING 70M 0AL # -> :0AL PONG 0AL 70M - if utils.isInternalServer(self.irc, args[1]): + if self.irc.isInternalServer(args[1]): self._send(args[1], 'PONG %s %s' % (args[1], source)) def handle_pong(self, source, command, args): @@ -650,7 +650,7 @@ class InspIRCdProtocol(TS6BaseProtocol): # SQUIT, in order to be consistent with other IRCds which make SQUITs # implicit. target = self._getSid(args[0]) - if utils.isInternalServer(self.irc, target): + if self.irc.isInternalServer(target): # The target has to be one of our servers in order to work... uplink = self.irc.servers[target].uplink reason = 'Requested by %s' % utils.getHostmask(self.irc, numeric) diff --git a/protocols/ts6.py b/protocols/ts6.py index 766f3a9..c313189 100644 --- a/protocols/ts6.py +++ b/protocols/ts6.py @@ -33,7 +33,7 @@ class TS6Protocol(TS6BaseProtocol): 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 - if not utils.isInternalServer(self.irc, server): + if not self.irc.isInternalServer(server): raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server) # Create an UIDGenerator instance for every SID, so that each gets # distinct values. @@ -61,7 +61,7 @@ class TS6Protocol(TS6BaseProtocol): channel = utils.toLower(self.irc, channel) # JOIN: # parameters: channelTS, channel, '+' (a plus sign) - if not utils.isInternalClient(self.irc, client): + if not self.irc.isInternalClient(client): log.error('(%s) Error trying to join client %r to %r (no such pseudoclient exists)', self.irc.name, client, channel) raise LookupError('No such PyLink PseudoClient exists.') self._send(client, "JOIN {ts} {channel} +".format(ts=self.irc.channels[channel].ts, channel=channel)) @@ -156,7 +156,7 @@ class TS6Protocol(TS6BaseProtocol): Sends mode changes from a PyLink client. should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendModes(numeric, target, modes, ts=ts) @@ -165,13 +165,13 @@ class TS6Protocol(TS6BaseProtocol): Sends mode changes from a PyLink server. should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') self._sendModes(numeric, target, modes, ts=ts) def killServer(self, numeric, target, reason): """Sends a kill from a PyLink server.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') # KILL: # parameters: target user, path @@ -186,7 +186,7 @@ class TS6Protocol(TS6BaseProtocol): def killClient(self, numeric, target, reason): """Sends a kill from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') assert target in self.irc.users, "Unknown target %r for killClient!" % target self._send(numeric, 'KILL %s :Killed (%s)' % (target, reason)) @@ -194,7 +194,7 @@ class TS6Protocol(TS6BaseProtocol): def topicServer(self, numeric, target, text): """Sends a topic change from a PyLink server. This is usually used on burst.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') # TB # capab: TB @@ -209,7 +209,7 @@ class TS6Protocol(TS6BaseProtocol): def inviteClient(self, numeric, target, channel): """Sends an INVITE from a PyLink client..""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'INVITE %s %s %s' % (target, channel, self.irc.channels[channel].ts)) @@ -219,7 +219,7 @@ class TS6Protocol(TS6BaseProtocol): log.debug('(%s) knockClient: Dropping KNOCK to %r since the IRCd ' 'doesn\'t support it.', self.irc.name, target) return - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') # No text value is supported here; drop it. self._send(numeric, 'KNOCK %s' % target) @@ -230,7 +230,7 @@ class TS6Protocol(TS6BaseProtocol): if field == 'HOST': self.irc.users[target].host = text self._send(self.irc.sid, 'CHGHOST %s :%s' % (target, text)) - if not utils.isInternalClient(self.irc, target): + if not self.irc.isInternalClient(target): # If the target isn't one of our clients, send hook payload # for other plugins to listen to. self.irc.callHooks([self.irc.sid, 'CHGHOST', @@ -443,7 +443,7 @@ class TS6Protocol(TS6BaseProtocol): destination = args[1] except IndexError: destination = self.irc.sid - if utils.isInternalServer(self.irc, destination): + if self.irc.isInternalServer(destination): self._send(destination, 'PONG %s %s' % (destination, source)) def handle_pong(self, source, command, args): diff --git a/protocols/ts6_common.py b/protocols/ts6_common.py index 5028098..1590a9a 100644 --- a/protocols/ts6_common.py +++ b/protocols/ts6_common.py @@ -54,19 +54,19 @@ class TS6BaseProtocol(Protocol): def kickClient(self, numeric, channel, target, reason=None): """Sends a kick from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendKick(numeric, channel, target, reason=reason) def kickServer(self, numeric, channel, target, reason=None): """Sends a kick from a PyLink server.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink PseudoServer exists.') self._sendKick(numeric, channel, target, reason=reason) def nickClient(self, numeric, newnick): """Changes the nick of a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'NICK %s %s' % (newnick, int(time.time()))) self.irc.users[numeric].nick = newnick @@ -74,7 +74,7 @@ class TS6BaseProtocol(Protocol): def partClient(self, client, channel, reason=None): """Sends a part from a PyLink client.""" channel = utils.toLower(self.irc, channel) - if not utils.isInternalClient(self.irc, client): + if not self.irc.isInternalClient(client): log.error('(%s) Error trying to part client %r to %r (no such pseudoclient exists)', self.irc.name, client, channel) raise LookupError('No such PyLink PseudoClient exists.') msg = "PART %s" % channel @@ -85,7 +85,7 @@ class TS6BaseProtocol(Protocol): def quitClient(self, numeric, reason): """Quits a PyLink client.""" - if utils.isInternalClient(self.irc, numeric): + if self.irc.isInternalClient(numeric): self._send(numeric, "QUIT :%s" % reason) self.removeClient(numeric) else: @@ -93,19 +93,19 @@ class TS6BaseProtocol(Protocol): def messageClient(self, numeric, target, text): """Sends a PRIVMSG from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'PRIVMSG %s :%s' % (target, text)) def noticeClient(self, numeric, target, text): """Sends a NOTICE from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'NOTICE %s :%s' % (target, text)) def topicClient(self, numeric, target, text): """Sends a TOPIC change from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'TOPIC %s :%s' % (target, text)) self.irc.channels[target].topic = text @@ -133,7 +133,7 @@ class TS6BaseProtocol(Protocol): for server in self.irc.servers.values(): if name == server.name: raise ValueError('A server named %r already exists!' % name) - if not utils.isInternalServer(self.irc, uplink): + if not self.irc.isInternalServer(uplink): raise ValueError('Server %r is not a PyLink internal PseudoServer!' % uplink) if not utils.isServerName(name): raise ValueError('Invalid server name %r' % name) diff --git a/protocols/unreal.py b/protocols/unreal.py index 5d27c03..878bcf5 100644 --- a/protocols/unreal.py +++ b/protocols/unreal.py @@ -58,7 +58,7 @@ class UnrealProtocol(TS6BaseProtocol): 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 - if not utils.isInternalServer(self.irc, server): + if not self.irc.isInternalServer(server): raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server) # Unreal 3.4 uses TS6-style UIDs. They don't start from AAAAAA like other IRCd's # do, but we can do that fine... @@ -107,7 +107,7 @@ class UnrealProtocol(TS6BaseProtocol): def joinClient(self, client, channel): """Joins a PyLink client to a channel.""" channel = utils.toLower(self.irc, channel) - if not utils.isInternalClient(self.irc, client): + if not self.irc.isInternalClient(client): raise LookupError('No such PyLink client exists.') self._send(client, "JOIN %s" % channel) self.irc.channels[channel].users.add(client) @@ -179,7 +179,7 @@ class UnrealProtocol(TS6BaseProtocol): def killServer(self, numeric, target, reason): """Sends a kill from a PyLink server.""" # <- :GL KILL 38QAAAAAA :hidden-1C620195!GL (test) - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink server exists.') assert target in self.irc.users, "Unknown target %r for killServer!" % target @@ -189,7 +189,7 @@ class UnrealProtocol(TS6BaseProtocol): def killClient(self, numeric, target, reason): """Sends a kill from a PyLink client.""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink client exists.') assert target in self.irc.users, "Unknown target %r for killClient!" % target self._send(numeric, 'KILL %s :%s!PyLink (%s)' % (target, self.irc.serverdata['hostname'], reason)) @@ -209,7 +209,7 @@ class UnrealProtocol(TS6BaseProtocol): # is through UMODE2, which sets the modes on the caller. # U:Lines can use SVSMODE/SVS2MODE, but I won't expect people to # U:Line a PyLink daemon... - if not utils.isInternalClient(self.irc, target): + if not self.irc.isInternalClient(target): raise ProtocolError('Cannot force mode change on external clients!') self._send(target, 'UMODE2 %s' % joinedmodes) @@ -218,7 +218,7 @@ class UnrealProtocol(TS6BaseProtocol): Sends mode changes from a PyLink client. The mode list should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink client exists.') self._sendModes(numeric, target, modes, ts=ts) @@ -227,13 +227,13 @@ class UnrealProtocol(TS6BaseProtocol): Sends mode changes from a PyLink server. The mode list should be a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output. """ - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink server exists.') self._sendModes(numeric, target, modes, ts=ts) def topicServer(self, numeric, target, text): """Sends a TOPIC change from a PyLink server.""" - if not utils.isInternalServer(self.irc, numeric): + if not self.irc.isInternalServer(numeric): raise LookupError('No such PyLink server exists.') self._send(numeric, 'TOPIC %s :%s' % (target, text)) self.irc.channels[target].topic = text @@ -247,7 +247,7 @@ class UnrealProtocol(TS6BaseProtocol): raise NotImplementedError("Changing field %r of a client is " "unsupported by this protocol." % field) - if utils.isInternalClient(self.irc, target): + if self.irc.isInternalClient(target): # It is one of our clients, use SETIDENT/HOST/NAME. if field == 'IDENT': self.irc.users[target].ident = text @@ -284,7 +284,7 @@ class UnrealProtocol(TS6BaseProtocol): def inviteClient(self, numeric, target, channel): """Sends an INVITE from a PyLink client..""" - if not utils.isInternalClient(self.irc, numeric): + if not self.irc.isInternalClient(numeric): raise LookupError('No such PyLink client exists.') self._send(numeric, 'INVITE %s %s' % (target, channel)) @@ -294,7 +294,7 @@ class UnrealProtocol(TS6BaseProtocol): # sent to all ops in a channel. # <- :unreal.midnight.vpn NOTICE @#test :[Knock] by GL|!gl@hidden-1C620195 (test) assert utils.isChannel(target), "Can only knock on channels!" - sender = utils.clientToServer(self.irc, numeric) + sender = self.irc.getServer(numeric) s = '[Knock] by %s (%s)' % (utils.getHostmask(self.irc, numeric), text) self._send(sender, 'NOTICE @%s :%s' % (target, s)) @@ -487,9 +487,9 @@ class UnrealProtocol(TS6BaseProtocol): self.irc.connected.set() 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 irc.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 + target = self.irc.nickToUid(target) or target if target not in self.irc.users and not utils.isChannel(target): log.debug("(%s) Possible desync? Got command target %s, who " "isn't in our user list!", self.irc.name, target) diff --git a/utils.py b/utils.py index f4de2a6..6cb6db3 100644 --- a/utils.py +++ b/utils.py @@ -158,19 +158,6 @@ def toLower(irc, text): text = text.replace('~', '^') return text.lower() -def nickToUid(irc, nick): - """Returns the UID of a user named nick, if present.""" - nick = toLower(irc, nick) - for k, v in irc.users.copy().items(): - if toLower(irc, v.nick) == nick: - return k - -def clientToServer(irc, numeric): - """Finds the SID of the server a user is on.""" - for server in irc.servers: - if numeric in irc.servers[server].users: - return server - _nickregex = r'^[A-Za-z\|\\_\[\]\{\}\^\`][A-Z0-9a-z\-\|\\_\[\]\{\}\^\`]*$' def isNick(s, nicklen=None): """Returns whether the string given is a valid nick.""" @@ -252,7 +239,7 @@ def parseModes(irc, target, args): arg = args.pop(0) # Convert nicks to UIDs implicitly; most IRCds will want # this already. - arg = nickToUid(irc, arg) or arg + arg = irc.nickToUid(arg) or arg if arg not in irc.users: # Target doesn't exist, skip it. log.debug('(%s) Skipping setting mode "%s %s"; the ' 'target doesn\'t seem to exist!', irc.name, @@ -467,19 +454,6 @@ def reverseModes(irc, target, modes, oldobj=None): else: return set(newmodes) -def isInternalClient(irc, numeric): - """ - Checks whether the given numeric is a PyLink Client, - returning the SID of the server it's on if so. - """ - for sid in irc.servers: - if irc.servers[sid].internal and numeric in irc.servers[sid].users: - return sid - -def isInternalServer(irc, sid): - """Returns whether the given SID is an internal PyLink server.""" - return (sid in irc.servers and irc.servers[sid].internal) - def isOper(irc, uid, allowAuthed=True, allowOper=True): """ Returns whether the given user has operator status on PyLink. This can be achieved @@ -514,7 +488,7 @@ def isManipulatableClient(irc, uid): set True to prevent interactions with opers (like mode changes) from causing desyncs. """ - return isInternalClient(irc, uid) and irc.users[uid].manipulatable + return irc.isInternalClient(uid) and irc.users[uid].manipulatable def getHostmask(irc, user): """Returns the hostmask of the given user, if present."""