这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
164 lines
5.0 KiB
Python
164 lines
5.0 KiB
Python
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, field
|
||
|
||
from yuxi.utils.logging_config import logger
|
||
|
||
|
||
@dataclass
|
||
class SetupStepResult:
|
||
step: str
|
||
success: bool
|
||
message: str = ""
|
||
data: dict | None = None
|
||
|
||
|
||
@dataclass
|
||
class SetupWizardResult:
|
||
app_key: str
|
||
app_secret: str
|
||
bot_app_id: str
|
||
verified: bool = False
|
||
error: str | None = None
|
||
bot_info: dict | None = None
|
||
steps: list[SetupStepResult] = field(default_factory=list)
|
||
|
||
def add_step(self, step: str, success: bool, message: str = "", data: dict | None = None) -> None:
|
||
self.steps.append(SetupStepResult(step=step, success=success, message=message, data=data))
|
||
|
||
|
||
async def run_setup_wizard(
|
||
token_manager,
|
||
http_client,
|
||
app_key: str,
|
||
app_secret: str,
|
||
bot_app_id: str = "",
|
||
) -> SetupWizardResult:
|
||
result = SetupWizardResult(
|
||
app_key=app_key,
|
||
app_secret=app_secret,
|
||
bot_app_id=bot_app_id,
|
||
)
|
||
|
||
if not app_key.strip() or not app_secret.strip():
|
||
result.error = "app_key 和 app_secret 不能为空"
|
||
result.add_step("input_validation", False, result.error)
|
||
return result
|
||
|
||
result.add_step("input_validation", True, "输入参数校验通过")
|
||
|
||
try:
|
||
import time
|
||
import hashlib
|
||
import hmac
|
||
import aiohttp
|
||
|
||
timestamp = int(time.time())
|
||
message = f"{app_key}{timestamp}"
|
||
signature = hmac.new(
|
||
app_secret.encode("utf-8"),
|
||
message.encode("utf-8"),
|
||
hashlib.sha256,
|
||
).hexdigest()
|
||
|
||
api_base = "https://open-api.yuanbao.tencent.com"
|
||
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.post(
|
||
f"{api_base}/api/auth/token",
|
||
json={
|
||
"app_key": app_key,
|
||
"timestamp": timestamp,
|
||
"signature": signature,
|
||
},
|
||
timeout=aiohttp.ClientTimeout(total=10),
|
||
) as resp:
|
||
if resp.status != 200:
|
||
result.error = f"认证失败: HTTP {resp.status}"
|
||
result.add_step("auth", False, result.error)
|
||
return result
|
||
token_data = await resp.json()
|
||
token = token_data.get("access_token", "")
|
||
expires_in = token_data.get("expires_in", 7200)
|
||
result.add_step("auth", True, f"认证成功,token 有效期 {expires_in}s")
|
||
|
||
headers = {"Authorization": f"Bearer {token}"}
|
||
async with session.get(
|
||
f"{api_base}/api/v1/bot/info",
|
||
headers=headers,
|
||
timeout=aiohttp.ClientTimeout(total=10),
|
||
) as resp:
|
||
if resp.status == 200:
|
||
bot_info = await resp.json()
|
||
result.verified = True
|
||
result.bot_info = bot_info
|
||
result.bot_app_id = bot_info.get("bot_app_id", bot_app_id)
|
||
result.add_step(
|
||
"bot_verify",
|
||
True,
|
||
f"Bot 验证成功: {result.bot_app_id}",
|
||
bot_info,
|
||
)
|
||
logger.info(f"[Yuanbao] Setup wizard verified: bot_app_id={result.bot_app_id}")
|
||
elif resp.status in (401, 403):
|
||
result.error = "凭据验证失败,请检查 app_key 和 app_secret"
|
||
result.add_step("bot_verify", False, result.error)
|
||
else:
|
||
result.error = f"Bot 信息获取失败: HTTP {resp.status}"
|
||
result.add_step("bot_verify", False, result.error)
|
||
|
||
except Exception as e:
|
||
result.error = f"设置向导出错: {e}"
|
||
result.add_step("unexpected_error", False, result.error)
|
||
logger.error(f"[Yuanbao] Setup wizard error: {e}")
|
||
|
||
return result
|
||
|
||
|
||
def generate_config_snippet(app_key: str, app_secret: str, bot_app_id: str) -> str:
|
||
return f"""```yaml
|
||
channels:
|
||
yuanbao:
|
||
enabled: true
|
||
app_key: "{app_key}"
|
||
app_secret: "{app_secret}"
|
||
bot_app_id: "{bot_app_id}"
|
||
dm_policy: pairing
|
||
# 群聊中需要 @机器人 才响应(仅群组生效)
|
||
requireMention: true
|
||
overflowPolicy: split
|
||
replyToMode: first
|
||
```"""
|
||
|
||
|
||
def generate_config_snippet_with_accounts(
|
||
app_key: str,
|
||
app_secret: str,
|
||
bot_app_id: str,
|
||
accounts: list[dict] | None = None,
|
||
) -> str:
|
||
accounts_config = ""
|
||
if accounts:
|
||
for acct in accounts:
|
||
accounts_config += f"""
|
||
accounts.{acct["id"]}:
|
||
appKey: "{acct.get("app_key", "")}"
|
||
appSecret: "{acct.get("app_secret", "")}"
|
||
name: "{acct.get("name", acct["id"])}"
|
||
enabled: true"""
|
||
|
||
return f"""```yaml
|
||
channels:
|
||
yuanbao:
|
||
enabled: true
|
||
app_key: "{app_key}"
|
||
app_secret: "{app_secret}"
|
||
bot_app_id: "{bot_app_id}"
|
||
dm_policy: pairing
|
||
# 群聊中需要 @机器人 才响应(仅群组生效)
|
||
requireMention: true
|
||
overflowPolicy: split
|
||
replyToMode: first
|
||
defaultAccount: "default"{accounts_config}
|
||
```"""
|