Note
Found a bug? Left a comment? Please, ping me (@leocx1000) in the discord.py support server if I haven't replied. Sometimes I may miss comments here, though I try to be attentive.
from typing import Any, Callable, Optional, TypeVar | |
import discord | |
from discord.ext import commands | |
T_contra = TypeVar('T_contra', contravariant=True) | |
class AsyncCooldownMapping(commands.CooldownMapping[T_contra]): | |
async def get_bucket(self, message: T_contra, current: Optional[float] = None) -> Optional[commands.Cooldown]: | |
if self._type is commands.BucketType.default: | |
return self._cooldown | |
self._verify_cache_integrity(current) | |
# this change: | |
key = await discord.utils.maybe_coroutine(self._bucket_key, message) | |
if key not in self._cache: | |
bucket = self.create_bucket(message) | |
if bucket is not None: | |
self._cache[key] = bucket | |
else: | |
bucket = self._cache[key] | |
return bucket | |
async def update_rate_limit( | |
self, message: T_contra, current: Optional[float] = None, tokens: int = 1 | |
) -> Optional[float]: | |
bucket = await self.get_bucket(message, current) | |
if bucket is None: | |
return None | |
return bucket.update_rate_limit(current, tokens=tokens) | |
def async_cooldown(rate: int, per: float, bucket: Callable[[commands.Context], Any]): | |
"""A cooldown decorator for message commands that allows for coroutines to be | |
passed as a cooldown for more complex operations. | |
.. note:: | |
This is registered as a **check**, and does NOT hook into discord.py's native cooldown | |
system, so setting :attr:`.commands.Command.cooldown_after_parsing` will NOT affect when | |
this cooldown is parsed, but it will be parsed along with the rest of the checks. | |
Parameters | |
------------ | |
rate: :class:`int` | |
The number of times a command can be used before triggering a cooldown. | |
per: :class:`float` | |
The amount of seconds to wait for a cooldown when it's been triggered. | |
type: Union[:class:`.commands.BucketType`, Callable[[:class:`.commands.Context`], Any]] | |
The type of cooldown to have. If callable, should return a key for the mapping. Can | |
be a coroutine. | |
""" | |
mapping = AsyncCooldownMapping(commands.Cooldown(rate, per), bucket) | |
async def predicate(ctx): | |
cooldown_obj = await mapping.get_bucket(ctx) | |
if cooldown_obj: | |
retry_after = cooldown_obj.update_rate_limit() | |
if retry_after: | |
raise commands.CommandOnCooldown(cooldown_obj, retry_after, bucket) # type: ignore | |
return True | |
return commands.check(predicate) |
Note
Found a bug? Left a comment? Please, ping me (@leocx1000) in the discord.py support server if I haven't replied. Sometimes I may miss comments here, though I try to be attentive.