"""DM Pairing flow for Synology Chat security policy. Handles the complete pairing lifecycle: unknown user triggers pairing request, approval adds user to allowlist, denial blocks permanently. Includes standardized approval/denial message templates for user notification. """ from __future__ import annotations from typing import Any from yuxi.utils.logging_config import logger _PAIRING_APPROVED_MESSAGE = "ForcePilot: your access has been approved. You may now interact with the bot." _PAIRING_DENIED_MESSAGE = "ForcePilot: your access request has been denied." class PairingManager: def __init__(self, security_policy): self._policy = security_policy self._pairing_requests: dict[str, dict[str, Any]] = {} def request_pairing(self, user_id: str, metadata: dict[str, Any] | None = None) -> dict[str, Any]: request_id = f"pairing:{user_id}" self._pairing_requests[request_id] = { "user_id": user_id, "status": "pending", "metadata": metadata or {}, } logger.info(f"[SynologyChat] Pairing requested for user {user_id}") return {"request_id": request_id, "status": "pending", "user_id": user_id} def approve(self, user_id: str) -> dict[str, Any]: request_id = f"pairing:{user_id}" if request_id not in self._pairing_requests: return {"status": "error", "message": f"No pairing request for user {user_id}"} self._pairing_requests[request_id]["status"] = "approved" self._policy.approve_pairing(user_id) logger.info(f"[SynologyChat] Pairing approved for user {user_id}") return { "request_id": request_id, "status": "approved", "user_id": user_id, "approved_message": _PAIRING_APPROVED_MESSAGE, } def deny(self, user_id: str) -> dict[str, Any]: request_id = f"pairing:{user_id}" if request_id not in self._pairing_requests: return {"status": "error", "message": f"No pairing request for user {user_id}"} self._pairing_requests[request_id]["status"] = "denied" self._policy.deny_pairing(user_id) logger.info(f"[SynologyChat] Pairing denied for user {user_id}") return { "request_id": request_id, "status": "denied", "user_id": user_id, "denied_message": _PAIRING_DENIED_MESSAGE, } def get_pending(self) -> list[dict[str, Any]]: return [r for r in self._pairing_requests.values() if r["status"] == "pending"] def get_status(self, user_id: str) -> dict[str, Any]: request_id = f"pairing:{user_id}" return self._pairing_requests.get(request_id, {"status": "not_requested", "user_id": user_id})