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.
This commit is contained in:
Eyejoker
2026-03-13 19:23:49 +09:00
parent a1db0e2161
commit fd5c81401d

View File

@@ -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<string>();
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<string, string> = {