def _handleMODE(self, nick, ident, host, params): message = None if nick in self.bot.users: user = self.bot.users[nick] else: user = IRCUser(nick, ident, host) if len(params) > 2: modeParams = params[2:] else: modeParams = [] if params[0][0] in self.bot.supportHelper.chanTypes: if params[0] not in self.bot.channels: self.bot.logger.warning( f'Received MODE message for unknown channel {params[0]}.') return channel = self.bot.channels[params[0]] modes = channel.setModes(params[1], modeParams) if not modes: return if len(modes['added']) > 0 or len(modes['removed']) > 0: message = IRCMessage('MODE', user, channel, '', self.bot, modes) elif params[0] == self.bot.nick: modes = self.bot.setUserModes(params[1]) if not modes: return if len(modes['added']) > 0 or len(modes['removed']) > 0: message = IRCMessage('MODE', user, None, '', self.bot, modes) if message: self.handleMessage(message)
def execute(self, message: IRCMessage): # split on unescaped | chain = re.split(r'(?<!\\)\|', message.parameters) response = None extraVars = {} for link in chain: link = link.strip() link = re.sub(r'\\\|', r'|', link) if response is not None: if hasattr(response, '__iter__'): return IRCResponse( ResponseType.Say, "Chain Error: segment before '{}' returned a list". format(link), message.replyTo) # replace $output with output of previous command link = link.replace('$output', response.response) extraVars.update(response.ExtraVars) for var, value in extraVars.items(): link = re.sub(r'\$\b{}\b'.format(re.escape(var)), '{}'.format(value), link) else: # replace $output with empty string if previous command had no output # (or this is the first command in the chain, # but for some reason has $output as a param) link = link.replace('$output', '') link = link.replace('$sender', message.user.nick) if message.channel is not None: link = link.replace('$channel', message.channel.name) else: link = link.replace('$channel', message.user.nick) # build a new message out of this 'link' in the chain inputMessage = IRCMessage(message.type, message.user, message.channel, self.bot.commandChar + link.lstrip(), self.bot) # might be used at some point to tell commands they're being called from Chain inputMessage.chained = True if inputMessage.command.lower( ) in self.bot.moduleHandler.mappedTriggers: command = self.bot.moduleHandler.mappedTriggers[ inputMessage.command.lower()] response = command.execute(inputMessage) else: return IRCResponse( ResponseType.Say, "{!r} is not a recognized command trigger".format( inputMessage.command), message.replyTo) if response.response is not None: # limit response length (chains can get pretty large) response.response = list( string.splitUTF8(response.response.encode('utf-8'), 700))[0] response.response = str(response.response, 'utf-8') return response
def execute(self, message: IRCMessage): # split on unescaped | chain = re.split(r'(?<!\\)\|', message.parameters) response = None extraVars = {} for link in chain: link = link.strip() link = re.sub(r'\\\|', r'|', link) if response is not None: if hasattr(response, '__iter__'): return IRCResponse(ResponseType.Say, "Chain Error: segment before '{}' returned a list" .format(link), message.replyTo) # replace $output with output of previous command link = link.replace('$output', response.response) extraVars.update(response.ExtraVars) for var, value in extraVars.items(): link = re.sub(r'\$\b{}\b'.format(re.escape(var)), '{}'.format(value), link) else: # replace $output with empty string if previous command had no output # (or this is the first command in the chain, # but for some reason has $output as a param) link = link.replace('$output', '') link = link.replace('$sender', message.user.nick) if message.channel is not None: link = link.replace('$channel', message.channel.name) else: link = link.replace('$channel', message.user.nick) # build a new message out of this 'link' in the chain inputMessage = IRCMessage(message.type, message.user, message.channel, self.bot.commandChar + link.lstrip(), self.bot) # might be used at some point to tell commands they're being called from Chain inputMessage.chained = True if inputMessage.command.lower() in self.bot.moduleHandler.mappedTriggers: command = self.bot.moduleHandler.mappedTriggers[inputMessage.command.lower()] response = command.execute(inputMessage) else: return IRCResponse(ResponseType.Say, "{!r} is not a recognized command trigger" .format(inputMessage.command), message.replyTo) if response.response is not None: # limit response length (chains can get pretty large) response.response = list(string.splitUTF8(response.response.encode('utf-8'), 700))[0] response.response = str(response.response, 'utf-8') return response
def _handleINVITE(self, nick, ident, host, params): if nick in self.bot.users: inviter = self.bot.users[nick] else: inviter = IRCUser(nick, ident, host) invitee = None if 'invite-notify' in self.bot.capabilities['finished'] and len( params) > 1: if params[0] not in self.bot.users: invitee = IRCUser(params[0]) else: invitee = self.bot.users[nick] chanName = params[1] else: chanName = params[0] if chanName not in self.bot.channels: channel = IRCChannel(params[0], self.bot) else: channel = self.bot.channels[chanName] if not invitee or invitee.nick == self.bot.nick: self.bot.output.cmdJOIN(chanName) message = IRCMessage('INVITE', inviter, channel, '', self.bot, {'invitee': invitee}) self.handleMessage(message)
def _handleKICK(self, nick, ident, host, params): if params[0] not in self.bot.channels: self.bot.logger.warning( f'Received KICK message for unknown channel {params[0]}.') return channel = self.bot.channels[params[0]] if params[1] not in channel.users: self.bot.logger.warning( f'Received KICK message for unknown user {nick} in channel {params[0]}.' ) return if nick not in self.bot.users: # Technically opers could KICK a user despite not being in the channel themselves kicker = IRCUser(nick, ident, host) else: kicker = self.bot.users[nick] kicked = self.bot.users[params[1]] reason = '' if len(params) > 2: reason = params[2] message = IRCMessage('KICK', kicker, channel, reason, self.bot, {'kicked': kicked}) self.handleMessage(message) # We need to run the action before we actually get rid of the user if kicked.nick == self.bot.nick: del self.bot.channels[params[0]] else: del channel.users[kicked.nick] del channel.ranks[kicked.nick]
def _aliasedMessage(self, message): if message.command.lower() not in self.aliases: return alias = self.aliases[message.command.lower()] newMsg = "{0}{1}".format(self.bot.commandChar, alias) newMsg = newMsg.replace("$sender", message.user.nick) if message.channel is not None: newMsg = newMsg.replace("$channel", message.channel.name) else: newMsg = newMsg.replace("$channel", message.user.nick) paramList = [ self._mangleReplacementPoints(param) for param in message.parameterList ] # if the alias contains numbered param replacement points, replace them if re.search(r'\$[0-9]+', newMsg): newMsg = newMsg.replace("$0", " ".join(paramList)) for i, param in enumerate(paramList): if newMsg.find("${}+".format(i + 1)) != -1: newMsg = newMsg.replace("${}+".format(i + 1), " ".join(paramList[i:])) else: newMsg = newMsg.replace("${}".format(i + 1), param) # if there are no numbered replacement points, append the full parameter list instead else: newMsg += " {}".format(" ".join(paramList)) newMsg = self._unmangleReplacementPoints(newMsg) return IRCMessage(message.type, message.user, message.channel, newMsg, self.bot)
def _handleTriggerCommand(self, message: IRCMessage, triggerCommand: str) -> IRCResponse: self.logger.debug(f"{triggerCommand} executing from a trigger") newMessage = IRCMessage(message.type, message.user, message.channel, f"{self.bot.commandChar}{triggerCommand}", self.bot) newCommand = newMessage.command.lower() self.logger.debug(f"Command appears to be: {newCommand}") if newCommand in self.bot.moduleHandler.mappedTriggers: return self.bot.moduleHandler.mappedTriggers[newCommand].execute(newMessage)
def _handleNumeric324(self, prefix, params): # 324: RPL_CHANNELMODEIS channel = self.bot.channels[params[1]] modeParams = params[3].split() if len(params) > 3 else [] modes = channel.setModes(params[2], modeParams) if modes: message = IRCMessage('324', IRCUser(prefix), None, '', self.bot, modes) self.handleMessage(message)
def _handleNumeric001(self, prefix, params): # 001: RPL_WELCOME self.bot.loggedIn = True self.bot.factory.connectionAttempts = 0 message = IRCMessage('001', IRCUser(prefix), None, '', self.bot) self.handleMessage(message) channels = self.bot.config.getWithDefault('channels', {}) for channel, key in channels.items(): self.bot.output.cmdJOIN(channel, key if key else '')
def execute(self, message: IRCMessage): if len(message.parameterList) < 2: return IRCResponse(ResponseType.Say, self.help(None), message.replyTo) command = message.parameterList[1].lower() delay = timeparse(message.parameterList[0]) if not delay: delay = 0 delayDelta = datetime.timedelta(seconds=delay) delayString = string.deltaTimeToString(delayDelta, 's') params = message.parameterList[2:] commandString = '{}{} {}'.format(self.bot.commandChar, command, ' '.join(params)) commandString = commandString.replace('$delayString', delayString) commandString = commandString.replace('$delay', str(delay)) newMessage = IRCMessage(message.type, message.user, message.channel, commandString, self.bot) moduleHandler = self.bot.moduleHandler if command in moduleHandler.mappedTriggers: module = moduleHandler.mappedTriggers[command].execute d = task.deferLater(reactor, delay, module, newMessage) d.addCallback(self._activate) d.addErrback(self._deferredError) return IRCResponse( ResponseType.Say, "OK, I'll execute that in {}".format(delayString), message.replyTo, { 'delay': delay, 'delayString': delayString }) else: if 'Alias' not in moduleHandler.commands: return IRCResponse( ResponseType.Say, "'{}' is not a recognized command".format(command), message.replyTo) if command not in moduleHandler.commands['Alias'].aliases: return IRCResponse( ResponseType.Say, "'{}' is not a recognized command or alias".format( command), message.replyTo) d = task.deferLater(reactor, delay, moduleHandler.commands['Alias'].execute, newMessage) d.addCallback(self._activate) d.addErrback(self._deferredError) return IRCResponse( ResponseType.Say, "OK, I'll execute that in {}".format(delayString), message.replyTo)
def activate(self): commandStr = '{}{} {}'.format(self.bot.commandChar, self.command, ' '.join(self.params)) self.logger.info("Activated {!r}".format(commandStr)) message = IRCMessage('PRIVMSG', IRCUser(self.user), IRCChannel(self.channel, self.bot), commandStr, self.bot) trigger = self.bot.moduleHandler.mappedTriggers[self.command].execute return trigger(message)
def _handleNOTICE(self, nick, ident, host, params): user = None if params[0][0] in self.bot.supportHelper.chanTypes: if params[0] in self.bot.channels: source = self.bot.channels[params[0]] else: # We got a notice for an unknown channel. Create a temporary IRCChannel object for it. source = IRCChannel(params[0], self.bot) if nick in self.bot.users: user = self.bot.users[nick] else: user = IRCUser(nick, ident, host) elif nick in self.bot.users: source = self.bot.users[nick] else: # We got a notice from an unknown user. Create a temporary IRCUser object for them. source = IRCUser(nick, ident, host) if isinstance(source, IRCChannel): message = IRCMessage('NOTICE', user, source, params[1], self.bot) else: message = IRCMessage('NOTICE', source, None, params[1], self.bot) self.handleMessage(message)
def _handlePRIVMSG(self, nick, ident, host, params): user = None if params[0][0] in self.bot.supportHelper.chanTypes: if params[0] in self.bot.channels: source = self.bot.channels[params[0]] else: # We got a message for an unknown channel. Create a temporary IRCChannel object for it. source = IRCChannel(params[0], self.bot) if nick in self.bot.users: user = self.bot.users[nick] else: user = IRCUser(nick, ident, host) elif nick in self.bot.users: source = self.bot.users[nick] user = source else: # We got a message from an unknown user. Create a temporary IRCUser object for them. source = IRCUser(nick, ident, host) if len(params) == 1: self.bot.logger.warning('Received a blank PRIVMSG') params.append('') if params[1] and params[1][0] == '\x01': msgType = 'CTCP' msgStr = params[1][1:len(params[1]) - 1] if msgStr.upper().startswith('ACTION'): msgType = 'ACTION' msgStr = msgStr[7:] if isinstance(source, IRCChannel): message = IRCMessage(msgType, user, source, msgStr, self.bot) else: message = IRCMessage(msgType, source, None, msgStr, self.bot) elif isinstance(source, IRCChannel): message = IRCMessage('PRIVMSG', user, source, params[1], self.bot) else: message = IRCMessage('PRIVMSG', source, None, params[1], self.bot) self.handleMessage(message)
def _handleTOPIC(self, nick, ident, host, params): if params[0] not in self.bot.channels: self.bot.logger.warning( f'Received TOPIC message for unknown channel {params[0]}.') return channel = self.bot.channels[params[0]] if nick not in self.bot.users: # A user that's not in the channel can change the topic so let's make a temporary user. user = IRCUser(nick, ident, host) else: user = self.bot.users[nick] oldTopic = channel.topic channel.topic = params[1] channel.topicSetter = user.fullUserPrefix() channel.topicTimestamp = timestamp(now()) message = IRCMessage('TOPIC', user, channel, params[1], self.bot, {'oldtopic': oldTopic}) self.handleMessage(message)
def _handleQUIT(self, nick, ident, host, params): if nick not in self.bot.users: self.bot.logger.warning( f'Received a QUIT message for unknown user {nick}.') return reason = '' if len(params) > 0: reason = params[0] user = self.bot.users[nick] quitChannels = [ chan for _, chan in self.bot.channels.items() if nick in chan.users ] message = IRCMessage('QUIT', user, None, reason, self.bot, {'quitChannels': quitChannels}) self.handleMessage(message) for channel in self.bot.channels.values(): if nick in channel.users: del channel.users[nick] del channel.ranks[nick]
def _handleNICK(self, nick, ident, host, params): if nick not in self.bot.users: self.bot.logger.warning( f'Received NICK message for unknown user {nick}.') return user = self.bot.users[nick] newNick = params[0] user.nick = newNick self.bot.users[newNick] = user del self.bot.users[nick] for channel in self.bot.channels.values(): if nick in channel.users: channel.users[newNick] = user channel.ranks[newNick] = channel.ranks[nick] del channel.users[nick] del channel.ranks[nick] if nick == self.bot.nick: self.bot.nick = newNick message = IRCMessage('NICK', user, None, newNick, self.bot, {'oldnick': nick}) self.handleMessage(message)
def _handleJOIN(self, nick, ident, host, params): if nick not in self.bot.users: user = IRCUser(nick, ident, host) if 'extended-join' in self.bot.capabilities['finished'] and len( params) > 1: if params[1] != '*': user.account = params[1] user.gecos = params[2] self.bot.users[nick] = user else: user = self.bot.users[nick] if params[0] not in self.bot.channels: channel = IRCChannel(params[0], self.bot) self.bot.output.cmdWHO(params[0]) self.bot.output.cmdMODE(params[0]) self.bot.channels[params[0]] = channel else: channel = self.bot.channels[params[0]] channel.users[nick] = user channel.ranks[nick] = '' message = IRCMessage('JOIN', user, channel, '', self.bot) self.handleMessage(message)
def _handlePART(self, nick, ident, host, params): if params[0] not in self.bot.channels: self.bot.logger.warning( f'Received PART message for unknown channel {params[0]}.') return channel = self.bot.channels[params[0]] if nick not in channel.users: self.bot.logger.warning( f'Received PART message for unknown user {nick} in channel {params[0]}.' ) return reason = '' if len(params) > 1: reason = params[1] user = self.bot.users[nick] # We need to run the action before we actually get rid of the user message = IRCMessage('PART', user, channel, reason, self.bot) self.handleMessage(message) if nick == self.bot.nick: del self.bot.channels[params[0]] else: del channel.users[nick] del channel.ranks[nick]
def execute(self, message: IRCMessage): subString = self._mangleEscapes(message.parameters) try: segments = list(self._parseSubcommandTree(subString)) except UnbalancedBracesException as e: red = colour(A.bold[A.fg.lightRed['']]) normal = colour(A.normal['']) error = (subString[:e.column] + red + subString[e.column] + normal + subString[e.column + 1:]) error = self._unmangleEscapes(error, False) return [ IRCResponse(ResponseType.Say, "Sub Error: {}".format(e.message), message.replyTo), IRCResponse(ResponseType.Say, error, message.replyTo) ] prevLevel = -1 responseStack = [] extraVars = {} metadata = {} for segment in segments: (level, command, start, end) = segment # We've finished executing subcommands at the previous depth, # so replace subcommands with their output at the current depth if level < prevLevel: command = self._substituteResponses(command, responseStack, level, extraVars, start) # Replace any extraVars in the command for var, value in extraVars.items(): command = re.sub(r'\$\b{}\b'.format(re.escape(var)), '{}'.format(value), command) # Build a new message out of this segment inputMessage = IRCMessage(message.type, message.user, message.channel, self.bot.commandChar + command.lstrip(), self.bot, metadata=metadata) # Execute the constructed message if inputMessage.command.lower( ) in self.bot.moduleHandler.mappedTriggers: module = self.bot.moduleHandler.mappedTriggers[ inputMessage.command.lower()] response = module.execute(inputMessage) """@type : IRCResponse""" else: return IRCResponse( ResponseType.Say, "'{}' is not a recognized command trigger".format( inputMessage.command), message.replyTo) # Push the response onto the stack responseStack.append((level, response.response, start, end)) # Update the extraVars dict extraVars.update(response.ExtraVars) metadata = self._recursiveMerge(metadata, response.Metadata) prevLevel = level responseString = self._substituteResponses(subString, responseStack, -1, extraVars, -1) responseString = self._unmangleEscapes(responseString) return IRCResponse(ResponseType.Say, responseString, message.replyTo, extraVars=extraVars, metadata=metadata)