Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-limnoria for openSUSE:Factory 
checked in at 2022-07-15 13:52:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-limnoria (Old)
 and      /work/SRC/openSUSE:Factory/.python-limnoria.new.1523 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-limnoria"

Fri Jul 15 13:52:26 2022 rev:26 rq:989193 version:2022.07.03

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-limnoria/python-limnoria.changes  
2022-04-21 15:48:52.932330563 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-limnoria.new.1523/python-limnoria.changes    
    2022-07-15 13:52:43.903568340 +0200
@@ -1,0 +2,7 @@
+Thu Jul 14 13:53:25 UTC 2022 - Atri Bhattacharya <badshah...@gmail.com>
+
+- Update to version 2022-07-03:
+  * PluginDownloader: replace automatic 2to3 step with a simple
+    warning.
+
+-------------------------------------------------------------------

Old:
----
  limnoria-2022.03.19.tar.gz

New:
----
  limnoria-2022.07.03.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-limnoria.spec ++++++
--- /var/tmp/diff_new_pack.Uzum38/_old  2022-07-15 13:52:44.383568496 +0200
+++ /var/tmp/diff_new_pack.Uzum38/_new  2022-07-15 13:52:44.387568497 +0200
@@ -18,9 +18,9 @@
 
 %define skip_python2 1
 %define appname limnoria
-%define srcver 2022-03-19
+%define srcver 2022-07-03
 Name:           python-limnoria
-Version:        2022.03.19
+Version:        2022.07.03
 Release:        0
 Summary:        A modified version of Supybot (an IRC bot and framework)
 License:        BSD-3-Clause
@@ -87,6 +87,7 @@
 %python_clone -a %{buildroot}%{_mandir}/man1/supybot-plugin-doc.1
 %python_clone -a %{buildroot}%{_mandir}/man1/supybot-test.1
 %python_clone -a %{buildroot}%{_mandir}/man1/supybot-wizard.1
+%python_clone -a %{buildroot}%{_mandir}/man1/supybot-reset-password.1
 %python_clone -a %{buildroot}%{_bindir}/supybot
 %python_clone -a %{buildroot}%{_bindir}/supybot-adduser
 %python_clone -a %{buildroot}%{_bindir}/supybot-botchk
@@ -107,7 +108,7 @@
 %{python_install_alternative supybot supybot-adduser supybot-botchk
   supybot-plugin-create supybot-plugin-doc supybot-reset-password supybot-test 
supybot-wizard
   supybot.1 supybot-adduser.1 supybot-botchk.1 supybot-plugin-create.1
-  supybot-plugin-doc.1 supybot-test.1 supybot-wizard.1
+  supybot-plugin-doc.1 supybot-reset-password.1 supybot-test.1 supybot-wizard.1
 }
 
 %postun
@@ -131,6 +132,7 @@
 %python_alternative %{_mandir}/man1/supybot-botchk.1
 %python_alternative %{_mandir}/man1/supybot-plugin-create.1
 %python_alternative %{_mandir}/man1/supybot-plugin-doc.1
+%python_alternative %{_mandir}/man1/supybot-reset-password.1
 %python_alternative %{_mandir}/man1/supybot-test.1
 %python_alternative %{_mandir}/man1/supybot-wizard.1
 

++++++ limnoria-2022.03.19.tar.gz -> limnoria-2022.07.03.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/man/supybot-reset-password.1 
new/Limnoria-master-2022-07-03/man/supybot-reset-password.1
--- old/Limnoria-master-2022-03-19/man/supybot-reset-password.1 1970-01-01 
01:00:00.000000000 +0100
+++ new/Limnoria-master-2022-07-03/man/supybot-reset-password.1 2022-06-23 
22:31:17.000000000 +0200
@@ -0,0 +1,35 @@
+.\" Process this file with
+.\" groff -man -Tascii supybot-reset-password.1
+.\"
+.TH SUPYBOT-RESET-PASSWORD 1 "JUNE 2022"
+.SH NAME
+supybot-reset-password \- Changes a user's password in a Supybot users.conf 
file
+.SH SYNOPSIS
+.B supybot-reset-password
+.RI [ options ] " users.conf
+.SH DESCRIPTION
+.B supybot-reset-password
+changes a user's password in a Supybot users.conf file
+.SH OPTIONS
+.TP
+.B \-\^\-version
+Show version of program.
+.TP
+.BR \-h ", " \-\^\-help
+Show summary of options.
+.TP
+.BR \-u " NAME" "\fR,\fP \-\^\-username=" NAME
+Specifies the username to use for the new user.
+.TP
+.BR \-p " PASSWORD" "\fR,\fP \-\^\-password=" PASSWORD
+Specifies the new password to use for the new user.
+.SH "SEE ALSO"
+.IR python (1),
+.IR supybot (1),
+.IR supybot-adduser (1)
+.SH AUTHOR
+This manual page was originally written by Valentin Lorentz
+<progval plus limnoria at progval dot net>.  Permission is granted to copy,
+distribute and/or modify this document under the terms of the Supybot
+license, a BSD-style license.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/AutoMode/plugin.py 
new/Limnoria-master-2022-07-03/plugins/AutoMode/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/AutoMode/plugin.py   2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/AutoMode/plugin.py   2022-06-23 
22:31:17.000000000 +0200
@@ -144,7 +144,7 @@
                     break
         try:
             do('op')
-            if 'h' in irc.state.supported['prefix']:
+            if 'h' in irc.state.supported.get('prefix', ''):
                 do('halfop')
         except Continue:
             return
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/BadWords/config.py 
new/Limnoria-master-2022-07-03/plugins/BadWords/config.py
--- old/Limnoria-master-2022-03-19/plugins/BadWords/config.py   2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/BadWords/config.py   2022-06-23 
22:31:17.000000000 +0200
@@ -113,6 +113,9 @@
     filtering.  If it's True, however, it will interact poorly with other
     plugins that do coloring or bolding of text.""")))
 
+conf.registerChannelValue(BadWords, 'selfCensor',
+    registry.Boolean(True, _("""Determines whether the bot will filter its own
+    messages.""")))
 conf.registerChannelValue(BadWords, 'kick',
     registry.Boolean(False, _("""Determines whether the bot will kick people 
