[commands] Provide a dynamic cooldown system
This commit is contained in:
@ -32,7 +32,7 @@ import sys
|
||||
import discord
|
||||
|
||||
from .errors import *
|
||||
from .cooldowns import Cooldown, BucketType, CooldownMapping, MaxConcurrency
|
||||
from .cooldowns import Cooldown, BucketType, CooldownMapping, MaxConcurrency, DynamicCooldownMapping
|
||||
from . import converter as converters
|
||||
from ._types import _BaseCommand
|
||||
from .cog import Cog
|
||||
@ -54,6 +54,7 @@ __all__ = (
|
||||
'bot_has_permissions',
|
||||
'bot_has_any_role',
|
||||
'cooldown',
|
||||
'dynamic_cooldown',
|
||||
'max_concurrency',
|
||||
'dm_only',
|
||||
'guild_only',
|
||||
@ -256,7 +257,10 @@ class Command(_BaseCommand):
|
||||
except AttributeError:
|
||||
cooldown = kwargs.get('cooldown')
|
||||
finally:
|
||||
self._buckets = CooldownMapping(cooldown)
|
||||
if cooldown is None:
|
||||
self._buckets = CooldownMapping(cooldown, BucketType.default)
|
||||
elif isinstance(cooldown, CooldownMapping):
|
||||
self._buckets = cooldown
|
||||
|
||||
try:
|
||||
max_concurrency = func.__commands_max_concurrency__
|
||||
@ -799,9 +803,10 @@ class Command(_BaseCommand):
|
||||
dt = ctx.message.edited_at or ctx.message.created_at
|
||||
current = dt.replace(tzinfo=datetime.timezone.utc).timestamp()
|
||||
bucket = self._buckets.get_bucket(ctx.message, current)
|
||||
retry_after = bucket.update_rate_limit(current)
|
||||
if retry_after:
|
||||
raise CommandOnCooldown(bucket, retry_after)
|
||||
if bucket is not None:
|
||||
retry_after = bucket.update_rate_limit(current)
|
||||
if retry_after:
|
||||
raise CommandOnCooldown(bucket, retry_after)
|
||||
|
||||
async def prepare(self, ctx):
|
||||
ctx.command = self
|
||||
@ -2014,9 +2019,48 @@ def cooldown(rate, per, type=BucketType.default):
|
||||
|
||||
def decorator(func):
|
||||
if isinstance(func, Command):
|
||||
func._buckets = CooldownMapping(Cooldown(rate, per, type))
|
||||
func._buckets = CooldownMapping(Cooldown(rate, per), type)
|
||||
else:
|
||||
func.__commands_cooldown__ = Cooldown(rate, per, type)
|
||||
func.__commands_cooldown__ = CooldownMapping(Cooldown(rate, per), type)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def dynamic_cooldown(cooldown, type=BucketType.default):
|
||||
"""A decorator that adds a dynamic cooldown to a :class:`.Command`
|
||||
|
||||
This differs from :func:`.cooldown` in that it takes a function that
|
||||
accepts a single parameter of type :class:`.discord.Message` and must
|
||||
return a :class:`.Cooldown`
|
||||
|
||||
A cooldown allows a command to only be used a specific amount
|
||||
of times in a specific time frame. These cooldowns can be based
|
||||
either on a per-guild, per-channel, per-user, per-role or global basis.
|
||||
Denoted by the third argument of ``type`` which must be of enum
|
||||
type :class:`.BucketType`.
|
||||
|
||||
If a cooldown is triggered, then :exc:`.CommandOnCooldown` is triggered in
|
||||
:func:`.on_command_error` and the local error handler.
|
||||
|
||||
A command can only have a single cooldown.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
------------
|
||||
cooldown: Callable[[:class:`.discord.Message`], :class:`.Cooldown`]
|
||||
A function that takes a message and returns a cooldown that will
|
||||
apply to this invocation
|
||||
type: :class:`.BucketType`
|
||||
The type of cooldown to have.
|
||||
"""
|
||||
if not callable(cooldown):
|
||||
raise TypeError("A callable must be provided")
|
||||
|
||||
def decorator(func):
|
||||
if isinstance(func, Command):
|
||||
func._buckets = DynamicCooldownMapping(cooldown, type)
|
||||
else:
|
||||
func.__commands_cooldown__ = DynamicCooldownMapping(cooldown, type)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
Reference in New Issue
Block a user