import os from yuxi.channel.extensions.douyin.types import DouyinAccount class DouyinConfig: ENV_MAP = { "client_key": "DOUYIN_CLIENT_KEY", "client_secret": "DOUYIN_CLIENT_SECRET", "dm_policy": "DOUYIN_DM_POLICY", "streaming": "DOUYIN_STREAMING", "remove_markdown": "DOUYIN_REMOVE_MD", "welcome_text": "DOUYIN_WELCOME_TEXT", } def __init__(self, config: dict | None = None): self._config = config or {} def list_account_ids(self, config: dict | None = None) -> list[str]: cfg = config or self._config accounts = cfg.get("accounts", []) if accounts: return [a.get("account_id", "default") for a in accounts] if self._resolve_client_key(cfg): return ["default"] return [] def resolve_account(self, account_id: str = "default", config: dict | None = None) -> DouyinAccount: cfg = config or self._config accounts = cfg.get("accounts", []) account_data = {} for a in accounts: if a.get("account_id") == account_id: account_data = a break return DouyinAccount( account_id=account_id, client_key=account_data.get("client_key") or self._resolve_client_key(cfg) or "", client_secret=account_data.get("client_secret") or self._env_or_config("client_secret", cfg) or "", dm_policy=account_data.get("dm_policy") or self._env_or_config("dm_policy", cfg) or "open", streaming=self._resolve_bool(account_data, "streaming", cfg, True), remove_markdown=self._resolve_bool(account_data, "remove_markdown", cfg, True), welcome_text=account_data.get("welcome_text") or self._env_or_config("welcome_text", cfg) or "你好!有什么可以帮助你的?", ) def is_configured(self, account: dict | None = None) -> bool: if account: return bool(account.get("client_key") and account.get("client_secret")) return bool(self._resolve_client_key(self._config) and self._env_or_config("client_secret", self._config)) def is_enabled(self, account: dict | None = None, config: dict | None = None) -> bool: cfg = config or self._config if account: return account.get("enabled", True) return cfg.get("enabled", True) def disabled_reason(self, account: dict | None = None) -> str: if account and account.get("enabled") is False: return "Account explicitly disabled" return "" def describe_account(self, account: dict | None = None) -> dict: if account: return {"account_id": account.get("account_id", "default")} return {"account_id": "default"} def _resolve_client_key(self, config: dict | None = None) -> str | None: return self._env_or_config("client_key", config) def _env_or_config(self, key: str, config: dict | None = None) -> str | None: cfg = config or self._config if key in cfg: val = cfg[key] if val: return str(val) env_key = self.ENV_MAP.get(key, "") if env_key: val = os.getenv(env_key) if val: return val return None def _resolve_bool(self, account_data: dict, key: str, config: dict | None, default: bool) -> bool: if key in account_data: val = account_data[key] if isinstance(val, bool): return val if isinstance(val, str): return val.lower() != "false" env_val = self._env_or_config(key, config) if env_val is not None: return env_val.lower() != "false" return default def config_schema(self) -> dict: return { "$schema": "https://json-schema.org/draft-07/schema#", "type": "object", "title": "抖音渠道配置", "properties": { "client_key": { "type": "string", "title": "Client Key", "description": "抖音开放平台小程序应用的 Client Key", }, "client_secret": { "type": "string", "title": "Client Secret", "x-ui-password": True, "description": "抖音开放平台小程序应用的 Client Secret", }, "dm_policy": { "type": "string", "enum": ["open", "pairing", "allowlist", "disabled"], "default": "open", "title": "DM 策略", }, "streaming": { "type": "boolean", "default": True, "title": "启用流式输出", }, "remove_markdown": { "type": "boolean", "default": True, "title": "移除 Markdown 格式", "description": "自动移除 AI 回复中的 Markdown 符号和外部 URL 链接", }, }, "required": ["client_key", "client_secret"], }