Reputation: 119
@client.command()
@commands.cooldown(5, 57600, commands.BucketType.user)
async def ping(ctx)
await ctx.send("pong")
So basically, when a user uses the prefix of the command (for example '!k') then types "ping" the bot will send "pong!" to the chat. This is a very basic command, but then comes the cooldown decorator. From the cooldown:
@commands.cooldown(5, 57600, commands.BucketType.user)
we can tell that the command can be used 5 times between 57600 seconds (16 hours) but what if I want a command to be used only 5 times per 16 hours AND there's ANOTHER cooldown between each time you use the command? Let me explain...
You can use the command 5 times per 16 hours
But when you use the command, you have to wait 1 hour to use it again
That way, you can not use the command 5 times straight away in under a minute
Think of it like a tree that grows an apple every hour but only grows 5 apples a day... How can I prevent a user from using the command 5 times straight away? Thanks -
Upvotes: 1
Views: 901
Reputation: 3924
Discord.py does not allow for multiple cooldown checks per command, so you will have to use a custom cooldown handler.
class CooldownManager:
def __init__(self, executions_allowed: int, cooldown_time: float):
self.state = {}
self.executions_allowed = executions_allowed
self.cooldown_time = cooldown_time
def time_left(self, key) -> float:
"""Attempt to execute. Return 0 if ready, or the number of seconds until you're allowed to execute again."""
if key not in self.state.keys():
self.state[key] = []
if len(self.state[key]) > 0:
# Clean up executions that have aged away
# self.state[key] is sorted with the newest first
for i in range(len(self.state[key])-1, -1, -1):
if self.state[key][i] + self.cooldown_time < time.time():
del self.state[key][i]
if len(self.state[key]) < self.executions_allowed:
self.state[key].append(time.time())
return 0
next_available_execution = self.state[key][len(self.state)-1] + self.cooldown_time
return next_available_execution - time.time()
else:
self.state[key].append(time.time())
return 0
def assert_cooldown(self, data):
"""Run this at the beginning of the command."""
time_left = self.time_left(data)
if time_left > 0:
raise commands.CommandOnCooldown('', retry_after=time_left)
cm1 = CooldownManager(1, 4.0) # execute once every 4 seconds
cm2 = CooldownManager(3, 120.0) # execute up to 3x every 2 minutes
@client.command(name='test')
async def multicooldown(ctx):
# Check both cooldowns. This raises `CommandOnCooldown` just like the vanilla cooldown handler, so you can catch that later in your command error check.
cm1.assert_cooldown(ctx.author.id) # This is basically the bucket type. You can use things like ctx.author.id, ctx.guild.id, etc
cm2.assert_cooldown(ctx.author.id)
# Do whatever you want here
await ctx.send('test')
Upvotes: 2