ForcePilot/backend/package/yuxi/channels/adapters/bluebubbles/setup_wizard.py
Kris 6aa1d52a8b feat(bluebubbles): 实现完整的BlueBubbles适配器基础组件
新增BlueBubbles适配器全套核心工具类与服务,包含会话管理、消息去重、Webhook验证、缓存系统、账号配置解析、聊天消息处理等完整功能模块,支持iMessage消息收发、群管理、反应特效、语音合成等能力,提供完善的健康检查与配置校验流程。
2026-05-12 00:42:57 +08:00

171 lines
6.0 KiB
Python

from __future__ import annotations
from typing import Any
from yuxi.channels.adapters.bluebubbles.config import BlueBubblesConfig
from yuxi.channels.adapters.bluebubbles.setup_core import validate_credentials, validate_server_url
class BlueBubblesSetupWizard:
def __init__(self):
self._config = BlueBubblesConfig()
self._steps: list[dict[str, Any]] = []
self._current_step = 0
self._completed_steps: set[str] = set()
@property
def steps(self) -> list[dict[str, Any]]:
return [
{
"id": "server_url",
"title": "BlueBubbles 服务器地址",
"description": "输入 BlueBubbles 服务器的 URL 地址",
"config_key": "server_url",
"required": True,
},
{
"id": "password",
"title": "服务器密码",
"description": "输入 BlueBubbles 服务器的密码",
"config_key": "password",
"required": True,
"sensitive": True,
},
{
"id": "dm_policy",
"title": "私聊策略",
"description": "选择私聊消息的处理策略",
"config_key": "dm_policy",
"options": ["open", "pairing", "allowlist", "blocklist"],
"default": "pairing",
},
{
"id": "group_policy",
"title": "群组策略",
"description": "选择群组消息的处理策略",
"config_key": "group_policy",
"options": ["open", "pairing", "allowlist", "blocklist"],
"default": "allowlist",
},
{
"id": "streaming_mode",
"title": "流式输出模式",
"description": "选择消息流式输出的方式",
"config_key": "streaming_mode",
"options": ["off", "partial", "block"],
"default": "partial",
},
{
"id": "tapback_enabled",
"title": "Tapback 反应",
"description": "是否启用 Tapback 消息反应功能",
"config_key": "tapback_enabled",
"type": "bool",
"default": True,
},
{
"id": "ai_vision_enabled",
"title": "AI 视觉分析",
"description": "是否启用图片 AI 视觉分析功能",
"config_key": "ai_vision_enabled",
"type": "bool",
"default": False,
},
{
"id": "webhook_secret",
"title": "Webhook 密钥",
"description": "设置 Webhook 签名验证密钥(留空则随机生成)",
"config_key": "webhook_secret",
"required": False,
"sensitive": True,
},
]
async def apply_config(self, config_updates: dict[str, Any]) -> dict[str, Any]:
results: dict[str, Any] = {"success": True, "validations": {}}
for key, value in config_updates.items():
if hasattr(self._config, key):
setattr(self._config, key, value)
self._completed_steps.add(key)
if "server_url" in config_updates:
validation = await validate_server_url(config_updates["server_url"])
results["validations"]["server_url"] = validation
if "server_url" in config_updates and "password" in config_updates:
validation = await validate_credentials(
config_updates["server_url"],
config_updates["password"],
)
results["validations"]["credentials"] = validation
if "webhook_secret" in config_updates and not config_updates["webhook_secret"]:
import secrets
self._config.webhook_secret = secrets.token_hex(32)
results["webhook_secret_generated"] = True
return results
def get_config_summary(self) -> dict[str, Any]:
return {
"server_url": self._config.server_url,
"dm_policy": self._config.dm_policy,
"group_policy": self._config.group_policy,
"streaming_mode": self._config.streaming_mode,
"tapback_enabled": self._config.tapback_enabled,
"ai_vision_enabled": self._config.ai_vision_enabled,
"webhook_path": self._config.webhook_path,
"completed_steps": list(self._completed_steps),
"total_steps": len(self.steps),
}
def to_config_dict(self) -> dict[str, Any]:
return self._config.model_dump()
def get_next_step(self) -> dict[str, Any] | None:
for step in self.steps:
if step["id"] not in self._completed_steps:
return step
return None
def mark_step_complete(self, step_id: str) -> None:
self._completed_steps.add(step_id)
@property
def is_complete(self) -> bool:
required_steps = {s["id"] for s in self.steps if s.get("required")}
return required_steps.issubset(self._completed_steps)
async def run_setup_wizard(
server_url: str = "",
password: str = "",
dm_policy: str = "pairing",
group_policy: str = "allowlist",
streaming_mode: str = "partial",
) -> dict[str, Any]:
wizard = BlueBubblesSetupWizard()
config_updates: dict[str, Any] = {}
if server_url:
config_updates["server_url"] = server_url
if password:
config_updates["password"] = password
if dm_policy:
config_updates["dm_policy"] = dm_policy
if group_policy:
config_updates["group_policy"] = group_policy
if streaming_mode:
config_updates["streaming_mode"] = streaming_mode
result = await wizard.apply_config(config_updates)
return {
"config": wizard.to_config_dict(),
"validations": result.get("validations", {}),
"complete": wizard.is_complete,
"summary": wizard.get_config_summary(),
}