refactor: 重构 onboarding 模块以避免循环依赖
This commit is contained in:
@@ -25,7 +25,7 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
|||||||
},
|
},
|
||||||
reload: { configPrefixes: ["channels.qqbot"] },
|
reload: { configPrefixes: ["channels.qqbot"] },
|
||||||
// CLI onboarding wizard
|
// CLI onboarding wizard
|
||||||
onboarding: qqbotOnboardingAdapter as any,
|
onboarding: qqbotOnboardingAdapter,
|
||||||
config: {
|
config: {
|
||||||
listAccountIds: (cfg) => listQQBotAccountIds(cfg),
|
listAccountIds: (cfg) => listQQBotAccountIds(cfg),
|
||||||
resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId),
|
resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId),
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* QQBot CLI Onboarding Adapter
|
* 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";
|
import { listQQBotAccountIds, resolveQQBotAccount } from "./config.js";
|
||||||
|
|
||||||
const DEFAULT_ACCOUNT_ID = "default";
|
const DEFAULT_ACCOUNT_ID = "default";
|
||||||
|
|
||||||
// 类型定义(从 moltbot 导入会有循环依赖问题)
|
// 内部类型(避免循环依赖)
|
||||||
interface MoltbotConfig {
|
interface MoltbotConfig {
|
||||||
channels?: {
|
channels?: {
|
||||||
qqbot?: QQBotChannelConfig;
|
qqbot?: QQBotChannelConfig;
|
||||||
@@ -30,78 +38,6 @@ interface QQBotChannelConfig {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WizardPrompter {
|
|
||||||
note: (message: string, title?: string) => Promise<void>;
|
|
||||||
text: (options: {
|
|
||||||
message: string;
|
|
||||||
placeholder?: string;
|
|
||||||
initialValue?: string;
|
|
||||||
validate?: (value: string | undefined) => string | undefined;
|
|
||||||
}) => Promise<string>;
|
|
||||||
confirm: (options: {
|
|
||||||
message: string;
|
|
||||||
initialValue?: boolean;
|
|
||||||
}) => Promise<boolean>;
|
|
||||||
select: <T>(options: {
|
|
||||||
message: string;
|
|
||||||
options: Array<{ value: T; label: string; hint?: string }>;
|
|
||||||
initialValue?: T;
|
|
||||||
}) => Promise<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChannelOnboardingStatus {
|
|
||||||
channel: string;
|
|
||||||
configured: boolean;
|
|
||||||
statusLines: string[];
|
|
||||||
selectionHint?: string;
|
|
||||||
quickstartScore?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChannelOnboardingStatusContext {
|
|
||||||
cfg: MoltbotConfig;
|
|
||||||
options?: unknown;
|
|
||||||
accountOverrides: Partial<Record<string, string>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChannelOnboardingConfigureContext {
|
|
||||||
cfg: MoltbotConfig;
|
|
||||||
runtime: unknown;
|
|
||||||
prompter: WizardPrompter;
|
|
||||||
options?: unknown;
|
|
||||||
accountOverrides: Partial<Record<string, string>>;
|
|
||||||
shouldPromptAccountIds: boolean;
|
|
||||||
forceAllowFrom: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChannelOnboardingResult {
|
|
||||||
cfg: MoltbotConfig;
|
|
||||||
accountId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChannelOnboardingAdapter {
|
|
||||||
channel: string;
|
|
||||||
getStatus: (ctx: ChannelOnboardingStatusContext) => Promise<ChannelOnboardingStatus>;
|
|
||||||
configure: (ctx: ChannelOnboardingConfigureContext) => Promise<ChannelOnboardingResult>;
|
|
||||||
disable?: (cfg: MoltbotConfig) => MoltbotConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示 QQBot 配置帮助
|
|
||||||
*/
|
|
||||||
async function noteQQBotHelp(prompter: WizardPrompter): Promise<void> {
|
|
||||||
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
|
* 解析默认账户 ID
|
||||||
*/
|
*/
|
||||||
@@ -114,16 +50,17 @@ function resolveDefaultQQBotAccountId(cfg: MoltbotConfig): string {
|
|||||||
* QQBot Onboarding Adapter
|
* QQBot Onboarding Adapter
|
||||||
*/
|
*/
|
||||||
export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||||
channel: "qqbot",
|
channel: "qqbot" as any,
|
||||||
|
|
||||||
getStatus: async ({ cfg }) => {
|
getStatus: async (ctx: ChannelOnboardingStatusContext): Promise<ChannelOnboardingStatus> => {
|
||||||
const configured = listQQBotAccountIds(cfg).some((accountId) => {
|
const { cfg } = ctx;
|
||||||
const account = resolveQQBotAccount(cfg, accountId);
|
const configured = listQQBotAccountIds(cfg as MoltbotConfig).some((accountId) => {
|
||||||
|
const account = resolveQQBotAccount(cfg as MoltbotConfig, accountId);
|
||||||
return Boolean(account.appId && account.clientSecret);
|
return Boolean(account.appId && account.clientSecret);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
channel: "qqbot",
|
channel: "qqbot" as any,
|
||||||
configured,
|
configured,
|
||||||
statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`],
|
statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`],
|
||||||
selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊",
|
selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊",
|
||||||
@@ -131,14 +68,17 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
|
configure: async (ctx: ChannelOnboardingConfigureContext): Promise<ChannelOnboardingResult> => {
|
||||||
const qqbotOverride = accountOverrides.qqbot?.trim();
|
const { cfg, prompter, accountOverrides, shouldPromptAccountIds } = ctx;
|
||||||
const defaultAccountId = resolveDefaultQQBotAccountId(cfg);
|
const moltbotCfg = cfg as MoltbotConfig;
|
||||||
|
|
||||||
|
const qqbotOverride = (accountOverrides as Record<string, string>).qqbot?.trim();
|
||||||
|
const defaultAccountId = resolveDefaultQQBotAccountId(moltbotCfg);
|
||||||
let accountId = qqbotOverride ?? defaultAccountId;
|
let accountId = qqbotOverride ?? defaultAccountId;
|
||||||
|
|
||||||
// 是否需要提示选择账户
|
// 是否需要提示选择账户
|
||||||
if (shouldPromptAccountIds && !qqbotOverride) {
|
if (shouldPromptAccountIds && !qqbotOverride) {
|
||||||
const existingIds = listQQBotAccountIds(cfg);
|
const existingIds = listQQBotAccountIds(moltbotCfg);
|
||||||
if (existingIds.length > 1) {
|
if (existingIds.length > 1) {
|
||||||
accountId = await prompter.select({
|
accountId = await prompter.select({
|
||||||
message: "选择 QQBot 账户",
|
message: "选择 QQBot 账户",
|
||||||
@@ -151,7 +91,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let next = cfg;
|
let next = moltbotCfg;
|
||||||
const resolvedAccount = resolveQQBotAccount(next, accountId);
|
const resolvedAccount = resolveQQBotAccount(next, accountId);
|
||||||
const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret);
|
const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret);
|
||||||
const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
|
const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
|
||||||
@@ -165,7 +105,17 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
|
|
||||||
// 显示帮助
|
// 显示帮助
|
||||||
if (!accountConfigured) {
|
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) => ({
|
disable: (cfg) => ({
|
||||||
...cfg,
|
...cfg,
|
||||||
channels: {
|
channels: {
|
||||||
...cfg.channels,
|
...(cfg as MoltbotConfig).channels,
|
||||||
qqbot: { ...cfg.channels?.qqbot, enabled: false },
|
qqbot: { ...(cfg as MoltbotConfig).channels?.qqbot, enabled: false },
|
||||||
},
|
},
|
||||||
}),
|
}) as any,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user