import random import time import logging logger = logging.getLogger(__name__) PAIRING_CODE_LENGTH = 6 PAIRING_CODE_EXPIRY_SECONDS = 3600 MAX_PENDING_PER_CHANNEL = 3 PAIRING_APPROVED_MESSAGE = "BlueBubbles DM pairing approved. You can now send iMessages to this channel." class PairingManager: def __init__(self): self._pending: dict[str, dict] = {} self._approved: set[str] = set() def generate_code(self, peer_id: str) -> str: self._cleanup_expired() active = sum(1 for v in self._pending.values() if v["status"] == "pending") if active >= MAX_PENDING_PER_CHANNEL: raise RuntimeError(f"Maximum pending pairings ({MAX_PENDING_PER_CHANNEL}) reached") code = "".join(str(random.randint(0, 9)) for _ in range(PAIRING_CODE_LENGTH)) self._pending[peer_id] = { "code": code, "created_at": time.time(), "status": "pending", } return code def verify_code(self, peer_id: str, code: str) -> bool: entry = self._pending.get(peer_id) if not entry: return False if time.time() - entry["created_at"] > PAIRING_CODE_EXPIRY_SECONDS: del self._pending[peer_id] return False if entry["code"] == code: entry["status"] = "approved" self._approved.add(peer_id) return True return False def is_approved(self, peer_id: str) -> bool: return peer_id in self._approved def revoke(self, peer_id: str): self._approved.discard(peer_id) self._pending.pop(peer_id, None) def _cleanup_expired(self): now = time.time() expired = [ pid for pid, entry in self._pending.items() if (now - entry["created_at"]) > PAIRING_CODE_EXPIRY_SECONDS ] for pid in expired: del self._pending[pid] def pending_count(self) -> int: self._cleanup_expired() return sum(1 for v in self._pending.values() if v["status"] == "pending")