From 0eade4697bd76b9cc99e74777531ba00df558ff5 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 10:45:24 +0100 Subject: Add unit test for mentions antispam rule --- tests/bot/rules/test_mentions.py | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/bot/rules/test_mentions.py diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py new file mode 100644 index 000000000..520184c2f --- /dev/null +++ b/tests/bot/rules/test_mentions.py @@ -0,0 +1,98 @@ +import unittest +from typing import List, NamedTuple, Tuple + +from bot.rules import mentions +from tests.helpers import async_test + + +class FakeMessage(NamedTuple): + author: str + mentions: List[None] + + +class Case(NamedTuple): + recent_messages: List[FakeMessage] + relevant_messages: Tuple[FakeMessage] + culprit: str + total_mentions: int + + +def msg(author: str, total_mentions: int) -> FakeMessage: + return FakeMessage(author=author, mentions=[None] * total_mentions) + + +class TestMentions(unittest.TestCase): + """Tests applying the `mentions` antispam rule.""" + + def setUp(self): + self.config = { + "max": 2, + "interval": 10 + } + + @async_test + async def test_mentions_within_limit(self): + """Messages with an allowed amount of mentions.""" + cases = ( + [msg("bob", 0)], + [msg("bob", 2)], + [msg("bob", 1), msg("bob", 1)], + [msg("bob", 1), msg("alice", 2)] + ) + + for recent_messages in cases: + last_message = recent_messages[0] + + with self.subTest( + last_message=last_message, + recent_messages=recent_messages, + config=self.config + ): + self.assertIsNone( + await mentions.apply(last_message, recent_messages, self.config) + ) + + @async_test + async def test_mentions_exceeding_limit(self): + """Messages with a higher than allowed amount of mentions.""" + cases = ( + Case( + [msg("bob", 3)], + (msg("bob", 3),), + ("bob",), + 3 + ), + Case( + [msg("alice", 2), msg("alice", 0), msg("alice", 1)], + (msg("alice", 2), msg("alice", 0), msg("alice", 1)), + ("alice",), + 3 + ), + Case( + [msg("bob", 2), msg("alice", 3), msg("bob", 2)], + (msg("bob", 2), msg("bob", 2)), + ("bob",), + 4 + ) + ) + + for recent_messages, relevant_messages, culprit, total_mentions in cases: + last_message = recent_messages[0] + + with self.subTest( + last_message=last_message, + recent_messages=recent_messages, + relevant_messages=relevant_messages, + culprit=culprit, + total_mentions=total_mentions, + cofig=self.config + ): + desired_output = ( + f"sent {total_mentions} mentions in {self.config['interval']}s", + culprit, + relevant_messages + ) + self.assertTupleEqual( + await mentions.apply(last_message, recent_messages, self.config), + desired_output + ) -- cgit v1.2.3 From 187d419810759992e19a792fd746f26960f3831a Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 10:46:34 +0100 Subject: Add missing docstring --- tests/bot/rules/test_mentions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py index 520184c2f..987a42c0a 100644 --- a/tests/bot/rules/test_mentions.py +++ b/tests/bot/rules/test_mentions.py @@ -18,6 +18,7 @@ class Case(NamedTuple): def msg(author: str, total_mentions: int) -> FakeMessage: + """Makes a message with `total_mentions` mentions.""" return FakeMessage(author=author, mentions=[None] * total_mentions) -- cgit v1.2.3 From 32caead77e4bad04689892a29005faa3ffde2e83 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 10:50:54 +0100 Subject: Adjust docstring asterisk to backtick for consistency --- tests/bot/rules/test_links.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/rules/test_links.py b/tests/bot/rules/test_links.py index be832843b..40336beb0 100644 --- a/tests/bot/rules/test_links.py +++ b/tests/bot/rules/test_links.py @@ -18,7 +18,7 @@ class Case(NamedTuple): def msg(author: str, total_links: int) -> FakeMessage: - """Makes a message with *total_links* links.""" + """Makes a message with `total_links` links.""" content = " ".join(["https://pydis.com"] * total_links) return FakeMessage(author=author, content=content) -- cgit v1.2.3 From b87a98c7749edfeb9fbc368f2bf9a7ecb9434662 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 10:54:26 +0100 Subject: Use range to build mock mentions list --- tests/bot/rules/test_mentions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py index 987a42c0a..e1a971dbb 100644 --- a/tests/bot/rules/test_mentions.py +++ b/tests/bot/rules/test_mentions.py @@ -19,7 +19,7 @@ class Case(NamedTuple): def msg(author: str, total_mentions: int) -> FakeMessage: """Makes a message with `total_mentions` mentions.""" - return FakeMessage(author=author, mentions=[None] * total_mentions) + return FakeMessage(author=author, mentions=list(range(total_mentions))) class TestMentions(unittest.TestCase): -- cgit v1.2.3 From b1c6b4e20578395a5766ac116fd4def1df777de7 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 13:49:55 +0100 Subject: Adjust type hint to correctly represent internal type --- tests/bot/rules/test_mentions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py index e1a971dbb..08dd1d6d5 100644 --- a/tests/bot/rules/test_mentions.py +++ b/tests/bot/rules/test_mentions.py @@ -7,7 +7,7 @@ from tests.helpers import async_test class FakeMessage(NamedTuple): author: str - mentions: List[None] + mentions: List[int] class Case(NamedTuple): -- cgit v1.2.3 From f915782192fd7b631b23fa31e524cacdc8e72614 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 16:25:23 +0100 Subject: Use MockMessage instead of custom FakeMessage --- tests/bot/rules/test_mentions.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py index 08dd1d6d5..e377f2164 100644 --- a/tests/bot/rules/test_mentions.py +++ b/tests/bot/rules/test_mentions.py @@ -2,24 +2,18 @@ import unittest from typing import List, NamedTuple, Tuple from bot.rules import mentions -from tests.helpers import async_test - - -class FakeMessage(NamedTuple): - author: str - mentions: List[int] +from tests.helpers import MockMessage, async_test class Case(NamedTuple): - recent_messages: List[FakeMessage] - relevant_messages: Tuple[FakeMessage] - culprit: str + recent_messages: List[MockMessage, ...] + culprit: Tuple[str] total_mentions: int -def msg(author: str, total_mentions: int) -> FakeMessage: +def msg(author: str, total_mentions: int) -> MockMessage: """Makes a message with `total_mentions` mentions.""" - return FakeMessage(author=author, mentions=list(range(total_mentions))) + return MockMessage(author=author, mentions=list(range(total_mentions))) class TestMentions(unittest.TestCase): @@ -59,26 +53,28 @@ class TestMentions(unittest.TestCase): cases = ( Case( [msg("bob", 3)], - (msg("bob", 3),), ("bob",), 3 ), Case( [msg("alice", 2), msg("alice", 0), msg("alice", 1)], - (msg("alice", 2), msg("alice", 0), msg("alice", 1)), ("alice",), 3 ), Case( [msg("bob", 2), msg("alice", 3), msg("bob", 2)], - (msg("bob", 2), msg("bob", 2)), ("bob",), 4 ) ) - for recent_messages, relevant_messages, culprit, total_mentions in cases: + for recent_messages, culprit, total_mentions in cases: last_message = recent_messages[0] + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) with self.subTest( last_message=last_message, -- cgit v1.2.3 From 16e6c36c6b370300ef7507d2e01421ad0d83f407 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Sat, 9 Nov 2019 16:27:02 +0100 Subject: Adjust incorrect type hint --- tests/bot/rules/test_mentions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/rules/test_mentions.py b/tests/bot/rules/test_mentions.py index e377f2164..ad49ead32 100644 --- a/tests/bot/rules/test_mentions.py +++ b/tests/bot/rules/test_mentions.py @@ -6,7 +6,7 @@ from tests.helpers import MockMessage, async_test class Case(NamedTuple): - recent_messages: List[MockMessage, ...] + recent_messages: List[MockMessage] culprit: Tuple[str] total_mentions: int -- cgit v1.2.3 From 2a9b1fc24ffe9679a565c0f9f4678357e9c80e44 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Wed, 13 Nov 2019 22:17:18 +0100 Subject: Adjust links rule to use proper MockMessage --- tests/bot/rules/test_links.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/bot/rules/test_links.py b/tests/bot/rules/test_links.py index 40336beb0..02a5d5501 100644 --- a/tests/bot/rules/test_links.py +++ b/tests/bot/rules/test_links.py @@ -2,25 +2,19 @@ import unittest from typing import List, NamedTuple, Tuple from bot.rules import links -from tests.helpers import async_test - - -class FakeMessage(NamedTuple): - author: str - content: str +from tests.helpers import MockMessage, async_test class Case(NamedTuple): - recent_messages: List[FakeMessage] - relevant_messages: Tuple[FakeMessage] + recent_messages: List[MockMessage] culprit: Tuple[str] total_links: int -def msg(author: str, total_links: int) -> FakeMessage: +def msg(author: str, total_links: int) -> MockMessage: """Makes a message with `total_links` links.""" content = " ".join(["https://pydis.com"] * total_links) - return FakeMessage(author=author, content=content) + return MockMessage(author=author, content=content) class LinksTests(unittest.TestCase): @@ -61,26 +55,28 @@ class LinksTests(unittest.TestCase): cases = ( Case( [msg("bob", 1), msg("bob", 2)], - (msg("bob", 1), msg("bob", 2)), ("bob",), 3 ), Case( [msg("alice", 1), msg("alice", 1), msg("alice", 1)], - (msg("alice", 1), msg("alice", 1), msg("alice", 1)), ("alice",), 3 ), Case( [msg("alice", 2), msg("bob", 3), msg("alice", 1)], - (msg("alice", 2), msg("alice", 1)), ("alice",), 3 ) ) - for recent_messages, relevant_messages, culprit, total_links in cases: + for recent_messages, culprit, total_links in cases: last_message = recent_messages[0] + relevant_messages = tuple( + msg + for msg in recent_messages + if msg.author == last_message.author + ) with self.subTest( last_message=last_message, -- cgit v1.2.3 From a2617d197f4863123caa33076d89b7612a902d60 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Wed, 13 Nov 2019 22:42:25 +0100 Subject: Adjust attachments rule to use MockMessage, restructure test cases --- tests/bot/rules/test_attachments.py | 43 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index 4bb0acf7c..2f8294922 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -1,26 +1,17 @@ import asyncio import unittest -from dataclasses import dataclass -from typing import Any, List from bot.rules import attachments +from tests.helpers import MockMessage -# Using `MagicMock` sadly doesn't work for this usecase -# since it's __eq__ compares the MagicMock's ID. We just -# want to compare the actual attributes we set. -@dataclass -class FakeMessage: - author: str - attachments: List[Any] - - -def msg(total_attachments: int) -> FakeMessage: - return FakeMessage(author='lemon', attachments=list(range(total_attachments))) +def msg(total_attachments: int) -> MockMessage: + """Builds a message with `total_attachments` attachments.""" + return MockMessage(author='lemon', attachments=list(range(total_attachments))) class AttachmentRuleTests(unittest.TestCase): - """Tests applying the `attachment` antispam rule.""" + """Tests applying the `attachments` antispam rule.""" def test_allows_messages_without_too_many_attachments(self): """Messages without too many attachments are allowed as-is.""" @@ -38,13 +29,25 @@ class AttachmentRuleTests(unittest.TestCase): def test_disallows_messages_with_too_many_attachments(self): """Messages with too many attachments trigger the rule.""" cases = ( - ((msg(4), msg(0), msg(6)), [msg(4), msg(6)], 10), - ((msg(6),), [msg(6)], 6), - ((msg(1),) * 6, [msg(1)] * 6, 6), + ([msg(4), msg(0), msg(6)], 10), + ([msg(6)], 6), + ([msg(1)] * 6, 6), ) - for messages, relevant_messages, total in cases: - with self.subTest(messages=messages, relevant_messages=relevant_messages, total=total): - last_message, *recent_messages = messages + for messages, total in cases: + last_message, *recent_messages = messages + relevant_messages = [last_message] + [ + msg + for msg in recent_messages + if msg.author == last_message.author + and len(msg.attachments) > 0 + ] + + with self.subTest( + last_message=last_message, + recent_messages=recent_messages, + relevant_messages=relevant_messages, + total=total + ): coro = attachments.apply(last_message, recent_messages, {'max': 5}) self.assertEqual( asyncio.run(coro), -- cgit v1.2.3 From dd098d91e35c2e333af14919d7405fe47f298ac2 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Wed, 13 Nov 2019 22:48:59 +0100 Subject: Use async_test helper to simplify coro testing --- tests/bot/rules/test_attachments.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index 2f8294922..770dd3201 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -1,8 +1,7 @@ -import asyncio import unittest from bot.rules import attachments -from tests.helpers import MockMessage +from tests.helpers import MockMessage, async_test def msg(total_attachments: int) -> MockMessage: @@ -13,7 +12,8 @@ def msg(total_attachments: int) -> MockMessage: class AttachmentRuleTests(unittest.TestCase): """Tests applying the `attachments` antispam rule.""" - def test_allows_messages_without_too_many_attachments(self): + @async_test + async def test_allows_messages_without_too_many_attachments(self): """Messages without too many attachments are allowed as-is.""" cases = ( (msg(0), msg(0), msg(0)), @@ -22,17 +22,23 @@ class AttachmentRuleTests(unittest.TestCase): ) for last_message, *recent_messages in cases: - with self.subTest(last_message=last_message, recent_messages=recent_messages): - coro = attachments.apply(last_message, recent_messages, {'max': 5}) - self.assertIsNone(asyncio.run(coro)) + with self.subTest( + last_message=last_message, + recent_messages=recent_messages + ): + self.assertIsNone( + await attachments.apply(last_message, recent_messages, {'max': 5}) + ) - def test_disallows_messages_with_too_many_attachments(self): + @async_test + async def test_disallows_messages_with_too_many_attachments(self): """Messages with too many attachments trigger the rule.""" cases = ( ([msg(4), msg(0), msg(6)], 10), ([msg(6)], 6), ([msg(1)] * 6, 6), ) + for messages, total in cases: last_message, *recent_messages = messages relevant_messages = [last_message] + [ @@ -48,8 +54,7 @@ class AttachmentRuleTests(unittest.TestCase): relevant_messages=relevant_messages, total=total ): - coro = attachments.apply(last_message, recent_messages, {'max': 5}) self.assertEqual( - asyncio.run(coro), + await attachments.apply(last_message, recent_messages, {'max': 5}), (f"sent {total} attachments in 5s", ('lemon',), relevant_messages) ) -- cgit v1.2.3 From 54598dd769b320a4284594835c15eabf9875b7aa Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 14 Nov 2019 20:15:32 +0100 Subject: Fix bug in attachments rule where last_message could potentially count twice in the sum of attachments --- bot/rules/attachments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/rules/attachments.py b/bot/rules/attachments.py index c550aed76..00bb2a949 100644 --- a/bot/rules/attachments.py +++ b/bot/rules/attachments.py @@ -7,14 +7,14 @@ async def apply( last_message: Message, recent_messages: List[Message], config: Dict[str, int] ) -> Optional[Tuple[str, Iterable[Member], Iterable[Message]]]: """Detects total attachments exceeding the limit sent by a single user.""" - relevant_messages = [last_message] + [ + relevant_messages = tuple( msg for msg in recent_messages if ( msg.author == last_message.author and len(msg.attachments) > 0 ) - ] + ) total_recent_attachments = sum(len(msg.attachments) for msg in relevant_messages) if total_recent_attachments > config['max']: -- cgit v1.2.3 From eef447a2c4e237a56b8f3cb72ee3e4bc54e7961c Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 14 Nov 2019 20:16:23 +0100 Subject: Adjust attachments rule unit test to correcty build the arguments for the tested rule --- tests/bot/rules/test_attachments.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index 770dd3201..a43741fcc 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -21,7 +21,9 @@ class AttachmentRuleTests(unittest.TestCase): (msg(0),), ) - for last_message, *recent_messages in cases: + for recent_messages in cases: + last_message = recent_messages[0] + with self.subTest( last_message=last_message, recent_messages=recent_messages @@ -39,14 +41,16 @@ class AttachmentRuleTests(unittest.TestCase): ([msg(1)] * 6, 6), ) - for messages, total in cases: - last_message, *recent_messages = messages - relevant_messages = [last_message] + [ + for recent_messages, total in cases: + last_message = recent_messages[0] + relevant_messages = tuple( msg for msg in recent_messages - if msg.author == last_message.author - and len(msg.attachments) > 0 - ] + if ( + msg.author == last_message.author + and len(msg.attachments) > 0 + ) + ) with self.subTest( last_message=last_message, -- cgit v1.2.3 From 37b526f372ebc981f5691c5aca1ca8c721da77f6 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 14 Nov 2019 20:18:57 +0100 Subject: Hold recent_messages in a list to respect type hint, set config in setUp --- tests/bot/rules/test_attachments.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index a43741fcc..fa6b63654 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -12,13 +12,16 @@ def msg(total_attachments: int) -> MockMessage: class AttachmentRuleTests(unittest.TestCase): """Tests applying the `attachments` antispam rule.""" + def setUp(self): + self.config = {"max": 5} + @async_test async def test_allows_messages_without_too_many_attachments(self): """Messages without too many attachments are allowed as-is.""" cases = ( - (msg(0), msg(0), msg(0)), - (msg(2), msg(2)), - (msg(0),), + [msg(0), msg(0), msg(0)], + [msg(2), msg(2)], + [msg(0)], ) for recent_messages in cases: @@ -29,7 +32,7 @@ class AttachmentRuleTests(unittest.TestCase): recent_messages=recent_messages ): self.assertIsNone( - await attachments.apply(last_message, recent_messages, {'max': 5}) + await attachments.apply(last_message, recent_messages, self.config) ) @async_test @@ -59,6 +62,6 @@ class AttachmentRuleTests(unittest.TestCase): total=total ): self.assertEqual( - await attachments.apply(last_message, recent_messages, {'max': 5}), + await attachments.apply(last_message, recent_messages, self.config), (f"sent {total} attachments in 5s", ('lemon',), relevant_messages) ) -- cgit v1.2.3 From 01731a8873f13cc8a85d08147941ffba7284cf20 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 14 Nov 2019 20:33:12 +0100 Subject: Make complex test cases namedtuples, recognize between various authors, pass config to subTest --- tests/bot/rules/test_attachments.py | 55 +++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index fa6b63654..d8d1b341f 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -1,12 +1,19 @@ import unittest +from typing import List, NamedTuple, Tuple from bot.rules import attachments from tests.helpers import MockMessage, async_test -def msg(total_attachments: int) -> MockMessage: +class Case(NamedTuple): + recent_messages: List[MockMessage] + culprit: Tuple[str] + total_attachments: int + + +def msg(author: str, total_attachments: int) -> MockMessage: """Builds a message with `total_attachments` attachments.""" - return MockMessage(author='lemon', attachments=list(range(total_attachments))) + return MockMessage(author=author, attachments=list(range(total_attachments))) class AttachmentRuleTests(unittest.TestCase): @@ -19,9 +26,9 @@ class AttachmentRuleTests(unittest.TestCase): async def test_allows_messages_without_too_many_attachments(self): """Messages without too many attachments are allowed as-is.""" cases = ( - [msg(0), msg(0), msg(0)], - [msg(2), msg(2)], - [msg(0)], + [msg("bob", 0), msg("bob", 0), msg("bob", 0)], + [msg("bob", 2), msg("bob", 2)], + [msg("bob", 2), msg("alice", 2), msg("bob", 2)], ) for recent_messages in cases: @@ -29,7 +36,8 @@ class AttachmentRuleTests(unittest.TestCase): with self.subTest( last_message=last_message, - recent_messages=recent_messages + recent_messages=recent_messages, + config=self.config ): self.assertIsNone( await attachments.apply(last_message, recent_messages, self.config) @@ -39,12 +47,29 @@ class AttachmentRuleTests(unittest.TestCase): async def test_disallows_messages_with_too_many_attachments(self): """Messages with too many attachments trigger the rule.""" cases = ( - ([msg(4), msg(0), msg(6)], 10), - ([msg(6)], 6), - ([msg(1)] * 6, 6), + Case( + [msg("bob", 4), msg("bob", 0), msg("bob", 6)], + ("bob",), + 10 + ), + Case( + [msg("bob", 4), msg("alice", 6), msg("bob", 2)], + ("bob",), + 6 + ), + Case( + [msg("alice", 6)], + ("alice",), + 6 + ), + ( + [msg("alice", 1) for _ in range(6)], + ("alice",), + 6 + ), ) - for recent_messages, total in cases: + for recent_messages, culprit, total_attachments in cases: last_message = recent_messages[0] relevant_messages = tuple( msg @@ -59,9 +84,15 @@ class AttachmentRuleTests(unittest.TestCase): last_message=last_message, recent_messages=recent_messages, relevant_messages=relevant_messages, - total=total + total_attachments=total_attachments, + config=self.config ): + desired_output = ( + f"sent {total_attachments} attachments in {self.config['max']}s", + culprit, + relevant_messages + ) self.assertEqual( await attachments.apply(last_message, recent_messages, self.config), - (f"sent {total} attachments in 5s", ('lemon',), relevant_messages) + desired_output ) -- cgit v1.2.3 From c74a6c4fb16052c00041b94c3a3e2ef10efe9827 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 14 Nov 2019 20:34:00 +0100 Subject: Specify assertion to be a tuple comparison --- tests/bot/rules/test_attachments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/rules/test_attachments.py b/tests/bot/rules/test_attachments.py index d8d1b341f..d7187f315 100644 --- a/tests/bot/rules/test_attachments.py +++ b/tests/bot/rules/test_attachments.py @@ -92,7 +92,7 @@ class AttachmentRuleTests(unittest.TestCase): culprit, relevant_messages ) - self.assertEqual( + self.assertTupleEqual( await attachments.apply(last_message, recent_messages, self.config), desired_output ) -- cgit v1.2.3 From 4f7bbb249b07ed84cf1caf5a22349f1da7b33091 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 10 Dec 2019 08:37:29 +0100 Subject: Whitelist Discord Testers invite link --- config-default.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config-default.yml b/config-default.yml index 930a1a0e6..f8337b6da 100644 --- a/config-default.yml +++ b/config-default.yml @@ -193,6 +193,7 @@ filter: - 544525886180032552 # kennethreitz.org - 590806733924859943 # Discord Hack Week - 423249981340778496 # Kivy + - 197038439483310086 # Discord Testers domain_blacklist: - pornhub.com -- cgit v1.2.3 From c2af146b676709029cc3a95347e7ab65aa24776a Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Tue, 10 Dec 2019 00:15:17 -0800 Subject: Add constants for voice state logging * Add ID for the voice-log channel. * Add IDs for admins & staff voice channels and make the mod log ignore them. --- bot/constants.py | 1 + config-default.yml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/constants.py b/bot/constants.py index 89504a2e0..389326edd 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -379,6 +379,7 @@ class Channels(metaclass=YAMLGetter): userlog: int user_event_a: int verification: int + voice_log: int class Webhooks(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index 930a1a0e6..8032b50a4 100644 --- a/config-default.yml +++ b/config-default.yml @@ -101,6 +101,7 @@ guild: channels: admins: &ADMINS 365960823622991872 admin_spam: &ADMIN_SPAM 563594791770914816 + admins_voice: &ADMINS_VOICE 500734494840717332 announcements: 354619224620138496 big_brother_logs: &BBLOGS 468507907357409333 bot: 267659945086812160 @@ -131,13 +132,15 @@ guild: python: 267624335836053506 reddit: 458224812528238616 staff_lounge: &STAFF_LOUNGE 464905259261755392 + staff_voice: &STAFF_VOICE 412375055910043655 talent_pool: &TALENT_POOL 534321732593647616 userlog: 528976905546760203 user_event_a: &USER_EVENT_A 592000283102674944 verification: 352442727016693763 + voice_log: 640292421988646961 staff_channels: [*ADMINS, *ADMIN_SPAM, *MOD_SPAM, *MODS, *HELPERS, *ORGANISATION, *DEFCON] - ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG] + ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG, *ADMINS_VOICE, *STAFF_VOICE] roles: admin: &ADMIN_ROLE 267628507062992896 -- cgit v1.2.3 From 58e13c16630f80b906bfe900d707d952f6a61232 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Tue, 10 Dec 2019 22:32:53 -0800 Subject: ModLog: log voice state updates * Add corresponding event to the Event enum so the event can be ignored --- bot/cogs/moderation/modlog.py | 49 +++++++++++++++++++++++++++++++++++++++++++ bot/constants.py | 2 ++ 2 files changed, 51 insertions(+) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 0df752a97..ac45782c9 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -25,6 +25,8 @@ CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position") MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick") ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions") +VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "afk": "AFK"} + class ModLog(Cog, name="ModLog"): """Logging for server events and staff actions.""" @@ -748,3 +750,50 @@ class ModLog(Cog, name="ModLog"): Icons.message_edit, Colour.blurple(), "Message edited (After)", after_response, channel_id=Channels.message_log ) + + @Cog.listener() + async def on_voice_state_update( + self, + member: discord.Member, + before: discord.VoiceState, + after: discord.VoiceState + ) -> None: + """Log member voice state changes to the voice log channel.""" + if member.guild.id != GuildConstant.id: + return + + if member.id in self._ignored[Event.voice_state_update]: + self._ignored[Event.voice_state_update].remove(member.id) + return + + diff = DeepDiff(before, after, exclude_paths="root.session_id") + + # A type change seems to always take precedent over a value change. Furthermore, it will + # include the value change along with the type change anyway. Therefore, it's OK to + # "overwrite" values_changed; in practice there will never even be anything to overwrite. + diff_values = {**diff.get("values_changed", {}), **diff.get("type_changes", {})} + + changes = [] + for attr, values in diff_values.items(): + if not attr: # Not sure why, but it happens + continue + + attr = attr[5:] # Remove "root." prefix + attr = VOICE_STATE_ATTRIBUTES.get(attr, attr.replace("_", " ").capitalize()) + + changes.append(f"**{attr}:** `{values['old_value']}` **->** `{values['new_value']}`") + + if not changes: + return + + message = "\n".join(f"{Emojis.bullet} {item}" for item in sorted(changes)) + message = f"**{member}** (`{member.id}`)\n{message}" + + await self.send_log_message( + icon_url=Icons.user_update, + colour=Colour.blurple(), + title="Voice state updated", + text=message, + thumbnail=member.avatar_url_as(static_format="png"), + channel_id=Channels.voice_log + ) diff --git a/bot/constants.py b/bot/constants.py index 389326edd..b96f23f83 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -544,6 +544,8 @@ class Event(Enum): message_delete = "message_delete" message_edit = "message_edit" + voice_state_update = "voice_state_update" + # Debug mode DEBUG_MODE = True if 'local' in os.environ.get("SITE_URL", "local") else False -- cgit v1.2.3 From cecf84ec894e44debb143d82703a0de5d4bf018c Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Tue, 10 Dec 2019 22:36:11 -0800 Subject: ModLog: use Unicode arrow when displaying value changes --- bot/cogs/moderation/modlog.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index ac45782c9..ffdd5b79f 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -207,7 +207,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -285,7 +285,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -335,7 +335,7 @@ class ModLog(Cog, name="ModLog"): new = value["new_value"] old = value["old_value"] - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) @@ -488,23 +488,23 @@ class ModLog(Cog, name="ModLog"): old = value.get("old_value") if new and old: - changes.append(f"**{key.title()}:** `{old}` **->** `{new}`") + changes.append(f"**{key.title()}:** `{old}` **→** `{new}`") done.append(key) if before.name != after.name: changes.append( - f"**Username:** `{before.name}` **->** `{after.name}`" + f"**Username:** `{before.name}` **→** `{after.name}`" ) if before.discriminator != after.discriminator: changes.append( - f"**Discriminator:** `{before.discriminator}` **->** `{after.discriminator}`" + f"**Discriminator:** `{before.discriminator}` **→** `{after.discriminator}`" ) if before.display_name != after.display_name: changes.append( - f"**Display name:** `{before.display_name}` **->** `{after.display_name}`" + f"**Display name:** `{before.display_name}` **→** `{after.display_name}`" ) if not changes: @@ -781,7 +781,7 @@ class ModLog(Cog, name="ModLog"): attr = attr[5:] # Remove "root." prefix attr = VOICE_STATE_ATTRIBUTES.get(attr, attr.replace("_", " ").capitalize()) - changes.append(f"**{attr}:** `{values['old_value']}` **->** `{values['new_value']}`") + changes.append(f"**{attr}:** `{values['old_value']}` **→** `{values['new_value']}`") if not changes: return -- cgit v1.2.3 From e2075a6455fdb47af50d6445d868b1c19187ba4f Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 11 Dec 2019 00:45:25 -0800 Subject: ModLog: make voice state event respect ignored channels --- bot/cogs/moderation/modlog.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index ffdd5b79f..00aba9872 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -759,7 +759,10 @@ class ModLog(Cog, name="ModLog"): after: discord.VoiceState ) -> None: """Log member voice state changes to the voice log channel.""" - if member.guild.id != GuildConstant.id: + if ( + member.guild.id != GuildConstant.id + or (before.channel and before.channel.id in GuildConstant.ignored) + ): return if member.id in self._ignored[Event.voice_state_update]: -- cgit v1.2.3 From 571608271a8d6198d11fdd7e79fb52243795e1b6 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 11 Dec 2019 00:51:28 -0800 Subject: ModLog: exclude most channel attributes from voice state diff --- bot/cogs/moderation/modlog.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 00aba9872..3f42388b1 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -25,7 +25,7 @@ CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position") MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick") ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions") -VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "afk": "AFK"} +VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "afk": "AFK", "channel.name": "Channel"} class ModLog(Cog, name="ModLog"): @@ -769,7 +769,13 @@ class ModLog(Cog, name="ModLog"): self._ignored[Event.voice_state_update].remove(member.id) return - diff = DeepDiff(before, after, exclude_paths="root.session_id") + # Exclude all channel attributes except the name. + diff = DeepDiff( + before, + after, + exclude_paths="root.session_id", + exclude_regex_paths=r"root\.channel\.(?!name)", + ) # A type change seems to always take precedent over a value change. Furthermore, it will # include the value change along with the type change anyway. Therefore, it's OK to -- cgit v1.2.3 From 98b018dcc7325999756f8f46c5aa06f316acd2cc Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 11 Dec 2019 01:17:01 -0800 Subject: ModLog: exclude afk attribute from voice state log The log will already show that the channel changes to the AFK channel so showing the attribute change is redundant. If the channel were not clearly named "AFK" then it might've made sense to keep the attribute. --- bot/cogs/moderation/modlog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 3f42388b1..98d9c546f 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -25,7 +25,7 @@ CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position") MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick") ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions") -VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "afk": "AFK", "channel.name": "Channel"} +VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "channel.name": "Channel"} class ModLog(Cog, name="ModLog"): @@ -773,7 +773,7 @@ class ModLog(Cog, name="ModLog"): diff = DeepDiff( before, after, - exclude_paths="root.session_id", + exclude_paths=("root.session_id", "root.afk"), exclude_regex_paths=r"root\.channel\.(?!name)", ) -- cgit v1.2.3 From 4191bf859d726e3e605c62472bb2ce2bb8a419e2 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 18 Dec 2019 08:55:26 -0800 Subject: Constants: add voice state emotes --- bot/constants.py | 4 ++++ config-default.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index b96f23f83..725792516 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -325,6 +325,10 @@ class Icons(metaclass=YAMLGetter): superstarify: str unsuperstarify: str + voice_state_blue: str + voice_state_green: str + voice_state_red: str + class CleanMessages(metaclass=YAMLGetter): section = "bot" diff --git a/config-default.yml b/config-default.yml index 8032b50a4..ae09db66a 100644 --- a/config-default.yml +++ b/config-default.yml @@ -92,6 +92,10 @@ style: superstarify: "https://cdn.discordapp.com/emojis/636288153044516874.png" unsuperstarify: "https://cdn.discordapp.com/emojis/636288201258172446.png" + voice_state_blue: "https://cdn.discordapp.com/emojis/656899769662439456.png" + voice_state_green: "https://cdn.discordapp.com/emojis/656899770094452754.png" + voice_state_red: "https://cdn.discordapp.com/emojis/656899769905709076.png" + guild: id: 267624335836053506 -- cgit v1.2.3 From adcafc3a2adeffc5998274d9dc73728178d64721 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 18 Dec 2019 09:13:09 -0800 Subject: ModLog: change voice state embed icon and colour Use a red icon when leaving or mute/deafened. Use a green icon when joining or unmuted/undeafened. Use a blue icon when changing channels or any other possible change. --- bot/cogs/moderation/modlog.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 98d9c546f..8dea44efe 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -782,15 +782,32 @@ class ModLog(Cog, name="ModLog"): # "overwrite" values_changed; in practice there will never even be anything to overwrite. diff_values = {**diff.get("values_changed", {}), **diff.get("type_changes", {})} + icon = Icons.voice_state_blue + colour = Colour.blurple() changes = [] + for attr, values in diff_values.items(): - if not attr: # Not sure why, but it happens + if not attr: # Not sure why, but it happens. continue - attr = attr[5:] # Remove "root." prefix + old = values["old_value"] + new = values["new_value"] + + attr = attr[5:] # Remove "root." prefix. attr = VOICE_STATE_ATTRIBUTES.get(attr, attr.replace("_", " ").capitalize()) - changes.append(f"**{attr}:** `{values['old_value']}` **→** `{values['new_value']}`") + changes.append(f"**{attr}:** `{old}` **→** `{new}`") + + # Set the embed icon and colour depending on which attribute changed. + if any(name in attr for name in ("Channel", "deaf", "mute")): + if new is None or new is True: + # Left a channel or was muted/deafened. + icon = Icons.voice_state_red + colour = Colours.soft_red + elif old is None or old is True: + # Joined a channel or was unmuted/undeafened. + icon = Icons.voice_state_green + colour = Colours.soft_green if not changes: return @@ -799,8 +816,8 @@ class ModLog(Cog, name="ModLog"): message = f"**{member}** (`{member.id}`)\n{message}" await self.send_log_message( - icon_url=Icons.user_update, - colour=Colour.blurple(), + icon_url=icon, + colour=colour, title="Voice state updated", text=message, thumbnail=member.avatar_url_as(static_format="png"), -- cgit v1.2.3 From 8210d839225d58e846260addcf9e298c04391bd8 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 18 Dec 2019 09:28:18 -0800 Subject: ModLog: support self_stream voice state This feature will be available in discord.py 1.3. --- bot/cogs/moderation/modlog.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 8dea44efe..8509e9c07 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -25,7 +25,11 @@ CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position") MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick") ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions") -VOICE_STATE_ATTRIBUTES = {"self_video": "Broadcasting", "channel.name": "Channel"} +VOICE_STATE_ATTRIBUTES = { + "channel.name": "Channel", + "self_stream": "Streaming", + "self_video": "Broadcasting", +} class ModLog(Cog, name="ModLog"): -- cgit v1.2.3 From c649b3b048e61e535bead99c4bbab25f2a82a66f Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Thu, 19 Dec 2019 14:56:53 -0800 Subject: Moderation: explicitly set active value when posting infractions Required due to changes in the API making the active field required. See python-discord/site/pull/317 --- bot/cogs/moderation/infractions.py | 6 +++--- bot/cogs/moderation/superstarify.py | 2 +- bot/cogs/watchchannels/bigbrother.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index 3536a3d38..fcfde1e68 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -203,7 +203,7 @@ class Infractions(InfractionScheduler, commands.Cog): if await utils.has_active_infraction(ctx, user, "mute"): return - infraction = await utils.post_infraction(ctx, user, "mute", reason, **kwargs) + infraction = await utils.post_infraction(ctx, user, "mute", reason, active=True, **kwargs) if infraction is None: return @@ -220,7 +220,7 @@ class Infractions(InfractionScheduler, commands.Cog): @respect_role_hierarchy() async def apply_kick(self, ctx: Context, user: Member, reason: str, **kwargs) -> None: """Apply a kick infraction with kwargs passed to `post_infraction`.""" - infraction = await utils.post_infraction(ctx, user, "kick", reason, **kwargs) + infraction = await utils.post_infraction(ctx, user, "kick", reason, active=False, **kwargs) if infraction is None: return @@ -235,7 +235,7 @@ class Infractions(InfractionScheduler, commands.Cog): if await utils.has_active_infraction(ctx, user, "ban"): return - infraction = await utils.post_infraction(ctx, user, "ban", reason, **kwargs) + infraction = await utils.post_infraction(ctx, user, "ban", reason, active=True, **kwargs) if infraction is None: return diff --git a/bot/cogs/moderation/superstarify.py b/bot/cogs/moderation/superstarify.py index 7631d9bbe..1e19e943e 100644 --- a/bot/cogs/moderation/superstarify.py +++ b/bot/cogs/moderation/superstarify.py @@ -133,7 +133,7 @@ class Superstarify(InfractionScheduler, Cog): # Post the infraction to the API reason = reason or f"old nick: {member.display_name}" - infraction = await utils.post_infraction(ctx, member, "superstar", reason, duration) + infraction = await utils.post_infraction(ctx, member, "superstar", reason, duration, active=True) id_ = infraction["id"] old_nick = member.display_name diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py index 306ed4c64..fbee4f5d7 100644 --- a/bot/cogs/watchchannels/bigbrother.py +++ b/bot/cogs/watchchannels/bigbrother.py @@ -65,7 +65,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"): await ctx.send(":x: The specified user is already being watched.") return - response = await post_infraction(ctx, user, 'watch', reason, hidden=True) + response = await post_infraction(ctx, user, 'watch', reason, hidden=True, active=True) if response is not None: self.watched_users[user.id] = response -- cgit v1.2.3 From 77edf9a2950ebb96eed56d3a6a858a561d6a72e7 Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Thu, 26 Dec 2019 21:35:59 +1000 Subject: Use a static discord shield on the readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a7f1b992..1e7b21271 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Python Utility Bot -[![Discord](https://img.shields.io/discord/267624335836053506?color=%237289DA&label=Python%20Discord&logo=discord&logoColor=white)](https://discord.gg/2B963hn) +[![Discord](https://img.shields.io/static/v1?label=Python%20Discord&logo=discord&message=%3E30k%20members&color=%237289DA&logoColor=white)](https://discord.gg/2B963hn) [![Build Status](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Bot?branchName=master)](https://dev.azure.com/python-discord/Python%20Discord/_build/latest?definitionId=1&branchName=master) [![Tests](https://img.shields.io/azure-devops/tests/python-discord/Python%20Discord/1?compact_message)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Bot?branchName=master) [![Coverage](https://img.shields.io/azure-devops/coverage/python-discord/Python%20Discord/1/master)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Bot?branchName=master) -- cgit v1.2.3 From 18076745d45f808e729f9c4a12252efaed901f3a Mon Sep 17 00:00:00 2001 From: Ava Date: Mon, 30 Dec 2019 00:42:18 +0200 Subject: Update filetype whitelist to include audio formats Discord has a built-in player for all of these and they are really just stripped down videos, which are allowed. --- config-default.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config-default.yml b/config-default.yml index f28c79712..15d0e51b8 100644 --- a/config-default.yml +++ b/config-default.yml @@ -374,6 +374,9 @@ anti_malware: - '.ai' # Illustrator - '.aep' # After Effects - '.xcf' # GIMP + - '.mp3' + - '.wav' + - '.ogg' reddit: -- cgit v1.2.3 From d5fad742bcc9566405a331f10203d175c2882001 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Mon, 30 Dec 2019 14:52:30 -0800 Subject: Watchchannels: show username in response for already watched users Makes it clear which user it is in the case of an ID being used to invoke the command. --- bot/cogs/watchchannels/bigbrother.py | 2 +- bot/cogs/watchchannels/talentpool.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py index 306ed4c64..1e53b98ea 100644 --- a/bot/cogs/watchchannels/bigbrother.py +++ b/bot/cogs/watchchannels/bigbrother.py @@ -62,7 +62,7 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"): return if user.id in self.watched_users: - await ctx.send(":x: The specified user is already being watched.") + await ctx.send(f":x: {user} is already being watched.") return response = await post_infraction(ctx, user, 'watch', reason, hidden=True) diff --git a/bot/cogs/watchchannels/talentpool.py b/bot/cogs/watchchannels/talentpool.py index cc8feeeee..f990ccff8 100644 --- a/bot/cogs/watchchannels/talentpool.py +++ b/bot/cogs/watchchannels/talentpool.py @@ -69,7 +69,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): return if user.id in self.watched_users: - await ctx.send(":x: The specified user is already being watched in the talent pool") + await ctx.send(f":x: {user} is already being watched in the talent pool") return # Manual request with `raise_for_status` as False because we want the actual response -- cgit v1.2.3 From 90b8a8fb8b5a32169ddc1d331eaa38d9c6bf270e Mon Sep 17 00:00:00 2001 From: Numerlor Date: Tue, 31 Dec 2019 23:51:46 +0100 Subject: Make sure description is truncated to max 1000 chars --- bot/cogs/doc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index 9506b195a..c1bb4ad41 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -307,8 +307,15 @@ class Doc(commands.Cog): if len(description) > 1000: shortened = description[:1000] last_paragraph_end = shortened.rfind('\n\n', 100) + # Search the shortened version for cutoff points in decreasing desirability, + # cutoff at 1000 if none are found. if last_paragraph_end == -1: - last_paragraph_end = shortened.rfind('. ') + for string in (". ", ", ", ",", " "): + last_paragraph_end = shortened.rfind(string) + if last_paragraph_end != -1: + break + else: + last_paragraph_end = 1000 description = description[:last_paragraph_end] # If there is an incomplete code block, cut it out @@ -318,7 +325,6 @@ class Doc(commands.Cog): description += f"... [read more]({permalink})" description = WHITESPACE_AFTER_NEWLINES_RE.sub('', description) - if signatures is None: # If symbol is a module, don't show signature. embed_description = description -- cgit v1.2.3 From 2b032a35fa64e770678b051c164dbfc53ef56893 Mon Sep 17 00:00:00 2001 From: Numerlor Date: Wed, 1 Jan 2020 00:43:03 +0100 Subject: Rename last_paragraph_end variable to a more fitting name --- bot/cogs/doc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index c1bb4ad41..16a690f39 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -306,17 +306,17 @@ class Doc(commands.Cog): # of a double newline (interpreted as a paragraph) before index 1000. if len(description) > 1000: shortened = description[:1000] - last_paragraph_end = shortened.rfind('\n\n', 100) + description_cutoff = shortened.rfind('\n\n', 100) # Search the shortened version for cutoff points in decreasing desirability, # cutoff at 1000 if none are found. - if last_paragraph_end == -1: + if description_cutoff == -1: for string in (". ", ", ", ",", " "): - last_paragraph_end = shortened.rfind(string) - if last_paragraph_end != -1: + description_cutoff = shortened.rfind(string) + if description_cutoff != -1: break else: - last_paragraph_end = 1000 - description = description[:last_paragraph_end] + description_cutoff = 1000 + description = description[:description_cutoff] # If there is an incomplete code block, cut it out if description.count("```") % 2: -- cgit v1.2.3 From 1da2c04e43551cf36646c85880647aa33c40cb24 Mon Sep 17 00:00:00 2001 From: Numerlor Date: Wed, 1 Jan 2020 00:43:33 +0100 Subject: Move comment into if body --- bot/cogs/doc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index 16a690f39..cb6f4f972 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -307,9 +307,9 @@ class Doc(commands.Cog): if len(description) > 1000: shortened = description[:1000] description_cutoff = shortened.rfind('\n\n', 100) - # Search the shortened version for cutoff points in decreasing desirability, - # cutoff at 1000 if none are found. if description_cutoff == -1: + # Search the shortened version for cutoff points in decreasing desirability, + # cutoff at 1000 if none are found. for string in (". ", ", ", ",", " "): description_cutoff = shortened.rfind(string) if description_cutoff != -1: -- cgit v1.2.3 From c398c6ecc50d7e78e729c1451c28801cfb771aab Mon Sep 17 00:00:00 2001 From: Daniel Brown Date: Thu, 9 Jan 2020 14:21:22 -0600 Subject: Adding PyGame and Ren'Py to the invite white list With the addition of the #game-development channel, I would expect we'll see an increase in people needing specific help with the various engines and libraries that Python has to offer. As part of this, I'm adding the servers for PyGame and Ren'Py to the white list to help people get to the proper resources they might need. --- config-default.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config-default.yml b/config-default.yml index 15d0e51b8..f303f6ff8 100644 --- a/config-default.yml +++ b/config-default.yml @@ -202,6 +202,8 @@ filter: - 590806733924859943 # Discord Hack Week - 423249981340778496 # Kivy - 197038439483310086 # Discord Testers + - 286633898581164032 # Ren'Py + - 349505959032389632 # PyGame domain_blacklist: - pornhub.com -- cgit v1.2.3 From 74d990540a1072c1782fa7593d7d1abe3c165f49 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 11 Jan 2020 18:58:33 +0100 Subject: Update Code Jam Participant ID --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index f303f6ff8..bf8509544 100644 --- a/config-default.yml +++ b/config-default.yml @@ -154,7 +154,7 @@ guild: contributor: 295488872404484098 core_developer: 587606783669829632 helpers: 267630620367257601 - jammer: 423054537079783434 + jammer: 591786436651646989 moderator: &MOD_ROLE 267629731250176001 muted: &MUTED_ROLE 277914926603829249 owner: &OWNER_ROLE 267627879762755584 -- cgit v1.2.3 From eaaebe963c0b9f79ce18142e343f57ef85fdd88c Mon Sep 17 00:00:00 2001 From: "S. Co1" Date: Sat, 11 Jan 2020 17:38:46 -0500 Subject: Add handling for empty PEP metadata values Some PEPs have a metadata field that is present but has an empty value (e.g. PEP 249), causing an exception to be raised when attempting to add it as an embed field value, which cannot be empty. This refactors the information parsing to prevent the field from being added if there is no value to provide, as well as cut down on copy+paste when populating fields in the embed. --- bot/cogs/utils.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 47a59db66..da278011a 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -62,14 +62,12 @@ class Utils(Cog): pep_embed.set_thumbnail(url="https://www.python.org/static/opengraph-icon-200x200.png") # Add the interesting information - if "Status" in pep_header: - pep_embed.add_field(name="Status", value=pep_header["Status"]) - if "Python-Version" in pep_header: - pep_embed.add_field(name="Python-Version", value=pep_header["Python-Version"]) - if "Created" in pep_header: - pep_embed.add_field(name="Created", value=pep_header["Created"]) - if "Type" in pep_header: - pep_embed.add_field(name="Type", value=pep_header["Type"]) + fields_to_check = ("Status", "Python-Version", "Created", "Type") + for field in fields_to_check: + # Check for a PEP metadata field that is present but has an empty value + # embed field values can't contain an empty string + if pep_header.get(field, ""): + pep_embed.add_field(name=field, value=pep_header[field]) elif response.status != 404: # any response except 200 and 404 is expected -- cgit v1.2.3 From 1fc68f1857a2f3abe00fe270bd050a3d2a6e2a03 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 10:41:08 +0100 Subject: Install `prometheus-async`. --- Pipfile | 1 + Pipfile.lock | 389 ++++++++++++++++++++++++++++++++--------------------------- 2 files changed, 211 insertions(+), 179 deletions(-) diff --git a/Pipfile b/Pipfile index 48d839fc3..68362ae78 100644 --- a/Pipfile +++ b/Pipfile @@ -19,6 +19,7 @@ deepdiff = "~=4.0" requests = "~=2.22" more_itertools = "~=7.2" urllib3 = ">=1.24.2,<1.25" +prometheus-async = {extras = ["aiohttp"],version = "~=19.2"} [dev-packages] coverage = "~=4.5" diff --git a/Pipfile.lock b/Pipfile.lock index 69caf4646..ab5dfb538 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c27d699b4aeeed204dee41f924f682ae2a670add8549a8826e58776594370582" + "sha256": "d9349e8c704b2b2403004039856d8d75aaebc76e4aa93390c4d177f583e73b71" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,11 @@ "default": { "aio-pika": { "hashes": [ - "sha256:1da038b3d2c1b49e0e816d87424e702912bb77f9b5197f2bf279217915b4f7ed", - "sha256:29fe851374b86c997a22174c04352b5941bc1c2e36bbf542918ac18a76cfc9d3" + "sha256:a5837277e53755078db3a9e8c45bbca605c8ba9ecba7a02d74a7a1779f444723", + "sha256:fa32e33b4b7d0804dcf439ae6ff24d2f0a83d1ba280ee9f555e647d71d394ff5" ], "index": "pypi", - "version": "==6.3.0" + "version": "==6.4.1" }, "aiodns": { "hashes": [ @@ -62,10 +62,10 @@ }, "aiormq": { "hashes": [ - "sha256:afc0d46837b121585e4faec0a7646706429b4e2f5110ae8d0b5cdc3708b4b0e5", - "sha256:dc0fbbc7f8ad5af6a2cc18e00ccc5f925984cde3db6e8fe952c07b7ef157b5f2" + "sha256:8c215a970133ab5ee7c478decac55b209af7731050f52d11439fe910fa0f9e9d", + "sha256:9210f3389200aee7d8067f6435f4a9eff2d3a30b88beb5eaae406ccc11c0fc01" ], - "version": "==2.9.1" + "version": "==3.2.0" }, "alabaster": { "hashes": [ @@ -90,25 +90,25 @@ }, "babel": { "hashes": [ - "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", - "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28" + "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38", + "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4" ], - "version": "==2.7.0" + "version": "==2.8.0" }, "beautifulsoup4": { "hashes": [ - "sha256:5279c36b4b2ec2cb4298d723791467e3000e5384a43ea0cdf5d45207c7e97169", - "sha256:6135db2ba678168c07950f9a16c4031822c6f4aec75a65e0a97bc5ca09789931", - "sha256:dcdef580e18a76d54002088602eba453eec38ebbcafafeaabd8cab12b6155d57" + "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", + "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", + "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae" ], - "version": "==4.8.1" + "version": "==4.8.2" }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "cffi": { "hashes": [ @@ -195,10 +195,10 @@ }, "imagesize": { "hashes": [ - "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", - "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" + "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", + "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" ], - "version": "==1.1.0" + "version": "==1.2.0" }, "jinja2": { "hashes": [ @@ -223,35 +223,35 @@ }, "lxml": { "hashes": [ - "sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", - "sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", - "sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", - "sha256:1409b14bf83a7d729f92e2a7fbfe7ec929d4883ca071b06e95c539ceedb6497c", - "sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", - "sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", - "sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", - "sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d", - "sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916", - "sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0", - "sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27", - "sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", - "sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", - "sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", - "sha256:9277562f175d2334744ad297568677056861070399cec56ff06abbe2564d1232", - "sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", - "sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", - "sha256:ae88588d687bd476be588010cbbe551e9c2872b816f2da8f01f6f1fda74e1ef0", - "sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", - "sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", - "sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", - "sha256:c7fccd08b14aa437fe096c71c645c0f9be0655a9b1a4b7cffc77bcb23b3d61d2", - "sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", - "sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", - "sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", - "sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681" - ], - "index": "pypi", - "version": "==4.4.1" + "sha256:00ac0d64949fef6b3693813fe636a2d56d97a5a49b5bbb86e4cc4cc50ebc9ea2", + "sha256:0571e607558665ed42e450d7bf0e2941d542c18e117b1ebbf0ba72f287ad841c", + "sha256:0e3f04a7615fdac0be5e18b2406529521d6dbdb0167d2a690ee328bef7807487", + "sha256:13cf89be53348d1c17b453867da68704802966c433b2bb4fa1f970daadd2ef70", + "sha256:217262fcf6a4c2e1c7cb1efa08bd9ebc432502abc6c255c4abab611e8be0d14d", + "sha256:223e544828f1955daaf4cefbb4853bc416b2ec3fd56d4f4204a8b17007c21250", + "sha256:277cb61fede2f95b9c61912fefb3d43fbd5f18bf18a14fae4911b67984486f5d", + "sha256:3213f753e8ae86c396e0e066866e64c6b04618e85c723b32ecb0909885211f74", + "sha256:4690984a4dee1033da0af6df0b7a6bde83f74e1c0c870623797cec77964de34d", + "sha256:4fcc472ef87f45c429d3b923b925704aa581f875d65bac80f8ab0c3296a63f78", + "sha256:61409bd745a265a742f2693e4600e4dbd45cc1daebe1d5fad6fcb22912d44145", + "sha256:678f1963f755c5d9f5f6968dded7b245dd1ece8cf53c1aa9d80e6734a8c7f41d", + "sha256:6c6d03549d4e2734133badb9ab1c05d9f0ef4bcd31d83e5d2b4747c85cfa21da", + "sha256:6e74d5f4d6ecd6942375c52ffcd35f4318a61a02328f6f1bd79fcb4ffedf969e", + "sha256:7b4fc7b1ecc987ca7aaf3f4f0e71bbfbd81aaabf87002558f5bc95da3a865bcd", + "sha256:7ed386a40e172ddf44c061ad74881d8622f791d9af0b6f5be20023029129bc85", + "sha256:8f54f0924d12c47a382c600c880770b5ebfc96c9fd94cf6f6bdc21caf6163ea7", + "sha256:ad9b81351fdc236bda538efa6879315448411a81186c836d4b80d6ca8217cdb9", + "sha256:bbd00e21ea17f7bcc58dccd13869d68441b32899e89cf6cfa90d624a9198ce85", + "sha256:c3c289762cc09735e2a8f8a49571d0e8b4f57ea831ea11558247b5bdea0ac4db", + "sha256:cf4650942de5e5685ad308e22bcafbccfe37c54aa7c0e30cd620c2ee5c93d336", + "sha256:cfcbc33c9c59c93776aa41ab02e55c288a042211708b72fdb518221cc803abc8", + "sha256:e301055deadfedbd80cf94f2f65ff23126b232b0d1fea28f332ce58137bcdb18", + "sha256:ebbfe24df7f7b5c6c7620702496b6419f6a9aa2fd7f005eb731cc80d7b4692b9", + "sha256:eff69ddbf3ad86375c344339371168640951c302450c5d3e9936e98d6459db06", + "sha256:f6ed60a62c5f1c44e789d2cf14009423cb1646b44a43e40a9cf6a21f077678a1" + ], + "index": "pypi", + "version": "==4.4.2" }, "markdownify": { "hashes": [ @@ -303,37 +303,25 @@ }, "multidict": { "hashes": [ - "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", - "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3", - "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef", - "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b", - "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73", - "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc", - "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3", - "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd", - "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351", - "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941", - "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d", - "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1", - "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b", - "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a", - "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3", - "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7", - "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0", - "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0", - "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014", - "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5", - "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036", - "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d", - "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a", - "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce", - "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1", - "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a", - "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9", - "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7", - "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b" - ], - "version": "==4.5.2" + "sha256:13f3ebdb5693944f52faa7b2065b751cb7e578b8dd0a5bb8e4ab05ad0188b85e", + "sha256:26502cefa86d79b86752e96639352c7247846515c864d7c2eb85d036752b643c", + "sha256:4fba5204d32d5c52439f88437d33ad14b5f228e25072a192453f658bddfe45a7", + "sha256:527124ef435f39a37b279653ad0238ff606b58328ca7989a6df372fd75d7fe26", + "sha256:5414f388ffd78c57e77bd253cf829373721f450613de53dc85a08e34d806e8eb", + "sha256:5eee66f882ab35674944dfa0d28b57fa51e160b4dce0ce19e47f495fdae70703", + "sha256:63810343ea07f5cd86ba66ab66706243a6f5af075eea50c01e39b4ad6bc3c57a", + "sha256:6bd10adf9f0d6a98ccc792ab6f83d18674775986ba9bacd376b643fe35633357", + "sha256:83c6ddf0add57c6b8a7de0bc7e2d656be3eefeff7c922af9a9aae7e49f225625", + "sha256:93166e0f5379cf6cd29746989f8a594fa7204dcae2e9335ddba39c870a287e1c", + "sha256:9a7b115ee0b9b92d10ebc246811d8f55d0c57e82dbb6a26b23c9a9a6ad40ce0c", + "sha256:a38baa3046cce174a07a59952c9f876ae8875ef3559709639c17fdf21f7b30dd", + "sha256:a6d219f49821f4b2c85c6d426346a5d84dab6daa6f85ca3da6c00ed05b54022d", + "sha256:a8ed33e8f9b67e3b592c56567135bb42e7e0e97417a4b6a771e60898dfd5182b", + "sha256:d7d428488c67b09b26928950a395e41cc72bb9c3d5abfe9f0521940ee4f796d4", + "sha256:dcfed56aa085b89d644af17442cdc2debaa73388feba4b8026446d168ca8dad7", + "sha256:f29b885e4903bd57a7789f09fe9d60b6475a6c1a4c0eca874d8558f00f9d4b51" + ], + "version": "==4.7.4" }, "ordered-set": { "hashes": [ @@ -343,10 +331,10 @@ }, "packaging": { "hashes": [ - "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", - "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" + "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", + "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" ], - "version": "==19.2" + "version": "==20.0" }, "pamqp": { "hashes": [ @@ -355,23 +343,56 @@ ], "version": "==2.3.0" }, + "prometheus-async": { + "extras": [ + "aiohttp" + ], + "hashes": [ + "sha256:227f516e5bf98a0dc602348381e182358f8b2ed24a8db05e8e34d9cf027bab83", + "sha256:3cc68d1f39e9bbf16dbd0b51103d87671b3cbd1d75a72cda472cd9a35cc9d0d2" + ], + "index": "pypi", + "version": "==19.2.0" + }, + "prometheus-client": { + "hashes": [ + "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da" + ], + "version": "==0.7.1" + }, "pycares": { "hashes": [ - "sha256:2ca080db265ea238dc45f997f94effb62b979a617569889e265c26a839ed6305", - "sha256:6f79c6afb6ce603009db2042fddc2e348ad093ece9784cbe2daa809499871a23", - "sha256:70918d06eb0603016d37092a5f2c0228509eb4e6c5a3faacb4184f6ab7be7650", - "sha256:755187d28d24a9ea63aa2b4c0638be31d65fbf7f0ce16d41261b9f8cb55a1b99", - "sha256:7baa4b1f2146eb8423ff8303ebde3a20fb444a60db761fba0430d104fe35ddbf", - "sha256:90b27d4df86395f465a171386bc341098d6d47b65944df46518814ae298f6cc6", - "sha256:9e090dd6b2afa65cb51c133883b2bf2240fd0f717b130b0048714b33fb0f47ce", - "sha256:a11b7d63c3718775f6e805d6464cb10943780395ab042c7e5a0a7a9f612735dd", - "sha256:b253f5dcaa0ac7076b79388a3ac80dd8f3bd979108f813baade40d3a9b8bf0bd", - "sha256:c7f4f65e44ba35e35ad3febc844270665bba21cfb0fb7d749434e705b556e087", - "sha256:cdb342e6a254f035bd976d95807a2184038fc088d957a5104dcaab8be602c093", - "sha256:cf08e164f8bfb83b9fe633feb56f2754fae6baefcea663593794fa0518f8f98c", - "sha256:df9bc694cf03673878ea8ce674082c5acd134991d64d6c306d4bd61c0c1df98f" - ], - "version": "==3.0.0" + "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f", + "sha256:0a24d2e580a8eb567140d7b69f12cb7de90c836bd7b6488ec69394d308605ac3", + "sha256:0c5bd1f6f885a219d5e972788d6eef7b8043b55c3375a845e5399638436e0bba", + "sha256:11c628402cc8fc8ef461076d4e47f88afc1f8609989ebbff0dbffcd54c97239f", + "sha256:18dfd4fd300f570d6c4536c1d987b7b7673b2a9d14346592c5d6ed716df0d104", + "sha256:1917b82494907a4a342db420bc4dd5bac355a5fa3984c35ba9bf51422b020b48", + "sha256:1b90fa00a89564df059fb18e796458864cc4e00cb55e364dbf921997266b7c55", + "sha256:1d8d177c40567de78108a7835170f570ab04f09084bfd32df9919c0eaec47aa1", + "sha256:236286f81664658b32c141c8e79d20afc3d54f6e2e49dfc8b702026be7265855", + "sha256:2e4f74677542737fb5af4ea9a2e415ec5ab31aa67e7b8c3c969fdb15c069f679", + "sha256:48a7750f04e69e1f304f4332b755728067e7c4b1abe2760bba1cacd9ff7a847a", + "sha256:7d86e62b700b21401ffe7fd1bbfe91e08489416fecae99c6570ab023c6896022", + "sha256:7e2d7effd08d2e5a3cb95d98a7286ebab71ab2fbce84fa93cc2dd56caf7240dd", + "sha256:81edb016d9e43dde7473bc3999c29cdfee3a6b67308fed1ea21049f458e83ae0", + "sha256:96c90e11b4a4c7c0b8ff5aaaae969c5035493136586043ff301979aae0623941", + "sha256:9a0a1845f8cb2e62332bca0aaa9ad5494603ac43fb60d510a61d5b5b170d7216", + "sha256:a05bbfdfd41f8410a905a818f329afe7510cbd9ee65c60f8860a72b6c64ce5dc", + "sha256:a5089fd660f0b0d228b14cdaa110d0d311edfa5a63f800618dbf1321dcaef66b", + "sha256:c457a709e6f2befea7e2996c991eda6d79705dd075f6521593ba6ebc1485b811", + "sha256:c5cb72644b04e5e5abfb1e10a0e7eb75da6684ea0e60871652f348e412cf3b11", + "sha256:cce46dd4717debfd2aab79d6d7f0cbdf6b1e982dc4d9bebad81658d59ede07c2", + "sha256:cfdd1f90bcf373b00f4b2c55ea47868616fe2f779f792fc913fa82a3d64ffe43", + "sha256:d88a279cbc5af613f73e86e19b3f63850f7a2e2736e249c51995dedcc830b1bb", + "sha256:eba9a9227438da5e78fc8eee32f32eb35d9a50cf0a0bd937eb6275c7cc3015fe", + "sha256:eee7b6a5f5b5af050cb7d66ab28179287b416f06d15a8974ac831437fec51336", + "sha256:f41ac1c858687e53242828c9f59c2e7b0b95dbcd5bdd09c7e5d3c48b0f89a25a", + "sha256:f8deaefefc3a589058df1b177275f79233e8b0eeee6734cf4336d80164ecd022", + "sha256:fa78e919f3bd7d6d075db262aa41079b4c02da315c6043c6f43881e2ebcdd623", + "sha256:fadb97d2e02dabdc15a0091591a972a938850d79ddde23d385d813c1731983f0" + ], + "version": "==3.1.1" }, "pycparser": { "hashes": [ @@ -381,17 +402,17 @@ }, "pygments": { "hashes": [ - "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", - "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b", + "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe" ], - "version": "==2.4.2" + "version": "==2.5.2" }, "pyparsing": { "hashes": [ - "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", - "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" ], - "version": "==2.4.5" + "version": "==2.4.6" }, "python-dateutil": { "hashes": [ @@ -416,22 +437,20 @@ }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" ], "index": "pypi", - "version": "==5.1.2" + "version": "==5.3" }, "requests": { "hashes": [ @@ -464,11 +483,11 @@ }, "sphinx": { "hashes": [ - "sha256:31088dfb95359384b1005619827eaee3056243798c62724fd3fa4b84ee4d71bd", - "sha256:52286a0b9d7caa31efee301ec4300dbdab23c3b05da1c9024b4e84896fb73d79" + "sha256:298537cb3234578b2d954ff18c5608468229e116a9757af3b831c2b2b4819159", + "sha256:e6e766b74f85f37a5f3e0773a1e1be8db3fcb799deb58ca6d18b70b0b44542a5" ], "index": "pypi", - "version": "==2.2.1" + "version": "==2.3.1" }, "sphinxcontrib-applehelp": { "hashes": [ @@ -546,21 +565,33 @@ ], "version": "==6.0" }, - "yarl": { + "wrapt": { "hashes": [ - "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9", - "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f", - "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb", - "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320", - "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842", - "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0", - "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829", - "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310", - "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4", - "sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8", - "sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1" + "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" ], - "version": "==1.3.0" + "version": "==1.11.2" + }, + "yarl": { + "hashes": [ + "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce", + "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6", + "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce", + "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae", + "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d", + "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f", + "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b", + "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b", + "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb", + "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462", + "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea", + "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70", + "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1", + "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a", + "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b", + "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080", + "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" + ], + "version": "==1.4.2" } }, "develop": { @@ -580,10 +611,10 @@ }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "cfgv": { "hashes": [ @@ -646,10 +677,11 @@ }, "dodgy": { "hashes": [ - "sha256:65e13cf878d7aff129f1461c13cb5fd1bb6dfe66bb5327e09379c3877763280c" + "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a", + "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6" ], "index": "pypi", - "version": "==0.1.9" + "version": "==0.2.1" }, "dparse": { "hashes": [ @@ -675,11 +707,11 @@ }, "flake8-annotations": { "hashes": [ - "sha256:6ac7ca1e706307686b60af8043ff1db31dc2cfc1233c8210d67a3d9b8f364736", - "sha256:b51131007000d67217608fa028a35ff80aa400b474e5972f1f99c2cf9d26bd2e" + "sha256:05b85538014c850a86dce7374bb6621c64481c24e35e8e90af1315f4d7a3dbaa", + "sha256:43e5233a76fda002b91a54a7cc4510f099c4bfd6279502ec70164016250eebd1" ], "index": "pypi", - "version": "==1.1.0" + "version": "==1.1.3" }, "flake8-bugbear": { "hashes": [ @@ -730,10 +762,10 @@ }, "identify": { "hashes": [ - "sha256:4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017", - "sha256:d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e" + "sha256:6f44e637caa40d1b4cb37f6ed3b262ede74901d28b1cc5b1fc07360871edd65d", + "sha256:72e9c4ed3bc713c7045b762b0d2e2115c572b85abfc1f4604f5a4fd4c6642b71" ], - "version": "==1.4.7" + "version": "==1.4.9" }, "idna": { "hashes": [ @@ -744,11 +776,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", - "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" + "sha256:bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359", + "sha256:f17c015735e1a88296994c0697ecea7e11db24290941983b08c9feb30921e6d8" ], "markers": "python_version < '3.8'", - "version": "==0.23" + "version": "==1.4.0" }, "mccabe": { "hashes": [ @@ -767,24 +799,24 @@ }, "nodeenv": { "hashes": [ - "sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a" + "sha256:561057acd4ae3809e665a9aaaf214afff110bbb6a6d5c8a96121aea6878408b3" ], - "version": "==1.3.3" + "version": "==1.3.4" }, "packaging": { "hashes": [ - "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", - "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" + "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", + "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" ], - "version": "==19.2" + "version": "==20.0" }, "pre-commit": { "hashes": [ - "sha256:9f152687127ec90642a2cc3e4d9e1e6240c4eb153615cb02aa1ad41d331cbb6e", - "sha256:c2e4810d2d3102d354947907514a78c5d30424d299dc0fe48f5aa049826e9b50" + "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850", + "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029" ], "index": "pypi", - "version": "==1.20.0" + "version": "==1.21.0" }, "pycodestyle": { "hashes": [ @@ -795,10 +827,10 @@ }, "pydocstyle": { "hashes": [ - "sha256:04c84e034ebb56eb6396c820442b8c4499ac5eb94a3bda88951ac3dc519b6058", - "sha256:66aff87ffe34b1e49bff2dd03a88ce6843be2f3346b0c9814410d34987fbab59" + "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586", + "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5" ], - "version": "==4.0.1" + "version": "==5.0.2" }, "pyflakes": { "hashes": [ @@ -809,29 +841,27 @@ }, "pyparsing": { "hashes": [ - "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", - "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" ], - "version": "==2.4.5" + "version": "==2.4.6" }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" ], "index": "pypi", - "version": "==5.1.2" + "version": "==5.3" }, "requests": { "hashes": [ @@ -893,6 +923,7 @@ "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" ], + "markers": "python_version < '3.8'", "version": "==1.4.0" }, "unittest-xml-reporting": { @@ -913,10 +944,10 @@ }, "virtualenv": { "hashes": [ - "sha256:11cb4608930d5fd3afb545ecf8db83fa50e1f96fc4fca80c94b07d2c83146589", - "sha256:d257bb3773e48cac60e475a19b608996c73f4d333b3ba2e4e57d5ac6134e0136" + "sha256:0d62c70883c0342d59c11d0ddac0d954d0431321a41ab20851facf2b222598f3", + "sha256:55059a7a676e4e19498f1aad09b8313a38fcc0cdbe4fdddc0e9b06946d21b4bb" ], - "version": "==16.7.7" + "version": "==16.7.9" }, "zipp": { "hashes": [ -- cgit v1.2.3 From 4d6c28d5608c50dccb9d78ce6c8385e003f0a4b3 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 10:41:19 +0100 Subject: Start Prometheus HTTP server on bot start. --- bot/bot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/bot.py b/bot/bot.py index 8f808272f..930aaf70e 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -4,6 +4,7 @@ from typing import Optional import aiohttp from discord.ext import commands +from prometheus_async.aio.web import start_http_server as start_prometheus_http_server from bot import api @@ -50,4 +51,6 @@ class Bot(commands.Bot): """Open an aiohttp session before logging in and connecting to Discord.""" self.http_session = aiohttp.ClientSession(connector=self.connector) + await start_prometheus_http_server(addr="0.0.0.0", port=9330) + log.debug("Started Prometheus server on port 9330.") await super().start(*args, **kwargs) -- cgit v1.2.3 From 52813ee63c7aa558c75e1fad320b3fc3050d1155 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:05:23 +0100 Subject: Add the `metrics` cog. --- bot/__main__.py | 1 + bot/cogs/metrics.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 bot/cogs/metrics.py diff --git a/bot/__main__.py b/bot/__main__.py index 84bc7094b..61271a692 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -40,6 +40,7 @@ bot.load_extension("bot.cogs.duck_pond") bot.load_extension("bot.cogs.free") bot.load_extension("bot.cogs.information") bot.load_extension("bot.cogs.jams") +bot.load_extension("bot.cogs.metrics") bot.load_extension("bot.cogs.moderation") bot.load_extension("bot.cogs.off_topic_names") bot.load_extension("bot.cogs.reddit") diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py new file mode 100644 index 000000000..79c754c21 --- /dev/null +++ b/bot/cogs/metrics.py @@ -0,0 +1,40 @@ +from collections import defaultdict + +from discord import Status +from discord.ext.commands import Cog +from prometheus_client import Gauge + +from bot.bot import Bot + + +class Metrics(Cog): + """Exports metrics for Prometheus.""" + + PREFIX = 'pydis_bot_' + + def __init__(self, bot: Bot) -> None: + self.bot = bot + + self.guild_members = Gauge( + name=f'{self.PREFIX}server_members', + documentation="Total members by status.", + labelnames=('guild_id', 'status') + ) + + @Cog.listener() + async def on_ready(self) -> None: + members_by_status = defaultdict(lambda: defaultdict(int)) + + await self.bot.request_offline_members(*self.bot.guilds) + for guild in self.bot.guilds: + for member in guild.members: + members_by_status[guild.id][member.status] += 1 + + for guild_id, members in members_by_status.items(): + for status, count in members.items(): + self.guild_members.labels(guild_id=guild_id, status=str(status)).set(count) + + +def setup(bot: Bot) -> None: + """Load the Metrics cog.""" + bot.add_cog(Metrics(bot)) -- cgit v1.2.3 From 4fd5d86ce2cee62f3d1cad1cf70dca11a6546d8e Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:22:53 +0100 Subject: Only request offline members for large guilds. --- bot/cogs/metrics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index 79c754c21..e356ba294 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -25,8 +25,9 @@ class Metrics(Cog): async def on_ready(self) -> None: members_by_status = defaultdict(lambda: defaultdict(int)) - await self.bot.request_offline_members(*self.bot.guilds) for guild in self.bot.guilds: + if guild.large: + await self.bot.request_offline_members(guild) for member in guild.members: members_by_status[guild.id][member.status] += 1 -- cgit v1.2.3 From 189a5db5010b7337278b8b534b8e76ec0b1a6acb Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:23:15 +0100 Subject: Track member joins and leaves. --- bot/cogs/metrics.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index e356ba294..ce58f763f 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -1,6 +1,6 @@ from collections import defaultdict -from discord import Status +from discord import Member from discord.ext.commands import Cog from prometheus_client import Gauge @@ -35,6 +35,20 @@ class Metrics(Cog): for status, count in members.items(): self.guild_members.labels(guild_id=guild_id, status=str(status)).set(count) + @Cog.listener() + async def on_member_join(self, member: Member) -> None: + self.guild_members.labels(guild_id=member.guild.id, status=str(member.status)).inc() + + @Cog.listener() + async def on_member_leave(self, member: Member) -> None: + self.guild_members.labels(guild_id=member.guild.id, status=str(member.status)).dec() + + @Cog.listener() + async def on_member_update(self, before: Member, after: Member) -> None: + if before.status is not after.status: + self.guild_members.labels(guild_id=after.guild.id, status=str(before.status)).dec() + self.guild_members.labels(guild_id=after.guild.id, status=str(after.status)).inc() + def setup(bot: Bot) -> None: """Load the Metrics cog.""" -- cgit v1.2.3 From bf361f0474dadba0519bc9019153398761758124 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:27:57 +0100 Subject: Track messages by channel. --- bot/cogs/metrics.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index ce58f763f..dc86f8e82 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -1,8 +1,8 @@ from collections import defaultdict -from discord import Member +from discord import Member, Message from discord.ext.commands import Cog -from prometheus_client import Gauge +from prometheus_client import Counter, Gauge from bot.bot import Bot @@ -16,10 +16,15 @@ class Metrics(Cog): self.bot = bot self.guild_members = Gauge( - name=f'{self.PREFIX}server_members', + name=f'{self.PREFIX}guild_members', documentation="Total members by status.", labelnames=('guild_id', 'status') ) + self.guild_messages = Counter( + name=f'{self.PREFIX}guild_messages', + documentation="Guild messages by channel.", + labelnames=('channel_id', 'channel_name') + ) @Cog.listener() async def on_ready(self) -> None: @@ -49,6 +54,12 @@ class Metrics(Cog): self.guild_members.labels(guild_id=after.guild.id, status=str(before.status)).dec() self.guild_members.labels(guild_id=after.guild.id, status=str(after.status)).inc() + @Cog.listener() + async def on_message(self, message: Message) -> None: + self.guild_messages.labels( + channel_id=message.channel.id, channel_name=message.channel.name + ).inc() + def setup(bot: Bot) -> None: """Load the Metrics cog.""" -- cgit v1.2.3 From 08ec201cafd1a276bf8f571c471dab911833cf44 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:32:33 +0100 Subject: Add documentation. --- bot/cogs/metrics.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index dc86f8e82..5d0ce4f98 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -8,7 +8,11 @@ from bot.bot import Bot class Metrics(Cog): - """Exports metrics for Prometheus.""" + """ + Exports metrics for Prometheus. + + See https://github.com/prometheus/client_python for metric documentation. + """ PREFIX = 'pydis_bot_' @@ -28,6 +32,7 @@ class Metrics(Cog): @Cog.listener() async def on_ready(self) -> None: + """Initialize the guild member counter.""" members_by_status = defaultdict(lambda: defaultdict(int)) for guild in self.bot.guilds: @@ -42,20 +47,24 @@ class Metrics(Cog): @Cog.listener() async def on_member_join(self, member: Member) -> None: + """Increment the member gauge.""" self.guild_members.labels(guild_id=member.guild.id, status=str(member.status)).inc() @Cog.listener() async def on_member_leave(self, member: Member) -> None: + """Decrement the member gauge.""" self.guild_members.labels(guild_id=member.guild.id, status=str(member.status)).dec() @Cog.listener() async def on_member_update(self, before: Member, after: Member) -> None: + """Update member gauges for the new and old status if applicable.""" if before.status is not after.status: self.guild_members.labels(guild_id=after.guild.id, status=str(before.status)).dec() self.guild_members.labels(guild_id=after.guild.id, status=str(after.status)).inc() @Cog.listener() async def on_message(self, message: Message) -> None: + """Increment the guild message counter.""" self.guild_messages.labels( channel_id=message.channel.id, channel_name=message.channel.name ).inc() -- cgit v1.2.3 From 35ac7a4e02170819c34ad7626d785edab8198f33 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 11:43:39 +0100 Subject: Add Guild ID to message counter. --- bot/cogs/metrics.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index 5d0ce4f98..0879e2221 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -21,13 +21,13 @@ class Metrics(Cog): self.guild_members = Gauge( name=f'{self.PREFIX}guild_members', - documentation="Total members by status.", + documentation="Total members by guild by status.", labelnames=('guild_id', 'status') ) self.guild_messages = Counter( name=f'{self.PREFIX}guild_messages', - documentation="Guild messages by channel.", - labelnames=('channel_id', 'channel_name') + documentation="Guild messages by guild by channel.", + labelnames=('channel_id', 'guild_id', 'channel_name') ) @Cog.listener() @@ -66,7 +66,9 @@ class Metrics(Cog): async def on_message(self, message: Message) -> None: """Increment the guild message counter.""" self.guild_messages.labels( - channel_id=message.channel.id, channel_name=message.channel.name + channel_id=message.channel.id, + channel_name=message.channel.name, + guild_id=message.guild.id, ).inc() -- cgit v1.2.3 From f464f8422a9d33fc09f9a977b011ce362e7e94c3 Mon Sep 17 00:00:00 2001 From: Thomas Petersson Date: Sun, 12 Jan 2020 16:14:05 +0100 Subject: feat(!rules): allow for throw away words after the command call --- bot/cogs/alias.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index c1db38462..2b499a537 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -3,7 +3,10 @@ import logging from typing import Union from discord import Colour, Embed, Member, User -from discord.ext.commands import Cog, Command, Context, clean_content, command, group +from discord.ext.commands import ( + Cog, Command, Context, clean_content, + command, Greedy, group, +) from bot.bot import Bot from bot.cogs.extensions import Extension @@ -81,7 +84,7 @@ class Alias (Cog): await self.invoke(ctx, "site faq") @command(name="rules", aliases=("rule",), hidden=True) - async def site_rules_alias(self, ctx: Context, *rules: int) -> None: + async def site_rules_alias(self, ctx: Context, rules: Greedy[int], *_: str) -> None: """Alias for invoking site rules.""" await self.invoke(ctx, "site rules", *rules) -- cgit v1.2.3 From 1016ac97c8ae8629387d064cdce88449b8e8d342 Mon Sep 17 00:00:00 2001 From: Thomas Petersson Date: Sun, 12 Jan 2020 16:23:11 +0100 Subject: fix(lint): wrong import order --- bot/cogs/alias.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index 2b499a537..d05a6a715 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -4,8 +4,8 @@ from typing import Union from discord import Colour, Embed, Member, User from discord.ext.commands import ( - Cog, Command, Context, clean_content, - command, Greedy, group, + Cog, Command, Context, Greedy, + clean_content, command, group, ) from bot.bot import Bot -- cgit v1.2.3 From 4dd4efc49536210ad8eaea6449e204b2533d8438 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 16:51:23 +0100 Subject: Remove underline from `self.PREFIX`. --- bot/cogs/metrics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index 0879e2221..6d3cc67bd 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -14,18 +14,18 @@ class Metrics(Cog): See https://github.com/prometheus/client_python for metric documentation. """ - PREFIX = 'pydis_bot_' + PREFIX = 'pydis_bot' def __init__(self, bot: Bot) -> None: self.bot = bot self.guild_members = Gauge( - name=f'{self.PREFIX}guild_members', + name=f'{self.PREFIX}_guild_members', documentation="Total members by guild by status.", labelnames=('guild_id', 'status') ) self.guild_messages = Counter( - name=f'{self.PREFIX}guild_messages', + name=f'{self.PREFIX}_guild_messages', documentation="Guild messages by guild by channel.", labelnames=('channel_id', 'guild_id', 'channel_name') ) -- cgit v1.2.3 From 439f70421dc3afd9ea2bc0cb3a966960b7062fdb Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 17:21:37 +0100 Subject: Empty commit for autodeploy. --- bot/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/__init__.py b/bot/__init__.py index 4a2df730d..789ace5c0 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -6,6 +6,7 @@ from pathlib import Path from logmatic import JsonFormatter + logging.TRACE = 5 logging.addLevelName(logging.TRACE, "TRACE") -- cgit v1.2.3 From 7805f8ad212bfc23391dd49f311501eed2c66166 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 20:02:17 +0100 Subject: Track command completions. --- bot/cogs/metrics.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index 6d3cc67bd..aff1b0eb2 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -1,7 +1,7 @@ from collections import defaultdict from discord import Member, Message -from discord.ext.commands import Cog +from discord.ext.commands import Cog, Context from prometheus_client import Counter, Gauge from bot.bot import Bot @@ -29,6 +29,11 @@ class Metrics(Cog): documentation="Guild messages by guild by channel.", labelnames=('channel_id', 'guild_id', 'channel_name') ) + self.command_completions = Counter( + name=f'{self.PREFIX}_command_completions', + documentation="Completed commands by command by guild.", + labelnames=('guild_id', 'command') + ) @Cog.listener() async def on_ready(self) -> None: @@ -71,6 +76,27 @@ class Metrics(Cog): guild_id=message.guild.id, ).inc() + @Cog.listener() + async def on_command_completion(self, ctx: Context) -> None: + """Increment the command completion counter.""" + if ctx.message.guild is not None: + path = [] + if ( + ctx.command.root_parent is not None + and ctx.command.root_parent is not ctx.command.parent + ): + path.append(ctx.command.root_parent.name) + + if ctx.command.parent is not None: + path.append(ctx.command.parent.name) + + path.append(ctx.command.name) + + self.command_completions.labels( + guild_id=ctx.message.guild.id, + command=' '.join(path), + ).inc() + def setup(bot: Bot) -> None: """Load the Metrics cog.""" -- cgit v1.2.3 From b0b9cdd0d54230c50bf6b4620909477d4adde8b7 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 20:10:23 +0100 Subject: Use parent path properly. --- bot/cogs/metrics.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index aff1b0eb2..fa858bbd6 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -80,21 +80,14 @@ class Metrics(Cog): async def on_command_completion(self, ctx: Context) -> None: """Increment the command completion counter.""" if ctx.message.guild is not None: - path = [] - if ( - ctx.command.root_parent is not None - and ctx.command.root_parent is not ctx.command.parent - ): - path.append(ctx.command.root_parent.name) - - if ctx.command.parent is not None: - path.append(ctx.command.parent.name) - - path.append(ctx.command.name) + if ctx.command.full_parent_name: + command = f'{ctx.command.full_parent_name} {ctx.command.name}' + else: + command = ctx.command.name self.command_completions.labels( guild_id=ctx.message.guild.id, - command=' '.join(path), + command=command, ).inc() -- cgit v1.2.3 From 82d2d5db0343ba0d2922727ebe8eae2eee57bac1 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 21:31:20 +0100 Subject: Track command completions by user. --- bot/cogs/metrics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index fa858bbd6..603057854 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -31,8 +31,8 @@ class Metrics(Cog): ) self.command_completions = Counter( name=f'{self.PREFIX}_command_completions', - documentation="Completed commands by command by guild.", - labelnames=('guild_id', 'command') + documentation="Completed commands by command by user by guild.", + labelnames=('guild_id', 'user_id', 'user_name', 'command') ) @Cog.listener() @@ -87,6 +87,8 @@ class Metrics(Cog): self.command_completions.labels( guild_id=ctx.message.guild.id, + user_id=ctx.author.id, + user_name=str(ctx.author), command=command, ).inc() -- cgit v1.2.3 From 99088f157b8cb818d333733c3c566e7a57c2de1b Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sun, 12 Jan 2020 23:09:23 +0100 Subject: Use commas instead of `by`. --- bot/cogs/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/metrics.py b/bot/cogs/metrics.py index 603057854..47c3cc55e 100644 --- a/bot/cogs/metrics.py +++ b/bot/cogs/metrics.py @@ -31,7 +31,7 @@ class Metrics(Cog): ) self.command_completions = Counter( name=f'{self.PREFIX}_command_completions', - documentation="Completed commands by command by user by guild.", + documentation="Completed commands by command, user, and guild.", labelnames=('guild_id', 'user_id', 'user_name', 'command') ) -- cgit v1.2.3 From 09b93a9cabc7cf05f0d6b06d26086f713c927dbf Mon Sep 17 00:00:00 2001 From: Numerlor Date: Mon, 13 Jan 2020 09:20:57 +0100 Subject: Replace sphinx mock classes with SimpleNamespaces; add user_agent to config --- bot/cogs/doc.py | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index cb6f4f972..6e7c00b6a 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -5,6 +5,7 @@ import re import textwrap from collections import OrderedDict from contextlib import suppress +from types import SimpleNamespace from typing import Any, Callable, Optional, Tuple import discord @@ -27,6 +28,16 @@ from bot.pagination import LinePaginator log = logging.getLogger(__name__) logging.getLogger('urllib3').setLevel(logging.WARNING) +# Since Intersphinx is intended to be used with Sphinx, +# we need to mock its configuration. +SPHINX_MOCK_APP = SimpleNamespace( + config=SimpleNamespace( + intersphinx_timeout=3, + tls_verify=True, + user_agent="python3:python-discord/bot:1.0.0" + ) +) + NO_OVERRIDE_GROUPS = ( "2to3fixer", "token", @@ -102,18 +113,6 @@ def markdownify(html: str) -> DocMarkdownConverter: return DocMarkdownConverter(bullets='•').convert(html) -class DummyObject(object): - """A dummy object which supports assigning anything, which the builtin `object()` does not support normally.""" - - -class SphinxConfiguration: - """Dummy configuration for use with intersphinx.""" - - config = DummyObject() - config.intersphinx_timeout = 3 - config.tls_verify = True - - class InventoryURL(commands.Converter): """ Represents an Intersphinx inventory URL. @@ -128,7 +127,7 @@ class InventoryURL(commands.Converter): async def convert(ctx: commands.Context, url: str) -> str: """Convert url to Intersphinx inventory URL.""" try: - intersphinx.fetch_inventory(SphinxConfiguration(), '', url) + intersphinx.fetch_inventory(SPHINX_MOCK_APP, '', url) except AttributeError: raise commands.BadArgument(f"Failed to fetch Intersphinx inventory from URL `{url}`.") except ConnectionError: @@ -162,7 +161,7 @@ class Doc(commands.Cog): await self.refresh_inventory() async def update_single( - self, package_name: str, base_url: str, inventory_url: str, config: SphinxConfiguration + self, package_name: str, base_url: str, inventory_url: str ) -> None: """ Rebuild the inventory for a single package. @@ -173,12 +172,10 @@ class Doc(commands.Cog): absolute paths that link to specific symbols * `inventory_url` is the absolute URL to the intersphinx inventory, fetched by running `intersphinx.fetch_inventory` in an executor on the bot's event loop - * `config` is a `SphinxConfiguration` instance to mock the regular sphinx - project layout, required for use with intersphinx """ self.base_urls[package_name] = base_url - package = await self._fetch_inventory(inventory_url, config) + package = await self._fetch_inventory(inventory_url) if not package: return None @@ -220,15 +217,11 @@ class Doc(commands.Cog): self.renamed_symbols.clear() async_cache.cache = OrderedDict() - # Since Intersphinx is intended to be used with Sphinx, - # we need to mock its configuration. - config = SphinxConfiguration() - # Run all coroutines concurrently - since each of them performs a HTTP # request, this speeds up fetching the inventory data heavily. coros = [ self.update_single( - package["package"], package["base_url"], package["inventory_url"], config + package["package"], package["base_url"], package["inventory_url"] ) for package in await self.bot.api_client.get('bot/documentation-links') ] await asyncio.gather(*coros) @@ -476,9 +469,9 @@ class Doc(commands.Cog): ) await ctx.send(embed=embed) - async def _fetch_inventory(self, inventory_url: str, config: SphinxConfiguration) -> Optional[dict]: + async def _fetch_inventory(self, inventory_url: str) -> Optional[dict]: """Get and return inventory from `inventory_url`. If fetching fails, return None.""" - fetch_func = functools.partial(intersphinx.fetch_inventory, config, '', inventory_url) + fetch_func = functools.partial(intersphinx.fetch_inventory, SPHINX_MOCK_APP, '', inventory_url) for retry in range(1, FAILED_REQUEST_RETRY_AMOUNT+1): try: package = await self.bot.loop.run_in_executor(None, fetch_func) -- cgit v1.2.3 From e56db510cfd7238cffc2353b8bf79b7d0f709209 Mon Sep 17 00:00:00 2001 From: Joseph Date: Tue, 14 Jan 2020 12:08:38 +0000 Subject: Add new partners to invite whitelist --- config-default.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config-default.yml b/config-default.yml index bf8509544..18a2da869 100644 --- a/config-default.yml +++ b/config-default.yml @@ -204,6 +204,8 @@ filter: - 197038439483310086 # Discord Testers - 286633898581164032 # Ren'Py - 349505959032389632 # PyGame + - 438622377094414346 # Pyglet + - 524691714909274162 # Panda3D domain_blacklist: - pornhub.com -- cgit v1.2.3 From 1abe1768ed06c6e0c99ac2b008cc0b7f562f92d5 Mon Sep 17 00:00:00 2001 From: Daniel Brown Date: Tue, 14 Jan 2020 10:27:16 -0600 Subject: Discord.py server added to whitelist Added discord.py's server to the invite whitelist --- config-default.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config-default.yml b/config-default.yml index 18a2da869..f66ba8794 100644 --- a/config-default.yml +++ b/config-default.yml @@ -206,6 +206,7 @@ filter: - 349505959032389632 # PyGame - 438622377094414346 # Pyglet - 524691714909274162 # Panda3D + - 336642139381301249 # discord.py domain_blacklist: - pornhub.com -- cgit v1.2.3