from __future__ import annotations import hashlib import time from typing import Any from yuxi.utils.logging_config import logger SECRET_PREFIXES = ("corp_secret", "app_secret", "token", "bridge_url") def _hash_credential(value: str) -> str: return hashlib.sha256(value.encode()).hexdigest()[:16] class WeChatSecretsAdapter: def __init__(self): self._watchlist: dict[str, str] = {} self._subscribers: list[callable] = [] self._last_check: float = 0.0 self._check_interval: float = 5.0 def register_subscriber(self, callback: callable) -> None: if callback not in self._subscribers: self._subscribers.append(callback) def _notify_subscribers(self, changed_keys: list[str]) -> None: for cb in self._subscribers: try: cb(changed_keys) except Exception as e: logger.warning(f"[WeChat/Secrets] Subscriber error: {e}") def build_watchlist(self, config: dict[str, Any]) -> dict[str, str]: snap: dict[str, str] = {} for key in SECRET_PREFIXES: value = config.get(key) if value: snap[key] = _hash_credential(str(value)) accounts = config.get("accounts", {}) for account_id, acc_cfg in accounts.items(): for key in SECRET_PREFIXES: value = acc_cfg.get(key) if value: snap[f"accounts.{account_id}.{key}"] = _hash_credential(str(value)) return snap def detect_changes(self, config: dict[str, Any]) -> list[str]: current = self.build_watchlist(config) changed: list[str] = [] for key, digest in current.items(): if key in self._watchlist and self._watchlist[key] != digest: changed.append(key) self._watchlist = current return changed def should_check(self) -> bool: now = time.monotonic() if now - self._last_check >= self._check_interval: self._last_check = now return True return False async def monitor_and_reload(self, config: dict[str, Any], reload_fn: callable) -> None: if not self.should_check(): return changed = self.detect_changes(config) if changed: logger.info(f"[WeChat/Secrets] Credential changes detected: {changed}") self._notify_subscribers(changed) if reload_fn: await reload_fn(changed) def snapshot_secrets(self, config: dict[str, Any]) -> list[dict[str, Any]]: items: list[dict[str, Any]] = [] for key in SECRET_PREFIXES: value = config.get(key) if value: items.append( { "key": key, "source": "root config", "configured": True, "digest_prefix": _hash_credential(str(value)), } ) accounts = config.get("accounts", {}) for account_id, acc_cfg in accounts.items(): for key in SECRET_PREFIXES: value = acc_cfg.get(key) if value: items.append( { "key": key, "source": f"accounts.{account_id}", "configured": True, "digest_prefix": _hash_credential(str(value)), } ) return items