from __future__ import annotations from enum import Enum from typing import Any from yuxi.utils.logging_config import logger class WizardStep(Enum): MODE_SELECTION = "mode_selection" CREDENTIALS = "credentials" VALIDATION = "validation" WEBHOOK = "webhook" CONFIRMATION = "confirmation" class WeChatSetupWizard: def __init__(self): self._current_step = WizardStep.MODE_SELECTION self._selected_mode: str = "" self._config_snapshot: dict[str, Any] = {} def get_available_modes(self) -> list[dict[str, Any]]: return [ { "id": "wecom", "label": "企业微信 (WeCom)", "description": "使用企业微信应用消息 API,需要 corp_id、corp_secret、agent_id", "required_fields": ["corp_id", "corp_secret", "agent_id"], }, { "id": "mp", "label": "公众号 (MP)", "description": "使用微信公众号客服消息 API,需要 app_id、app_secret", "required_fields": ["app_id", "app_secret"], }, { "id": "personal", "label": "个人微信 (Bridge)", "description": "通过桥接服务连接个人微信,需要 bridge_url", "required_fields": ["bridge_url"], }, ] async def select_mode(self, mode: str) -> dict[str, Any]: if mode not in ("wecom", "mp", "personal"): return { "success": False, "error": f"Invalid mode: {mode}", "available": [m["id"] for m in self.get_available_modes()], } self._selected_mode = mode self._current_step = WizardStep.CREDENTIALS return {"success": True, "mode": mode, "next_step": "credentials"} async def set_credentials(self, credentials: dict[str, Any]) -> dict[str, Any]: if not self._selected_mode: return {"success": False, "error": "Please select a mode first"} required = { "wecom": ["corp_id", "corp_secret", "agent_id"], "mp": ["app_id", "app_secret"], "personal": ["bridge_url"], }.get(self._selected_mode, []) missing = [k for k in required if not credentials.get(k)] if missing: return {"success": False, "error": f"Missing required fields: {missing}"} self._config_snapshot = {} for k in required: self._config_snapshot[k] = credentials[k] self._current_step = WizardStep.VALIDATION return {"success": True, "config": self._config_snapshot, "next_step": "validation"} async def validate_connection(self, http_client_factory=None) -> dict[str, Any]: if not self._config_snapshot: return {"success": False, "error": "No credentials configured"} if self._selected_mode == "wecom": valid = all(self._config_snapshot.get(k) for k in ("corp_id", "corp_secret")) elif self._selected_mode == "mp": valid = all(self._config_snapshot.get(k) for k in ("app_id", "app_secret")) elif self._selected_mode == "personal": valid = bool(self._config_snapshot.get("bridge_url")) else: valid = False self._current_step = WizardStep.WEBHOOK if valid else WizardStep.CREDENTIALS return { "success": valid, "mode": self._selected_mode, "message": "Credentials validated" if valid else "Invalid credentials", "next_step": "webhook" if valid else "credentials", } async def configure_webhook(self, webhook_url: str) -> dict[str, Any]: if not self._config_snapshot: return {"success": False, "error": "No credentials configured"} self._config_snapshot["webhook_url"] = webhook_url self._config_snapshot["token"] = self._config_snapshot.get("token", "") self._current_step = WizardStep.CONFIRMATION return { "success": True, "webhook_url": webhook_url, "next_step": "confirmation", "note": "请在企业微信后台/公众号配置服务器地址为以上 webhook_url,并配置相同的 Token / EncodingAESKey" if self._selected_mode == "wecom" else "", } async def confirm_and_apply(self) -> dict[str, Any]: if self._current_step != WizardStep.CONFIRMATION: return {"success": False, "error": "Please complete all steps before confirming"} config = { "enabled": True, "mode": self._selected_mode, **self._config_snapshot, } logger.info(f"[WeChat/SetupWizard] Configuration confirmed for mode={self._selected_mode}") return { "success": True, "mode": self._selected_mode, "config": config, "message": f"微信 {self._selected_mode} 模式配置完成,请启动适配器以验证连接", } def get_current_step_info(self) -> dict[str, Any]: return { "step": self._current_step.value, "mode": self._selected_mode, "config_snapshot": dict(self._config_snapshot), } def reset(self) -> None: self._current_step = WizardStep.MODE_SELECTION self._selected_mode = "" self._config_snapshot = {} class WeChatSetupAdapter: ROBUST_READ_COMMANDS = [ "wechat config validate", "wechat config show", "wechat status", ] @staticmethod def get_wizard_summary(config: dict[str, Any]) -> dict[str, Any]: has_corp = all(config.get(k) for k in ("corp_id", "corp_secret", "agent_id")) has_mp = all(config.get(k) for k in ("app_id", "app_secret")) has_bridge = bool(config.get("bridge_url")) mode = "wecom" if has_corp else "mp" if has_mp else "personal" if has_bridge else "unconfigured" return { "mode": mode, "configured": mode != "unconfigured", "webhook_configured": bool(config.get("webhook_url")), "enabled": config.get("enabled", False), }