diff -Nru limnoria-2023.8.10/debian/changelog limnoria-2023.9.24/debian/changelog --- limnoria-2023.8.10/debian/changelog 2023-08-12 06:00:58.000000000 +0000 +++ limnoria-2023.9.24/debian/changelog 2023-09-27 05:56:50.000000000 +0000 @@ -1,8 +1,21 @@ -limnoria (2023.8.10-1~bpo22.04.1) jammy-backports; urgency=medium +limnoria (2023.9.24-1~bpo22.04.1) jammy-backports; urgency=medium * No-change backport to jammy. - -- Unit 193 Sat, 12 Aug 2023 02:00:58 -0400 + -- Unit 193 Wed, 27 Sep 2023 01:56:50 -0400 + +limnoria (2023.9.24-1) unstable; urgency=medium + + * New upstream version 2023.9.24. + - Drop upstream patch. + + -- Unit 193 Tue, 26 Sep 2023 02:13:18 -0400 + +limnoria (2023.8.10-2) unstable; urgency=medium + + * Add patch from upstream to fix cleanup after tests. Closes: #1046558 + + -- Mattia Rizzolo Tue, 05 Sep 2023 11:00:57 +0530 limnoria (2023.8.10-1) unstable; urgency=medium diff -Nru limnoria-2023.8.10/debian/rules limnoria-2023.9.24/debian/rules --- limnoria-2023.8.10/debian/rules 2023-07-10 23:32:02.000000000 +0000 +++ limnoria-2023.9.24/debian/rules 2023-09-26 06:08:51.000000000 +0000 @@ -13,6 +13,7 @@ python{version} {build_dir}/supybot/scripts/limnoria_test.py \ test \ -v \ + --clean-after \ --no-setuid \ --no-network \ --plugins-dir={build_dir}/supybot/plugins/" \ diff -Nru limnoria-2023.8.10/limnoria.egg-info/PKG-INFO limnoria-2023.9.24/limnoria.egg-info/PKG-INFO --- limnoria-2023.8.10/limnoria.egg-info/PKG-INFO 2023-08-10 18:00:04.000000000 +0000 +++ limnoria-2023.9.24/limnoria.egg-info/PKG-INFO 2023-09-24 18:07:20.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: limnoria -Version: 2023.8.10 +Version: 2023.9.24 Summary: A multipurpose Python IRC bot, designed for flexibility and robustness , while being easy to install, set up, and maintain. Home-page: https://limnoria.net/ Download-URL: https://pypi.python.org/pypi/limnoria diff -Nru limnoria-2023.8.10/PKG-INFO limnoria-2023.9.24/PKG-INFO --- limnoria-2023.8.10/PKG-INFO 2023-08-10 18:00:05.071384400 +0000 +++ limnoria-2023.9.24/PKG-INFO 2023-09-24 18:07:21.031319600 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: limnoria -Version: 2023.8.10 +Version: 2023.9.24 Summary: A multipurpose Python IRC bot, designed for flexibility and robustness , while being easy to install, set up, and maintain. Home-page: https://limnoria.net/ Download-URL: https://pypi.python.org/pypi/limnoria diff -Nru limnoria-2023.8.10/plugins/MessageParser/plugin.py limnoria-2023.9.24/plugins/MessageParser/plugin.py --- limnoria-2023.8.10/plugins/MessageParser/plugin.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/MessageParser/plugin.py 2023-09-24 17:43:59.000000000 +0000 @@ -192,20 +192,23 @@ return max_triggers = self.registryValue('maxTriggers', channel, irc.network) for (channel, regexp, action) in results: - for match in re.finditer(regexp, msg.args[1]): - if match is not None: - thisaction = action - self._updateRank(irc.network, channel, regexp) - for (i, j) in enumerate(match.groups()): - if match.group(i+1) is not None: - # Need a lambda to prevent re.sub from - # interpreting backslashes in the replacement - thisaction = re.sub(r'\$' + str(i+1), lambda _: match.group(i+1), thisaction) - actions.append((regexp, thisaction)) - if max_triggers != 0 and max_triggers == len(actions): - break - if max_triggers != 0 and max_triggers == len(actions): - break + try: + for match in re.finditer(regexp, msg.args[1]): + if match is not None: + thisaction = action + self._updateRank(irc.network, channel, regexp) + for (i, j) in enumerate(match.groups()): + if match.group(i+1) is not None: + # Need a lambda to prevent re.sub from + # interpreting backslashes in the replacement + thisaction = re.sub(r'\$' + str(i+1), lambda _: match.group(i+1), thisaction) + actions.append((regexp, thisaction)) + if max_triggers != 0 and max_triggers == len(actions): + break + if max_triggers != 0 and max_triggers == len(actions): + break + except Exception: + self.log.exception('Error while handling %r', regexp) for (regexp, action) in actions: diff -Nru limnoria-2023.8.10/plugins/Network/plugin.py limnoria-2023.9.24/plugins/Network/plugin.py --- limnoria-2023.8.10/plugins/Network/plugin.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/Network/plugin.py 2023-09-24 17:43:59.000000000 +0000 @@ -306,6 +306,17 @@ irc.reply(format("%L", sorted(otherIrc.state.capabilities_ls))) capabilities = wrap(capabilities, ['networkIrc']) + def authenticate(self, irc, msg, args): + """takes no arguments + + Manually initiate SASL authentication. + """ + if 'sasl' in irc.state.capabilities_ack: + irc.startSasl(msg) + irc.replySuccess() + else: + irc.error(_('SASL not supported')) + authenticate = wrap(authenticate) Class = Network diff -Nru limnoria-2023.8.10/plugins/PluginDownloader/plugin.py limnoria-2023.9.24/plugins/PluginDownloader/plugin.py --- limnoria-2023.8.10/plugins/PluginDownloader/plugin.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/PluginDownloader/plugin.py 2023-09-24 18:07:10.000000000 +0000 @@ -227,6 +227,10 @@ 'oddluck', 'limnoria-plugins', ), + 'appas': GithubRepository( + 'matiasw', + 'my-limnoria-plugins', + ), }) class PluginDownloader(callbacks.Plugin): diff -Nru limnoria-2023.8.10/plugins/SedRegex/config.py limnoria-2023.9.24/plugins/SedRegex/config.py --- limnoria-2023.8.10/plugins/SedRegex/config.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/SedRegex/config.py 2023-09-24 17:43:59.000000000 +0000 @@ -57,6 +57,17 @@ conf.registerChannelValue(SedRegex, 'ignoreRegex', registry.Boolean(True, _("""Should Perl/sed regex replacing ignore messages which look like valid regex?"""))) +conf.registerChannelValue(SedRegex, 'format', + registry.String(_('$nick meant to say: $replacement'), _("""Sets the format + string for a message edited by the original + author. Required fields: $nick (nick of the + author), $replacement (edited message)"""))) +conf.registerChannelValue(SedRegex.format, 'other', + registry.String(_('$otherNick thinks $nick meant to say: $replacement'), _(""" + Sets the format string for a message edited by + another author. Required fields: $nick (nick + of the original author), $otherNick (nick of + the editor), $replacement (edited message)"""))) conf.registerGlobalValue(SedRegex, 'processTimeout', registry.PositiveFloat(0.5, _("""Sets the timeout when processing a single regexp. The default should be adequate unless diff -Nru limnoria-2023.8.10/plugins/SedRegex/plugin.py limnoria-2023.9.24/plugins/SedRegex/plugin.py --- limnoria-2023.8.10/plugins/SedRegex/plugin.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/SedRegex/plugin.py 2023-09-24 17:43:59.000000000 +0000 @@ -222,10 +222,6 @@ 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) @@ -239,8 +235,15 @@ subst = axe_spaces(subst) - return _("%s meant to say: %s") % \ - (messageprefix, subst) + if m.nick == msg.nick: + fmt = self.registryValue('format', msg.channel, irc.network) + env = {'replacement': subst} + else: + fmt = self.registryValue('format.other', msg.channel, irc.network) + env = {'otherNick': msg.nick, 'replacement': subst} + + return ircutils.standardSubstitute(irc, m, fmt, env) + except Exception as e: self.log.warning(_("SedRegex error: %s"), e, exc_info=True) raise diff -Nru limnoria-2023.8.10/plugins/SedRegex/test.py limnoria-2023.9.24/plugins/SedRegex/test.py --- limnoria-2023.8.10/plugins/SedRegex/test.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/SedRegex/test.py 2023-09-24 17:43:59.000000000 +0000 @@ -279,6 +279,23 @@ with conf.supybot.protocols.irc.strictRfc.context(True): self.assertSnarfNoResponse('%s: s/123/321/' % ircutils.nickFromHostmask(frm), frm=self.__class__.other2) + def testFmtString(self): + fmt = "<$nick>: $replacement" + with conf.supybot.plugins.sedregex.format.context(fmt): + self.feedMsg('frog') + self.feedMsg('s/frog/frogged/') + m = self.getMsg(' ') + self.assertIn('<%s>: frogged' % self.nick, str(m)) + + def testFmtStringOtherPerson(self): + fmt = "(edited by $otherNick) <$nick>: $replacement" + with conf.supybot.plugins.sedregex.format.other.context(fmt): + self.feedMsg('frog', frm=self.__class__.other) + self.feedMsg('s/frog/frogged/', frm=self.__class__.other2) + m = self.getMsg(' ') + self.assertIn('(edited by %s) <%s>: frogged' % (ircutils.nickFromHostmask(self.__class__.other2), + ircutils.nickFromHostmask(self.__class__.other)), str(m)) + # TODO: test ignores # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff -Nru limnoria-2023.8.10/plugins/Services/plugin.py limnoria-2023.9.24/plugins/Services/plugin.py --- limnoria-2023.8.10/plugins/Services/plugin.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/plugins/Services/plugin.py 2023-09-24 17:43:59.000000000 +0000 @@ -37,6 +37,7 @@ import supybot.conf as conf import supybot.utils as utils from supybot.commands import * +import supybot.irclib as irclib import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks @@ -372,6 +373,30 @@ self.log.info('Received notice from NickServ %s: %q.', on, ircutils.stripFormatting(msg.args[1])) + def do903(self, irc, msg): # RPL_SASLSUCCESS + if self.disabled(irc): + return + state = self._getState(irc) + state.identified = True + for channel in irc.state.channels.keys(): + self.checkPrivileges(irc, channel) + if irc.state.fsm in [irclib.IrcStateFsm.States.CONNECTED, + irclib.IrcStateFsm.States.CONNECTED_SASL]: + for channel in state.channels: + irc.queueMsg(networkGroup.channels.join(channel)) + waitingJoins = state.waitingJoins + state.waitingJoins = [] + for join in waitingJoins: + irc.sendMsg(join) + + do907 = do903 # ERR_SASLALREADY, just to be sure we didn't miss it + + def do901(self, irc, msg): # RPL_LOGGEDOUT + if self.disabled(irc): + return + state = self._getState(irc) + state.identified = False + def checkPrivileges(self, irc, channel): if self.disabled(irc): return diff -Nru limnoria-2023.8.10/src/irclib.py limnoria-2023.9.24/src/irclib.py --- limnoria-2023.8.10/src/irclib.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/src/irclib.py 2023-09-24 17:43:59.000000000 +0000 @@ -559,14 +559,14 @@ self.States.UNINITIALIZED, ]) - def on_sasl_cap(self, irc, msg): - '''Whenever we see the 'sasl' capability in a CAP LS response''' + def on_sasl_start(self, irc, msg): + '''Whenever we initiate a SASL transaction.''' if self.state == self.States.INIT_CAP_NEGOTIATION: self._transition(irc, msg, self.States.INIT_SASL) elif self.state == self.States.CONNECTED: self._transition(irc, msg, self.States.CONNECTED_SASL) else: - raise ValueError('Got sasl cap while in state %s' % self.state) + raise ValueError('Started SASL while in state %s' % self.state) def on_sasl_auth_finished(self, irc, msg): '''When sasl auth either succeeded or failed.''' @@ -1729,7 +1729,6 @@ self.authenticate_decoder = None self.sasl_next_mechanisms = [] self.sasl_current_mechanism = None - for mechanism in network_config.sasl.mechanisms(): if mechanism == 'ecdsa-nist256p-challenge': if not crypto: @@ -1767,17 +1766,13 @@ else: self.sasl_next_mechanisms.append(mechanism) - if self.sasl_next_mechanisms: - self.REQUEST_CAPABILITIES.add('sasl') - - # Note: echo-message is only requested if labeled-response is available. REQUEST_CAPABILITIES = set(['account-notify', 'extended-join', 'multi-prefix', 'metadata-notify', 'account-tag', 'userhost-in-names', 'invite-notify', 'server-time', 'chghost', 'batch', 'away-notify', 'message-tags', 'msgid', 'setname', 'labeled-response', 'echo-message', - 'standard-replies']) + 'sasl', 'standard-replies']) """IRCv3 capabilities requested when they are available. echo-message is special-cased to be requested only with labeled-response. @@ -1901,17 +1896,21 @@ def _maybeStartSasl(self, msg): if not self.sasl_authenticated and \ 'sasl' in self.state.capabilities_ack: - self.state.fsm.on_sasl_cap(self, msg) - assert 'sasl' in self.state.capabilities_ls, ( - 'Got "CAP ACK sasl" without receiving "CAP LS sasl" or ' - '"CAP NEW sasl" first.') - s = self.state.capabilities_ls['sasl'] - if s is not None: - available = set(map(str.lower, s.split(','))) - self.sasl_next_mechanisms = [ - x for x in self.sasl_next_mechanisms - if x.lower() in available] - self.tryNextSaslMechanism(msg) + self.startSasl(msg) + + def startSasl(self, msg): + self.state.fsm.on_sasl_start(self, msg) + assert 'sasl' in self.state.capabilities_ls, ( + 'Starting SASL without receiving "CAP LS sasl" or ' + '"CAP NEW sasl" first.') + self.resetSasl() + s = self.state.capabilities_ls['sasl'] + if s is not None: + available = set(map(str.lower, s.split(','))) + self.sasl_next_mechanisms = [ + x for x in self.sasl_next_mechanisms + if x.lower() in available] + self.tryNextSaslMechanism(msg) def doAuthenticate(self, msg): self.state.fsm.expect_state([ diff -Nru limnoria-2023.8.10/src/scripts/limnoria_test.py limnoria-2023.9.24/src/scripts/limnoria_test.py --- limnoria-2023.8.10/src/scripts/limnoria_test.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/src/scripts/limnoria_test.py 2023-09-24 17:43:59.000000000 +0000 @@ -49,8 +49,9 @@ registryFilename = os.path.join('test-conf', 'test.conf') fd = open(registryFilename, 'w') fd.write(""" -supybot.directories.data: %(base_dir)s/test-data +supybot.directories.backup: /dev/null supybot.directories.conf: %(base_dir)s/test-conf +supybot.directories.data: %(base_dir)s/test-data supybot.directories.log: %(base_dir)s/test-logs supybot.reply.whenNotCommand: True supybot.log.stdout: False @@ -131,6 +132,9 @@ parser.add_option('-c', '--clean', action='store_true', default=False, dest='clean', help='Cleans the various data/conf/logs' 'directories before running tests.') + parser.add_option('--clean-after', action='store_true', default=False, + dest='clean_after', help='Cleans the various data/conf/logs' + 'directories after running tests.') parser.add_option('-t', '--timeout', action='store', type='float', dest='timeout', help='Sets the timeout, in seconds, for tests to return ' @@ -238,6 +242,12 @@ if hasattr(unittest, 'asserts'): print('Total asserts: %s' % unittest.asserts) + if options.clean_after: + log.setLevel(100) # don't log anything anymore + shutil.rmtree(conf.supybot.directories.log()) + shutil.rmtree(conf.supybot.directories.conf()) + shutil.rmtree(conf.supybot.directories.data()) + if result.wasSuccessful(): sys.exit(0) else: diff -Nru limnoria-2023.8.10/src/version.py limnoria-2023.9.24/src/version.py --- limnoria-2023.8.10/src/version.py 2023-08-10 18:00:04.000000000 +0000 +++ limnoria-2023.9.24/src/version.py 2023-09-24 18:07:20.000000000 +0000 @@ -1,4 +1,4 @@ -version = '2023.08.10' +version = '2023.09.24' try: # For import from setup.py import supybot.utils.python supybot.utils.python._debug_software_version = version diff -Nru limnoria-2023.8.10/test/test_irclib.py limnoria-2023.9.24/test/test_irclib.py --- limnoria-2023.8.10/test/test_irclib.py 2023-08-10 17:59:59.000000000 +0000 +++ limnoria-2023.9.24/test/test_irclib.py 2023-09-24 17:43:59.000000000 +0000 @@ -1494,26 +1494,41 @@ class SaslTestCase(SupyTestCase, CapNegMixin): def setUp(self): - pass + self._default_mechanisms = conf.supybot.networks.test.sasl.mechanisms() + + def tearDown(self): + conf.supybot.networks.test.sasl.mechanisms.setValue(self._default_mechanisms) + conf.supybot.networks.test.sasl.username.setValue('') + conf.supybot.networks.test.sasl.password.setValue('') + conf.supybot.networks.test.certfile.setValue('') def testPlain(self): - try: - conf.supybot.networks.test.sasl.username.setValue('jilles') - conf.supybot.networks.test.sasl.password.setValue('sesame') - self.irc = irclib.Irc('test') - finally: - conf.supybot.networks.test.sasl.username.setValue('') - conf.supybot.networks.test.sasl.password.setValue('') + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + conf.supybot.networks.test.sasl.mechanisms.setValue( + ['scram-sha-256', 'plain']) + + self.irc = irclib.Irc('test') + self.assertEqual(self.irc.sasl_current_mechanism, None) - self.irc.sasl_next_mechanisms = ['scram-sha-256', 'plain'] - self.startCapNegociation() + if irclib.scram: + self.assertEqual(self.irc.sasl_next_mechanisms, + ['scram-sha-256', 'plain']) + + self.startCapNegociation() + + m = self.irc.takeMsg() + self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE', + args=('SCRAM-SHA-256',))) + self.irc.feedMsg(ircmsgs.IrcMsg(command='904', + args=('mechanism not available',))) + else: + self.assertEqual(self.irc.sasl_next_mechanisms, + ['plain']) + + self.startCapNegociation() - m = self.irc.takeMsg() - self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE', - args=('SCRAM-SHA-256',))) - self.irc.feedMsg(ircmsgs.IrcMsg(command='904', - args=('mechanism not available',))) m = self.irc.takeMsg() self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE', @@ -1531,17 +1546,17 @@ self.endCapNegociation() def testExternalFallbackToPlain(self): - try: - conf.supybot.networks.test.sasl.username.setValue('jilles') - conf.supybot.networks.test.sasl.password.setValue('sesame') - conf.supybot.networks.test.certfile.setValue('foo') - self.irc = irclib.Irc('test') - finally: - conf.supybot.networks.test.sasl.username.setValue('') - conf.supybot.networks.test.sasl.password.setValue('') - conf.supybot.networks.test.certfile.setValue('') + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + conf.supybot.networks.test.certfile.setValue('foo') + conf.supybot.networks.test.sasl.mechanisms.setValue( + ['external', 'plain']) + + self.irc = irclib.Irc('test') + self.assertEqual(self.irc.sasl_current_mechanism, None) - self.irc.sasl_next_mechanisms = ['external', 'plain'] + self.assertEqual(self.irc.sasl_next_mechanisms, + ['external', 'plain']) self.startCapNegociation() @@ -1567,17 +1582,17 @@ self.endCapNegociation() def testFilter(self): - try: - conf.supybot.networks.test.sasl.username.setValue('jilles') - conf.supybot.networks.test.sasl.password.setValue('sesame') - conf.supybot.networks.test.certfile.setValue('foo') - self.irc = irclib.Irc('test') - finally: - conf.supybot.networks.test.sasl.username.setValue('') - conf.supybot.networks.test.sasl.password.setValue('') - conf.supybot.networks.test.certfile.setValue('') + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + conf.supybot.networks.test.certfile.setValue('foo') + conf.supybot.networks.test.sasl.mechanisms.setValue( + ['external', 'plain']) + + self.irc = irclib.Irc('test') + self.assertEqual(self.irc.sasl_current_mechanism, None) - self.irc.sasl_next_mechanisms = ['external', 'plain'] + self.assertEqual(self.irc.sasl_next_mechanisms, + ['external', 'plain']) self.startCapNegociation(caps='sasl=foo,plain,bar') @@ -1597,15 +1612,16 @@ self.endCapNegociation() def testReauthenticate(self): - try: - conf.supybot.networks.test.sasl.username.setValue('jilles') - conf.supybot.networks.test.sasl.password.setValue('sesame') - self.irc = irclib.Irc('test') - finally: - conf.supybot.networks.test.sasl.username.setValue('') - conf.supybot.networks.test.sasl.password.setValue('') + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + conf.supybot.networks.test.sasl.mechanisms.setValue( + ['external', 'plain']) + + self.irc = irclib.Irc('test') + self.assertEqual(self.irc.sasl_current_mechanism, None) - self.irc.sasl_next_mechanisms = ['plain'] + self.assertEqual(self.irc.sasl_next_mechanisms, + ['plain']) self.startCapNegociation(caps='') @@ -1622,16 +1638,12 @@ self.irc.takeMsg() # None. But even if it was CAP REQ sasl, it would be ok self.assertEqual(self.irc.takeMsg(), None) - try: - conf.supybot.networks.test.sasl.username.setValue('jilles') - conf.supybot.networks.test.sasl.password.setValue('sesame') - self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', - args=('*', 'DEL', 'sasl'))) - self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', - args=('*', 'NEW', 'sasl=PLAIN'))) - finally: - conf.supybot.networks.test.sasl.username.setValue('') - conf.supybot.networks.test.sasl.password.setValue('') + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', + args=('*', 'DEL', 'sasl'))) + self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', + args=('*', 'NEW', 'sasl=PLAIN'))) m = self.irc.takeMsg() self.assertEqual(m.command, 'CAP', 'Expected CAP, got %r.' % m)