def handle_mode(irc, source, command, args): """Protect against forced deoper attempts.""" target = args['target'] 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 ('-o', None) in modes and (target == irc.pseudoclient.uid or not utils.isManipulatableClient(irc, target)): irc.proto.modeServer(irc.sid, target, {('+o', None)})
def _sendKill(self, numeric, target, reason): self._send(numeric, 'KILL %s :%s' % (target, reason)) # 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): self.removeClient(target)
def killClient(self, numeric, target, reason): """Sends a kill from a PyLink client.""" if not utils.isInternalClient(self.irc, 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)) self.removeClient(target)
def topicClient(self, numeric, target, text): """Sends a TOPIC change from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'TOPIC %s :%s' % (target, text)) self.irc.channels[target].topic = text self.irc.channels[target].topicset = True
def quitClient(self, numeric, reason): """Quits a PyLink client.""" if utils.isInternalClient(self.irc, numeric): self._send(numeric, "QUIT :%s" % reason) self.removeClient(numeric) else: raise LookupError("No such PyLink PseudoClient exists.")
def joinClient(self, client, channel): """Joins a PyLink client to a channel.""" # InspIRCd doesn't distinguish between burst joins and regular joins, # 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) 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.') # Strip out list-modes, they shouldn't be ever sent in FJOIN. modes = [ m for m in self.irc.channels[channel].modes if m[0] not in self.irc.cmodes['*A'] ] self._send( server, "FJOIN {channel} {ts} {modes} :,{uid}".format( ts=self.irc.channels[channel].ts, uid=client, channel=channel, modes=utils.joinModes(modes))) self.irc.channels[channel].users.add(client) self.irc.users[client].channels.add(channel)
def inviteClient(self, numeric, target, channel): """Sends an INVITE from a PyLink client..""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send( numeric, 'INVITE %s %s %s' % (target, channel, self.irc.channels[channel].ts))
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): 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 modeClient(self, numeric, target, modes, ts=None): """ 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): raise LookupError('No such PyLink client exists.') self._sendModes(numeric, target, modes, ts=ts)
def knockClient(self, numeric, target, text): """Sends a KNOCK from a PyLink client.""" if 'KNOCK' not in self.irc.caps: 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): raise LookupError('No such PyLink PseudoClient exists.') # No text value is supported here; drop it. self._send(numeric, 'KNOCK %s' % target)
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): 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 if reason: msg += " :%s" % reason self._send(client, msg) self.handle_part(client, 'PART', [channel])
def joinClient(self, client, channel): """Joins a PyLink client to a channel.""" channel = utils.toLower(self.irc, channel) # JOIN: # parameters: channelTS, channel, '+' (a plus sign) if not utils.isInternalClient(self.irc, 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)) self.irc.channels[channel].users.add(client) self.irc.users[client].channels.add(channel)
def handle_fantasy(irc, source, command, args): """Fantasy command handler.""" try: # First, try to fetch the config-defined prefix. prefixes = [irc.botdata["prefix"]] except KeyError: # Config option is missing. prefixes = [] if irc.botdata.get("respondtonick"): # If responding to nick is enabled, add variations of the current nick # to the prefix list: "<nick>," and "<nick>:" nick = irc.pseudoclient.nick prefixes += [nick + ',', nick + ':'] if not prefixes: # We finished with an empty prefixes list, meaning fantasy is misconfigured! log.warning( "(%s) Fantasy prefix was not set in configuration - " "fantasy commands will not work!", irc.name) return channel = args['target'] text = args['text'] for prefix in prefixes: # Cycle through the prefixes list we finished with. # The following conditions must be met for an incoming message for # fantasy to trigger: # 1) The message target is a channel. # 2) The message starts with one of our fantasy prefixes. # 3) The main PyLink client is in the channel where the command was # called. # 4) The sender is NOT a PyLink client (this prevents infinite # 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): # Cut off the length of the prefix from the text. text = text[len(prefix):] # Set the "place last command was called in" variable to the # channel in question, so that replies from fantasy-supporting # plugins get forwarded to it. irc.called_by = channel # Finally, call the bot command and break. irc.callCommand(source, text) break
def _sendModes(self, numeric, target, modes, ts=None): """Internal function to send mode changes from a PyLink client/server.""" # <- :unreal.midnight.vpn MODE #endlessvoid +ntCo GL 1444361345 utils.applyModes(self.irc, target, modes) joinedmodes = utils.joinModes(modes) if utils.isChannel(target): # The MODE command is used for channel mode changes only ts = ts or self.irc.channels[utils.toLower(self.irc, target)].ts self._send(numeric, 'MODE %s %s %s' % (target, joinedmodes, ts)) else: # For user modes, the only way to set modes (for non-U:Lined servers) # 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): raise ProtocolError( 'Cannot force mode change on external clients!') self._send(target, 'UMODE2 %s' % joinedmodes)
def mode(irc, source, args): """<source> <target> <modes> 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. <target> can be either a nick or a channel.""" utils.checkAuthenticated(irc, source, allowOper=False) try: modesource, target, modes = args[0], args[1], args[2:] except IndexError: irc.reply( 'Error: Not enough arguments. Needs 3: source nick, target, modes to set.' ) return target = utils.nickToUid(irc, target) or target extclient = target in irc.users and not utils.isInternalClient(irc, target) parsedmodes = utils.parseModes(irc, target, modes) ischannel = target in irc.channels if not (target in irc.users or ischannel): irc.reply("Error: Invalid channel or nick %r." % target) return elif not parsedmodes: irc.reply("Error: No valid modes were given.") return elif not (ischannel or utils.isManipulatableClient(irc, target)): irc.reply( "Error: Can only set modes on channels or non-protected PyLink clients." ) return if utils.isInternalServer(irc, modesource): # Setting modes from a server. irc.proto.modeServer(modesource, target, parsedmodes) else: # Setting modes from a client. modesource = utils.nickToUid(irc, modesource) irc.proto.modeClient(modesource, target, parsedmodes) irc.callHooks([ modesource, 'PYLINK_BOTSPLUGIN_MODE', { 'target': target, 'modes': parsedmodes, 'parse_as': 'MODE' } ])
def killClient(self, numeric, target, reason): """Sends a kill from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendKill(numeric, target, reason)
def kickClient(self, numeric, channel, target, reason=None): """Sends a kick from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._sendKick(numeric, channel, target, reason=reason)
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): irc.called_by = source irc.callCommand(source, args['text'])
def nickClient(self, numeric, newnick): """Changes the nick of a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'NICK %s %s' % (newnick, int(time.time()))) self.irc.users[numeric].nick = newnick
def messageClient(self, numeric, target, text): """Sends a PRIVMSG from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'PRIVMSG %s :%s' % (target, text))
def knockClient(self, numeric, target, text): """Sends a KNOCK from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'ENCAP * KNOCK %s :%s' % (target, text))
def noticeClient(self, numeric, target, text): """Sends a NOTICE from a PyLink client.""" if not utils.isInternalClient(self.irc, numeric): raise LookupError('No such PyLink PseudoClient exists.') self._send(numeric, 'NOTICE %s :%s' % (target, text))