from __future__ import annotations from yuxi.utils.logging_config import logger class ChannelConfig: def __init__(self, yaml_path: str): self._yaml_path = yaml_path self._data: dict = {} self._load() def _load(self) -> None: try: import yaml with open(self._yaml_path) as f: self._data = yaml.safe_load(f) or {} except FileNotFoundError: logger.warning("channel config not found: %s, using defaults", self._yaml_path) self._data = {} except Exception as exc: logger.warning("channel config load error: %s, using defaults", exc) self._data = {} async def reload(self) -> None: self._load() logger.info("channel config reloaded from %s", self._yaml_path) async def on_config_updated(self, config: dict) -> list[str]: old_auth_token = self._data.get("auth", {}).get("token") old_auth_password = self._data.get("auth", {}).get("password") old_feishu_encrypt_key = self._data.get("feishu", {}).get("encrypt_key") self._data = config updated = [] new_auth_token = config.get("auth", {}).get("token") new_auth_password = config.get("auth", {}).get("password") new_feishu_encrypt_key = config.get("feishu", {}).get("encrypt_key") if old_auth_token != new_auth_token: updated.append("auth_token") if old_auth_password != new_auth_password: updated.append("auth_password") if old_feishu_encrypt_key != new_feishu_encrypt_key: updated.append("feishu_encrypt_key") return updated @property def auth_token(self) -> str | None: return self._data.get("auth", {}).get("token") @property def auth_password(self) -> str | None: return self._data.get("auth", {}).get("password") @property def access_policies(self) -> dict: return self._data.get("access_policies", {}) @property def allow_from(self) -> dict: return self._data.get("allow_from", {}) @property def keyword_blocklist(self) -> set[str]: keywords = self._data.get("keyword_blocklist", []) return set(kw.lower() for kw in keywords) @property def hook_mappings(self) -> list[dict]: return self._data.get("hooks", {}).get("mappings", []) @property def feishu_verification_token(self) -> str | None: return self._data.get("feishu", {}).get("verification_token") @property def feishu_encrypt_key(self) -> str | None: return self._data.get("feishu", {}).get("encrypt_key") @property def max_auth_attempts(self) -> int: return self._data.get("auth", {}).get("max_attempts", 5) @property def lockout_seconds(self) -> int: return self._data.get("auth", {}).get("lockout_seconds", 300) @property def allow_anonymous(self) -> bool: return self._data.get("auth", {}).get("allow_anonymous", False) @property def mention_gate_config(self) -> dict: return self._data.get("mention_gate", {}) @property def feishu_ws_config(self) -> dict | None: feishu = self._data.get("feishu", {}) mode = feishu.get("mode", "webhook") if mode not in ("websocket", "both"): return None return { "app_id": feishu.get("app_id", ""), "app_secret": feishu.get("app_secret", ""), "mode": mode, } @property def dingtalk_ws_config(self) -> dict | None: dingtalk = self._data.get("dingtalk", {}) mode = dingtalk.get("mode", "webhook") if mode not in ("websocket", "both"): return None return { "client_id": dingtalk.get("client_id", ""), "client_secret": dingtalk.get("client_secret", ""), "mode": mode, } def get_channel_config(self, channel_name: str) -> dict: return self._data.get(channel_name, {}) @property def raw_data(self) -> dict: return self._data