From 6fbd7883fee19e62898eacea08b4d7b7f7fc74db Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 4 Oct 2021 20:26:59 +0100 Subject: Monkey patch http.send_typing to catch 403s Sometimes discord turns off typing events by throwing 403's, so we should catch those --- bot/monkey_patches.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 bot/monkey_patches.py (limited to 'bot/monkey_patches.py') diff --git a/bot/monkey_patches.py b/bot/monkey_patches.py new file mode 100644 index 00000000..fe81f2e3 --- /dev/null +++ b/bot/monkey_patches.py @@ -0,0 +1,78 @@ +import logging +from datetime import datetime, timedelta + +from discord import Forbidden, http +from discord.ext import commands + +log = logging.getLogger(__name__) + + +def trace_log(self: logging.Logger, msg: str, *args, **kwargs) -> None: + """ + Log 'msg % args' with severity 'TRACE'. + + To pass exception information, use the keyword argument exc_info with a true value, e.g. + logger.trace("Houston, we have an %s", "interesting problem", exc_info=1) + """ + if self.isEnabledFor(logging.TRACE): + self._log(logging.TRACE, msg, args, **kwargs) + + +class Command(commands.Command): + """ + A `discord.ext.commands.Command` subclass which supports root aliases. + + A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as + top-level commands rather than being aliases of the command's group. It's stored as an attribute + also named `root_aliases`. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.root_aliases = kwargs.get("root_aliases", []) + + if not isinstance(self.root_aliases, (list, tuple)): + raise TypeError("Root aliases of a command must be a list or a tuple of strings.") + + +class Group(commands.Group): + """ + A `discord.ext.commands.Group` subclass which supports root aliases. + + A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as + top-level groups rather than being aliases of the command's group. It's stored as an attribute + also named `root_aliases`. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.root_aliases = kwargs.get("root_aliases", []) + + if not isinstance(self.root_aliases, (list, tuple)): + raise TypeError("Root aliases of a group must be a list or a tuple of strings.") + + +def patch_typing() -> None: + """ + Sometimes discord turns off typing events by throwing 403's. + + Handle those issues by patching the trigger_typing method so it ignores 403's in general. + """ + log.debug("Patching send_typing, which should fix things breaking when discord disables typing events. Stay safe!") + + original = http.HTTPClient.send_typing + last_403 = None + + async def honeybadger_type(self, channel_id: int) -> None: # noqa: ANN001 + nonlocal last_403 + if last_403 and (datetime.utcnow() - last_403) < timedelta(minutes=5): + log.warning("Not sending typing event, we got a 403 less than 5 minutes ago.") + return + try: + await original(self, channel_id) + except Forbidden: + last_403 = datetime.utcnow() + log.warning("Got a 403 from typing event!") + pass + + http.HTTPClient.send_typing = honeybadger_type -- cgit v1.2.3 From 163a6e14c22b7992f44dca699de9e71c11362613 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sun, 5 Dec 2021 13:11:03 +0000 Subject: Patch d.py's message converters to infer channelID from the given context Discord.py's message converter is supposed to infer channelID based on ctx.channel if only a messageID is given. A refactor (linked below) a few weeks before d.py's archival broke this, so that if only a messageID is given to the converter, it will only find that message if it's in the bot's cache. --- bot/__init__.py | 5 +++++ bot/monkey_patches.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'bot/monkey_patches.py') diff --git a/bot/__init__.py b/bot/__init__.py index ae53a5a5..3136c863 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -43,6 +43,11 @@ if os.name == "nt": monkey_patches.patch_typing() +# This patches any convertors that use PartialMessage, but not the PartialMessageConverter itself +# as library objects are made by this mapping. +# https://github.com/Rapptz/discord.py/blob/1a4e73d59932cdbe7bf2c281f25e32529fc7ae1f/discord/ext/commands/converter.py#L984-L1004 +commands.converter.PartialMessageConverter = monkey_patches.FixedPartialMessageConverter + # Monkey-patch discord.py decorators to use the both the Command and Group subclasses which supports root aliases. # Must be patched before any cogs are added. commands.command = partial(commands.command, cls=monkey_patches.Command) diff --git a/bot/monkey_patches.py b/bot/monkey_patches.py index fa6627d1..19965c19 100644 --- a/bot/monkey_patches.py +++ b/bot/monkey_patches.py @@ -1,10 +1,12 @@ import logging +import re from datetime import datetime, timedelta from discord import Forbidden, http from discord.ext import commands log = logging.getLogger(__name__) +MESSAGE_ID_RE = re.compile(r'(?P[0-9]{15,20})$') class Command(commands.Command): @@ -65,3 +67,25 @@ def patch_typing() -> None: pass http.HTTPClient.send_typing = honeybadger_type + + +class FixedPartialMessageConverter(commands.PartialMessageConverter): + """ + Make the Message converter infer channelID from the given context if only a messageID is given. + + Discord.py's Message converter is supposed to infer channelID based + on ctx.channel if only a messageID is given. A refactor commit, linked below, + a few weeks before d.py's archival broke this defined behaviour of the converter. + Currently, if only a messageID is given to the converter, it will only find that message + if it's in the bot's cache. + + https://github.com/Rapptz/discord.py/commit/1a4e73d59932cdbe7bf2c281f25e32529fc7ae1f + """ + + @staticmethod + def _get_id_matches(ctx: commands.Context, argument: str) -> tuple[int, int, int]: + """Inserts ctx.channel.id before calling super method if argument is just a messageID.""" + match = MESSAGE_ID_RE.match(argument) + if match: + argument = f"{ctx.channel.id}-{match.group('message_id')}" + return commands.PartialMessageConverter._get_id_matches(ctx, argument) -- cgit v1.2.3 From ce16d0df6c10121f761899daea07d3656c4e7537 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 8 Jan 2022 22:27:48 +0000 Subject: Change single quote to double quote. This trivial change is actually to try and reproduce a bug we had with our GitHub actions. --- bot/monkey_patches.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/monkey_patches.py') diff --git a/bot/monkey_patches.py b/bot/monkey_patches.py index 19965c19..36708415 100644 --- a/bot/monkey_patches.py +++ b/bot/monkey_patches.py @@ -6,7 +6,7 @@ from discord import Forbidden, http from discord.ext import commands log = logging.getLogger(__name__) -MESSAGE_ID_RE = re.compile(r'(?P[0-9]{15,20})$') +MESSAGE_ID_RE = re.compile(r"(?P[0-9]{15,20})$") class Command(commands.Command): -- cgit v1.2.3 From 4f823346d56929964a092bd8119ec54f1458701e Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 8 Jan 2022 22:44:28 +0000 Subject: Remove trailing whitespace Like the previous commit, this trivial change is actually to try and reproduce a bug we had with our GitHub actions. --- bot/monkey_patches.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/monkey_patches.py') diff --git a/bot/monkey_patches.py b/bot/monkey_patches.py index 36708415..925d3206 100644 --- a/bot/monkey_patches.py +++ b/bot/monkey_patches.py @@ -6,7 +6,7 @@ from discord import Forbidden, http from discord.ext import commands log = logging.getLogger(__name__) -MESSAGE_ID_RE = re.compile(r"(?P[0-9]{15,20})$") +MESSAGE_ID_RE = re.compile(r"(?P[0-9]{15,20})$") class Command(commands.Command): -- cgit v1.2.3