import random import time CODE_LENGTH = 6 CODE_TTL_SECONDS = 600 PENDING_MAX = 3 RATE_LIMIT_SECONDS = 3600 class JDPairing: def __init__(self): self._pending: dict[str, dict] = {} self._user_last_code_at: dict[str, float] = {} def generate_code(self, user_id: str) -> str | None: now = time.time() if user_id in self._user_last_code_at: elapsed = now - self._user_last_code_at[user_id] if elapsed < RATE_LIMIT_SECONDS: return None if len(self._pending) >= PENDING_MAX: oldest = min(self._pending.values(), key=lambda p: p["created_at"]) if now - oldest["created_at"] < CODE_TTL_SECONDS: return None expired_user = next(k for k, v in self._pending.items() if v == oldest) del self._pending[expired_user] code = _generate_code() self._pending[user_id] = {"code": code, "created_at": now} self._user_last_code_at[user_id] = now return code def verify(self, user_id: str, code: str) -> bool: entry = self._pending.get(user_id) if not entry: return False if time.time() - entry["created_at"] > CODE_TTL_SECONDS: del self._pending[user_id] return False if entry["code"] != code.upper().strip(): return False del self._pending[user_id] return True def get_pending_count(self) -> int: return len(self._pending) def _generate_code() -> str: no_ambiguous = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" return "".join(random.choices(no_ambiguous, k=CODE_LENGTH))