From fd5c81401d0644d0b49a6d59a30bc97273ec07d5 Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Fri, 13 Mar 2026 19:23:49 +0900 Subject: [PATCH] feat: also parse markdown image links for Discord image sending Support [name.png](/path) format in addition to [SendImage: /path]. LLMs naturally produce markdown links, so both patterns are recognized. Only absolute paths with image extensions are matched. --- src/channels/discord.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/channels/discord.ts b/src/channels/discord.ts index 3154572..115fb81 100644 --- a/src/channels/discord.ts +++ b/src/channels/discord.ts @@ -303,19 +303,35 @@ export class DiscordChannel implements Channel { const textChannel = channel as TextChannel; - // Extract image attachments from [SendImage: /path] tags + // Extract image attachments from agent responses. + // Supports: + // [SendImage: /absolute/path] — explicit tag + // [name.png](/absolute/path/name.png) — markdown link with image extension + const IMAGE_EXTS = /\.(png|jpe?g|gif|webp|svg|bmp)$/i; const SEND_IMAGE_RE = /\[SendImage:\s*(\/[^\]]+)\]/g; + const MD_LINK_RE = /\[[^\]]*\]\((\/[^)]+)\)/g; const imageFiles: string[] = []; + const seen = new Set(); let match; + while ((match = SEND_IMAGE_RE.exec(text)) !== null) { const imgPath = match[1].trim(); - if (fs.existsSync(imgPath)) { + if (!seen.has(imgPath) && fs.existsSync(imgPath)) { imageFiles.push(imgPath); - } else { - logger.warn({ path: imgPath }, 'SendImage file not found, skipping'); + seen.add(imgPath); } } - let cleaned = text.replace(SEND_IMAGE_RE, '').trim(); + while ((match = MD_LINK_RE.exec(text)) !== null) { + const imgPath = match[1].trim(); + if (!seen.has(imgPath) && IMAGE_EXTS.test(imgPath) && fs.existsSync(imgPath)) { + imageFiles.push(imgPath); + seen.add(imgPath); + } + } + let cleaned = text + .replace(SEND_IMAGE_RE, '') + .replace(MD_LINK_RE, (full, p) => IMAGE_EXTS.test(p) && seen.has(p.trim()) ? '' : full) + .trim(); // Convert @username mentions to Discord mention format const mentionMap: Record = {