From 1499141f091204af1d61379b64a55dcaba314467 Mon Sep 17 00:00:00 2001 From: James Lu Date: Fri, 6 May 2016 21:19:28 -0700 Subject: [PATCH] Import SedRegex plugin as of https://github.com/jlu5/SupyPlugins/commit/2a556a1b84db3e634754846760ec5b4fb1c0545a Co-authored-by: Michael Daniel Telatynski Co-authored-by: nyuszika7h --- plugins/SedRegex/README.md | 1 + plugins/SedRegex/__init__.py | 61 +++++++++++ plugins/SedRegex/config.py | 60 +++++++++++ plugins/SedRegex/local/__init__.py | 1 + plugins/SedRegex/plugin.py | 163 +++++++++++++++++++++++++++++ plugins/SedRegex/test.py | 36 +++++++ 6 files changed, 322 insertions(+) create mode 100644 plugins/SedRegex/README.md create mode 100644 plugins/SedRegex/__init__.py create mode 100644 plugins/SedRegex/config.py create mode 100644 plugins/SedRegex/local/__init__.py create mode 100644 plugins/SedRegex/plugin.py create mode 100644 plugins/SedRegex/test.py diff --git a/plugins/SedRegex/README.md b/plugins/SedRegex/README.md new file mode 100644 index 000000000..5127f0f52 --- /dev/null +++ b/plugins/SedRegex/README.md @@ -0,0 +1 @@ +History replacer using sed-style expressions. diff --git a/plugins/SedRegex/__init__.py b/plugins/SedRegex/__init__.py new file mode 100644 index 000000000..25b4daa63 --- /dev/null +++ b/plugins/SedRegex/__init__.py @@ -0,0 +1,61 @@ +### +# Copyright (c) 2015, Michael Daniel Telatynski +# Copyright (c) 2015-2016, James Lu +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +""" +History replacer using sed-style expressions. +""" + +import supybot +import supybot.world as world + +__version__ = "2016.02.28.1+git" +__author__ = supybot.Author("Michael Daniel Telatynski", "t3chguy", "postmaster@webdevguru.co.uk") +__contributors__ = {supybot.Author("James Lu", "GLolol", "glolol@overdrivenetworks.com"): + ["options bolding the replacement text", "misc. bug fixes and enhancements"], + supybot.Author('nyuszika7h', 'nyuszika7h', 'nyuszika7h@openmailbox.org'): + ["_unpack_sed method within plugin.py"] + } +__url__ = 'https://github.com/GLolol/SupyPlugins' + +from . import config +from . import plugin +from imp import reload + +reload(config) +reload(plugin) + +if world.testing: + from . import test + +Class = plugin.Class +configure = config.configure + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugins/SedRegex/config.py b/plugins/SedRegex/config.py new file mode 100644 index 000000000..0a64864c7 --- /dev/null +++ b/plugins/SedRegex/config.py @@ -0,0 +1,60 @@ +### +# Copyright (c) 2015, Michael Daniel Telatynski +# Copyright (c) 2015, James Lu +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +import supybot.conf as conf +import supybot.registry as registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('SedRegex') +except: + _ = lambda x: x + +def configure(advanced): + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('SedRegex', True) + if advanced: + output("""The SedRegex plugin allows you to make Perl/sed-style regex + replacements to your chat history.""") + +SedRegex = conf.registerPlugin('SedRegex') + +conf.registerChannelValue(SedRegex, 'displayErrors', + registry.Boolean(True, _("""Should errors be displayed?"""))) +conf.registerChannelValue(SedRegex, 'boldReplacementText', + registry.Boolean(True, _("""Should the replacement text be bolded?"""))) +conf.registerChannelValue(SedRegex, 'enable', + registry.Boolean(False, _("""Should Perl/sed-style regex replacing + work in this channel?"""))) +conf.registerChannelValue(SedRegex, 'ignoreRegex', + registry.Boolean(True, _("""Should Perl/sed regex replacing + ignore messages which look like valid regex?"""))) + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugins/SedRegex/local/__init__.py b/plugins/SedRegex/local/__init__.py new file mode 100644 index 000000000..e86e97b86 --- /dev/null +++ b/plugins/SedRegex/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/plugins/SedRegex/plugin.py b/plugins/SedRegex/plugin.py new file mode 100644 index 000000000..acd7bb67c --- /dev/null +++ b/plugins/SedRegex/plugin.py @@ -0,0 +1,163 @@ +### +# Copyright (c) 2015, Michael Daniel Telatynski +# Copyright (c) 2015-2016, James Lu +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot.commands import * +import supybot.plugins as plugins +import supybot.ircmsgs as ircmsgs +import supybot.callbacks as callbacks +import supybot.ircutils as ircutils +import supybot.ircdb as ircdb + +import re + +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('SedRegex') +except ImportError: + _ = lambda x: x + + +SED_REGEX = re.compile(r"^(?:(?P.+?)[:,] )?s(?P[^\w\s])(?P.*?)(?P=delim)" + r"(?P.*?)(?:(?P=delim)(?P[gi]*))?$") + +class SedRegex(callbacks.PluginRegexp): + """History replacer using sed-style regex syntax.""" + threaded = True + public = True + unaddressedRegexps = ['replacer'] + + @staticmethod + def _unpack_sed(expr): + if '\0' in expr: + raise ValueError('Expression can\'t contain NUL') + + delim = expr[1] + escaped_expr = '' + + for (i, c) in enumerate(expr): + if c == delim and i > 0: + if expr[i - 1] == '\\': + escaped_expr = escaped_expr[:-1] + '\0' + continue + + escaped_expr += c + + match = SED_REGEX.search(escaped_expr) + + if not match: + return + + groups = match.groupdict() + pattern = groups['pattern'].replace('\0', delim) + replacement = groups['replacement'].replace('\0', delim) + + if groups['flags']: + raw_flags = set(groups['flags']) + else: + raw_flags = set() + + flags = 0 + count = 1 + + for flag in raw_flags: + if flag == 'g': + count = 0 + if flag == 'i': + flags |= re.IGNORECASE + + pattern = re.compile(pattern, flags) + + return (pattern, replacement, count) + + 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(_("SedRegex error: %s"), e) + if self.registryValue('displayErrors', msg.args[0]): + irc.error(_("SedRegex 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(_("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) + replacer.__doc__ = SED_REGEX.pattern + +Class = SedRegex + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/SedRegex/test.py b/plugins/SedRegex/test.py new file mode 100644 index 000000000..0655e0453 --- /dev/null +++ b/plugins/SedRegex/test.py @@ -0,0 +1,36 @@ +### +# Copyright (c) 2015, Michael Daniel Telatynski +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot.test import * + +class SedRegexTestCase(PluginTestCase): + plugins = ('SedRegex',) + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: