Conditional cooldown for debugging custom commands
Want to have different cooldown for different user groups?
Md Shahriyar Alam
2 years ago
A fixed cooldown treats everyone the same. That's rarely what you want: the moderator running a cleanup command shouldn't wait as long as the member spamming a meme command.
With the has_permission and has_any_permission helpers, cooldowns become conditional — different limits for different people, decided at runtime.
Quick Answer: How Do You Set Different Cooldowns Per Role?
Call cooldown() inside a condition. Whichever branch runs decides the limit:
if has_permission(user, "administrator"):
cooldown("10 seconds", user.id)
else:
cooldown("1 minutes", user.id)Drop that in a command's pre hook. Admins can now re-run the command every 10 seconds; everyone else waits a minute.
That's the whole idea — the rest of this guide is what you can do with it.
Skipping the Cooldown Entirely
Often you don't want a shorter limit for staff — you want none. Because the cooldown only applies when cooldown() actually runs, you skip it by not calling it:
if not has_permission(user, "administrator"):
cooldown("1 minutes", user.id)Regular members get a one-minute cooldown. Administrators get no cooldown at all, because for them that line never executes.
This is the pattern worth internalising: a cooldown that isn't called doesn't exist. Every conditional cooldown is just deciding whether — and with what duration — to make that call.
How the Pieces Work
has_permission(user, "administrator")
Returns True if the member has every permission you list:
has_permission(user, "manage_messages", "kick_members") # needs bothhas_any_permission(user, ...)
Returns True if they have at least one:
has_any_permission(user, "manage_messages", "kick_members") # either is enoughUse has_any_permission when several roles should qualify — most staff checks are naturally "any of these", not "all of these".
cooldown(time, key)
Starts a cooldown for the given duration. The key decides who it applies to:
user.id→ per user. Everyone has their own timer.channel_id→ per channel. One person's use blocks the whole channel.- A fixed string → server-wide. One timer for everybody.
Durations are plain English: "10 seconds", "1 minutes", "2 hours", "1 days".
Patterns Worth Stealing
Three tiers instead of two:
if has_permission(user, "administrator"):
pass # no cooldown
elif has_any_permission(user, "manage_messages", "kick_members"):
cooldown("10 seconds", user.id)
else:
cooldown("1 minutes", user.id)Role-based rather than permission-based — useful for boosters or supporters, who have no special Discord permissions:
if not has_any_role(user, "Server Booster", "Supporter"):
cooldown("2 minutes", user.id)Per-channel cooldown so one busy channel doesn't get flooded, while other channels stay usable:
cooldown("30 seconds", channel_id)A shared, server-wide cooldown for something genuinely expensive:
cooldown("1 hours", "global_announcement")A custom message instead of the default rejection:
cooldown("1 minutes", user.id, error_message="Slow down — try again in a minute.")Allow a few uses before the limit bites — count sets how many runs are permitted inside the window:
cooldown("1 minutes", user.id, count=3)Three uses per minute, then they wait.
Where to Put It
Cooldowns belong in the pre hook, before any real work. That's the point: reject early, before the command has queried a database, called an API, or posted anything.
Put it at the very top, above your other logic:
if not has_permission(user, "administrator"):
cooldown("1 minutes", user.id)
# ...the rest of your commandTroubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Everyone shares one cooldown | No key passed, or a fixed key | Pass user.id as the key for per-user limits |
| Cooldown never triggers | cooldown() sits in an unreached branch | Check your condition — an inverted if skips it silently |
| Admins still rate-limited | Permission name typo | Use exact names: administrator, manage_messages, kick_members |
| Cooldown applies after the work | Called too late in the hook | Move the cooldown() call to the top of pre code |
Frequently Asked Questions
Can I give admins no cooldown in Discord?
Yes — wrap the call in a negative check so it never runs for them: if not has_permission(user, "administrator"): cooldown("1 minutes", user.id).
What's the difference between has_permission and has_any_permission?
has_permission requires all the permissions you list. has_any_permission requires at least one. For staff checks, has_any_permission is usually what you want.
Can cooldowns be per channel instead of per user?
Yes. The second argument is the key — pass channel_id for a per-channel cooldown, user.id for per-user, or any fixed string for a server-wide one.
Can I allow a few uses before the cooldown starts?
Yes. cooldown("1 minutes", user.id, count=3) permits three runs inside the window before blocking further use.
Can I base cooldowns on roles rather than permissions?
Yes — use has_any_role(user, "Booster", "Supporter"). Handy for members who deserve a shorter wait without holding any moderation permissions.
Do cooldowns survive a bot restart?
Yes. Cooldowns are persisted, so nobody bypasses one by waiting for a restart.
The Bigger Point
Conditional cooldowns are a small example of something larger: because these are your commands, every rule is a decision you make rather than a setting you're given.
Different limits per role, per channel, per time of day, or based on a member variable you track yourself — it's all the same if.
Possibilities are endless. You just have to think. 🚀
Loading comments…
Need help?
Have a question, a suggestion, or stuck on something? Reach out — we're happy to help.