Skip to content

Rate limiting

"Rate limiting" here comes in two kinds: the bot's custom command rate limiting (application layer), and Discord's platform API rate limit (handled automatically by discord.py).

Custom command rate limiting

The Tools module and the Emoji module implement sliding-window rate limiting for the following commands to prevent abuse:

  • random
  • uuid
  • to-file
  • e
  • emoji-info

In addition, all commands have a global rate-limit fallback of 10 reqs / 10s (handled uniformly by the @u.requires() decorator).

How it works

The RateLimiter (utils.py) keys on (command, user ID) and records the call timestamps within the window:

python
def hit(self, key, limit, window) -> tuple[bool, float]:
    # Returns (allowed?, seconds to wait if limited)
  • Each user is counted independently for each command.
  • When the limit is exceeded, the bot replies with :hourglass_flowing_sand: Rate limited, retry in Ns (a temporary message).

Quota rules

User typeQuota
Regular userBase quota (e.g. random: 10)
ModBase quota × mod_multiplier (default ×3)
AdminNot rate-limited

Here, Admin means a server admin or config admin (is_admin); Mod means someone matched in the mod list. See Permission system for details.

Configuration

yaml
tools:
  enabled: true
  ratelimit:
    enabled: true          # Whether to enable rate limiting
    window: 60             # Time window (seconds)
    mod_multiplier: 3      # Mod quota multiplier (relative to normal users); admin is not rate limited
    random: 10             # random: max uses per window for normal users
    uuid: 10               # uuid:   max uses per window for normal users
    "2file": 10            # to-file:  max uses per window for normal users (YAML key: "2file")
FieldTypeDefaultDescription
enabledbooltrueWhether to enable rate limiting
windowint60Time window (seconds)
mod_multiplierint3Mod quota multiplier relative to regular users
randomint10Max number of random calls per window (regular users)
uuidint10Max number of uuid calls per window (regular users)
"2file"int10Max number of to-file calls per window (YAML key name, internal field to_file)

TIP

"2file" is the YAML key name of the to-file command (starts with a digit so it must be quoted); the internal Pydantic field name is to_file, mapped via Field(alias="2file").

Discord API rate limit

Beyond application-layer rate limiting, the Discord platform itself imposes a rate limit on API calls. This part is handled automatically by discord.py: the library parses the X-RateLimit-* response headers and, when a limit is hit, automatically queues / backs off and retries, with no extra configuration needed.

Scenarios worth noting:

  • Bulk message cleanup (clear-message): uses bulk_delete (up to 100 messages per batch), and Discord does not allow bulk-deleting messages older than 14 days. When the number of such expired messages does not exceed tools.clear_single_delete_max (default 20, set to 0 to disable), it falls back to deleting them one by one with Message.delete() (not subject to the 14-day limit); if the threshold is exceeded, they are still counted individually as "undeletable because older than 14 days", to avoid triggering rate limits with a large number of per-message requests.
  • Emoji sending: makes an HTTP request to the remote base_url (via the proxy config), affected by the availability of the remote service.
  • Emoji search autocomplete: too large a max_results may cause Discord autocomplete calls to fail; the default is 25.

If you use it heavily on a large self-hosted server, it is still recommended to set reasonable custom rate-limit quotas to avoid triggering Discord's global rate limiting through excessive requests.

Released under the MIT License