diff --git a/NoTrigger/README.md b/NoTrigger/README.md index b0c0cf5..39b2102 100644 --- a/NoTrigger/README.md +++ b/NoTrigger/README.md @@ -1,12 +1,16 @@ -NoTrigger is an anti-abuse script that modifies outFilter to prevent triggering other bots. +NoTrigger is an anti-abuse plugin that modifies outFilter to prevent triggering other bots. ## Short description In short, NoTrigger works by: - - Prepending messages that start with a symbol (```!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~```) with a [zero width space](https://en.wikipedia.org/wiki/Zero-width_space) (ZWSP), since these are often used as prefixes for bots. This has the effect of being completely invisible, and tricks most bots into ignoring yours! + - Prepending messages that start with a symbol (```!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~```) or any configured prefixes with a [zero width space](https://en.wikipedia.org/wiki/Zero-width_space) (ZWSP), since these are often used as prefixes for bots. This has the effect of being completely invisible, and tricks most bots into ignoring yours! - Prepending messages with a ZWSP if the channel is set to block colors and a message begins with a formatting code (sneaky attackers can otherwise do something like `\x02!echo hello` to bypass filters). - Optionally, prepending messages with a ZWSP if they match `: ` or `, `, since some bots are taught to respond to their nicks. - Optionally, blocking all channel-wide CTCPs (except for ACTION). + - Optionally, stripping the bell character from any outgoing messages. + - Optionally, appending messages that end with any configured suffixes with a ZWSP. + +To enable NoTrigger, set the `plugins.NoTrigger.enable` config option `True` for the channels in question. You can find a list of NoTrigger's options (toggling the things mentioned above) by running `config list plugins.NoTrigger`. ## Longer description/Backstory on why I wrote this Sometimes when you have a public development channel with many bots residing in it, someone will come along and do something really evil: that is, create a huge message loop by chaining all your innocent bots together! diff --git a/NoTrigger/config.py b/NoTrigger/config.py index 3bebd50..f688e94 100644 --- a/NoTrigger/config.py +++ b/NoTrigger/config.py @@ -28,6 +28,8 @@ ### +import string + import supybot.conf as conf import supybot.registry as registry try: @@ -38,7 +40,6 @@ except: # without the i18n module _ = lambda x: x - def configure(advanced): # This will be called by supybot to configure this module. advanced is # a bool that specifies whether the user identified himself as an advanced @@ -52,22 +53,32 @@ NoTrigger = conf.registerPlugin('NoTrigger') conf.registerChannelValue(NoTrigger, 'enable', registry.Boolean(True, _("""Enable protection against triggering other bots."""))) conf.registerChannelValue(NoTrigger, 'spaceBeforeNicks', - registry.Boolean(False, _("""Add a space before messages beginning with + registry.Boolean(False, _("""Add a space before messages beginning with "blah: " or "blah, ", preventing the bot from triggering other bots that - respond to nick. This can cause some weird spacings with error messages and - other command replies, so I wouldn't recommend enabling it unless absolutely - necessary."""))) + respond to nick."""))) + conf.registerChannelValue(NoTrigger, 'colorAware', - registry.Boolean(True, _("""Toggles whether the bot should be aware of colour-stripping - modes. (+c or +S on most IRCds)"""))) -conf.registerGroup(NoTrigger, 'colorAware') + registry.Boolean(True, _("""Toggles whether the bot should be aware of color-stripping + channel modes (+c or +S on most IRCds)."""))) conf.registerChannelValue(NoTrigger.colorAware, 'modes', - registry.SpaceSeparatedListOfStrings("c S", _("""Determines a list of modes that should - be treated as colour-blocking modes. This is usually +c (block) and +S (stripcolour) on - UnrealIRCd/InspIRCd, and just +c (stripcolour) on charybdis-based daemons."""))) + registry.SpaceSeparatedListOfStrings("c S", _("""Defines a space-separated list of modes that should + be treated as color-blocking modes. This is usually +c (block) and +S (stripcolour) on + UnrealIRCd/InspIRCd, and just +c (stripcolor) on charybdis-based daemons."""))) + conf.registerChannelValue(NoTrigger, 'blockCtcp', - registry.Boolean(False, _("""Determines whether the bot should block all CTCPs (\001 codes) + registry.Boolean(False, _("""Determines whether the bot should block all outbound channel CTCPs except CTCP actions. If you are using the Ctcp plugin, you will want to turn this off."""))) +conf.registerChannelValue(NoTrigger, 'prefixes', + registry.SpaceSeparatedListOfStrings(' '.join(string.punctuation), + _("""Defines a space-separated list of prefix triggers the bot should ignore."""))) + +conf.registerChannelValue(NoTrigger, 'suffixes', + registry.SpaceSeparatedListOfStrings('', + _("""Defines a space-separated list of suffix triggers the bot should ignore."""))) + +conf.registerChannelValue(NoTrigger, 'blockBell', + registry.Boolean(True, + _("""Determines whether the bot should strip bell characters (\x07) from any messages it sends."""))) # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/NoTrigger/plugin.py b/NoTrigger/plugin.py index 2d0f68e..7866580 100644 --- a/NoTrigger/plugin.py +++ b/NoTrigger/plugin.py @@ -46,13 +46,13 @@ except ImportError: class NoTrigger(callbacks.Plugin): - """Mods outFilter to prevent the bot from triggering other bots.""" + """Modifies outFilter to prevent the bot from triggering other bots.""" def __init__(self, irc): self.__parent = super(NoTrigger, self) self.__parent.__init__(irc) - # This uses Unicode Character 'ZERO WIDTH SPACE' (U+200B) for - # padding, which looks nicer (it's invisible) and does the trick. + # This appends the Unicode character 'ZERO WIDTH SPACE' (U+200B) for + # which is absolutely invisible and stops bots from being triggered by us. if version_info[0] >= 3: self.padchar = "\u200B" else: @@ -60,6 +60,8 @@ class NoTrigger(callbacks.Plugin): self.padchar = u('\u200B')[0] def isChanStripColor(self, irc, channel): + """Returns whether the given channel has a color-stripping mode (usually + +c or +S, but configurable) set.""" try: c = irc.state.channels[channel] for item in self.registryValue('colorAware.modes'): @@ -74,9 +76,8 @@ class NoTrigger(callbacks.Plugin): ircutils.isChannel(msg.args[0]) and \ self.registryValue('enable', msg.args[0]): s = msg.args[1] - prefixes = string.punctuation - rpairs = {"\007": ""} - suffixes = ("moo") + prefixes = self.registryValue('prefixes', msg.args[0]) + suffixes = self.registryValue('suffixes', msg.args[0]) if self.registryValue('colorAware') and \ self.isChanStripColor(irc, msg.args[0]) and \ s.startswith(("\003", "\002", "\017", "\037", "\026")): @@ -105,11 +106,13 @@ class NoTrigger(callbacks.Plugin): "CTCP due to config " "plugins.notrigger.blockCtcp.", msg.args[0], irc.network) - for k, v in rpairs.items(): - s = s.replace(k, v) + if self.registryValue('blockBell', msg.args[0]): + self.log.debug("NoTrigger (%s/%s): removing bell character" + "from outgoing message.", msg.args[0], irc.network) + s = s.replace('\x07', '') if s.startswith(tuple(prefixes)): s = self.padchar + s - if s.endswith(suffixes): + if s.endswith(tuple(suffixes)): s += self.padchar msg = ircmsgs.privmsg(msg.args[0], s, msg=msg) return msg diff --git a/NoTrigger/test.py b/NoTrigger/test.py index e006721..0833661 100644 --- a/NoTrigger/test.py +++ b/NoTrigger/test.py @@ -30,7 +30,6 @@ from supybot.test import * - class NoTriggerTestCase(ChannelPluginTestCase): plugins = ('NoTrigger', 'Utilities', 'Reply') config = {'supybot.plugins.notrigger.enable': True, @@ -39,13 +38,30 @@ class NoTriggerTestCase(ChannelPluginTestCase): def testSpaceBeforePrefixes(self): self.assertNotRegexp('echo !test', '^!test$') + self.assertNotRegexp('echo $test', '^\$test$') def testSpaceBeforeNicks(self): self.assertNotRegexp('echo example: hello', '^example: hello$') self.assertNotRegexp('echo user1, hello', '^user1, hello$') - def testCTCPBlocking(self): + def testBlockCTCP(self): self.assertResponse('echo \x01PING abcd\x01', 'PING abcd') self.assertAction('reply action jumps around', 'jumps around') + def testBlockBell(self): + self.assertResponse('echo \x07', '') + self.assertResponse('echo evil bell char\x07', 'evil bell char') + + def testConfigurablePrefixes(self): + with conf.supybot.plugins.notrigger.prefixes.context("moo !"): + self.assertNotRegexp('echo moo test', '^moo test$') + self.assertNotRegexp('echo !test', '^!test$') + self.assertResponse('echo .test', '.test') + + def testConfigurableSuffixes(self): + with conf.supybot.plugins.notrigger.suffixes.context("abcd +"): + self.assertNotRegexp('echo moo test+', '^moo test+$') + self.assertNotRegexp('echo 1234 abcd', '^!1234 abcd$') + self.assertResponse('echo test-', 'test-') + # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: