"""Per-account rate limiter на мутации через MCP (канон mcp.md: чувствительные
операции). Token bucket в памяти процесса; после рестарт окно чистое — для
защиты от спама агента хватает (Telegram сам флудит глубже).
Ключ — account_id: у каждого аккаунта свой Telegram-флуд, лимит честнее
per-user. Плюс отдельное окно — на выдачу кодов логина (login_flow сам).
"""
import time
from collections import defaultdict, deque
_windows: dict[int, deque] = defaultdict(deque)
def check_write(account_id: int, limit_n: int, window_s: float) -> dict | None:
"""None — проход; dict — {"error": 429, "detail": …} для возврата как данных."""
now = time.monotonic()
win = _windows[account_id]
while win and now - win[0] > window_s:
win.popleft()
if len(win) >= limit_n:
retry = int(window_s - (now - win[0])) + 1
return {"error": 429, "detail": f"rate limit: {limit_n} writes / {int(window_s)}s per account, retry in ~{retry}s"}
win.append(now)
return None