How to Implement Discord Bot Cooldown per User Without External Storage
🔍 WiseChecker

How to Implement Discord Bot Cooldown per User Without External Storage

You want a Discord bot that limits how often a user can run a command, but you do not want to use a database or file storage. Many bot developers need cooldowns to prevent spam and abuse. Storing cooldown data in memory avoids setup of Redis, SQLite, or JSON files. This article explains how to implement a per-user cooldown using Python’s time module and an in-memory dictionary.

Key Takeaways: In-Memory Bot Cooldown Without a Database

  • Dictionary + time.time(): Store user IDs and timestamps in a Python dictionary; check elapsed seconds against your cooldown value.
  • discord.py @commands.cooldown decorator: Built-in rate limiter that uses an in-memory bucket; no external storage needed.
  • discord.ext.tasks.loop for cleanup: Periodically remove expired entries from your dictionary to prevent memory bloat.

ADVERTISEMENT

Understanding In-Memory Cooldown for Discord Bots

A cooldown prevents a user from running the same command more than once within a set time window. When you use external storage like a database, the cooldown persists across bot restarts and across multiple bot instances. In-memory cooldown stores data only in the bot process’s RAM. This approach is the simplest to code, requires zero setup, and works perfectly for single-instance bots or small communities.

The core mechanism is a Python dictionary where each key is a user ID and each value is the timestamp of the last command execution. When a user runs a command, the bot checks if the current time minus the stored timestamp is less than the cooldown duration. If true, the bot rejects the command. If false, the bot updates the timestamp and runs the command.

Limitations of In-Memory Cooldown

All cooldown data is lost when the bot restarts. If you run multiple bot processes behind a load balancer, each process has its own dictionary, so a user could bypass cooldown by hitting a different instance. For these cases, you need external storage. For most single-process bots, in-memory is fast and reliable.

How to Implement a Custom Cooldown Using a Dictionary

This method gives you full control over the cooldown logic. You decide the error message, the cooldown duration per command, and whether to apply different cooldowns to different roles.

  1. Import the time module
    Add import time at the top of your bot script. The time.time() function returns the current Unix timestamp in seconds.
  2. Create a cooldown dictionary
    Define a global dictionary: cooldowns = {}. Each key will be a user ID as an integer, and each value will be a float timestamp.
  3. Define a cooldown check function
    Write a function that takes the user ID and cooldown duration in seconds. It returns True if the user can proceed, False if the cooldown is active. Inside, get the current time with time.time(). Look up the user ID in the dictionary. If the user has no entry, or if current time minus stored time is greater than the duration, update the entry and return True. Otherwise return False.
  4. Use the check inside a command
    In your @bot.command() function, call the cooldown check at the top. If it returns False, send an error message and use return to stop execution. Example: if not can_proceed(ctx.author.id, 30): await ctx.send('You must wait 30 seconds between uses.'); return
  5. Add periodic cleanup to prevent memory growth
    Use discord.ext.tasks.loop to run a function every few minutes. The function iterates over the dictionary and removes entries where the timestamp is older than the maximum cooldown you use. This keeps the dictionary small.

ADVERTISEMENT

Using Discord.py’s Built-in Cooldown Decorator

The discord.py library provides a cooldown system that also uses in-memory storage. It is easier to implement than a custom dictionary and includes automatic error handling.

  1. Import the decorator
    Add from discord.ext.commands import cooldown, BucketType to your imports.
  2. Apply the cooldown decorator
    Place @commands.cooldown(1, 30, BucketType.user) above your command. The first argument is the number of uses allowed. The second is the time window in seconds. The third is the bucket type: BucketType.user for per-user, BucketType.channel for per-channel, BucketType.guild for per-server, or BucketType.default for global.
  3. Handle the cooldown error
    Create an error handler for your bot or for a cog. Use @bot.event and listen for on_command_error. Check if the error is commands.CommandOnCooldown. If so, send a message with the remaining time: await ctx.send(f'Command on cooldown. Try again in {error.retry_after:.2f} seconds.')
  4. Reset cooldown manually if needed
    You can reset a user’s cooldown by calling ctx.command.reset_cooldown(ctx). This is useful for admin commands that bypass cooldown.

Common Mistakes and Things to Avoid

Dictionary Grows Without Cleanup

If you never remove old entries, the dictionary will grow indefinitely. Use a periodic task to delete entries older than your longest cooldown. For example, if your longest cooldown is 3600 seconds, remove entries older than 3600 seconds every 10 minutes.

Cooldown Not Thread-Safe

Discord bots run in a single thread, so dictionary access is safe. If you use asyncio tasks that modify the dictionary concurrently, use asyncio.Lock to prevent race conditions. For most commands, this is not needed.

Using Wrong Bucket Type

If you apply BucketType.default, the cooldown applies to all users globally. That means the entire bot stops responding to that command for the cooldown period after one user uses it. Always use BucketType.user for per-user cooldown.

Cooldown Persists Across Bot Restarts

In-memory cooldown does not survive a restart. If your bot restarts frequently, users can immediately reuse commands. If this is a problem, switch to a database. For most bots, restarts are rare and the trade-off is acceptable.

Custom Dictionary vs Built-In Cooldown: Comparison

Item Custom Dictionary Built-In Decorator
Setup effort Write your own check function Single decorator line
Error messages Full control over text and formatting Must handle CommandOnCooldown error
Cooldown reset Manually delete dictionary key Use reset_cooldown() method
Multiple commands Each command needs its own check Each command gets its own decorator
Memory cleanup Must implement periodic cleanup Automatic internal cleanup
Bucket types Only user-based (you code others) User, channel, guild, global

You can now implement a per-user cooldown for your Discord bot without any external storage. Use the custom dictionary method if you need fine-grained control over error messages or cooldown durations per role. Use the built-in decorator for speed and simplicity. For production bots that restart often or run multiple instances, consider adding Redis or SQLite to persist cooldown data across restarts.

ADVERTISEMENT