Refactored from image support to generic media

This commit is contained in:
fat-operator
2026-03-07 00:13:38 +00:00
parent 8c25897532
commit 067965da50
3 changed files with 37 additions and 1385 deletions

1362
bridge/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -127,7 +127,14 @@ export class WhatsAppClient {
const mediaPaths: string[] = [];
if (unwrapped.imageMessage) {
const path = await this.downloadImage(msg, unwrapped.imageMessage.mimetype ?? undefined);
const path = await this.downloadMedia(msg, unwrapped.imageMessage.mimetype ?? undefined);
if (path) mediaPaths.push(path);
} else if (unwrapped.documentMessage) {
const path = await this.downloadMedia(msg, unwrapped.documentMessage.mimetype ?? undefined,
unwrapped.documentMessage.fileName ?? undefined);
if (path) mediaPaths.push(path);
} else if (unwrapped.videoMessage) {
const path = await this.downloadMedia(msg, unwrapped.videoMessage.mimetype ?? undefined);
if (path) mediaPaths.push(path);
}
@@ -148,29 +155,31 @@ export class WhatsAppClient {
});
}
private async downloadImage(msg: any, mimetype?: string): Promise<string | null> {
private async downloadMedia(msg: any, mimetype?: string, fileName?: string): Promise<string | null> {
try {
const mediaDir = join(homedir(), '.nanobot', 'media');
await mkdir(mediaDir, { recursive: true });
const buffer = await downloadMediaMessage(msg, 'buffer', {}) as Buffer;
const mime = mimetype || 'image/jpeg';
const extMap: Record<string, string> = {
'image/jpeg': '.jpg',
'image/png': '.png',
'image/gif': '.gif',
'image/webp': '.webp',
};
const ext = extMap[mime] || '.jpg';
let outFilename: string;
if (fileName) {
// Documents have a filename — use it with a unique prefix to avoid collisions
const prefix = `wa_${Date.now()}_${randomBytes(4).toString('hex')}_`;
outFilename = prefix + fileName;
} else {
const mime = mimetype || 'application/octet-stream';
// Derive extension from mimetype subtype (e.g. "image/png" → ".png", "application/pdf" → ".pdf")
const ext = '.' + (mime.split('/').pop()?.split(';')[0] || 'bin');
outFilename = `wa_${Date.now()}_${randomBytes(4).toString('hex')}${ext}`;
}
const filename = `wa_${Date.now()}_${randomBytes(4).toString('hex')}${ext}`;
const filepath = join(mediaDir, filename);
const filepath = join(mediaDir, outFilename);
await writeFile(filepath, buffer);
return filepath;
} catch (err) {
console.error('Failed to download image:', err);
console.error('Failed to download media:', err);
return null;
}
}
@@ -191,14 +200,14 @@ export class WhatsAppClient {
return message.imageMessage.caption || '';
}
// Video with caption
if (message.videoMessage?.caption) {
return `[Video] ${message.videoMessage.caption}`;
// Video with optional caption
if (message.videoMessage) {
return message.videoMessage.caption || '';
}
// Document with caption
if (message.documentMessage?.caption) {
return `[Document] ${message.documentMessage.caption}`;
// Document with optional caption
if (message.documentMessage) {
return message.documentMessage.caption || '';
}
// Voice/Audio message

View File

@@ -2,6 +2,7 @@
import asyncio
import json
import mimetypes
from collections import OrderedDict
from loguru import logger
@@ -128,12 +129,16 @@ class WhatsAppChannel(BaseChannel):
logger.info("Voice message received from {}, but direct download from bridge is not yet supported.", sender_id)
content = "[Voice Message: Transcription not available for WhatsApp yet]"
# Extract media paths (images downloaded by the bridge)
# Extract media paths (images/documents/videos downloaded by the bridge)
media_paths = data.get("media") or []
# For image messages without caption, provide descriptive content
if not content and media_paths:
content = "[image]"
# Build content tags matching Telegram's pattern: [image: /path] or [file: /path]
if media_paths:
for p in media_paths:
mime, _ = mimetypes.guess_type(p)
media_type = "image" if mime and mime.startswith("image/") else "file"
media_tag = f"[{media_type}: {p}]"
content = f"{content}\n{media_tag}" if content else media_tag
await self._handle_message(
sender_id=sender_id,