with
     a warning when they use bad words.""")))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/BadWords/messages.pot 
new/Limnoria-master-2022-07-03/plugins/BadWords/messages.pot
--- old/Limnoria-master-2022-03-19/plugins/BadWords/messages.pot        
2022-03-17 22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/BadWords/messages.pot        
2022-06-23 22:31:17.000000000 +0200
@@ -81,18 +81,24 @@
 
 #: config.py:117
 msgid ""
+"Determines whether the bot will filter its own\n"
+"    messages."
+msgstr ""
+
+#: config.py:120
+msgid ""
 "Determines whether the bot will kick people with\n"
 "    a warning when they use bad words."
 msgstr ""
 
-#: config.py:120
+#: config.py:123
 msgid ""
 "You have been kicked for using a word\n"
 "    prohibited in the presence of this bot.  Please use more appropriate\n"
 "    language in the future."
 msgstr ""
 
-#: config.py:122
+#: config.py:125
 msgid ""
 "Determines the kick message used by the\n"
 "    bot when kicking users for saying bad words."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/BadWords/plugin.py 
new/Limnoria-master-2022-07-03/plugins/BadWords/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/BadWords/plugin.py   2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/BadWords/plugin.py   2022-06-23 
22:31:17.000000000 +0200
@@ -104,9 +104,10 @@
             self.lastModified = time.time()
 
     def outFilter(self, irc, msg):
+        channel = msg.channel
         if self.filtering and msg.command == 'PRIVMSG' \
-                and (self.words() or self.phrases()):
-            channel = msg.channel
+                and (self.words() or self.phrases()) \
+                and self.registryValue('selfCensor', channel, irc.network):
             self.updateRegexp(channel, irc.network)
             s = msg.args[1]
             if self.registryValue('stripFormatting'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/Geography/plugin.py 
new/Limnoria-master-2022-07-03/plugins/Geography/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/Geography/plugin.py  2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/Geography/plugin.py  2022-06-23 
22:31:17.000000000 +0200
@@ -110,6 +110,21 @@
             _("Could not find the timezone of this location."), Raise=True
         )
 
+    def _format_utc_offset(self, offset_seconds):
+        sign = "+" if offset_seconds >= 0 else "-"
+
+        # Make modulos work as expected
+        offset_seconds = abs(offset_seconds)
+
+        (offset_minutes, offset_seconds) = divmod(offset_seconds, 60)
+        (offset_hours, offset_minutes) = divmod(offset_minutes, 60)
+        offset = f"{offset_hours}:{offset_minutes:02}:{offset_seconds:02}"
+
+        # hide seconds and minutes if they are zero
+        offset = re.sub("(:00)+$", "", offset)
+
+        return f"UTC{sign}{offset}"
+
     @wrap(["text"])
     def timezone(self, irc, msg, args, query):
         """<location name to search>
@@ -134,33 +149,28 @@
             if timezone is None:
                 continue
 
-            offset = str(datetime.datetime.now(tz=timezone).utcoffset())
-            if not offset.startswith("-"):
-                offset = "+" + offset
-
-            # hide seconds and minutes if they are zero
-            offset = re.sub("(:00)+$", "", offset)
+            offset_seconds = int(
+                datetime.datetime.now(tz=timezone).utcoffset().total_seconds())
+            offset = self._format_utc_offset(offset_seconds)
 
             # Extract a human-friendly name, depending on the type of
             # the timezone object:
             if hasattr(timezone, "key"):
                 # instance of zoneinfo.ZoneInfo
-                irc.reply(format("%s (currently UTC%s)", timezone.key, offset))
+                irc.reply(format("%s (currently %s)", timezone.key, offset))
                 return
             elif hasattr(timezone, "zone"):
                 # instance of pytz.timezone
-                irc.reply(format("%s (currently UTC%s)", timezone.zone, 
offset))
+                irc.reply(format("%s (currently %s)", timezone.zone, offset))
                 return
             else:
                 # probably datetime.timezone built from a constant offset
                 try:
-                    offset = timezone.utcoffset(now).seconds
+                    offset = int(timezone.utcoffset(now).total_seconds())
                 except NotImplementedError:
                     continue
 
-                hours = int(offset / 3600)
-                minutes = int(offset / 60 % 60)
-                irc.reply("UTC+%0.2i:%0.2i" % (hours, minutes))
+                irc.reply(self._format_utc_offset(offset))
                 return
 
         irc.error(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/plugins/Geography/test.py 
new/Limnoria-master-2022-07-03/plugins/Geography/test.py
--- old/Limnoria-master-2022-03-19/plugins/Geography/test.py    2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/Geography/test.py    2022-06-23 
22:31:17.000000000 +0200
@@ -68,41 +68,85 @@
     @mock
     def testTimezonePytz(self):
         tz = pytz.timezone("Europe/Paris")
-
         with patch.object(wikidata, "timezone_from_uri", return_value=tz):
             self.assertRegexp(
                 "timezone Foo Bar", r"Europe/Paris \(currently UTC\+[12]\)"
             )
 
+        tz = pytz.timezone("America/New_York")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone New York", r"America/New_York \(currently UTC-[45]\)"
+            )
+
+        tz = pytz.timezone("Canada/Newfoundland")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone Newfoundland",
+                r"Canada/Newfoundland \(currently UTC-[23]:30\)"
+            )
+
+        tz = pytz.timezone("Asia/Kolkata")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)"
+            )
+
     @skipIf(not zoneinfo, "Python is older than 3.9")
     @mock
     def testTimezoneZoneinfo(self):
         tz = zoneinfo.ZoneInfo("Europe/Paris")
