RateLimitMiddleware
Global before-middleware that enforces per-user request rate limits using the FSM storage's atomic increment() counter. In v0.5.0 the implementation was completely rewritten — a single storage call replaces the previous broken CAS loop.
Basic Setup
from kurigram_addons import RateLimitMiddleware, KurigramClient, MemoryStorage
app = KurigramClient("bot", bot_token="TOKEN", storage=MemoryStorage())
@app.on_startup
async def setup_middleware(client):
rate_limiter = RateLimitMiddleware(
limit=5, # max 5 requests
period=60, # per 60-second window
)
await client.include_middleware(rate_limiter, kind="before", priority=100)Custom Response When Limited
async def on_limited(update, client):
if hasattr(update, "reply"):
await update.reply("⏳ Too many requests — please wait a moment.")
rate_limiter = RateLimitMiddleware(
limit=5,
period=60,
on_limited=on_limited,
)Custom Key Function
By default requests are bucketed per user ID. Supply key_functo bucket by chat, IP, or any other dimension.
def key_by_chat(update) -> str:
chat = getattr(update, "chat", None)
return f"chat:{chat.id}" if chat else "unknown"
rate_limiter = RateLimitMiddleware(
limit=20, period=60,
key_func=key_by_chat,
)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | int | — | Max requests per window (required) |
period | float | — | Window duration in seconds (required) |
on_limited | Callable | None | None | Async callback when limit exceeded; receives (update, client) |
key_func | Callable | None | user ID | Extract rate-limit bucket key from update |
How It Works
Each request calls storage.increment(key, amount=1, ttl=period), which atomically increments a counter and sets a TTL if the key is new. If the returned count exceeds limit, on_limitedis called and the handler is skipped. Counters are stored under a dedicated__rl__: key namespace to prevent collisions with real FSM state.