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