-
         with patch.object(wikidata, "timezone_from_uri", return_value=tz):
             self.assertRegexp(
                 "timezone Foo Bar", r"Europe/Paris \(currently UTC\+[12]\)"
             )
 
+        tz = zoneinfo.ZoneInfo("America/New_York")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone New York", r"America/New_York \(currently UTC-[45]\)"
+            )
+
+        tz = zoneinfo.ZoneInfo("Canada/Newfoundland")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone Newfoundland",
+                r"Canada/Newfoundland \(currently UTC-[23]:30\)"
+            )
+
+        tz = zoneinfo.ZoneInfo("Asia/Kolkata")
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertRegexp(
+                "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)"
+            )
+
     @skipIf(not zoneinfo, "Python is older than 3.9")
     @mock
     def testTimezoneAbsolute(self):
         tz = datetime.timezone(datetime.timedelta(hours=4))
-
         with patch.object(wikidata, "timezone_from_uri", return_value=tz):
-            self.assertResponse("timezone Foo Bar", "UTC+04:00")
+            self.assertResponse("timezone Foo Bar", "UTC+4")
 
         tz = datetime.timezone(datetime.timedelta(hours=4, minutes=30))
+        with patch.object(wikidata, "timezone_from_uri", return_value=tz):
+            self.assertResponse("timezone Foo Bar", "UTC+4:30")
 
+        tz = datetime.timezone(datetime.timedelta(hours=-4, minutes=30))
         with patch.object(wikidata, "timezone_from_uri", return_value=tz):
-            self.assertResponse("timezone Foo Bar", "UTC+04:30")
+            self.assertResponse("timezone Foo Bar", "UTC-3:30")
 
     @skipIf(not network, "Network test")
     def testTimezoneIntegration(self):
         self.assertRegexp(
             "timezone Metz, France", r"Europe/Paris \(currently UTC\+[12]\)"
         )
