Python 3.9 introduced the zoneinfo module, which provides the only
feature we used pytz for (getting a datetime.tzinfo object from
an IANA timezone id); so let's use it instead of a third-party
dependency.
This commit reverts db7ef3f025
(though it keeps the year updates)
After discussion with several people, it seems better to mention
copyright owners explicitly. eg. https://reuse.software/faq/#vcs-copyright
explains the issue of using VCSs to track copyright.
As db7ef3f025 only replaced mentions
of my name with 'The Limnoria Contributors', this commit only needs
to undo that + add one person who contributed to setup.py.
When the target is a channel it is not in, it would treat it as a user;
which, if userCapabilityRequired is empty, would unconditionally send it
to a channel.
This would usually result in an error, that would be logged,
hence the loop.
Caused by 'rss announce add' triggering headline announces, that would
delay the execution of the 'remove' commands.
Thanks to @mapreri and @Unit193 for help in reproducing the issue
and confirming the patch.
This is because the recommended method ('owner ircquote nickserv register mypassword bot@example.com')
does not work on charybdis, as Limnoria inserts a colon
before the trailing argument and Charybdis' m_alias module
does not parse commands using the IRC syntax, so it
considers the leading colon to be part of the email address.
The alternative would be to change the recommended command to:
'owner ircquote PRIVMSG nickserv :register mypassword bot@example.com'
but it is prone to typos, so I think we should avoid it.
feedparser should always catch the error, but someone reported it doesn't
catch this error on TLS cert issues:
```
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/supybot/plugins/RSS/plugin.py", line 86, in newf
f(*args, **kwargs)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/supybot/plugins/RSS/plugin.py", line 351, in update_feeds
self.update_feed_if_needed(feed)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/supybot/plugins/RSS/plugin.py", line 337, in update_feed_if_needed
self.update_feed(feed)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/supybot/plugins/RSS/plugin.py", line 311, in update_feed
d = feedparser.parse(feed.url, etag=feed.etag,
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/feedparser/api.py", line 214, in parse
data = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers, result)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/feedparser/api.py", line 114, in _open_resource
return http.get(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers, result)
File "/home/falso/virtualenv/limnoria/lib/python3.8/site-packages/feedparser/http.py", line 158, in get
f = opener.open(request)
File "/usr/lib/python3.8/urllib/request.py", line 525, in open
response = self._open(req, data)
File "/usr/lib/python3.8/urllib/request.py", line 542, in _open
result = self._call_chain(self.handle_open, protocol, protocol +
File "/usr/lib/python3.8/urllib/request.py", line 502, in _call_chain
result = func(*args)
File "/usr/lib/python3.8/urllib/request.py", line 1393, in https_open
return self.do_open(http.client.HTTPSConnection, req,
File "/usr/lib/python3.8/urllib/request.py", line 1354, in do_open
r = h.getresponse()
File "/usr/lib/python3.8/http/client.py", line 1347, in getresponse
response.begin()
File "/usr/lib/python3.8/http/client.py", line 307, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.8/http/client.py", line 268, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "/usr/lib/python3.8/socket.py", line 669, in readinto
return self._sock.recv_into(b)
File "/usr/lib/python3.8/ssl.py", line 1241, in recv_into
return self.read(nbytes, buffer)
File "/usr/lib/python3.8/ssl.py", line 1099, in read
return self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out
```
So let's catch the error just in case, so it doesn't block all other
feeds.
It mistakenly used the bot's nick as target when the message is in private,
so 'more' after a private message always answered the user did not send
a command before (because said command was attributed to the bot)
The default behavior was to announce feeds on all channels with the same name,
which is rarely what was expected.
Instead, this limits it to the current network.
Until now, only `waitingJoins` was stored separately per network, while
`channels`, `sentGhost` and `identified` had one common value per plugin
instance. Instead of making everything a dictionary indexed by network
name like `waitingJoins`, let's bundle all the state together in a class
and store *its* instances in such a dictionary.
This fixes at least one race condition, for which a test case was added.
Even with `noJoinsUntilIdentified` set, the bot would let joins through
as long as *any* one network has already finished identifying.
I didn't observe any error with the current set of tests but adding
another one that used "services password" caused oen of these tests
to fail. Given that tests shouldn't leave traces in global state,
let's reset the configured passwords in finally blocks.
by moving the 'smart' reply() method from NestedCommandsIrcProxy
to ReplyIrcProxy.
There is no reason only commands should have a paginated output
and not snarfers defined in PluginRegexp.
Starting with this commit, there should be no "original" text
in READMEs.
Instead, the entire text should be in docstrings, and READMEs
should be automatically generated from these docstrings.
Motivation:
* The same help is available both via IRC and in the README
(although the README can be more detailed, as only the first
paragraph will be shown on IRC)
* This will allow auto-generating plugin help on docs.limnoria.net
using the same content as the README, as it's sourced from the
docstrings
Additionally, this converts all READMEs from Markdown to ReST,
because the documentation already uses ReST, and other docstrings
in the codebase are in ReST for this reason.
Many people are renaming their default branches from master to main or
something else (I just renamed mine). Using HEAD works no matter what
the default branch is named.
Sometimes (very rarely), the test errors with:
```
======================================================================
FAIL: testHistsearch (Aka.test.AkaChannelTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/supybot/test.py", line 214, in runTest
originalRunTest()
File "/usr/lib/python3/dist-packages/supybot/plugins/Aka/test.py", line 67, in testHistsearch
self.assertResponse('histsearch .*', '@echo foo')
File "/usr/lib/python3/dist-packages/supybot/test.py", line 402, in assertResponse
self.assertEqual(m.args[1], expectedResponse,
AssertionError: '@aka add histsearch "last --from [cif true[80 chars]"]]"' != '@echo foo'
- @aka add histsearch "last --from [cif true \"echo test\" \"echo test\"] --regexp [concat \"m/$1/\" [re s/g// \"@2\"]]"
+ @echo foo
: '@echo foo' != '@aka add histsearch "last --from [cif true \\"echo test\\" \\"echo test\\"] --regexp [concat \\"m/$1/\\" [re s/g// \\"@2\\"]]"'
```
ie. histsearch returns the 'aka add' message instead of the 'echo foo'
one.
My guess is that if they happen too closely to each other, they get
added to the history in the wrong order (how? it's not threaded)
So this commit works around the issue by making the regexp unambiguous.