aboutsummaryrefslogtreecommitdiffstats
path: root/botcore/utils
diff options
context:
space:
mode:
authorGravatar Chris Lovering <[email protected]>2022-02-21 02:12:19 +0000
committerGravatar Chris Lovering <[email protected]>2022-02-24 20:07:37 +0000
commit3e16dfc1b64ecfb4aa12aebc60e119dd620ef315 (patch)
tree50f07bcf1046d64394f34d8d46b9d4b02438b4d3 /botcore/utils
parentChange discord API wrapper to disnake (diff)
Add disnake monkey patches
This covers the monkeypatches for Commands and Groups to allow for root aliases, along with a monkey patch for typing events, in case discord decides to raise 403s again. Co-authored-by: Mark <[email protected]> Co-authored-by: Hassan Abouelela <[email protected]>
Diffstat (limited to 'botcore/utils')
-rw-r--r--botcore/utils/__init__.py3
-rw-r--r--botcore/utils/monkey_patches.py83
2 files changed, 85 insertions, 1 deletions
diff --git a/botcore/utils/__init__.py b/botcore/utils/__init__.py
index 71354334..671ef49f 100644
--- a/botcore/utils/__init__.py
+++ b/botcore/utils/__init__.py
@@ -1,6 +1,6 @@
"""Useful utilities and tools for discord bot development."""
-from botcore.utils import (caching, channel, extensions, logging, members, regex, scheduling)
+from botcore.utils import (caching, channel, extensions, logging, members, monkey_patches, regex, scheduling)
__all__ = [
caching,
@@ -8,6 +8,7 @@ __all__ = [
extensions,
logging,
members,
+ monkey_patches,
regex,
scheduling,
]
diff --git a/botcore/utils/monkey_patches.py b/botcore/utils/monkey_patches.py
new file mode 100644
index 00000000..abbb37a5
--- /dev/null
+++ b/botcore/utils/monkey_patches.py
@@ -0,0 +1,83 @@
+"""Contains all common monkey patches, used to alter disnake to fit our needs."""
+
+import logging
+from datetime import datetime, timedelta
+from functools import partial, partialmethod
+
+from disnake import Forbidden, http
+from disnake.ext import commands
+
+log = logging.getLogger(__name__)
+
+
+class _Command(commands.Command):
+ """
+ A :obj:`disnake.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, _Command):
+ """
+ A :obj:`disnake.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 _patch_typing() -> None:
+ """
+ Sometimes Discord turns off typing events by throwing 403s.
+
+ Handle those issues by patching disnake's internal ``send_typing`` method so it ignores 403s 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!")
+
+ http.HTTPClient.send_typing = honeybadger_type
+
+
+def apply_monkey_patches() -> None:
+ """
+ Applies all common monkey patches for our bots.
+
+ Patches :obj:`disnake.ext.commands.Command` and :obj:`disnake.ext.commands.Group` to support root aliases.
+ A ``root_aliases`` keyword argument is added to these two objects, 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``
+
+ Patches disnake's internal ``send_typing`` method so that it ignores 403 errors from Discord.
+ When under heavy load Discord has added a CloudFlare worker to this route, which causes 403 errors to be thrown.
+ """
+ commands.command = partial(commands.command, cls=_Command)
+ commands.GroupMixin.command = partialmethod(commands.GroupMixin.command, cls=_Command)
+
+ commands.group = partial(commands.group, cls=_Group)
+ commands.GroupMixin.group = partialmethod(commands.GroupMixin.group, cls=_Group)
+ _patch_typing()