feat: 支持可选配置图床服务器地址
This commit is contained in:
@@ -63,6 +63,7 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
||||
clientSecret,
|
||||
clientSecretFile: input.tokenFile,
|
||||
name: input.name,
|
||||
imageServerBaseUrl: input.imageServerBaseUrl,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@ export function resolveQQBotAccount(
|
||||
dmPolicy: qqbot?.dmPolicy,
|
||||
allowFrom: qqbot?.allowFrom,
|
||||
systemPrompt: qqbot?.systemPrompt,
|
||||
imageServerBaseUrl: qqbot?.imageServerBaseUrl,
|
||||
};
|
||||
appId = qqbot?.appId ?? "";
|
||||
} else {
|
||||
@@ -97,6 +98,7 @@ export function resolveQQBotAccount(
|
||||
clientSecret,
|
||||
secretSource,
|
||||
systemPrompt: accountConfig.systemPrompt,
|
||||
imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL,
|
||||
config: accountConfig,
|
||||
};
|
||||
}
|
||||
@@ -107,7 +109,7 @@ export function resolveQQBotAccount(
|
||||
export function applyQQBotAccountConfig(
|
||||
cfg: MoltbotConfig,
|
||||
accountId: string,
|
||||
input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string }
|
||||
input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string; imageServerBaseUrl?: string }
|
||||
): MoltbotConfig {
|
||||
const next = { ...cfg };
|
||||
|
||||
@@ -124,6 +126,7 @@ export function applyQQBotAccountConfig(
|
||||
? { clientSecretFile: input.clientSecretFile }
|
||||
: {}),
|
||||
...(input.name ? { name: input.name } : {}),
|
||||
...(input.imageServerBaseUrl ? { imageServerBaseUrl: input.imageServerBaseUrl } : {}),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
@@ -144,6 +147,7 @@ export function applyQQBotAccountConfig(
|
||||
? { clientSecretFile: input.clientSecretFile }
|
||||
: {}),
|
||||
...(input.name ? { name: input.name } : {}),
|
||||
...(input.imageServerBaseUrl ? { imageServerBaseUrl: input.imageServerBaseUrl } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -96,8 +96,15 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
||||
throw new Error("QQBot not configured (missing appId or clientSecret)");
|
||||
}
|
||||
|
||||
// 尝试启动图床服务器
|
||||
const imageServerBaseUrl = await ensureImageServer(log);
|
||||
// 如果配置了公网 URL,启动图床服务器
|
||||
let imageServerBaseUrl: string | null = null;
|
||||
if (account.imageServerBaseUrl) {
|
||||
await ensureImageServer(log);
|
||||
imageServerBaseUrl = account.imageServerBaseUrl;
|
||||
log?.info(`[qqbot:${account.accountId}] Image server enabled with URL: ${imageServerBaseUrl}`);
|
||||
} else {
|
||||
log?.info(`[qqbot:${account.accountId}] Image server disabled (no imageServerBaseUrl configured)`);
|
||||
}
|
||||
|
||||
let reconnectAttempts = 0;
|
||||
let isAborted = false;
|
||||
@@ -232,15 +239,17 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
||||
const envelopeOptions = pluginRuntime.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
||||
|
||||
// 组装消息体,添加系统提示词
|
||||
const builtinPrompt = `由于平台限制,你的回复中不可以包含任何URL。
|
||||
let builtinPrompt = "由于平台限制,你的回复中不可以包含任何URL。";
|
||||
|
||||
// 只有配置了图床公网地址,才告诉 AI 可以发送图片
|
||||
if (imageServerBaseUrl) {
|
||||
builtinPrompt += `
|
||||
|
||||
【发送图片】
|
||||
如果需要发送本地图片文件给用户,请在回复中使用以下格式:
|
||||
MEDIA:/绝对路径/图片文件.png
|
||||
|
||||
例如:MEDIA:/home/ubuntu/clawd/downloads/image.png
|
||||
|
||||
系统会自动将该路径的图片发送给用户。支持 png、jpg、gif、webp 格式。`;
|
||||
你可以发送本地图片文件给用户。只需在回复中直接引用图片的绝对路径即可,系统会自动处理。
|
||||
支持 png、jpg、gif、webp 格式。`;
|
||||
}
|
||||
|
||||
const systemPrompts = [builtinPrompt];
|
||||
if (account.systemPrompt) {
|
||||
systemPrompts.push(account.systemPrompt);
|
||||
@@ -486,13 +495,16 @@ MEDIA:/绝对路径/图片文件.png
|
||||
textWithoutImages = textWithoutImages.replace(match[0], "").trim();
|
||||
}
|
||||
|
||||
// 处理剩余文本中的 URL 点号
|
||||
const originalText = textWithoutImages;
|
||||
textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2");
|
||||
|
||||
const hasReplacement = textWithoutImages !== originalText;
|
||||
if (hasReplacement && textWithoutImages.trim()) {
|
||||
textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)";
|
||||
// 处理剩余文本中的 URL 点号(只有在没有图片的情况下才替换,避免误伤)
|
||||
const hasImages = imageUrls.length > 0;
|
||||
let hasReplacement = false;
|
||||
if (!hasImages) {
|
||||
const originalText = textWithoutImages;
|
||||
textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2");
|
||||
hasReplacement = textWithoutImages !== originalText;
|
||||
if (hasReplacement && textWithoutImages.trim()) {
|
||||
textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -29,12 +29,14 @@ interface QQBotChannelConfig {
|
||||
clientSecret?: string;
|
||||
clientSecretFile?: string;
|
||||
name?: string;
|
||||
imageServerBaseUrl?: string;
|
||||
accounts?: Record<string, {
|
||||
enabled?: boolean;
|
||||
appId?: string;
|
||||
clientSecret?: string;
|
||||
clientSecretFile?: string;
|
||||
name?: string;
|
||||
imageServerBaseUrl?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -195,6 +197,44 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
).trim();
|
||||
}
|
||||
|
||||
// 询问是否配置图片发送功能
|
||||
let imageServerBaseUrl: string | null = null;
|
||||
const existingImageUrl = (next.channels?.qqbot as QQBotChannelConfig)?.imageServerBaseUrl
|
||||
|| process.env.QQBOT_IMAGE_SERVER_BASE_URL;
|
||||
|
||||
const wantImageSupport = await prompter.confirm({
|
||||
message: "是否启用图片发送功能?(需要服务器有公网 IP)",
|
||||
initialValue: Boolean(existingImageUrl),
|
||||
});
|
||||
|
||||
if (wantImageSupport) {
|
||||
imageServerBaseUrl = String(
|
||||
await prompter.text({
|
||||
message: "请输入服务器公网地址(格式: http://公网IP:18765)",
|
||||
placeholder: "例如: http://123.45.67.89:18765",
|
||||
initialValue: existingImageUrl || undefined,
|
||||
validate: (value) => {
|
||||
if (!value?.trim()) return "公网地址不能为空";
|
||||
if (!value.startsWith("http://") && !value.startsWith("https://")) {
|
||||
return "地址必须以 http:// 或 https:// 开头";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}),
|
||||
).trim();
|
||||
|
||||
await prompter.note(
|
||||
[
|
||||
"图片发送功能已启用。请确保:",
|
||||
"1. 服务器防火墙已开放 18765 端口",
|
||||
"2. 云服务器安全组已放行 18765 端口(入站)",
|
||||
"",
|
||||
"如果图片发送失败,请检查端口是否可从公网访问。",
|
||||
].join("\n"),
|
||||
"图片功能配置",
|
||||
);
|
||||
}
|
||||
|
||||
// 应用配置
|
||||
if (appId && clientSecret) {
|
||||
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||
@@ -207,6 +247,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
enabled: true,
|
||||
appId,
|
||||
clientSecret,
|
||||
...(imageServerBaseUrl ? { imageServerBaseUrl } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -225,6 +266,38 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
enabled: true,
|
||||
appId,
|
||||
clientSecret,
|
||||
...(imageServerBaseUrl ? { imageServerBaseUrl } : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
} else if (imageServerBaseUrl) {
|
||||
// 只更新 imageServerBaseUrl
|
||||
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||
next = {
|
||||
...next,
|
||||
channels: {
|
||||
...next.channels,
|
||||
qqbot: {
|
||||
...next.channels?.qqbot,
|
||||
imageServerBaseUrl,
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
next = {
|
||||
...next,
|
||||
channels: {
|
||||
...next.channels,
|
||||
qqbot: {
|
||||
...next.channels?.qqbot,
|
||||
accounts: {
|
||||
...(next.channels?.qqbot as QQBotChannelConfig)?.accounts,
|
||||
[accountId]: {
|
||||
...(next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId],
|
||||
imageServerBaseUrl,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface ResolvedQQBotAccount {
|
||||
secretSource: "config" | "file" | "env" | "none";
|
||||
/** 系统提示词 */
|
||||
systemPrompt?: string;
|
||||
/** 图床服务器公网地址 */
|
||||
imageServerBaseUrl?: string;
|
||||
config: QQBotAccountConfig;
|
||||
}
|
||||
|
||||
@@ -35,6 +37,8 @@ export interface QQBotAccountConfig {
|
||||
allowFrom?: string[];
|
||||
/** 系统提示词,会添加在用户消息前面 */
|
||||
systemPrompt?: string;
|
||||
/** 图床服务器公网地址,用于发送图片,例如 http://your-ip:18765 */
|
||||
imageServerBaseUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user