新增BlueBubbles适配器全套核心工具类与服务,包含会话管理、消息去重、Webhook验证、缓存系统、账号配置解析、聊天消息处理等完整功能模块,支持iMessage消息收发、群管理、反应特效、语音合成等能力,提供完善的健康检查与配置校验流程。
171 lines
6.0 KiB
Python
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(),
|
|
}
|