From df897e57ea0dbbaeb3e2a07a1e443c5b2df34c36 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Mon, 30 Nov 2020 22:26:23 +0300 Subject: Adds Member Checks Before Changing Voice Adds a check that checks if the user object is an instance of guild member, before performing guild operations. Adds tests. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/exts/moderation/infraction/infractions.py | 12 ++++++++++-- tests/bot/exts/moderation/infraction/test_infractions.py | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py index 18e937e87..78c326c47 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -257,6 +257,10 @@ class Infractions(InfractionScheduler, commands.Cog): self.mod_log.ignore(Event.member_update, user.id) async def action() -> None: + # Skip members that left the server + if not isinstance(user, Member): + return + await user.add_roles(self._muted_role, reason=reason) log.trace(f"Attempting to kick {user} from voice because they've been muted.") @@ -351,9 +355,13 @@ class Infractions(InfractionScheduler, commands.Cog): if reason: reason = textwrap.shorten(reason, width=512, placeholder="...") - await user.move_to(None, reason="Disconnected from voice to apply voiceban.") + action = None + + # Skip members that left the server + if isinstance(user, Member): + await user.move_to(None, reason="Disconnected from voice to apply voiceban.") + action = user.remove_roles(self._voice_verified_role, reason=reason) - action = user.remove_roles(self._voice_verified_role, reason=reason) await self.apply_infraction(ctx, infraction, user, action) # endregion diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py index bf557a484..4ba5a4feb 100644 --- a/tests/bot/exts/moderation/infraction/test_infractions.py +++ b/tests/bot/exts/moderation/infraction/test_infractions.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch from bot.constants import Event from bot.exts.moderation.infraction.infractions import Infractions -from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole +from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockUser class TruncationTests(unittest.IsolatedAsyncioTestCase): @@ -164,6 +164,19 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): ) self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, "my_return_value") + @patch("bot.exts.moderation.infraction._utils.get_active_infraction", return_value=None) + @patch("bot.exts.moderation.infraction._utils.post_infraction") + @patch("bot.exts.moderation.infraction.infractions.Infractions.apply_infraction") + async def test_voice_ban_user_left_guild(self, apply_infraction_mock, post_infraction_mock, _): + """Should voice ban user that left the guild without throwing an error.""" + infraction = {"foo": "bar"} + post_infraction_mock.return_value = {"foo": "bar"} + + user = MockUser() + await self.cog.voiceban(self.cog, self.ctx, user, reason=None) + post_infraction_mock.assert_called_once_with(self.ctx, user, "voice_ban", None, active=True) + apply_infraction_mock.assert_called_once_with(self.ctx, infraction, user, None) + async def test_voice_unban_user_not_found(self): """Should include info to return dict when user was not found from guild.""" self.guild.get_member.return_value = None -- cgit v1.2.3 From 5e93396e1655715187d176d1cfdd32aa54616a1a Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 16 Jan 2021 18:03:38 +0100 Subject: Add an allow_moderation_roles argument to the wait_for_deletion() util The `allow_moderation_roles` bool can be specified to allow anyone with a role in `MODERATION_ROLES` to delete the message. --- bot/utils/messages.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/utils/messages.py b/bot/utils/messages.py index 42bde358d..b0b6cbf82 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -11,7 +11,7 @@ from discord.errors import HTTPException from discord.ext.commands import Context import bot -from bot.constants import Emojis, NEGATIVE_REPLIES +from bot.constants import Emojis, NEGATIVE_REPLIES, MODERATION_ROLES log = logging.getLogger(__name__) @@ -22,12 +22,15 @@ async def wait_for_deletion( deletion_emojis: Sequence[str] = (Emojis.trashcan,), timeout: float = 60 * 5, attach_emojis: bool = True, + allow_moderation_roles: bool = True ) -> None: """ Wait for up to `timeout` seconds for a reaction by any of the specified `user_ids` to delete the message. An `attach_emojis` bool may be specified to determine whether to attach the given `deletion_emojis` to the message in the given `context`. + An `allow_moderation_roles` bool may also be specified to allow anyone with a role in `MODERATION_ROLES` to delete + the message. """ if message.guild is None: raise ValueError("Message must be sent on a guild") @@ -46,6 +49,7 @@ async def wait_for_deletion( reaction.message.id == message.id and str(reaction.emoji) in deletion_emojis and user.id in user_ids + or allow_moderation_roles and any(role.id in MODERATION_ROLES for role in user.roles) ) with contextlib.suppress(asyncio.TimeoutError): -- cgit v1.2.3 From eb78b9c261ecbcea4b2ca5bb0d423f7b3bfb9a0f Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 16 Jan 2021 18:55:26 +0100 Subject: Restrict paginator usage to the author and moderators --- bot/pagination.py | 13 ++++++++++--- bot/utils/messages.py | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bot/pagination.py b/bot/pagination.py index 182b2fa76..09dbad7b5 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -8,6 +8,7 @@ from discord.abc import User from discord.ext.commands import Context, Paginator from bot import constants +from bot.constants import MODERATION_ROLES FIRST_EMOJI = "\u23EE" # [:track_previous:] LEFT_EMOJI = "\u2B05" # [:arrow_left:] @@ -210,6 +211,9 @@ class LinePaginator(Paginator): Pagination will also be removed automatically if no reaction is added for five minutes (300 seconds). + The interaction will be limited to `restrict_to_user` (ctx.author by default) or + to any user with a moderation role. + Example: >>> embed = discord.Embed() >>> embed.set_author(name="Some Operation", url=url, icon_url=icon) @@ -218,10 +222,10 @@ class LinePaginator(Paginator): def event_check(reaction_: discord.Reaction, user_: discord.Member) -> bool: """Make sure that this reaction is what we want to operate on.""" no_restrictions = ( - # Pagination is not restricted - not restrict_to_user # The reaction was by a whitelisted user - or user_.id == restrict_to_user.id + user_.id == restrict_to_user.id + # The reaction was by a moderator + or any(role.id in MODERATION_ROLES for role in user_.roles) ) return ( @@ -242,6 +246,9 @@ class LinePaginator(Paginator): scale_to_size=scale_to_size) current_page = 0 + if not restrict_to_user: + restrict_to_user = ctx.author + if not lines: if exception_on_empty_embed: log.exception("Pagination asked for empty lines iterable") diff --git a/bot/utils/messages.py b/bot/utils/messages.py index b0b6cbf82..832ad4d55 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -11,7 +11,7 @@ from discord.errors import HTTPException from discord.ext.commands import Context import bot -from bot.constants import Emojis, NEGATIVE_REPLIES, MODERATION_ROLES +from bot.constants import Emojis, MODERATION_ROLES, NEGATIVE_REPLIES log = logging.getLogger(__name__) -- cgit v1.2.3 From 37bfd7569cdeb0900ee131c22f67aed3f78692ba Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Wed, 20 Jan 2021 20:19:47 +0300 Subject: Cleans Up Tests --- tests/bot/exts/moderation/infraction/test_infractions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py index 4ba5a4feb..13efee054 100644 --- a/tests/bot/exts/moderation/infraction/test_infractions.py +++ b/tests/bot/exts/moderation/infraction/test_infractions.py @@ -3,8 +3,9 @@ import unittest from unittest.mock import AsyncMock, MagicMock, Mock, patch from bot.constants import Event +from bot.exts.moderation.infraction import _utils from bot.exts.moderation.infraction.infractions import Infractions -from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockUser +from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockUser, autospec class TruncationTests(unittest.IsolatedAsyncioTestCase): @@ -164,9 +165,8 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): ) self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, "my_return_value") - @patch("bot.exts.moderation.infraction._utils.get_active_infraction", return_value=None) - @patch("bot.exts.moderation.infraction._utils.post_infraction") - @patch("bot.exts.moderation.infraction.infractions.Infractions.apply_infraction") + @autospec(_utils, "post_infraction", "get_active_infraction", return_value=None) + @autospec(Infractions, "apply_infraction") async def test_voice_ban_user_left_guild(self, apply_infraction_mock, post_infraction_mock, _): """Should voice ban user that left the guild without throwing an error.""" infraction = {"foo": "bar"} @@ -175,7 +175,7 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): user = MockUser() await self.cog.voiceban(self.cog, self.ctx, user, reason=None) post_infraction_mock.assert_called_once_with(self.ctx, user, "voice_ban", None, active=True) - apply_infraction_mock.assert_called_once_with(self.ctx, infraction, user, None) + apply_infraction_mock.assert_called_once_with(self.cog, self.ctx, infraction, user, None) async def test_voice_unban_user_not_found(self): """Should include info to return dict when user was not found from guild.""" -- cgit v1.2.3 From 06c5eba63f735237b44f2b1da9944bc7cddd2177 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Thu, 21 Jan 2021 01:26:41 +0300 Subject: Restructures Voice Ban Action Updates the voice ban action so the infraction pardoning is always run, and so all operations are handled in the _scheduler. Updates tests. --- bot/exts/moderation/infraction/infractions.py | 11 ++++--- .../exts/moderation/infraction/test_infractions.py | 37 +++++++++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py index 78c326c47..be4327bb0 100644 --- a/bot/exts/moderation/infraction/infractions.py +++ b/bot/exts/moderation/infraction/infractions.py @@ -355,14 +355,15 @@ class Infractions(InfractionScheduler, commands.Cog): if reason: reason = textwrap.shorten(reason, width=512, placeholder="...") - action = None + async def action() -> None: + # Skip members that left the server + if not isinstance(user, Member): + return - # Skip members that left the server - if isinstance(user, Member): await user.move_to(None, reason="Disconnected from voice to apply voiceban.") - action = user.remove_roles(self._voice_verified_role, reason=reason) + await user.remove_roles(self._voice_verified_role, reason=reason) - await self.apply_infraction(ctx, infraction, user, action) + await self.apply_infraction(ctx, infraction, user, action()) # endregion # region: Base pardon functions diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py index 13efee054..86c2617ea 100644 --- a/tests/bot/exts/moderation/infraction/test_infractions.py +++ b/tests/bot/exts/moderation/infraction/test_infractions.py @@ -1,6 +1,7 @@ +import inspect import textwrap import unittest -from unittest.mock import AsyncMock, MagicMock, Mock, patch +from unittest.mock import ANY, AsyncMock, MagicMock, Mock, patch from bot.constants import Event from bot.exts.moderation.infraction import _utils @@ -133,20 +134,29 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): self.assertIsNone(await self.cog.apply_voice_ban(self.ctx, self.user, "foobar")) self.cog.mod_log.ignore.assert_called_once_with(Event.member_update, self.user.id) + async def action_tester(self, action, reason: str) -> None: + """Helper method to test voice ban action.""" + self.assertTrue(inspect.iscoroutine(action)) + await action + + self.user.move_to.assert_called_once_with(None, reason=ANY) + self.user.remove_roles.assert_called_once_with(self.cog._voice_verified_role, reason=reason) + @patch("bot.exts.moderation.infraction.infractions._utils.post_infraction") @patch("bot.exts.moderation.infraction.infractions._utils.get_active_infraction") async def test_voice_ban_apply_infraction(self, get_active_infraction, post_infraction_mock): """Should ignore Voice Verified role removing.""" self.cog.mod_log.ignore = MagicMock() self.cog.apply_infraction = AsyncMock() - self.user.remove_roles = MagicMock(return_value="my_return_value") get_active_infraction.return_value = None post_infraction_mock.return_value = {"foo": "bar"} - self.assertIsNone(await self.cog.apply_voice_ban(self.ctx, self.user, "foobar")) - self.user.remove_roles.assert_called_once_with(self.cog._voice_verified_role, reason="foobar") - self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, "my_return_value") + reason = "foobar" + self.assertIsNone(await self.cog.apply_voice_ban(self.ctx, self.user, reason)) + self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, ANY) + + await self.action_tester(self.cog.apply_infraction.call_args[0][-1], reason) @patch("bot.exts.moderation.infraction.infractions._utils.post_infraction") @patch("bot.exts.moderation.infraction.infractions._utils.get_active_infraction") @@ -154,16 +164,16 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): """Should truncate reason for voice ban.""" self.cog.mod_log.ignore = MagicMock() self.cog.apply_infraction = AsyncMock() - self.user.remove_roles = MagicMock(return_value="my_return_value") get_active_infraction.return_value = None post_infraction_mock.return_value = {"foo": "bar"} self.assertIsNone(await self.cog.apply_voice_ban(self.ctx, self.user, "foobar" * 3000)) - self.user.remove_roles.assert_called_once_with( - self.cog._voice_verified_role, reason=textwrap.shorten("foobar" * 3000, 512, placeholder="...") - ) - self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, "my_return_value") + self.cog.apply_infraction.assert_awaited_once_with(self.ctx, {"foo": "bar"}, self.user, ANY) + + # Test action + action = self.cog.apply_infraction.call_args[0][-1] + await self.action_tester(action, textwrap.shorten("foobar" * 3000, 512, placeholder="...")) @autospec(_utils, "post_infraction", "get_active_infraction", return_value=None) @autospec(Infractions, "apply_infraction") @@ -175,7 +185,12 @@ class VoiceBanTests(unittest.IsolatedAsyncioTestCase): user = MockUser() await self.cog.voiceban(self.cog, self.ctx, user, reason=None) post_infraction_mock.assert_called_once_with(self.ctx, user, "voice_ban", None, active=True) - apply_infraction_mock.assert_called_once_with(self.cog, self.ctx, infraction, user, None) + apply_infraction_mock.assert_called_once_with(self.cog, self.ctx, infraction, user, ANY) + + # Test action + action = self.cog.apply_infraction.call_args[0][-1] + self.assertTrue(inspect.iscoroutine(action)) + await action async def test_voice_unban_user_not_found(self): """Should include info to return dict when user was not found from guild.""" -- cgit v1.2.3 From a41b932436b8b5c07c0f5ea3bd7ee886fa01e88e Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Thu, 21 Jan 2021 02:25:46 +0300 Subject: Updates Apply Infraction Docstring --- bot/exts/moderation/infraction/_scheduler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index 242b2d30f..a73f2e8da 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -102,6 +102,7 @@ class InfractionScheduler: """ Apply an infraction to the user, log the infraction, and optionally notify the user. + `action_coro`, if not provided, will result in the infraction not getting scheduled for deletion. `user_reason`, if provided, will be sent to the user in place of the infraction reason. `additional_info` will be attached to the text field in the mod-log embed. -- cgit v1.2.3 From 576146af6a01b387c6b0abec1309169aa77a5bf2 Mon Sep 17 00:00:00 2001 From: PH-KDX <50588793+PH-KDX@users.noreply.github.com> Date: Fri, 22 Jan 2021 16:23:39 +0100 Subject: Create voice.md This tag would provide info for users which are not voice-verified, so that they can easily be directed toward the appropriate channel. --- bot/resources/tags/voice.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 bot/resources/tags/voice.md diff --git a/bot/resources/tags/voice.md b/bot/resources/tags/voice.md new file mode 100644 index 000000000..3d88b0c71 --- /dev/null +++ b/bot/resources/tags/voice.md @@ -0,0 +1,3 @@ +**Voice verification** + +Can’t talk in voice chat? Check out <#764802555427029012> to get access. The criteria for verifying are specified there. -- cgit v1.2.3 From eb45e9895a0bf1b294bbdca9b154ee433942989a Mon Sep 17 00:00:00 2001 From: PH-KDX <50588793+PH-KDX@users.noreply.github.com> Date: Sat, 23 Jan 2021 19:46:09 +0100 Subject: Rename voice.md to voice-verification.md --- bot/resources/tags/voice-verification.md | 3 +++ bot/resources/tags/voice.md | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 bot/resources/tags/voice-verification.md delete mode 100644 bot/resources/tags/voice.md diff --git a/bot/resources/tags/voice-verification.md b/bot/resources/tags/voice-verification.md new file mode 100644 index 000000000..3d88b0c71 --- /dev/null +++ b/bot/resources/tags/voice-verification.md @@ -0,0 +1,3 @@ +**Voice verification** + +Can’t talk in voice chat? Check out <#764802555427029012> to get access. The criteria for verifying are specified there. diff --git a/bot/resources/tags/voice.md b/bot/resources/tags/voice.md deleted file mode 100644 index 3d88b0c71..000000000 --- a/bot/resources/tags/voice.md +++ /dev/null @@ -1,3 +0,0 @@ -**Voice verification** - -Can’t talk in voice chat? Check out <#764802555427029012> to get access. The criteria for verifying are specified there. -- cgit v1.2.3 From 46ff6949ac583fb705c52431297e6c7a47ad231b Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 24 Jan 2021 17:39:36 +0100 Subject: Make sure that the paginator doesn't choke on DMs --- bot/pagination.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/pagination.py b/bot/pagination.py index 09dbad7b5..3b16cc9ff 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -4,6 +4,7 @@ import typing as t from contextlib import suppress import discord +from discord import Member from discord.abc import User from discord.ext.commands import Context, Paginator @@ -225,7 +226,7 @@ class LinePaginator(Paginator): # The reaction was by a whitelisted user user_.id == restrict_to_user.id # The reaction was by a moderator - or any(role.id in MODERATION_ROLES for role in user_.roles) + or isinstance(user_, Member) and any(role.id in MODERATION_ROLES for role in user_.roles) ) return ( -- cgit v1.2.3 From 9dea9e6fa57682b94b93f7ff6567d58862ada7ed Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 28 Jan 2021 21:57:54 +0000 Subject: catch the response error and deal with it --- bot/converters.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/converters.py b/bot/converters.py index d0a9731d6..880b1fe38 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -575,7 +575,10 @@ class Infraction(Converter): return infractions[0] else: - return await ctx.bot.api_client.get(f"bot/infractions/{arg}") + try: + return await ctx.bot.api_client.get(f"bot/infractions/{arg}") + except ResponseCodeError: + raise BadArgument("The provided infraction could not be found.") Expiry = t.Union[Duration, ISODateTime] -- cgit v1.2.3 From 3f0565b04fa08f7d799bc5b05af6f0926800aaa1 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 29 Jan 2021 22:16:51 +0000 Subject: handle within the error handler --- bot/converters.py | 5 +---- bot/exts/backend/error_handler.py | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/converters.py b/bot/converters.py index 880b1fe38..d0a9731d6 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -575,10 +575,7 @@ class Infraction(Converter): return infractions[0] else: - try: - return await ctx.bot.api_client.get(f"bot/infractions/{arg}") - except ResponseCodeError: - raise BadArgument("The provided infraction could not be found.") + return await ctx.bot.api_client.get(f"bot/infractions/{arg}") Expiry = t.Union[Duration, ISODateTime] diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index b8bb3757f..8923a6b3d 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -85,6 +85,12 @@ class ErrorHandler(Cog): else: await self.handle_unexpected_error(ctx, e.original) return # Exit early to avoid logging. + elif isinstance(e, errors.ConversionError): + if isinstance(e.original, ResponseCodeError): + await self.handle_api_error(ctx, e.original) + else: + await self.handle_unexpected_error(ctx, e.original) + return # Exit early to avoid logging. elif not isinstance(e, errors.DisabledCommand): # ConversionError, MaxConcurrencyReached, ExtensionError await self.handle_unexpected_error(ctx, e) -- cgit v1.2.3 From acc8bcfd2371035b315538d525a7b9231664fd34 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 29 Jan 2021 22:30:01 +0000 Subject: Remove ConversionError from comment, as its now handled above. --- bot/exts/backend/error_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index 8923a6b3d..ed7962b06 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -92,7 +92,7 @@ class ErrorHandler(Cog): await self.handle_unexpected_error(ctx, e.original) return # Exit early to avoid logging. elif not isinstance(e, errors.DisabledCommand): - # ConversionError, MaxConcurrencyReached, ExtensionError + # MaxConcurrencyReached, ExtensionError await self.handle_unexpected_error(ctx, e) return # Exit early to avoid logging. -- cgit v1.2.3 From ced0af2b2936b1fab4f5601bd7b1c00bbbd9130a Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 30 Jan 2021 15:19:17 +0100 Subject: Make sure that restrictions also applies to moderators Without this, if a moderator add a reaction to any message, all the messages currently listening for reaction will pass the check since the user has a moderation role. --- bot/utils/messages.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/utils/messages.py b/bot/utils/messages.py index 832ad4d55..077dd9569 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -48,8 +48,10 @@ async def wait_for_deletion( return ( reaction.message.id == message.id and str(reaction.emoji) in deletion_emojis - and user.id in user_ids - or allow_moderation_roles and any(role.id in MODERATION_ROLES for role in user.roles) + and ( + user.id in user_ids + or allow_moderation_roles and any(role.id in MODERATION_ROLES for role in user.roles) + ) ) with contextlib.suppress(asyncio.TimeoutError): -- cgit v1.2.3 From 8130b34fe0d9c59b75294844b9f05942a4e6b5ad Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 30 Jan 2021 22:24:36 +0000 Subject: Protect against overflows caused by large expirations --- bot/converters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/converters.py b/bot/converters.py index d0a9731d6..0d9a519df 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -350,7 +350,7 @@ class Duration(DurationDelta): try: return now + delta - except ValueError: + except (ValueError, OverflowError): raise BadArgument(f"`{duration}` results in a datetime outside the supported range.") -- cgit v1.2.3 From 7432f580300c58321d3a37695779026fe5e51f8c Mon Sep 17 00:00:00 2001 From: wookie184 Date: Tue, 2 Feb 2021 14:16:17 +0000 Subject: Add tag on float imprecision Adds a tag on float imprecision --- bot/resources/tags/floats.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 bot/resources/tags/floats.md diff --git a/bot/resources/tags/floats.md b/bot/resources/tags/floats.md new file mode 100644 index 000000000..4b0651930 --- /dev/null +++ b/bot/resources/tags/floats.md @@ -0,0 +1,19 @@ +**Floating Point Arithmetic** +You may have noticed that when doing arithmetic with floats in Python you sometimes get strange results, like this: +```python +>>> 0.1 + 0.2 +0.30000000000000004 +``` +**Why this happens** +Internally your computer stores floats as as binary fractions. Many decimal values cannot be stored as exact binary fractions, which means an approximation has to be used. + +**How you can avoid this** +If you require an exact decimal representation, you can use the [decimal](https://docs.python.org/3/library/decimal.html) or [fractions](https://docs.python.org/3/library/fractions.html) module. Here is an example using the decimal module: +```python +>>> from decimal import Decimal +>>> Decimal('0.1') + Decimal('0.2') +Decimal('0.3') +``` +Note that we enter in the number we want as a string so we don't pass on the imprecision from the float. + +For more details on why this happens check out this [page in the python docs](https://docs.python.org/3/tutorial/floatingpoint.html) or this [Computerphile video](https://www.youtube.com/watch/PZRI1IfStY0). -- cgit v1.2.3 From 9c1e5fd22e71f919bcbb7e2215430a0b4d518310 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Wed, 3 Feb 2021 10:00:42 +0000 Subject: Mention math.isclose and add an example --- bot/resources/tags/floats.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/resources/tags/floats.md b/bot/resources/tags/floats.md index 4b0651930..7129b91bb 100644 --- a/bot/resources/tags/floats.md +++ b/bot/resources/tags/floats.md @@ -8,12 +8,13 @@ You may have noticed that when doing arithmetic with floats in Python you someti Internally your computer stores floats as as binary fractions. Many decimal values cannot be stored as exact binary fractions, which means an approximation has to be used. **How you can avoid this** -If you require an exact decimal representation, you can use the [decimal](https://docs.python.org/3/library/decimal.html) or [fractions](https://docs.python.org/3/library/fractions.html) module. Here is an example using the decimal module: + You can use [math.isclose](https://docs.python.org/3/library/math.html#math.isclose) to check if two floats are close, or to get an exact decimal representation, you can use the [decimal](https://docs.python.org/3/library/decimal.html) or [fractions](https://docs.python.org/3/library/fractions.html) module. Here are some examples: ```python ->>> from decimal import Decimal ->>> Decimal('0.1') + Decimal('0.2') +>>> math.isclose(0.1 + 0.2, 0.3) +True +>>> decimal.Decimal('0.1') + decimal.Decimal('0.2') Decimal('0.3') ``` -Note that we enter in the number we want as a string so we don't pass on the imprecision from the float. +Note that with `decimal.Decimal` we enter the number we want as a string so we don't pass on the imprecision from the float. For more details on why this happens check out this [page in the python docs](https://docs.python.org/3/tutorial/floatingpoint.html) or this [Computerphile video](https://www.youtube.com/watch/PZRI1IfStY0). -- cgit v1.2.3 From 47c4321b1fbd49896a6935c58e1bec9dbaf6918f Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:02:52 -0800 Subject: Added how_to_get_help constant. --- bot/constants.py | 1 + config-default.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 95e22513f..6b86d33a3 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -406,6 +406,7 @@ class Channels(metaclass=YAMLGetter): meta: int python_general: int + how_to_get_help: int cooldown: int diff --git a/config-default.yml b/config-default.yml index d3b267159..913d5ca09 100644 --- a/config-default.yml +++ b/config-default.yml @@ -158,6 +158,7 @@ guild: python_general: &PY_GENERAL 267624335836053506 # Python Help: Available + how_to_get_help: 704250143020417084 cooldown: 720603994149486673 # Topical -- cgit v1.2.3 From 0f46bf58bea83da9434b53ddfda3ce8331829588 Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:07:37 -0800 Subject: Added dynamic available help channels message --- bot/exts/help_channels/_cog.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 0995c8a79..c4ec820b5 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,6 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot +from bot.constants import Channels, Categories from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -20,6 +21,9 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ +AVAILABLE_HELP_CHANNELS = """ +Currently available help channel(s): {available} +""" class HelpChannels(commands.Cog): @@ -72,6 +76,11 @@ class HelpChannels(commands.Cog): self.last_notification: t.Optional[datetime] = None + # Acquiring and modifying the channel to dynamically update the available help channels message. + self.how_to_get_help: discord.TextChannel = None + self.available_help_channels: t.Set[int] = set() + self.dynamic_message: discord.Message = None + # Asyncio stuff self.queue_tasks: t.List[asyncio.Task] = [] self.init_task = self.bot.loop.create_task(self.init_cog()) @@ -114,6 +123,9 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) + self.available_help_channels.remove(message.channel.id) + await self.update_available_help_channels() + # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -275,6 +287,15 @@ class HelpChannels(commands.Cog): # This may confuse users. So would potentially long delays for the cog to become ready. self.close_command.enabled = True + # Getting channels that need to be included in the dynamic message. + task = asyncio.create_task(self.update_available_help_channels()) + self.queue_tasks.append(task) + + await task + + log.trace(f"Dynamic available help message updated.") + self.queue_tasks.remove(task) + await self.init_available() _stats.report_counts() @@ -332,6 +353,10 @@ class HelpChannels(commands.Cog): category_id=constants.Categories.help_available, ) + # Adding the help channel to the dynamic message, and editing/sending that message. + self.available_help_channels.add(channel.id) + await self.update_available_help_channels() + _stats.report_counts() async def move_to_dormant(self, channel: discord.TextChannel) -> None: @@ -461,3 +486,26 @@ class HelpChannels(commands.Cog): self.queue_tasks.remove(task) return channel + + async def update_available_help_channels(self) -> None: + """Updates the dynamic message within #how-to-get-help for available help channels.""" + if not self.available_help_channels: + available_channels_category = await channel_utils.try_get_channel(Categories.help_available) + self.available_help_channels = set( + c.id for c in available_channels_category.channels if 'help-' in c.name + ) + + if self.how_to_get_help is None: + self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) + + if self.dynamic_message is None: + self.dynamic_message = await self.how_to_get_help.history(limit=1).next() + + available_channels = AVAILABLE_HELP_CHANNELS.format( + available=', '.join(f"<#{c}>" for c in self.available_help_channels) + ) + + try: + await self.dynamic_message.edit(content=available_channels) + except discord.Forbidden: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From 5f17d4d19d0952c91ead096a52b206eea86851fe Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 16:18:20 -0800 Subject: Fixed up linting errors. --- bot/exts/help_channels/_cog.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index c4ec820b5..943b63a42 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Channels, Categories +from bot.constants import Categories, Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -293,7 +293,7 @@ class HelpChannels(commands.Cog): await task - log.trace(f"Dynamic available help message updated.") + log.trace("Dynamic available help message updated.") self.queue_tasks.remove(task) await self.init_available() @@ -499,7 +499,8 @@ class HelpChannels(commands.Cog): self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) if self.dynamic_message is None: - self.dynamic_message = await self.how_to_get_help.history(limit=1).next() + last_message = await self.how_to_get_help.history(limit=1) + self.dynamic_message = next(last_message) available_channels = AVAILABLE_HELP_CHANNELS.format( available=', '.join(f"<#{c}>" for c in self.available_help_channels) -- cgit v1.2.3 From 219ac90d494793a99d77ef5a4e912151e936b1d8 Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 17:12:25 -0800 Subject: Fixed logic in case dynamic message doesn't exist. --- bot/exts/help_channels/_cog.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 943b63a42..730635f08 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -495,18 +495,16 @@ class HelpChannels(commands.Cog): c.id for c in available_channels_category.channels if 'help-' in c.name ) - if self.how_to_get_help is None: - self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - - if self.dynamic_message is None: - last_message = await self.how_to_get_help.history(limit=1) - self.dynamic_message = next(last_message) - available_channels = AVAILABLE_HELP_CHANNELS.format( available=', '.join(f"<#{c}>" for c in self.available_help_channels) ) - try: - await self.dynamic_message.edit(content=available_channels) - except discord.Forbidden: - self.dynamic_message = await self.how_to_get_help.send(available_channels) + if self.how_to_get_help is None: + self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) + + if self.dynamic_message is None: + try: + self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) + await self.dynamic_message.edit(content=available_channels) + except discord.NotFound: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From 12d8670de79011dc1095293ddc7b256f033fcead Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:22:21 -0800 Subject: 'None' is now shown if no channels are available. --- bot/exts/help_channels/_cog.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 730635f08..2f14146ab 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -123,12 +123,13 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - self.available_help_channels.remove(message.channel.id) - await self.update_available_help_channels() - # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") + # Removing the help channel from the dynamic message, and editing/sending that message. + self.available_help_channels.remove(message.channel.id) + await self.update_available_help_channels() + def create_channel_queue(self) -> asyncio.Queue: """ Return a queue of dormant channels to use for getting the next available channel. @@ -496,15 +497,15 @@ class HelpChannels(commands.Cog): ) available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(f"<#{c}>" for c in self.available_help_channels) + available=', '.join(f"<#{c}>" for c in self.available_help_channels) or None ) if self.how_to_get_help is None: self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - if self.dynamic_message is None: - try: + try: + if self.dynamic_message is None: self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) - await self.dynamic_message.edit(content=available_channels) - except discord.NotFound: - self.dynamic_message = await self.how_to_get_help.send(available_channels) + await self.dynamic_message.edit(content=available_channels) + except discord.NotFound: + self.dynamic_message = await self.how_to_get_help.send(available_channels) -- cgit v1.2.3 From d2a10cd1b758625ea165e599c0271d9e43f0ab8a Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:35:11 -0800 Subject: Alphabetized config-default.yml. --- config-default.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-default.yml b/config-default.yml index 913d5ca09..fc1f3b3a8 100644 --- a/config-default.yml +++ b/config-default.yml @@ -158,8 +158,8 @@ guild: python_general: &PY_GENERAL 267624335836053506 # Python Help: Available - how_to_get_help: 704250143020417084 cooldown: 720603994149486673 + how_to_get_help: 704250143020417084 # Topical discord_py: 343944376055103488 -- cgit v1.2.3 From 3a4d38bc27dc8214af91ee8ab598a5b60897815f Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 18:53:54 -0800 Subject: Removed unnecessary update method call. --- bot/exts/help_channels/_cog.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 2f14146ab..d50197339 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -123,12 +123,11 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - # Not awaited because it may indefinitely hold the lock while waiting for a channel. - scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") - # Removing the help channel from the dynamic message, and editing/sending that message. self.available_help_channels.remove(message.channel.id) - await self.update_available_help_channels() + + # Not awaited because it may indefinitely hold the lock while waiting for a channel. + scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") def create_channel_queue(self) -> asyncio.Queue: """ -- cgit v1.2.3 From 88b88ba5c42f7db2d253493fa9b3749287d31ffb Mon Sep 17 00:00:00 2001 From: xithrius Date: Thu, 4 Feb 2021 19:14:02 -0800 Subject: Replaced fetching available category for old one. --- bot/exts/help_channels/_cog.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index d50197339..4520b408d 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,7 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Categories, Channels +from bot.constants import Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -490,9 +490,8 @@ class HelpChannels(commands.Cog): async def update_available_help_channels(self) -> None: """Updates the dynamic message within #how-to-get-help for available help channels.""" if not self.available_help_channels: - available_channels_category = await channel_utils.try_get_channel(Categories.help_available) self.available_help_channels = set( - c.id for c in available_channels_category.channels if 'help-' in c.name + c.id for c in self.available_category.channels if 'help-' in c.name ) available_channels = AVAILABLE_HELP_CHANNELS.format( -- cgit v1.2.3 From 2940ba31bb5118f7c9668d14c3d9ffa7611b3890 Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 5 Feb 2021 02:55:50 -0800 Subject: Modified the dynamic to be bold to catch eyes. --- bot/exts/help_channels/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 4520b408d..e9333b9a6 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -22,7 +22,7 @@ HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ AVAILABLE_HELP_CHANNELS = """ -Currently available help channel(s): {available} +**Currently available help channel(s):** {available} """ -- cgit v1.2.3 From 555c1a8a4543f051b950c72a4b89805db988ca76 Mon Sep 17 00:00:00 2001 From: Senjan21 <53477086+Senjan21@users.noreply.github.com> Date: Fri, 5 Feb 2021 12:29:27 +0100 Subject: Add jumplink to deleted messages --- bot/exts/moderation/modlog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index e4b119f41..2dae9d268 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -546,6 +546,7 @@ class ModLog(Cog, name="ModLog"): f"**Author:** {format_user(author)}\n" f"**Channel:** {channel.category}/#{channel.name} (`{channel.id}`)\n" f"**Message ID:** `{message.id}`\n" + f"[Jump to message]({message.jump_url})\n" "\n" ) else: @@ -553,6 +554,7 @@ class ModLog(Cog, name="ModLog"): f"**Author:** {format_user(author)}\n" f"**Channel:** #{channel.name} (`{channel.id}`)\n" f"**Message ID:** `{message.id}`\n" + f"[Jump to message]({message.jump_url})\n" "\n" ) -- cgit v1.2.3 From 3a08d74e6ecc5a038ccfc97e973dac161171c6c2 Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Fri, 5 Feb 2021 19:50:45 +0400 Subject: Make `defaultdict` tag --- bot/resources/tags/defaultdict.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bot/resources/tags/defaultdict.md diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md new file mode 100644 index 000000000..a15ebff2a --- /dev/null +++ b/bot/resources/tags/defaultdict.md @@ -0,0 +1,20 @@ +**[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** + +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. + +```py +>>> from collections import defaultdict +>>> my_dict = defaultdict(int, {"foo": 1, "bar": 2}) +>>> print(my_dict) +defaultdict(, {'foo': 1, 'bar': 2}) +``` + +In this example, we've used the `int` function - this means that if we try to access a non-existent key, it provides the default value of 0. + +```py +>>> print(my_dict["foobar"]) +0 +>>> print(my_dict) +defaultdict(, {'foo': 1, 'bar': 2, 'foobar': 0}) +``` -- cgit v1.2.3 From 6d9e17634ac10ce911e68d544a52aaa928298929 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 01:25:44 -0800 Subject: Reformatted string constant for available help channels. --- bot/exts/help_channels/_cog.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index e9333b9a6..fbfc585a4 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -21,9 +21,7 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = """ -**Currently available help channel(s):** {available} -""" +AVAILABLE_HELP_CHANNELS = """**Currently available help channel(s):** {available}""" class HelpChannels(commands.Cog): -- cgit v1.2.3 From bbce0a8cb17d0771a4823e67675cf4dc26f72b2a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 11:37:34 +0200 Subject: Create local-file tag about sending local files to Discord --- bot/resources/tags/local-file.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 bot/resources/tags/local-file.md diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md new file mode 100644 index 000000000..309ca4820 --- /dev/null +++ b/bot/resources/tags/local-file.md @@ -0,0 +1,24 @@ +Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of `discord.File` class: +```py +# When you know the file exact path, you can pass it. +file = discord.File("/this/is/path/to/my/file.png", filename="file.png") + +# When you have the file-like object, then you can pass this instead path. +with open("/this/is/path/to/my/file.png", "rb") as f: + file = discord.File(f) +``` +When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. +Please note that `filename` can't contain underscores. This is Discord limitation. + +`discord.Embed` instance has method `set_image` what can be used to set attachment as image: +```py +embed = discord.Embed() +# Set other fields +embed.set_image("attachment://file.png") # Filename here must be exactly same as attachment filename. +``` +After this, you can send embed and attachment to Discord: +```py +await channel.send(file=file, embed=embed) +``` +This example uses `discord.Channel` for sending, but any `discord.Messageable` can be used for sending. + -- cgit v1.2.3 From dc72923a4c74207fc405764a8e8afc1b4b239b37 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 01:52:42 -0800 Subject: Available channels are no longer stored as IDs. --- bot/exts/help_channels/_cog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index fbfc585a4..dae9b5730 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -76,7 +76,7 @@ class HelpChannels(commands.Cog): # Acquiring and modifying the channel to dynamically update the available help channels message. self.how_to_get_help: discord.TextChannel = None - self.available_help_channels: t.Set[int] = set() + self.available_help_channels: t.Set[discord.TextChannel] = set() self.dynamic_message: discord.Message = None # Asyncio stuff @@ -122,7 +122,7 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) # Removing the help channel from the dynamic message, and editing/sending that message. - self.available_help_channels.remove(message.channel.id) + self.available_help_channels.remove(message.channel) # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -352,7 +352,7 @@ class HelpChannels(commands.Cog): ) # Adding the help channel to the dynamic message, and editing/sending that message. - self.available_help_channels.add(channel.id) + self.available_help_channels.add(channel) await self.update_available_help_channels() _stats.report_counts() @@ -489,11 +489,11 @@ class HelpChannels(commands.Cog): """Updates the dynamic message within #how-to-get-help for available help channels.""" if not self.available_help_channels: self.available_help_channels = set( - c.id for c in self.available_category.channels if 'help-' in c.name + c for c in self.available_category.channels if not _channel.is_excluded_channel(c) ) available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(f"<#{c}>" for c in self.available_help_channels) or None + available=', '.join(c.mention for c in self.available_help_channels) or None ) if self.how_to_get_help is None: -- cgit v1.2.3 From 2b437bfc4858d6ed08eee43defd9a97584140706 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 02:09:20 -0800 Subject: Removed unnecessary task creation. --- bot/exts/help_channels/_cog.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index dae9b5730..2dbe930d3 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -286,13 +286,8 @@ class HelpChannels(commands.Cog): self.close_command.enabled = True # Getting channels that need to be included in the dynamic message. - task = asyncio.create_task(self.update_available_help_channels()) - self.queue_tasks.append(task) - - await task - + await self.update_available_help_channels() log.trace("Dynamic available help message updated.") - self.queue_tasks.remove(task) await self.init_available() _stats.report_counts() -- cgit v1.2.3 From 88fba5fd3489988320431b8a96879941988b5f13 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sat, 6 Feb 2021 02:21:20 -0800 Subject: Formatted available constant, added missing dynamic message trace --- bot/exts/help_channels/_cog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 2dbe930d3..554c27c95 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -21,7 +21,7 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = """**Currently available help channel(s):** {available}""" +AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}" class HelpChannels(commands.Cog): @@ -500,3 +500,4 @@ class HelpChannels(commands.Cog): await self.dynamic_message.edit(content=available_channels) except discord.NotFound: self.dynamic_message = await self.how_to_get_help.send(available_channels) + log.trace("A dynamic message was sent for later modification because one couldn't be found.") -- cgit v1.2.3 From d0c87c7f12ca20ec9be54bf0d299ca23a5e559db Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 12:54:45 +0200 Subject: discord.Channel -> discord.TextChannel --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 309ca4820..a587139ee 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,5 +20,5 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.Channel` for sending, but any `discord.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any `discord.Messageable` can be used for sending. -- cgit v1.2.3 From 44be3e8a7411a715b502802863dfc1fb2d6658c3 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 12:56:52 +0200 Subject: discord.Messageable -> discord.abc.Messageable --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index a587139ee..d78258fa2 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,5 +20,5 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any `discord.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. -- cgit v1.2.3 From 6db50230970188b4b1a24ec0b4ff84b4896cc78a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:03:35 +0200 Subject: Remove additional newline from end of tag --- bot/resources/tags/local-file.md | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index d78258fa2..9e4e0e551 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -21,4 +21,3 @@ After this, you can send embed and attachment to Discord: await channel.send(file=file, embed=embed) ``` This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. - -- cgit v1.2.3 From e1fa3182254727c564afc86d87fc7043b2444c3c Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:56:50 +0200 Subject: Mention instance in comment about Messageable --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 9e4e0e551..c28e14a05 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -20,4 +20,4 @@ After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any `discord.abc.Messageable` can be used for sending. +This example uses `discord.TextChannel` for sending, but any instance of `discord.abc.Messageable` can be used for sending. -- cgit v1.2.3 From 90a9bac84cdac0288c256157f1b5769b0cd2b973 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 14:03:41 +0200 Subject: Add hyperlinks for local file tag discord.py references --- bot/resources/tags/local-file.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index c28e14a05..344f35667 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -1,4 +1,4 @@ -Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of `discord.File` class: +Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of [`discord.File`](https://discordpy.readthedocs.io/en/latest/api.html#discord.File) class: ```py # When you know the file exact path, you can pass it. file = discord.File("/this/is/path/to/my/file.png", filename="file.png") @@ -10,14 +10,14 @@ with open("/this/is/path/to/my/file.png", "rb") as f: When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. Please note that `filename` can't contain underscores. This is Discord limitation. -`discord.Embed` instance has method `set_image` what can be used to set attachment as image: +[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instance has method [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) what can be used to set attachment as image: ```py embed = discord.Embed() # Set other fields -embed.set_image("attachment://file.png") # Filename here must be exactly same as attachment filename. +embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` After this, you can send embed and attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -This example uses `discord.TextChannel` for sending, but any instance of `discord.abc.Messageable` can be used for sending. +This example uses [`discord.TextChannel`](https://discordpy.readthedocs.io/en/latest/api.html#discord.TextChannel) for sending, but any instance of [`discord.abc.Messageable`](https://discordpy.readthedocs.io/en/latest/api.html#discord.abc.Messageable) can be used for sending. -- cgit v1.2.3 From 8f51f239f2ded1d7176a94039d2332ef74532a95 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:07:07 +0200 Subject: Fix grammar of local-file tag --- bot/resources/tags/local-file.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 344f35667..52539c64e 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -10,13 +10,13 @@ with open("/this/is/path/to/my/file.png", "rb") as f: When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. Please note that `filename` can't contain underscores. This is Discord limitation. -[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instance has method [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) what can be used to set attachment as image: +[`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py embed = discord.Embed() # Set other fields embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` -After this, you can send embed and attachment to Discord: +After this, you send an embed with an attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -- cgit v1.2.3 From 496129080733096ab7eddd03128750b9fd3a53a2 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:17:00 +0200 Subject: Add back removed 'can' to local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 52539c64e..a4aeee736 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -16,7 +16,7 @@ embed = discord.Embed() # Set other fields embed.set_image(url="attachment://file.png") # Filename here must be exactly same as attachment filename. ``` -After this, you send an embed with an attachment to Discord: +After this, you can send an embed with an attachment to Discord: ```py await channel.send(file=file, embed=embed) ``` -- cgit v1.2.3 From 65ea1657de016a3ba1e58412950ae4bf735bf0fe Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:19:40 +0200 Subject: Add missing 'a' article in local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index a4aeee736..29fce3ff5 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -8,7 +8,7 @@ with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. -Please note that `filename` can't contain underscores. This is Discord limitation. +Please note that `filename` can't contain underscores. This is a Discord limitation.. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py -- cgit v1.2.3 From 691f2393f0fed5d17ec641d5006ea2e486015614 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:21:55 +0200 Subject: Remove unnecessary period from local-file tag --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index 29fce3ff5..fdce5605c 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -8,7 +8,7 @@ with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. -Please note that `filename` can't contain underscores. This is a Discord limitation.. +Please note that `filename` can't contain underscores. This is a Discord limitation. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: ```py -- cgit v1.2.3 From df22551dbf7a4dae4e374eb1dd95d9354b73474c Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Sat, 6 Feb 2021 21:41:13 +0400 Subject: Fix trailing whitespaces --- bot/resources/tags/defaultdict.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md index a15ebff2a..9361d6f2a 100644 --- a/bot/resources/tags/defaultdict.md +++ b/bot/resources/tags/defaultdict.md @@ -1,6 +1,6 @@ **[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** -The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. ```py -- cgit v1.2.3 From 5dc8f0e7e0cf150a9a89787b518fdbb7f8f2ba5c Mon Sep 17 00:00:00 2001 From: Anand Krishna <40204976+anand2312@users.noreply.github.com> Date: Sat, 6 Feb 2021 23:28:04 +0400 Subject: Correct examples, reword description --- bot/resources/tags/defaultdict.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bot/resources/tags/defaultdict.md b/bot/resources/tags/defaultdict.md index 9361d6f2a..b6c3175fc 100644 --- a/bot/resources/tags/defaultdict.md +++ b/bot/resources/tags/defaultdict.md @@ -1,20 +1,21 @@ **[`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)** -The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically create the key and generate a default value for it. +The Python `defaultdict` type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the `defaultdict` will automatically insert the key and generate a default value for it. While instantiating a `defaultdict`, we pass in a function that tells it how to create a default value for missing keys. ```py >>> from collections import defaultdict ->>> my_dict = defaultdict(int, {"foo": 1, "bar": 2}) ->>> print(my_dict) -defaultdict(, {'foo': 1, 'bar': 2}) +>>> my_dict = defaultdict(int) +>>> my_dict +defaultdict(, {}) ``` -In this example, we've used the `int` function - this means that if we try to access a non-existent key, it provides the default value of 0. +In this example, we've used the `int` class which returns 0 when called like a function, so any missing key will get a default value of 0. You can also get an empty list by default with `list` or an empty string with `str`. ```py ->>> print(my_dict["foobar"]) +>>> my_dict["foo"] 0 ->>> print(my_dict) -defaultdict(, {'foo': 1, 'bar': 2, 'foobar': 0}) +>>> my_dict["bar"] += 5 +>>> my_dict +defaultdict(, {'foo': 0, 'bar': 5}) ``` -- cgit v1.2.3 From fc451e1b3c375a73b000cea21f822b7f95d900d7 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Feb 2021 22:04:10 +0200 Subject: Put filename between backticks --- bot/resources/tags/local-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/local-file.md b/bot/resources/tags/local-file.md index fdce5605c..ae41d589c 100644 --- a/bot/resources/tags/local-file.md +++ b/bot/resources/tags/local-file.md @@ -7,7 +7,7 @@ file = discord.File("/this/is/path/to/my/file.png", filename="file.png") with open("/this/is/path/to/my/file.png", "rb") as f: file = discord.File(f) ``` -When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing filename to it is not necessary. +When using the file-like object, you have to open it in `rb` mode. Also, in this case, passing `filename` to it is not necessary. Please note that `filename` can't contain underscores. This is a Discord limitation. [`discord.Embed`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed) instances have a [`set_image`](https://discordpy.readthedocs.io/en/latest/api.html#discord.Embed.set_image) method which can be used to set an attachment as an image: -- cgit v1.2.3 From 1cb760e158259264bc9cf575a609bd2b6e64d1f3 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Sun, 7 Feb 2021 15:14:54 +0300 Subject: Revert "Dynamic available help channels message" --- bot/constants.py | 1 - bot/exts/help_channels/_cog.py | 40 ---------------------------------------- config-default.yml | 1 - 3 files changed, 42 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 6b86d33a3..95e22513f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -406,7 +406,6 @@ class Channels(metaclass=YAMLGetter): meta: int python_general: int - how_to_get_help: int cooldown: int diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py index 554c27c95..0995c8a79 100644 --- a/bot/exts/help_channels/_cog.py +++ b/bot/exts/help_channels/_cog.py @@ -11,7 +11,6 @@ from discord.ext import commands from bot import constants from bot.bot import Bot -from bot.constants import Channels from bot.exts.help_channels import _caches, _channel, _cooldown, _message, _name, _stats from bot.utils import channel as channel_utils, lock, scheduling @@ -21,7 +20,6 @@ NAMESPACE = "help" HELP_CHANNEL_TOPIC = """ This is a Python help channel. You can claim your own help channel in the Python Help: Available category. """ -AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}" class HelpChannels(commands.Cog): @@ -74,11 +72,6 @@ class HelpChannels(commands.Cog): self.last_notification: t.Optional[datetime] = None - # Acquiring and modifying the channel to dynamically update the available help channels message. - self.how_to_get_help: discord.TextChannel = None - self.available_help_channels: t.Set[discord.TextChannel] = set() - self.dynamic_message: discord.Message = None - # Asyncio stuff self.queue_tasks: t.List[asyncio.Task] = [] self.init_task = self.bot.loop.create_task(self.init_cog()) @@ -121,9 +114,6 @@ class HelpChannels(commands.Cog): await _caches.unanswered.set(message.channel.id, True) - # Removing the help channel from the dynamic message, and editing/sending that message. - self.available_help_channels.remove(message.channel) - # Not awaited because it may indefinitely hold the lock while waiting for a channel. scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}") @@ -285,10 +275,6 @@ class HelpChannels(commands.Cog): # This may confuse users. So would potentially long delays for the cog to become ready. self.close_command.enabled = True - # Getting channels that need to be included in the dynamic message. - await self.update_available_help_channels() - log.trace("Dynamic available help message updated.") - await self.init_available() _stats.report_counts() @@ -346,10 +332,6 @@ class HelpChannels(commands.Cog): category_id=constants.Categories.help_available, ) - # Adding the help channel to the dynamic message, and editing/sending that message. - self.available_help_channels.add(channel) - await self.update_available_help_channels() - _stats.report_counts() async def move_to_dormant(self, channel: discord.TextChannel) -> None: @@ -479,25 +461,3 @@ class HelpChannels(commands.Cog): self.queue_tasks.remove(task) return channel - - async def update_available_help_channels(self) -> None: - """Updates the dynamic message within #how-to-get-help for available help channels.""" - if not self.available_help_channels: - self.available_help_channels = set( - c for c in self.available_category.channels if not _channel.is_excluded_channel(c) - ) - - available_channels = AVAILABLE_HELP_CHANNELS.format( - available=', '.join(c.mention for c in self.available_help_channels) or None - ) - - if self.how_to_get_help is None: - self.how_to_get_help = await channel_utils.try_get_channel(Channels.how_to_get_help) - - try: - if self.dynamic_message is None: - self.dynamic_message = await self.how_to_get_help.fetch_message(self.how_to_get_help.last_message_id) - await self.dynamic_message.edit(content=available_channels) - except discord.NotFound: - self.dynamic_message = await self.how_to_get_help.send(available_channels) - log.trace("A dynamic message was sent for later modification because one couldn't be found.") diff --git a/config-default.yml b/config-default.yml index fc1f3b3a8..d3b267159 100644 --- a/config-default.yml +++ b/config-default.yml @@ -159,7 +159,6 @@ guild: # Python Help: Available cooldown: 720603994149486673 - how_to_get_help: 704250143020417084 # Topical discord_py: 343944376055103488 -- cgit v1.2.3