Nextcord Slash Cooldown’s#

Given the lack of official support, here is how to use cooldown’s (unofficially).

pip install function-cooldowns

Check out the Github here

Want support? Broke something? Ask me in the nextcord discord, Skelmis#9135

Decorator arguments#

1def cooldown(
2    limit: int,
3    time_period: float,
4    bucket: CooldownBucketProtocol,
5    check: Optional[MaybeCoro] = lambda *args, **kwargs: True,
6):
limit: int

How many call’s can be made in the time period specified by time_period

time_period: float

The time period related to limit

bucket: CooldownBucketProtocol

The Bucket implementation to use as a bucket to separate cooldown buckets.

check: Optional[MaybeCoro]

A Callable which dictates whether or not to apply the cooldown on current invoke.

If this Callable returns a truthy value, then the cooldown will be used for the current call.

I.e. If you wished to bypass cooldowns, you would return False if you invoked the Callable.

Possible buckets#

Most basic bucket.

 1from cooldowns import CooldownBucket
 2
 3CooldownBucket.all
 4CooldownBucket.args
 5CooldownBucket.kwargs
 6
 7"""
 8A collection of generic CooldownBucket's for usage in cooldown's.
 9
10Attributes
11==========
12all
13    The buckets are defined using all
14    arguments passed to the :type:`Callable`
15args
16    The buckets are defined using all
17    non-keyword arguments passed to the :type:`Callable`
18kwargs
19    The buckets are defined using all
20    keyword arguments passed to the :type:`Callable`
21"""

Slash command bucket.

 1from cooldowns import SlashBucket
 2
 3SlashBucket.author
 4SlashBucket.guild
 5SlashBucket.channel
 6SlashBucket.command
 7
 8"""
 9A collection of generic cooldown bucket's for usage
10with nextcord slash commands which take ``Interaction``
11
12Attributes
13==========
14author
15    Rate-limits the command per person.
16guild
17    Rate-limits the command per guild.
18channel
19    Rate-limits the command per channel
20command
21    Rate-limits the entire command as one.
22"""

Example usage#

Rate-limits the command to 1 call per person every 15 seconds in your main file.

 1import cooldowns
 2
 3...
 4
 5@bot.slash_command(
 6    description="Ping command",
 7)
 8@cooldowns.cooldown(1, 15, bucket=cooldowns.SlashBucket.author)
 9async def ping(interaction: nextcord.Interaction):
10    await interaction.response.send_message("Pong!")

Example cog usage#

Rate-limits the command to 1 call per guild every 30 seconds.

 1import cooldowns
 2
 3...
 4
 5@nextcord.slash_command(
 6    description="Ping command",
 7)
 8@cooldowns.cooldown(1, 30, bucket=cooldowns.SlashBucket.guild)
 9async def ping(self, interaction: nextcord.Interaction):
10    await interaction.response.send_message("Pong!")

Handling cooldown’s#

Here is an example error handler

 1from cooldowns import CallableOnCooldown
 2
 3...
 4
 5@bot.event
 6async def on_application_command_error(inter: nextcord.Interaction, error):
 7    error = getattr(error, "original", error)
 8
 9    if isinstance(error, CallableOnCooldown):
10        await inter.send(
11            f"You are being rate-limited! Retry in `{error.retry_after}` seconds."
12        )
13
14    else:
15        raise error

The error CallableOnCooldown has the following attributes.

func: Callable

The Callable which is currently rate-limited

cooldown: Cooldown

The Cooldown which applies to the current cooldown

retry_after: float

How many seconds before you can retry the Callable

resets_at: datetime.datetime

The exact datetime this cooldown resets.

Get remaining calls#

Definition

 1def get_remaining_calls(func: MaybeCoro, *args, **kwargs) -> int:
 2    """
 3    Given a :type:`Callable`, return the amount of remaining
 4    available calls before these arguments will result
 5    in the callable being rate-limited.
 6
 7    Parameters
 8    ----------
 9    func: MaybeCoro
10        The :type:`Callable` you want to check.
11    args
12        Any arguments you will pass.
13    kwargs
14        Any key-word arguments you will pass.
15
16    Returns
17    -------
18    int
19        How many more times this :type:`Callable`
20        can be called without being rate-limited.
21
22    Raises
23    ------
24    NoRegisteredCooldowns
25        The given :type:`Callable` has no cooldowns.
26
27    Notes
28    -----
29    This aggregates all attached cooldowns
30    and returns the lowest remaining amount.
31    """

Example usage

1from cooldowns import get_remaining_calls, cooldown, SlashBucket
2
3@bot.slash_command()
4@cooldown(2, 10, SlashBucket.command)
5async def test(inter):
6    ...
7    calls_left = get_remaining_calls(test, inter)
8    await inter.send(f"You can call this {calls_left} times before getting rate-limited")

Cooldown checks#

Here’s an example check to only apply a cooldown if the first argument is equal to 1.

1@cooldown(
2    1, 1, bucket=CooldownBucket.args, check=lambda *args, **kwargs: args[0] == 1
3)
4async def test_func(*args, **kwargs) -> (tuple, dict):
5    return args, kwargs

Here’s one use an async check. Functionally its the same as the previous one.

1async def mock_db_check(*args, **kwargs):
2    # You can do database calls here or anything
3    # since this is an async context
4    return args[0] == 1
5
6@cooldown(1, 1, bucket=CooldownBucket.args, check=mock_db_check)
7async def test_func(*args, **kwargs) -> (tuple, dict):
8    return args, kwargs

Custom buckets#

All you need is an enum with the process method.

Heres an example which rate-limits based off of the first argument.

 1class CustomBucket(Enum):
 2    first_arg = 1
 3
 4    def process(self, *args, **kwargs):
 5        if self is CustomBucket.first_arg:
 6            # This bucket is based ONLY off
 7            # of the first argument passed
 8            return args[0]
 9
10# Then to use
11@cooldown(1, 1, bucket=CustomBucket.first_arg)
12async def test_func(*args, **kwargs):
13    .....

Stacking cooldown’s#

Stack as many cooldown’s as you want, just note Python starts from the bottom decor and works its way up.

1# Can call ONCE time_period second using the same args
2# Can call TWICE time_period second using the same kwargs
3@cooldown(1, 1, bucket=CooldownBucket.args)
4@cooldown(2, 1, bucket=CooldownBucket.kwargs)
5async def test_func(*args, **kwargs) -> (tuple, dict):
6    return args, kwargs