1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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()
|