From 6c6c5bd7fd30f464c685d9ff0295f3e55333c3d1 Mon Sep 17 00:00:00 2001 From: sliverp Date: Fri, 30 Jan 2026 15:24:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84=E5=9B=BE=E7=89=87=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gateway.ts | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/gateway.ts b/src/gateway.ts index da678d7..a48bf5d 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -65,21 +65,22 @@ export interface GatewayContext { /** * 启动图床服务器 */ -async function ensureImageServer(log?: GatewayContext["log"]): Promise { +async function ensureImageServer(log?: GatewayContext["log"], publicBaseUrl?: string): Promise { if (isImageServerRunning()) { - return `http://0.0.0.0:${IMAGE_SERVER_PORT}`; + return publicBaseUrl || `http://0.0.0.0:${IMAGE_SERVER_PORT}`; } try { const config: Partial = { port: IMAGE_SERVER_PORT, storageDir: IMAGE_SERVER_DIR, - baseUrl: `http://0.0.0.0:${IMAGE_SERVER_PORT}`, + // 使用用户配置的公网地址,而不是 0.0.0.0 + baseUrl: publicBaseUrl || `http://0.0.0.0:${IMAGE_SERVER_PORT}`, ttlSeconds: 3600, // 1 小时过期 }; await startImageServer(config); - log?.info(`[qqbot] Image server started on port ${IMAGE_SERVER_PORT}`); - return `http://0.0.0.0:${IMAGE_SERVER_PORT}`; + log?.info(`[qqbot] Image server started on port ${IMAGE_SERVER_PORT}, baseUrl: ${config.baseUrl}`); + return config.baseUrl!; } catch (err) { log?.error(`[qqbot] Failed to start image server: ${err}`); return null; @@ -99,7 +100,8 @@ export async function startGateway(ctx: GatewayContext): Promise { // 如果配置了公网 URL,启动图床服务器 let imageServerBaseUrl: string | null = null; if (account.imageServerBaseUrl) { - await ensureImageServer(log); + // 使用用户配置的公网地址作为 baseUrl + await ensureImageServer(log, account.imageServerBaseUrl); imageServerBaseUrl = account.imageServerBaseUrl; log?.info(`[qqbot:${account.accountId}] Image server enabled with URL: ${imageServerBaseUrl}`); } else { @@ -457,6 +459,32 @@ export async function startGateway(ctx: GatewayContext): Promise { replyText = replyText.replace(match[0], "").trim(); } + // 0.5. 提取本地绝对文件路径(/path/to/image.png 或 /path/to/image_123_png 格式) + // 支持标准扩展名和下划线替换后的扩展名 + const localPathRegex = /(\/[^\s\n]+?(?:\.(?:png|jpg|jpeg|gif|webp)|_(?:png|jpg|jpeg|gif|webp)(?:\s|$)))/gi; + const localPathMatches = [...replyText.matchAll(localPathRegex)]; + + for (const match of localPathMatches) { + let localPath = match[1].trim(); + if (localPath && imageServerBaseUrl) { + // 如果是下划线格式的扩展名,转换回点格式 + localPath = localPath.replace(/_(?=(?:png|jpg|jpeg|gif|webp)$)/, "."); + try { + const savedUrl = saveImageFromPath(localPath); + if (savedUrl) { + imageUrls.push(savedUrl); + log?.info(`[qqbot:${account.accountId}] Saved local path image to server: ${localPath}`); + } else { + log?.error(`[qqbot:${account.accountId}] Local path not found or not image: ${localPath}`); + } + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to save local path image: ${err}`); + } + } + // 从文本中移除本地路径 + replyText = replyText.replace(match[0], "").trim(); + } + // 1. 提取 base64 图片(data:image/xxx;base64,...) const base64ImageRegex = /!\[([^\]]*)\]\((data:image\/[^;]+;base64,[A-Za-z0-9+/=]+)\)|(?