From c113ef131a67c3e50d6fb89e64a5bc85bf1d3d0f Mon Sep 17 00:00:00 2001 From: sliverp Date: Thu, 29 Jan 2026 10:53:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20onboarding=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=BB=A5=E9=81=BF=E5=85=8D=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/channel.ts | 2 +- src/onboarding.ts | 128 ++++++++++++++-------------------------------- 2 files changed, 40 insertions(+), 90 deletions(-) diff --git a/src/channel.ts b/src/channel.ts index dcfc1ad..c0ef685 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -25,7 +25,7 @@ export const qqbotPlugin: ChannelPlugin = { }, reload: { configPrefixes: ["channels.qqbot"] }, // CLI onboarding wizard - onboarding: qqbotOnboardingAdapter as any, + onboarding: qqbotOnboardingAdapter, config: { listAccountIds: (cfg) => listQQBotAccountIds(cfg), resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId), diff --git a/src/onboarding.ts b/src/onboarding.ts index 8697d9b..189792e 100644 --- a/src/onboarding.ts +++ b/src/onboarding.ts @@ -1,12 +1,20 @@ /** * QQBot CLI Onboarding Adapter + * + * 提供 moltbot onboard 命令的交互式配置支持 */ -import type { ResolvedQQBotAccount } from "./types.js"; +import type { + ChannelOnboardingAdapter, + ChannelOnboardingStatus, + ChannelOnboardingStatusContext, + ChannelOnboardingConfigureContext, + ChannelOnboardingResult, +} from "clawdbot/plugin-sdk"; import { listQQBotAccountIds, resolveQQBotAccount } from "./config.js"; const DEFAULT_ACCOUNT_ID = "default"; -// 类型定义(从 moltbot 导入会有循环依赖问题) +// 内部类型(避免循环依赖) interface MoltbotConfig { channels?: { qqbot?: QQBotChannelConfig; @@ -30,78 +38,6 @@ interface QQBotChannelConfig { }>; } -interface WizardPrompter { - note: (message: string, title?: string) => Promise; - text: (options: { - message: string; - placeholder?: string; - initialValue?: string; - validate?: (value: string | undefined) => string | undefined; - }) => Promise; - confirm: (options: { - message: string; - initialValue?: boolean; - }) => Promise; - select: (options: { - message: string; - options: Array<{ value: T; label: string; hint?: string }>; - initialValue?: T; - }) => Promise; -} - -interface ChannelOnboardingStatus { - channel: string; - configured: boolean; - statusLines: string[]; - selectionHint?: string; - quickstartScore?: number; -} - -interface ChannelOnboardingStatusContext { - cfg: MoltbotConfig; - options?: unknown; - accountOverrides: Partial>; -} - -interface ChannelOnboardingConfigureContext { - cfg: MoltbotConfig; - runtime: unknown; - prompter: WizardPrompter; - options?: unknown; - accountOverrides: Partial>; - shouldPromptAccountIds: boolean; - forceAllowFrom: boolean; -} - -interface ChannelOnboardingResult { - cfg: MoltbotConfig; - accountId?: string; -} - -interface ChannelOnboardingAdapter { - channel: string; - getStatus: (ctx: ChannelOnboardingStatusContext) => Promise; - configure: (ctx: ChannelOnboardingConfigureContext) => Promise; - disable?: (cfg: MoltbotConfig) => MoltbotConfig; -} - -/** - * 显示 QQBot 配置帮助 - */ -async function noteQQBotHelp(prompter: WizardPrompter): Promise { - await prompter.note( - [ - "1) 打开 QQ 开放平台: https://q.qq.com/", - "2) 创建机器人应用,获取 AppID 和 ClientSecret", - "3) 在「开发设置」中添加沙箱成员(测试阶段)", - "4) 你也可以设置环境变量 QQBOT_APP_ID 和 QQBOT_CLIENT_SECRET", - "", - "文档: https://bot.q.qq.com/wiki/", - ].join("\n"), - "QQ Bot 配置", - ); -} - /** * 解析默认账户 ID */ @@ -114,16 +50,17 @@ function resolveDefaultQQBotAccountId(cfg: MoltbotConfig): string { * QQBot Onboarding Adapter */ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { - channel: "qqbot", + channel: "qqbot" as any, - getStatus: async ({ cfg }) => { - const configured = listQQBotAccountIds(cfg).some((accountId) => { - const account = resolveQQBotAccount(cfg, accountId); + getStatus: async (ctx: ChannelOnboardingStatusContext): Promise => { + const { cfg } = ctx; + const configured = listQQBotAccountIds(cfg as MoltbotConfig).some((accountId) => { + const account = resolveQQBotAccount(cfg as MoltbotConfig, accountId); return Boolean(account.appId && account.clientSecret); }); return { - channel: "qqbot", + channel: "qqbot" as any, configured, statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`], selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊", @@ -131,14 +68,17 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { }; }, - configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => { - const qqbotOverride = accountOverrides.qqbot?.trim(); - const defaultAccountId = resolveDefaultQQBotAccountId(cfg); + configure: async (ctx: ChannelOnboardingConfigureContext): Promise => { + const { cfg, prompter, accountOverrides, shouldPromptAccountIds } = ctx; + const moltbotCfg = cfg as MoltbotConfig; + + const qqbotOverride = (accountOverrides as Record).qqbot?.trim(); + const defaultAccountId = resolveDefaultQQBotAccountId(moltbotCfg); let accountId = qqbotOverride ?? defaultAccountId; // 是否需要提示选择账户 if (shouldPromptAccountIds && !qqbotOverride) { - const existingIds = listQQBotAccountIds(cfg); + const existingIds = listQQBotAccountIds(moltbotCfg); if (existingIds.length > 1) { accountId = await prompter.select({ message: "选择 QQBot 账户", @@ -151,7 +91,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { } } - let next = cfg; + let next = moltbotCfg; const resolvedAccount = resolveQQBotAccount(next, accountId); const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret); const allowEnv = accountId === DEFAULT_ACCOUNT_ID; @@ -165,7 +105,17 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { // 显示帮助 if (!accountConfigured) { - await noteQQBotHelp(prompter); + await prompter.note( + [ + "1) 打开 QQ 开放平台: https://q.qq.com/", + "2) 创建机器人应用,获取 AppID 和 ClientSecret", + "3) 在「开发设置」中添加沙箱成员(测试阶段)", + "4) 你也可以设置环境变量 QQBOT_APP_ID 和 QQBOT_CLIENT_SECRET", + "", + "文档: https://bot.q.qq.com/wiki/", + ].join("\n"), + "QQ Bot 配置", + ); } // 检测环境变量 @@ -283,14 +233,14 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { } } - return { cfg: next, accountId }; + return { cfg: next as any, accountId }; }, disable: (cfg) => ({ ...cfg, channels: { - ...cfg.channels, - qqbot: { ...cfg.channels?.qqbot, enabled: false }, + ...(cfg as MoltbotConfig).channels, + qqbot: { ...(cfg as MoltbotConfig).channels?.qqbot, enabled: false }, }, - }), + }) as any, };