-        self.assertResponse("timezone Saint-Denis, La R??union", "UTC+04:00")
+        self.assertResponse("timezone Saint-Denis, La R??union", "UTC+4")
+        self.assertRegexp(
+            "timezone Delhi", r"Asia/Kolkata \(currently UTC\+5:30\)"
+        )
+        self.assertRegexp(
+            "timezone Newfoundland", r"UTC-[23]:30"
+        )
 
 
 class GeographyLocaltimeTestCase(PluginTestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/PluginDownloader/plugin.py 
new/Limnoria-master-2022-07-03/plugins/PluginDownloader/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/PluginDownloader/plugin.py   
2022-03-17 22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/PluginDownloader/plugin.py   
2022-06-23 22:31:17.000000000 +0200
@@ -121,11 +121,11 @@
         assert directory is not None, \
                 'No valid directory in supybot.directories.plugins.'
 
+        possibly_incompatible = False
         try:
             assert archive.getmember(prefix + dirname).isdir(), \
                 'This is not a valid plugin (it is a file, not a directory).'
 
-            run_2to3 = minisix.PY3
             for file in archive.getmembers():
                 if file.name.startswith(prefix + dirname):
                     extractedFile = archive.extractfile(file)
@@ -140,42 +140,18 @@
                         os.mkdir(newFileName)
                     else:
                         with open(newFileName, 'ab') as fd:
-                            reload_imported = False
                             for line in extractedFile.readlines():
-                                if minisix.PY3:
-                                    if b'import reload' in line:
-                                        reload_imported = True
-                                    elif not reload_imported and \
-                                            b'reload(' in line:
-                                        fd.write(b'from importlib import 
reload\n')
-                                        reload_imported = True
+                                if file.name.endswith('__init__.py') and \
+                                        line.startswith((b'import config', 
b'import plugin')):
+                                    possibly_incompatible = True
                                 fd.write(line)
-                    if newFileName.endswith('__init__.py'):
-                        with open(newFileName) as fd:
-                            lines = list(filter(lambda x:'import plugin' in x,
-                                fd.readlines()))
-                            if lines and lines[0].startswith('from . import'):
-                                # This should be already Python 3-compatible
-                                run_2to3 = False
         finally:
             archive.close()
             del archive
-        if run_2to3:
-            try:
-                import lib2to3
-            except ImportError:
-                return _('Plugin is probably not compatible with your '
-                        'Python version (3.x) and could not be converted '
-                        'because 2to3 is not installed.')
-            import subprocess
-            fixers = []
-            subprocess.Popen(['2to3', '-wn', os.path.join(directory, plugin)]) 
\
-                    .wait()
-            return _('Plugin was designed for Python 2, but an attempt to '
-                    'convert it to Python 3 has been made. There is no '
-                    'guarantee it will work, though.')
-        else:
-            return _('Plugin successfully installed.')
+        if possibly_incompatible:
+            return _('Plugin installed. However, it may be incompatible with '
+                     'Python 3 and require manual code changes to work 
correctly.')
+        return _('Plugin successfully installed.')
 
     def getInfo(self, plugin):
         archive = self._download(plugin)
@@ -201,65 +177,11 @@
                                                    'progval',
                                                    'Supybot-plugins'
                                                    ),
-               'quantumlemur':     GithubRepository(
-                                                   'quantumlemur',
-                                                   'Supybot-plugins',
-                                                   ),
-               'stepnem':          GithubRepository(
-                                                   'stepnem',
-                                                   'supybot-plugins',
-                                                   ),
-               'code4lib-snapshot':GithubRepository(
-                                                   'code4lib',
-                                                   'supybot-plugins',
-                                                   'Supybot-plugins-20060723',
-                                                   ),
-               'code4lib-edsu':    GithubRepository(
-                                                   'code4lib',
-                                                   'supybot-plugins',
-                                                   'edsu-plugins',
-                                                   ),
-               'code4lib':         GithubRepository(
-                                                   'code4lib',
-                                                   'supybot-plugins',
-                                                   'plugins',
-                                                   ),
-               'nanotube-bitcoin': GithubRepository(
-                                                   'nanotube',
-                                                   'supybot-bitcoin-'
-                                                             'marketmonitor',
-                                                   ),
-               'mtughan-weather':  GithubRepository(
-                                                   'mtughan',
-                                                   'Supybot-Weather',
-                                                   ),
                'SpiderDave':       GithubRepository(
                                                    'SpiderDave',
                                                    'spidey-supybot-plugins',
                                                    'Plugins',
                                                    ),
-               'doorbot':          GithubRepository(
-                                                   'hacklab',
-                                                   'doorbot',
-                                                   ),
-               'boombot':          GithubRepository(
-                                                   'nod',
-                                                   'boombot',
-                                                   'plugins',
-                                                   ),
-               'mailed-notifier':  GithubRepository(
-                                                   'tbielawa',
-                                                   'supybot-mailed-notifier',
-                                                   ),
-               'pingdom':          GithubRepository(
-                                                   'rynop',
-                                                   'supyPingdom',
-                                                   'plugins',
-                                                   ),
-               'scrum':            GithubRepository(
-                                                   'amscanne',
-                                                   'supybot-scrum',
-                                                   ),
                'Hoaas':            GithubRepository(
                                                    'Hoaas',
                                                    'Supybot-plugins'
@@ -268,23 +190,11 @@
                                                    'nyuszika7h',
                                                    'limnoria-plugins'
                                                    ),
-               'nyuszika7h-old':   GithubRepository(
-                                                   'nyuszika7h',
-                                                   'Supybot-plugins'
-                                                   ),
-               'resistivecorpse':  GithubRepository(
-                                                   'resistivecorpse',
-                                                   'supybot-plugins'
-                                                   ),
                'frumious':         GithubRepository(
                                                    'frumiousbandersnatch',
                                                    'sobrieti-plugins',
                                                    'plugins',
                                                    ),
-               'jonimoose':        GithubRepository(
-                                                   'Jonimoose',
-                                                   'Supybot-plugins',
-                                                   ),
                'skgsergio':        GithubRepository(
                                                    'skgsergio',
                                                    'Limnoria-plugins',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/plugins/PluginDownloader/test.py 
new/Limnoria-master-2022-07-03/plugins/PluginDownloader/test.py
--- old/Limnoria-master-2022-03-19/plugins/PluginDownloader/test.py     
2022-03-17 22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/PluginDownloader/test.py     
2022-06-23 22:31:17.000000000 +0200
@@ -29,11 +29,9 @@
 ###
 
 import os
-import sys
 import shutil
 
 from supybot.test import *
-import supybot.utils.minisix as minisix
 
 pluginsPath = '%s/test-plugins' % os.getcwd()
 
@@ -62,7 +60,7 @@
 
     def testRepolist(self):
         self.assertRegexp('repolist', '(.*, )?progval(, .*)?')
-        self.assertRegexp('repolist', '(.*, )?quantumlemur(, .*)?')
+        self.assertRegexp('repolist', '(.*, )?jlu5(, .*)?')
         self.assertRegexp('repolist progval', '(.*, )?AttackProtector(, .*)?')
 
     def testInstallprogval(self):
@@ -76,44 +74,19 @@
             self.assertRegexp('plugindownloader install progval Darcs',
                     'Error:.*not available.*supybot.commands.allowShell')
 
-    def testInstallQuantumlemur(self):
-        self.assertError('plugindownloader install quantumlemur 
AttackProtector')
-        self.assertNotError('plugindownloader install quantumlemur Listener')
-        self.assertError('plugindownloader install quantumlemur 
AttackProtector')
-        self._testPluginInstalled('Listener')
-
-    def testInstallStepnem(self):
-        self.assertNotError('plugindownloader install stepnem Freenode')
-        self._testPluginInstalled('Freenode')
-
-    def testInstallNanotubeBitcoin(self):
-        self.assertNotError('plugindownloader install nanotube-bitcoin GPG')
-        self._testPluginInstalled('GPG')
-
-    def testInstallMtughanWeather(self):
-        self.assertNotError('plugindownloader install mtughan-weather '
-                            'WunderWeather')
-        self._testPluginInstalled('WunderWeather')
-
-    def testInstallSpiderDave(self):
-        self.assertNotError('plugindownloader install SpiderDave Pastebin')
-        self._testPluginInstalled('Pastebin')
-
     def testInstallNonAsciiInit(self):
         self.assertNotError('plugindownloader install Hoaas DuckDuckGo')
         self._testPluginInstalled('DuckDuckGo')
 
+    def testInstallLegacyWarning(self):
+        self.assertRegexp('plugindownloader install frumious Codepoints',
+                          'may be incompatible')
+
     def testInfo(self):
         self.assertResponse('plugindownloader info progval Twitter',
                 'Advanced Twitter plugin for Supybot, with capabilities '
                 'handling, and per-channel user account.')
 
-    if minisix.PY3:
-        def test_2to3(self):
-            self.assertRegexp('plugindownloader install SpiderDave Pastebin',
-                    'convert')
-            self.assertNotError('load Pastebin')
-
 if not network:
     class PluginDownloaderTestCase(PluginTestCase):
         pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/plugins/Time/plugin.py 
new/Limnoria-master-2022-07-03/plugins/Time/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/Time/plugin.py       2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/Time/plugin.py       2022-06-23 
22:31:17.000000000 +0200
@@ -28,10 +28,11 @@
 # POSSIBILITY OF SUCH DAMAGE.
 ###
 
+import re
 import sys
 import time
 TIME = time # For later use.
-from datetime import datetime
+from datetime import datetime, timedelta, timezone
 
 import supybot.conf as conf
 import supybot.log as log
@@ -194,25 +195,35 @@
     elapsed = wrap(elapsed, ['int'])
 
     @internationalizeDocstring
-    def tztime(self, irc, msg, args, timezone):
+    def tztime(self, irc, msg, args, tz):
         """<region>/<city> (or <region>/<state>/<city>)
 
         Takes a city and its region, and returns its local time. This
         command uses the IANA Time Zone Database."""
-        try:
-            timezone = utils.time.iana_timezone(timezone)
-        except utils.time.UnknownTimeZone:
-            irc.error(_('Unknown timezone'))
-        except utils.time.MissingTimezoneLibrary:
-            irc.error(_(
-                'Timezone-related commands are not available. '
-                'Your administrator need to either upgrade Python to '
-                'version 3.9 or greater, or install pytz.'))
-        except utils.time.TimezoneException as e:
-            irc.error(e.args[0])
+        parsed_utc_tz = re.match(
+                "UTC(?P<hours>[-+][0-9]+)(:(?P<minutes>[0-6][0-9]))?", tz)
+        if parsed_utc_tz:
+            groups = parsed_utc_tz.groupdict()
+            tz = timezone(timedelta(
+                hours=int(groups["hours"]),
+                minutes=int(groups["minutes"] or "00")
+            ))
         else:
-            format = self.registryValue("format", msg.channel, irc.network)
-            irc.reply(datetime.now(timezone).strftime(format))
+            try:
+                tz = utils.time.iana_timezone(tz)
+            except utils.time.UnknownTimeZone:
+                irc.error(_('Unknown timezone'), Raise=True)
+            except utils.time.MissingTimezoneLibrary:
+                irc.error(_(
+                    'Timezone-related commands are not available. '
+                    'Your administrator need to either upgrade Python to '
+                    'version 3.9 or greater, or install pytz.'),
+                    Raise=True)
+            except utils.time.TimezoneException as e:
+                irc.error(e.args[0], Raise=True)
+
+        format = self.registryValue("format", msg.channel, irc.network)
+        irc.reply(datetime.now(tz).strftime(format))
     tztime = wrap(tztime, ['text'])
 
     def ddate(self, irc, msg, args, year=None, month=None, day=None):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/plugins/Time/test.py 
new/Limnoria-master-2022-07-03/plugins/Time/test.py
--- old/Limnoria-master-2022-03-19/plugins/Time/test.py 2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/Time/test.py 2022-06-23 
22:31:17.000000000 +0200
@@ -97,6 +97,8 @@
         self.assertNotError('tztime Europe/Paris')
         self.assertNotError('tztime America/Indiana/Knox')
         self.assertNotError('tztime UTC')
+        self.assertNotError('tztime UTC+10')
+        self.assertNotError('tztime UTC+5:30')
         self.assertError('tztime Europe/Gniarf')
 
     @skipIf(not has_dateutil, 'python-dateutil is missing')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/plugins/Web/plugin.py 
new/Limnoria-master-2022-07-03/plugins/Web/plugin.py
--- old/Limnoria-master-2022-03-19/plugins/Web/plugin.py        2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/plugins/Web/plugin.py        2022-06-23 
22:31:17.000000000 +0200
@@ -151,7 +151,8 @@
         size = conf.supybot.protocols.http.peekSize()
 
         parsed_url = utils.web.urlparse(url)
-        if parsed_url.netloc.endswith(('youtube.com', '.youtube.com')):
+        if parsed_url.netloc == 'youtube.com' \
+                or parsed_url.netloc.endswith(('.youtube.com')):
             # there is a lot of Javascript before the <title>
             size = 409600
         if parsed_url.netloc in ('reddit.com', 'www.reddit.com', 
'new.reddit.com'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/scripts/supybot-plugin-create 
new/Limnoria-master-2022-07-03/scripts/supybot-plugin-create
--- old/Limnoria-master-2022-03-19/scripts/supybot-plugin-create        
2022-03-17 22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/scripts/supybot-plugin-create        
2022-06-23 22:31:17.000000000 +0200
@@ -92,13 +92,10 @@
 
 from supybot import utils, plugins, ircutils, callbacks
 from supybot.commands import *
-try:
-    from supybot.i18n import PluginInternationalization
-    _ = PluginInternationalization('%s')
-except ImportError:
-    # Placeholder that allows to run the plugin on a bot
-    # without the i18n module
-    _ = lambda x: x
+from supybot.i18n import PluginInternationalization
+
+
+_ = PluginInternationalization('%s')
 
 
 class %s(callbacks.Plugin):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Limnoria-master-2022-03-19/scripts/supybot-reset-password 
new/Limnoria-master-2022-07-03/scripts/supybot-reset-password
--- old/Limnoria-master-2022-03-19/scripts/supybot-reset-password       
2022-03-17 22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/scripts/supybot-reset-password       
2022-06-23 22:31:17.000000000 +0200
@@ -51,7 +51,7 @@
                       help='username for the user.')
     parser.add_option('-p', '--password', action='store', default='',
                       dest='password',
-                      help='password for the user.')
+                      help='new password for the user.')
     (options, args) = parser.parse_args()
     if len(args) != 1:
         parser.error('Specify the users.conf file you\'d like to use.  '
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/setup.py 
new/Limnoria-master-2022-07-03/setup.py
--- old/Limnoria-master-2022-03-19/setup.py     2022-03-17 22:29:10.000000000 
+0100
+++ new/Limnoria-master-2022-07-03/setup.py     2022-06-23 22:31:17.000000000 
+0200
@@ -216,6 +216,7 @@
                 ('share/man/man1', ['man/supybot-botchk.1']),
                 ('share/man/man1', ['man/supybot-wizard.1']),
                 ('share/man/man1', ['man/supybot-adduser.1']),
+                ('share/man/man1', ['man/supybot-reset-password.1']),
                 ('share/man/man1', ['man/supybot-plugin-doc.1']),
                 ('share/man/man1', ['man/supybot-plugin-create.1']),
         ],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/__init__.py 
new/Limnoria-master-2022-07-03/src/__init__.py
--- old/Limnoria-master-2022-03-19/src/__init__.py      2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/__init__.py      2022-06-23 
22:31:17.000000000 +0200
@@ -33,10 +33,7 @@
 
 from . import i18n
 
-builtins = (__builtins__ if isinstance(__builtins__, dict) else 
__builtins__.__dict__)
-builtins['supybotInternationalization'] = i18n.PluginInternationalization()
 from . import utils
-del builtins['supybotInternationalization']
 
 (__builtins__ if isinstance(__builtins__, dict) else 
__builtins__.__dict__)['format'] = utils.str.format
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/callbacks.py 
new/Limnoria-master-2022-07-03/src/callbacks.py
--- old/Limnoria-master-2022-03-19/src/callbacks.py     2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/callbacks.py     2022-06-23 
22:31:17.000000000 +0200
@@ -256,6 +256,10 @@
         # no harm in doing this extra check, in case a plugin is replying
         # across network (as it may happen with '@network command').
         ret.server_tags['+draft/reply'] = msg.server_tags['msgid']
+        if msg.channel and not irc.isChannel(ret.args[0]):
+            # If replying in non-channel to a channel message, use the tag
+            # defined in https://github.com/ircv3/ircv3-specifications/pull/498
+            ret.server_tags["+draft/channel-context"] = msg.channel
     return ret
 
 def error(*args, **kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/conf.py 
new/Limnoria-master-2022-07-03/src/conf.py
--- old/Limnoria-master-2022-03-19/src/conf.py  2022-03-17 22:29:10.000000000 
+0100
+++ new/Limnoria-master-2022-07-03/src/conf.py  2022-06-23 22:31:17.000000000 
+0200
@@ -911,6 +911,10 @@
     ValidDriverModule('default', _("""Determines what driver module the 
     bot will use. Current, the only (and default) driver is Socket.""")))
 
+registerGlobalValue(supybot.drivers, 'minReconnectWait',
+    registry.PositiveFloat(10.0, _("""Determines the minimum time the bot will
+    wait before attempting to reconnect to an IRC server.""")))
+
 registerGlobalValue(supybot.drivers, 'maxReconnectWait',
     registry.PositiveFloat(300.0, _("""Determines the maximum time the bot will
     wait before attempting to reconnect to an IRC server.  The bot may, of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/drivers/Socket.py 
new/Limnoria-master-2022-07-03/src/drivers/Socket.py
--- old/Limnoria-master-2022-03-19/src/drivers/Socket.py        2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/drivers/Socket.py        2022-06-23 
22:31:17.000000000 +0200
@@ -92,7 +92,7 @@
         return ret
 
     def resetDelay(self):
-        self.currentDelay = 10.0
+        self.currentDelay = conf.supybot.drivers.minReconnectWait()
 
     def _getNextServer(self):
         oldServer = getattr(self, 'currentServer', None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/i18n.py 
new/Limnoria-master-2022-07-03/src/i18n.py
--- old/Limnoria-master-2022-03-19/src/i18n.py  2022-03-17 22:29:10.000000000 
+0100
+++ new/Limnoria-master-2022-07-03/src/i18n.py  2022-06-23 22:31:17.000000000 
+0200
@@ -43,9 +43,11 @@
 IN_MSGID = 2
 WAITING_FOR_MSGSTR = 3
 IN_MSGSTR = 4
+NEXT_IS_FUZZY = 5
 
 MSGID = 'msgid "'
 MSGSTR = 'msgstr "'
+FUZZY = '#, fuzzy'
 
 currentLocale = 'en'
 
@@ -68,10 +70,12 @@
     """Imports the conf into this module"""
     global conf
     conf = __import__('supybot.conf').conf
+    class Languages(conf.registry.OnlySomeStrings):
+        validStrings = ['de', 'en', 'es', 'fi', 'fr', 'it']
     conf.registerGlobalValue(conf.supybot, 'language',
-        conf.registry.String(currentLocale, """Determines the bot's default
-        language if translations exist. Currently supported are 'de', 'en',
-        'es', 'fi', 'fr' and 'it'."""))
+        Languages(currentLocale, """Determines the bot's default
+        language if translations exist. Currently supported are: %s"""
+        % ', '.join(Languages.validStrings)))
     conf.supybot.language.addCallback(reloadLocalesIfRequired)
 
 def getPluginDir(plugin_name):
@@ -111,7 +115,6 @@
 
 i18nClasses = weakref.WeakValueDictionary()
 internationalizedCommands = weakref.WeakValueDictionary()
-internationalizedFunctions = [] # No need to know their name
 
 def reloadLocalesIfRequired():
     global currentLocale
@@ -122,12 +125,13 @@
         reloadLocales()
 
 def reloadLocales():
+    import supybot.utils as utils
+
     for pluginClass in i18nClasses.values():
         pluginClass.loadLocale()
     for command in list(internationalizedCommands.values()):
         internationalizeDocstring(command)
-    for function in internationalizedFunctions:
-        function.loadLocale()
+    utils.str._relocalizeFunctions(PluginInternationalization())
 
 def normalize(string, removeNewline=False):
     import supybot.utils as utils
@@ -148,7 +152,13 @@
         line = line[0:-1] # Remove the ending \n
         line = line
 
-        if line.startswith(MSGID):
+        if step == WAITING_FOR_MSGID and line.startswith(FUZZY):
+            step = NEXT_IS_FUZZY
+        elif step == NEXT_IS_FUZZY and line.startswith(MSGID):
+            # Don't use fuzzy strings; they may have a mismatched number of %s 
or be
+            # outright wrong; use English instead.
+            step = WAITING_FOR_MSGID
+        elif line.startswith(MSGID):
             # Don't check if step is WAITING_FOR_MSGID
             untranslated = ''
             translated = ''
@@ -193,7 +203,6 @@
     return translations
 
 
-i18nSupybot = None
 def PluginInternationalization(name='supybot'):
     # This is a proxy that prevents having several objects for the same plugin
     if name in i18nClasses:
@@ -318,40 +327,6 @@
                 name in self._l10nFunctions:
             return self._l10nFunctions[name]
 
-    def internationalizeFunction(self, name):
-        """Decorates functions and internationalize their code.
-
-        Only useful for Supybot core functions"""
-        if self.name != 'supybot':
-            return
-        class FunctionInternationalizer:
-            def __init__(self, parent, name):
-                self._parent = parent
-                self._name = name
-            def __call__(self, obj):
-                obj = InternationalizedFunction(self._parent, self._name, obj)
-                obj.loadLocale()
-                return obj
-        return FunctionInternationalizer(self, name)
-
-class InternationalizedFunction:
-    """Proxy for functions that need to be fully localized.
-
-    The localization code is in locales/LOCALE.py"""
-    def __init__(self, internationalizer, name, function):
-        self._internationalizer = internationalizer
-        self._name = name
-        self._origin = function
-        internationalizedFunctions.append(self)
-    def loadLocale(self):
-        self.__call__ = self._internationalizer.localizeFunction(self._name)
-        if self.__call__ == None:
-            self.restore()
-    def restore(self):
-        self.__call__ = self._origin
-
-    def __call__(self, *args, **kwargs):
-        return self._origin(*args, **kwargs)
 
 try:
     class InternationalizedString(str):
@@ -367,6 +342,7 @@
         know if a string is already localized"""
         pass
 
+
 def internationalizeDocstring(obj):
     """Decorates functions and internationalize their docstring.
 
@@ -383,3 +359,13 @@
             # attribute '__doc__' of 'type' objects is not writable
             pass
     return obj
+
+
+def _install():
+    from . import utils
+    _ = PluginInternationalization()
+    utils.gen._ = _
+    utils.str._ = _
+    utils.str._relocalizeFunctions(_)
+
+_install()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/irclib.py 
new/Limnoria-master-2022-07-03/src/irclib.py
--- old/Limnoria-master-2022-03-19/src/irclib.py        2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/irclib.py        2022-06-23 
22:31:17.000000000 +0200
@@ -1809,9 +1809,13 @@
 
         `msg` is the message that triggered this call."""
         self.state.fsm.expect_state([
-            # Normal CAP ACK / CAP NAK during cap negotiation
+            # Normal CAP ACK / CAP NAK during cap negotiation:
             IrcStateFsm.States.INIT_CAP_NEGOTIATION,
-            # CAP ACK / CAP NAK after a CAP NEW (probably)
+            # Sigyn sends CAP REQ when it sees RPL_SASLSUCCESS, so we get the
+            # CAP ACK while waiting for MOTD on some IRCds (eg. InspIRCd):
+            IrcStateFsm.States.INIT_WAITING_MOTD,
+            IrcStateFsm.States.INIT_MOTD,
+            # CAP ACK / CAP NAK after a CAP NEW (probably):
             IrcStateFsm.States.CONNECTED,
         ])
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/setup.py 
new/Limnoria-master-2022-07-03/src/setup.py
--- old/Limnoria-master-2022-03-19/src/setup.py 2022-03-17 22:29:10.000000000 
+0100
+++ new/Limnoria-master-2022-07-03/src/setup.py 2022-06-23 22:31:17.000000000 
+0200
@@ -79,7 +79,14 @@
                     break
 
         module_name = kwargs['name'].replace('-', '_')
-        kwargs.setdefault('packages', [module_name])
+
+        if 'packages' not in kwargs:
+            kwargs["packages"] = [module_name] + [
+                "%s.%s" % (module_name, package_name.replace('-', '_'))
+                for package_name
+                in setuptools.find_packages(where=".")
+            ]
+
         kwargs.setdefault('package_dir', {module_name: '.'})
         kwargs.setdefault('entry_points', {
             'limnoria.plugins': '%s = %s' % (capitalized_name, module_name)})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/utils/__init__.py 
new/Limnoria-master-2022-07-03/src/utils/__init__.py
--- old/Limnoria-master-2022-03-19/src/utils/__init__.py        2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/utils/__init__.py        2022-06-23 
22:31:17.000000000 +0200
@@ -50,6 +50,7 @@
 
 builtins = (__builtins__ if isinstance(__builtins__, dict) else 
__builtins__.__dict__)
 
+
 # We use this often enough that we're going to stick it in builtins.
 def force(x):
     if callable(x):
@@ -58,8 +59,6 @@
         return x
 builtins['force'] = force
 
-internationalization = builtins.get('supybotInternationalization', None)
-
 # These imports need to happen below the block above, so things get put into
 # __builtins__ appropriately.
 from .gen import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/utils/gen.py 
new/Limnoria-master-2022-07-03/src/utils/gen.py
--- old/Limnoria-master-2022-03-19/src/utils/gen.py     2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/utils/gen.py     2022-06-23 
22:31:17.000000000 +0200
@@ -45,7 +45,9 @@
 from .str import format
 from .file import mktemp
 from . import minisix
-from . import internationalization as _
+
+# will be replaced by supybot.i18n.install()
+_ = lambda x: x
 
 def warn_non_constant_time(f):
     @functools.wraps(f)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/src/utils/str.py 
new/Limnoria-master-2022-07-03/src/utils/str.py
--- old/Limnoria-master-2022-03-19/src/utils/str.py     2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/src/utils/str.py     2022-06-23 
22:31:17.000000000 +0200
@@ -38,20 +38,46 @@
 import time
 import string
 import textwrap
+import functools
 
 from . import minisix
 from .iter import any
 from .structures import TwoWayDictionary
 
-from . import internationalization as _
-internationalizeFunction = _.internationalizeFunction
-
 try:
     from charade.universaldetector import UniversalDetector
     charadeLoaded = True
 except ImportError:
     charadeLoaded = False
 
+# will be replaced by supybot.i18n.install()
+_ = lambda x: x
+
+# used by supybot.i18n.reloadLocales() to (re)load the localized function of
+# these functions
+_localizedFunctions = {}
+_defaultFunctions = {}
+
+
+def internationalizeFunction(f):
+    f_name = f.__name__
+    _localizedFunctions[f_name] = f
+    _defaultFunctions[f_name] = f
+
+    @functools.wraps(f)
+    def newf(*args, **kwargs):
+        f = _localizedFunctions[f_name]
+        assert f is not None, "_localizedFunctions[%s] is None" % f_name
+        return f(*args, **kwargs)
+
+    return newf
+
+
+def _relocalizeFunctions(localizer):
+    for f_name in list(_localizedFunctions):
+        f = localizer.localizeFunction(f_name) or _defaultFunctions[f_name]
+        _localizedFunctions[f_name] = f
+
 if minisix.PY3:
     def decode_raw_line(line):
         #first, try to decode using utf-8
@@ -390,7 +416,7 @@
                 L[i] = L[i].upper()
         return ''.join(L)
 
-@internationalizeFunction('pluralize')
+@internationalizeFunction
 def pluralize(s):
     """Returns the plural of s.  Put any exceptions to the general English
     rule of appending 's' in the plurals dictionary.
@@ -413,7 +439,7 @@
     else:
         return matchCase(s, s+'s')
 
-@internationalizeFunction('depluralize')
+@internationalizeFunction
 def depluralize(s):
     """Returns the singular of s."""
     consonants = 'bcdfghjklmnpqrstvwxz'
@@ -467,7 +493,7 @@
         else:
             return format('%s %s %s', n, between, item)
 
-@internationalizeFunction('ordinal')
+@internationalizeFunction
 def ordinal(i):
     """Returns i + the ordinal indicator for the number.
 
@@ -486,7 +512,7 @@
         ord = 'rd'
     return '%s%s' % (i, ord)
 
-@internationalizeFunction('be')
+@internationalizeFunction
 def be(i):
     """Returns the form of the verb 'to be' based on the number i."""
     if i == 1:
@@ -494,7 +520,7 @@
     else:
         return 'are'
 
-@internationalizeFunction('has')
+@internationalizeFunction
 def has(i):
     """Returns the form of the verb 'to have' based on the number i."""
     if i == 1:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Limnoria-master-2022-03-19/test/test_callbacks.py 
new/Limnoria-master-2022-07-03/test/test_callbacks.py
--- old/Limnoria-master-2022-03-19/test/test_callbacks.py       2022-03-17 
22:29:10.000000000 +0100
+++ new/Limnoria-master-2022-07-03/test/test_callbacks.py       2022-06-23 
22:31:17.000000000 +0200
@@ -634,6 +634,51 @@
             finally:
                 self.irc.state.capabilities_ack.remove('message-tags')
 
+    def testClientTagReplyChannel(self):
+        self.irc.addCallback(self.First(self.irc))
+
+        try:
+            conf.supybot.protocols.irc.experimentalExtensions.setValue(True)
+            self.irc.state.capabilities_ack.add('message-tags')
+
+            # Reply in channel to channel message -> +draft/channel-context
+            # is absent
+            self.irc.feedMsg(ircmsgs.IrcMsg(
+                command='PRIVMSG', prefix=self.prefix,
+                args=('#foo', '%s: firstcmd' % self.nick),
+                server_tags={'msgid': 'foobar'}))
+            msg = self.irc.takeMsg()
+            self.assertEqual(msg, ircmsgs.IrcMsg(
+                command='PRIVMSG', args=('#foo', '%s: foo' % self.nick),
+                server_tags={'+draft/reply': 'foobar'}))
+
+            # Reply in private to channel message -> +draft/channel-context
+            # is present
+            with conf.supybot.reply.inPrivate.context(True):
+                self.irc.feedMsg(ircmsgs.IrcMsg(
+                    command='PRIVMSG', prefix=self.prefix,
+                    args=('#foo', '%s: firstcmd' % self.nick),
+                    server_tags={'msgid': 'foobar'}))
+                msg = self.irc.takeMsg()
+                self.assertEqual(msg, ircmsgs.IrcMsg(
+                    command='NOTICE', args=(self.nick, 'foo'),
+                    server_tags={'+draft/reply': 'foobar',
+                                 '+draft/channel-context': '#foo'}))
+
+            # Reply in private to private message -> +draft/channel-context
+            # is absent
+            self.irc.feedMsg(ircmsgs.IrcMsg(
+                command='PRIVMSG', prefix=self.prefix,
+                args=(self.nick, 'firstcmd'),
+                server_tags={'msgid': 'foobar'}))
+            msg = self.irc.takeMsg()
+            self.assertEqual(msg, ircmsgs.IrcMsg(
+                command='NOTICE', args=(self.nick, 'foo'),
+                server_tags={'+draft/reply': 'foobar'}))
+        finally:
+            conf.supybot.protocols.irc.experimentalExtensions.setValue(False)
+            self.irc.state.capabilities_ack.remove('message-tags')
+
     class TwoRepliesFirstAction(callbacks.Plugin):
         def testactionreply(self, irc, msg, args):
             irc.reply('foo', action=True)

Reply via email to