def doPrivmsg(self, irc, msg): assert self is irc.callbacks[0], \ 'Owner isn\'t first callback: %r' % irc.callbacks if ircmsgs.isCtcp(msg): return s = callbacks.addressed(irc.nick, msg) if s: ignored = ircdb.checkIgnored(msg.prefix) if ignored: self.log.info('Ignoring command from %s.', msg.prefix) return maximum = conf.supybot.abuse.flood.command.maximum() self.commands.enqueue(msg) if conf.supybot.abuse.flood.command() \ and self.commands.len(msg) > maximum \ and not ircdb.checkCapability(msg.prefix, 'trusted'): punishment = conf.supybot.abuse.flood.command.punishment() banmask = conf.supybot.protocols.irc.banmask \ .makeBanmask(msg.prefix) self.log.info('Ignoring %s for %s seconds due to an apparent ' 'command flood.', banmask, punishment) ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.notify(): irc.reply('You\'ve given me %s commands within the last ' '%i seconds; I\'m now ignoring you for %s.' % (maximum, conf.supybot.abuse.flood.interval(), utils.timeElapsed(punishment, seconds=False))) return try: tokens = callbacks.tokenize(s, channel=msg.args[0]) self.Proxy(irc, msg, tokens) except SyntaxError as e: irc.queueMsg(callbacks.error(msg, str(e)))
def doPrivmsg(self, irc, msg): assert self is irc.callbacks[0], \ 'Owner isn\'t first callback: %r' % irc.callbacks if ircmsgs.isCtcp(msg): return s = callbacks.addressed(irc.nick, msg) if s: ignored = ircdb.checkIgnored(msg.prefix) if ignored: self.log.info('Ignoring command from %s.', msg.prefix) return maximum = conf.supybot.abuse.flood.command.maximum() self.commands.enqueue(msg) if conf.supybot.abuse.flood.command() \ and self.commands.len(msg) > maximum \ and not ircdb.checkCapability(msg.prefix, 'trusted'): punishment = conf.supybot.abuse.flood.command.punishment() banmask = ircutils.banmask(msg.prefix) self.log.info('Ignoring %s for %s seconds due to an apparent ' 'command flood.', banmask, punishment) ircdb.ignores.add(banmask, time.time() + punishment) irc.reply('You\'ve given me %s commands within the last ' 'minute; I\'m now ignoring you for %s.' % (maximum, utils.timeElapsed(punishment, seconds=False))) return try: tokens = callbacks.tokenize(s, channel=msg.args[0]) self.Proxy(irc, msg, tokens) except SyntaxError, e: irc.queueMsg(callbacks.error(msg, str(e)))
def _replacer_process(self, irc, msg, target, pattern, replacement, count, messages): for m in messages: if m.command in ('PRIVMSG', 'NOTICE') and \ ircutils.strEqual(m.args[0], msg.args[0]) and m.tagged('receivedBy') == irc: if target and m.nick != target: continue # Don't snarf ignored users' messages unless specifically # told to. if ircdb.checkIgnored(m.prefix) and not target: continue # When running substitutions, ignore the "* nick" part of any actions. action = ircmsgs.isAction(m) if action: text = ircmsgs.unAction(m) else: text = m.args[1] # Test messages sent before SedRegex was activated. Mark them all as seen # so we only need to do this check once per message. if not m.tagged(TAG_SEEN): m.tag(TAG_SEEN) if SED_REGEX.match(m.args[1]): m.tag(TAG_IS_REGEX) # Ignore messages containing a regexp if ignoreRegex is on. if self.registryValue('ignoreRegex', msg.channel, irc.network) and m.tagged(TAG_IS_REGEX): self.log.debug( "Skipping message %s because it is tagged as isRegex", m.args[1]) continue if m.nick == msg.nick: messageprefix = msg.nick else: messageprefix = '%s thinks %s' % (msg.nick, m.nick) try: replace_result = pattern.search(text) if replace_result: if self.registryValue('boldReplacementText', msg.channel, irc.network): replacement = ircutils.bold(replacement) subst = pattern.sub(replacement, text, count) if action: # If the message was an ACTION, prepend the nick back. subst = '* %s %s' % (m.nick, subst) subst = axe_spaces(subst) return _("%s meant to say: %s") % \ (messageprefix, subst) except Exception as e: self.log.warning(_("SedRegex error: %s"), e, exc_info=True) raise self.log.debug( _("SedRegex: Search %r not found in the last %i messages of %s."), msg.args[1], len(irc.state.history), msg.args[0]) raise SearchNotFoundError()
def replacer(self, irc, msg, regex): if not self.registryValue('enable', msg.args[0]): return iterable = reversed(irc.state.history) msg.tag('Replacer') try: (pattern, replacement, count) = self._unpack_sed(msg.args[1]) except (ValueError, re.error) as e: self.log.warning(_("Replacer error: %s"), e) if self.registryValue('displayErrors', msg.args[0]): irc.error(_("Replacer error: %s" % e), Raise=True) return next(iterable) for m in iterable: if m.command in ('PRIVMSG', 'NOTICE') and \ m.args[0] == msg.args[0]: target = regex.group('nick') if not ircutils.isNick(str(target), strictRfc=True): return if target and m.nick != target: continue # Don't snarf ignored users' messages unless specifically # told to. if ircdb.checkIgnored(m.prefix) and not target: continue # When running substitutions, ignore the "* nick" part of any actions. action = ircmsgs.isAction(m) if action: text = ircmsgs.unAction(m) else: text = m.args[1] if self.registryValue('ignoreRegex', msg.args[0]) and \ m.tagged('Replacer'): continue if m.nick == msg.nick: messageprefix = msg.nick else: messageprefix = '%s thinks %s' % (msg.nick, m.nick) if regexp_wrapper(text, pattern, timeout=0.05, plugin_name=self.name(), fcn_name='replacer'): if self.registryValue('boldReplacementText', msg.args[0]): replacement = ircutils.bold(replacement) subst = process(pattern.sub, replacement, text, count, timeout=0.05) if action: # If the message was an ACTION, prepend the nick back. subst = '* %s %s' % (m.nick, subst) irc.reply(_("%s meant to say: %s") % (messageprefix, subst), prefixNick=False) return self.log.debug(_("Replacer: Search %r not found in the last %i messages of %s."), msg.args[1], len(irc.state.history), msg.args[0]) if self.registryValue("displayErrors", msg.args[0]): irc.error(_("Search not found in the last %i messages.") % len(irc.state.history), Raise=True)
def doPrivmsg(self, irc, msg): (targets, t) = msg.args text = escape(t) if msg.prefix == irc.prefix: return for channel in targets.split(','): if irc.isChannel(channel) and channel in irc.state.channels: learn = self.registryValue('learn', channel=channel) replyRandom = self.registryValue('replyPercent', channel=channel) > 0.00 replyAddressed = self.registryValue('replyWhenAddressed', channel=channel) called = False if ircdb.checkIgnored(msg.prefix, channel): continue if not learn and not replyRandom and not replyAddressed: continue if replyAddressed and msg.addressed: text = escape(callbacks.addressed(irc.nick, msg)) m = None if learn: m = self.callHailo(channel, '-L', text) else: m = self.callHailo(channel, '-r', text) if m != None and len(m): if self.registryValue('checkSimilarity', channel=channel): if similar(m, text) < self.registryValue( 'similarity', channel=channel): irc.queueMsg(ircmsgs.privmsg(channel, m)) else: m = self.callHailo(channel, '-R', '') if m != None and len(m): irc.queueMsg(ircmsgs.privmsg(channel, m)) else: irc.queueMsg(ircmsgs.privmsg(channel, m)) called = True if not msg.addressed and replyRandom and randint( 1, 99) < self.registryValue('replyPercent', channel=channel) * 100: m = None if learn: m = self.callHailo(channel, '-L', text) else: m = self.callHailo(channel, '-r', text) called = True if m != None and len(m): if self.registryValue('checkSimilarity', channel=channel): if similar(m, text) < self.registryValue( 'similarity', channel=channel): irc.queueMsg(ircmsgs.privmsg(channel, m)) else: irc.queueMsg(ircmsgs.privmsg(channel, m)) if not called and learn: self.callHailo(channel, '-l', text)
def _doPrivmsgs(self, irc, msg): """If the given message is a command, triggers Limnoria's command-dispatching for that command. Takes the same arguments as ``doPrivmsg`` would, but ``msg`` can potentially be an artificial message synthesized in doBatch from a multiline batch. Usually, a command is a single message, so ``payload=msg.params[0]`` However, when ``msg`` is part of a multiline message, the payload is the concatenation of multiple messages. See <https://ircv3.net/specs/extensions/multiline>. """ assert self is irc.callbacks[0], \ 'Owner isn\'t first callback: %r' % irc.callbacks if ircmsgs.isCtcp(msg): return s = callbacks.addressed(irc, msg) if s: ignored = ircdb.checkIgnored(msg.prefix) if ignored: self.log.info('Ignoring command from %s.', msg.prefix) return maximum = conf.supybot.abuse.flood.command.maximum() self.commands.enqueue(msg) if conf.supybot.abuse.flood.command() \ and self.commands.len(msg) > maximum \ and not ircdb.checkCapability(msg.prefix, 'trusted'): punishment = conf.supybot.abuse.flood.command.punishment() banmask = conf.supybot.protocols.irc.banmask \ .makeBanmask(msg.prefix) self.log.info( 'Ignoring %s for %s seconds due to an apparent ' 'command flood.', banmask, punishment) ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.notify(): irc.reply('You\'ve given me %s commands within the last ' '%i seconds; I\'m now ignoring you for %s.' % (maximum, conf.supybot.abuse.flood.interval(), utils.timeElapsed(punishment, seconds=False))) return try: tokens = callbacks.tokenize(s, channel=msg.channel, network=irc.network) self.Proxy(irc, msg, tokens) except SyntaxError as e: if conf.supybot.reply.error.detailed(): irc.error(str(e)) else: irc.replyError(msg=msg) self.log.info('Syntax error: %s', e)
def _replacer_process(self, irc, msg, target, pattern, replacement, count, messages): for m in messages: if m.command in ('PRIVMSG', 'NOTICE') and \ ircutils.strEqual(m.args[0], msg.args[0]) and m.tagged('receivedBy') == irc: if target and m.nick != target: continue # Don't snarf ignored users' messages unless specifically # told to. if ircdb.checkIgnored(m.prefix) and not target: continue # When running substitutions, ignore the "* nick" part of any actions. action = ircmsgs.isAction(m) if action: text = ircmsgs.unAction(m) else: text = m.args[1] if self.registryValue('ignoreRegex', msg.channel, irc.network) and \ m.tagged('Replacer'): continue if m.nick == msg.nick: messageprefix = msg.nick else: messageprefix = '%s thinks %s' % (msg.nick, m.nick) try: replace_result = pattern.search(text) if replace_result: if self.registryValue('boldReplacementText', msg.channel, irc.network): replacement = ircutils.bold(replacement) subst = pattern.sub(replacement, text, count) if action: # If the message was an ACTION, prepend the nick back. subst = '* %s %s' % (m.nick, subst) subst = axe_spaces(subst) return _("%s meant to say: %s") % \ (messageprefix, subst) except Exception as e: self.log.warning(_("SedRegex error: %s"), e, exc_info=True) raise self.log.debug( _("SedRegex: Search %r not found in the last %i messages of %s."), msg.args[1], len(irc.state.history), msg.args[0]) raise SearchNotFound()
def doPrivmsg (self,irc,msg): (targets, t) = msg.args text = escape(t) if msg.prefix == irc.prefix: return for channel in targets.split(','): if irc.isChannel(channel) and channel in irc.state.channels: learn = self.registryValue('learn',channel=channel) replyRandom = self.registryValue('replyPercent',channel=channel) > 0.00 replyAddressed = self.registryValue('replyWhenAddressed',channel=channel) called = False if ircdb.checkIgnored(msg.prefix,channel): continue if not learn and not replyRandom and not replyAddressed: continue if replyAddressed and msg.addressed: text = escape(callbacks.addressed(irc.nick,msg)) m = None if learn: m = self.callHailo(channel,'-L',text) else: m = self.callHailo(channel,'-r',text) if m != None and len(m): if self.registryValue('checkSimilarity',channel=channel): if similar(m,text) < self.registryValue('similarity',channel=channel): irc.queueMsg(ircmsgs.privmsg(channel,m)) else: m = self.callHailo(channel,'-R','') if m != None and len(m): irc.queueMsg(ircmsgs.privmsg(channel,m)) else: irc.queueMsg(ircmsgs.privmsg(channel,m)) called = True if not msg.addressed and replyRandom and randint(1,99) < self.registryValue('replyPercent',channel=channel)*100: m = None if learn: m = self.callHailo(channel,'-L',text) else: m = self.callHailo(channel,'-r',text) called = True if m != None and len(m): if self.registryValue('checkSimilarity',channel=channel): if similar(m,text) < self.registryValue('similarity',channel=channel): irc.queueMsg(ircmsgs.privmsg(channel,m)) else: irc.queueMsg(ircmsgs.privmsg(channel,m)) if not called and learn: self.callHailo(channel,'-l',text)
def relay(self, irc, msg, channel=None): channel = (channel or msg.args[0]).lower() self.log.debug("RelayNext (%s): got channel %s", irc.network, channel) if not channel in irc.state.channels: return # Check for ignored events first. Checking for "'.' not in msg.nick" is for skipping # ignore checks from servers. ignoredevents = map(str.upper, self.registryValue('events.userIgnored', channel)) if msg.command in ignoredevents and msg.nick != irc.nick and '.' not in msg.nick and\ ircdb.checkIgnored(msg.prefix, channel): self.log.debug("RelayNext (%s): ignoring message from %s", irc.network, msg.prefix) return # Get the source channel source = "%s@%s" % (channel, irc.network) source = source.lower() out_s = self._format(irc, msg, channel) if out_s: for relay in self.db.values(): self.log.debug("RelayNext (%s): check if %s in %s", irc.network, source, relay) if source in relay: # If our channel is in a relay self.log.debug("RelayNext: found %s to be in relay %s", source, relay) # Remove ourselves from the target channels so we don't get duplicated messages targets = list(relay) targets.remove(source) self.log.debug("RelayNext: found targets %s for relay %s", targets, relay) if self.registryValue("antiflood.enable", channel): # Flood prevention timeout - how long commands of a certain type # should cease being relayed after flood prevention triggers timeout = self.registryValue("antiflood.timeout", channel) # If <maximum> messages of the same kind on one channel is # received in <seconds> seconds, flood prevention timeout is # triggered. maximum = self.registryValue("antiflood.maximum", channel) seconds = self.registryValue("antiflood.seconds", channel) # Store the message in a counter, with the keys taking the # form of (source channel@network, command name). If the counter # doesn't already exist, create one here. try: self.msgcounters[(source, msg.command)].enqueue(msg.prefix) except KeyError: self.msgcounters[( source, msg.command)] = TimeoutQueue(seconds) # Two different limits: one for messages and one for all others if msg.command == "PRIVMSG": maximum = self.registryValue( "antiflood.maximum", channel) else: maximum = self.registryValue( "antiflood.maximum.nonPrivmsgs", channel) if len(self.msgcounters[(source, msg.command)]) > maximum: # Amount of messages in the counter surpassed our limit, # announce the flood and block relaying messages of the # same type for X seconds self.log.debug( "RelayNext (%s): message from %s blocked by " "flood protection.", irc.network, channel) if self.floodTriggered.get((source, msg.command)): # However, only send the announcement once. return c = msg.command e = format( "Flood detected on %s (%s %ss/%s seconds), " "not relaying %ss for %s seconds!", channel, maximum, c, seconds, c, timeout) out_s = self._format(irc, msg, channel, announcement=e) self.floodTriggered[(source, msg.command)] = True self.log.info("RelayNext (%s): %s", irc.network, e) else: self.floodTriggered[(source, msg.command)] = False for cn in targets: # Iterate over all the relay targets for this message: # each target is stored internally as a #channel@netname # string. target, net = cn.split("@") otherIrc = world.getIrc(net) if otherIrc is None: self.log.debug( "RelayNext: message to network %r" " dropped, we are not connected " "there!", net) return target_chanobj = otherIrc.state.channels.get(target) if (not target_chanobj ) or otherIrc.nick not in target_chanobj.users: # We're not in the target relay channel! self.log.debug( "RelayNext: message to %s@%s " "dropped, we are not in that " "channel!", target, net) else: out_msg = ircmsgs.privmsg(target, out_s) # Tag the message as relayed so we (and other relayers) don't # try to relay it again. out_msg.tag('relayedMsg') otherIrc.queueMsg(out_msg)
def relay(self, irc, msg, channel=None): channel = (channel or msg.args[0]).lower() self.log.debug("RelayNext (%s): got channel %s", irc.network, channel) if not channel in irc.state.channels: return # Check for ignored events first. Checking for "'.' not in msg.nick" is for skipping # ignore checks from servers. ignoredevents = map(str.upper, self.registryValue('events.userIgnored', channel)) if msg.command in ignoredevents and msg.nick != irc.nick and '.' not in msg.nick and\ ircdb.checkIgnored(msg.prefix, channel): self.log.debug("RelayNext (%s): ignoring message from %s", irc.network, msg.prefix) return # Get the source channel source = "%s@%s" % (channel, irc.network) source = source.lower() out_s = self._format(irc, msg, channel) if out_s: for relay in self.db.values(): self.log.debug("RelayNext (%s): check if %s in %s", irc.network, source, relay) if source in relay: # If our channel is in a relay self.log.debug("RelayNext: found %s to be in relay %s", source, relay) # Remove ourselves from the target channels so we don't get duplicated messages targets = list(relay) targets.remove(source) self.log.debug("RelayNext: found targets %s for relay %s", targets, relay) if self.registryValue("antiflood.enable", channel): # Flood prevention timeout - how long commands of a certain type # should cease being relayed after flood prevention triggers timeout = self.registryValue("antiflood.timeout", channel) # If <maximum> messages of the same kind on one channel is # received in <seconds> seconds, flood prevention timeout is # triggered. maximum = self.registryValue("antiflood.maximum", channel) seconds = self.registryValue("antiflood.seconds", channel) # Store the message in a counter, with the keys taking the # form of (source channel@network, command name). If the counter # doesn't already exist, create one here. try: self.msgcounters[(source, msg.command)].enqueue(msg.prefix) except KeyError: self.msgcounters[(source, msg.command)] = TimeoutQueue(seconds) # Two different limits: one for messages and one for all others if msg.command == "PRIVMSG": maximum = self.registryValue("antiflood.maximum", channel) else: maximum = self.registryValue("antiflood.maximum.nonPrivmsgs", channel) if len(self.msgcounters[(source, msg.command)]) > maximum: # Amount of messages in the counter surpassed our limit, # announce the flood and block relaying messages of the # same type for X seconds self.log.debug("RelayNext (%s): message from %s blocked by " "flood protection.", irc.network, channel) if self.floodTriggered.get((source, msg.command)): # However, only send the announcement once. return c = msg.command e = format("Flood detected on %s (%s %ss/%s seconds), " "not relaying %ss for %s seconds!", channel, maximum, c, seconds, c, timeout) out_s = self._format(irc, msg, channel, announcement=e) self.floodTriggered[(source, msg.command)] = True self.log.info("RelayNext (%s): %s", irc.network, e) else: self.floodTriggered[(source, msg.command)] = False for cn in targets: # Iterate over all the relay targets for this message: # each target is stored internally as a #channel@netname # string. target, net = cn.split("@") otherIrc = world.getIrc(net) if otherIrc is None: self.log.debug("RelayNext: message to network %r" " dropped, we are not connected " "there!", net) return target_chanobj = otherIrc.state.channels.get(target) if (not target_chanobj) or otherIrc.nick not in target_chanobj.users: # We're not in the target relay channel! self.log.debug("RelayNext: message to %s@%s " "dropped, we are not in that " "channel!", target, net) else: out_msg = ircmsgs.privmsg(target, out_s) # Tag the message as relayed so we (and other relayers) don't # try to relay it again. out_msg.tag('relayedMsg') otherIrc.queueMsg(out_msg)
def replacer(self, irc, msg, regex): if not self.registryValue('enable', msg.args[0]): return iterable = reversed(irc.state.history) msg.tag('Replacer') try: (pattern, replacement, count, flags) = self._unpack_sed(msg.args[1]) except (ValueError, re.error) as e: self.log.warning(_("SedRegex error: %s"), e) if self.registryValue('displayErrors', msg.args[0]): irc.error('%s.%s: %s' % (e.__class__.__module__, e.__class__.__name__, e)) return next(iterable) if 's' in flags: # Special 's' flag lets the bot only look at self messages target = msg.nick else: target = regex.group('nick') if not ircutils.isNick(str(target), strictRfc=True): return for m in iterable: if m.command in ('PRIVMSG', 'NOTICE') and \ m.args[0] == msg.args[0] and m.tagged('receivedBy') == irc: if target and m.nick != target: continue # Don't snarf ignored users' messages unless specifically # told to. if ircdb.checkIgnored(m.prefix) and not target: continue # When running substitutions, ignore the "* nick" part of any actions. action = ircmsgs.isAction(m) if action: text = ircmsgs.unAction(m) else: text = m.args[1] if self.registryValue('ignoreRegex', msg.args[0]) and \ m.tagged('Replacer'): continue if m.nick == msg.nick: messageprefix = msg.nick else: messageprefix = '%s thinks %s' % (msg.nick, m.nick) try: regex_timeout = self.registryValue('processTimeout') if regexp_wrapper(text, pattern, timeout=regex_timeout, plugin_name=self.name(), fcn_name='replacer'): if self.registryValue('boldReplacementText', msg.args[0]): replacement = ircutils.bold(replacement) subst = process(pattern.sub, replacement, text, count, timeout=0.05) if action: # If the message was an ACTION, prepend the nick back. subst = '* %s %s' % (m.nick, subst) subst = axe_spaces(subst) irc.reply(_("%s meant to say: %s") % (messageprefix, subst), prefixNick=False) return except (ValueError, re.error) as e: if self.registryValue('displayErrors', msg.args[0]): irc.error('%s.%s: %s' % (e.__class__.__module__, e.__class__.__name__, e)) return self.log.debug(_("SedRegex: Search %r not found in the last %i messages of %s."), msg.args[1], len(irc.state.history), msg.args[0]) if self.registryValue("displayErrors", msg.args[0]): irc.error(_("Search not found in the last %i messages.") % len(irc.state.history), Raise=True)