feat: 支持可选配置图床服务器地址

This commit is contained in:
sliverp
2026-01-30 15:12:28 +08:00
parent a13aacbc3b
commit a9b0c3ff10
5 changed files with 111 additions and 17 deletions

View File

@@ -63,6 +63,7 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
clientSecret,
clientSecretFile: input.tokenFile,
name: input.name,
imageServerBaseUrl: input.imageServerBaseUrl,
});
},
},

View File

@@ -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 } : {}),
},
},
},

View File

@@ -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 {

View File

@@ -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,
},
},
},

View File

@@ -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;
}
/**