Created
September 18, 2024 12:38
-
-
Save LeoCx1000/aa9461c73b1f0dc2d99a5b1a6d74d421 to your computer and use it in GitHub Desktop.
d.py cooldown decorator with async bucket support.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment