from __future__ import annotations import re from dataclasses import dataclass from typing import Any from yuxi.utils.logging_config import logger _APPROVER_ID_RE = re.compile(r"^ou_[a-f0-9]+$", re.IGNORECASE) @dataclass class FeishuApprovalAdapter: approvers: list[str] timeout_s: float = 300.0 def normalize_approver_id(self, raw_id: str) -> str | None: cleaned = raw_id.strip() if _APPROVER_ID_RE.match(cleaned): return cleaned if cleaned.startswith("on_"): return cleaned if cleaned.startswith("ou_"): return cleaned logger.debug("[FeishuApproval] Invalid approver ID format: %s", cleaned) return None def check_approval(self, open_id: str) -> bool: for approver in self.approvers: normalized = self.normalize_approver_id(approver) if normalized and normalized == open_id: return True return False @classmethod def from_config(cls, config: dict[str, Any]) -> FeishuApprovalAdapter: approval_cfg = config.get("approval", {}) or {} return cls( approvers=approval_cfg.get("approvers", []), timeout_s=float(approval_cfg.get("timeout", 300)), )