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[] = []; const mediaPaths: string[] = [];
if (unwrapped.imageMessage) { 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); 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 { try {
const mediaDir = join(homedir(), '.nanobot', 'media'); const mediaDir = join(homedir(), '.nanobot', 'media');
await mkdir(mediaDir, { recursive: true }); await mkdir(mediaDir, { recursive: true });
const buffer = await downloadMediaMessage(msg, 'buffer', {}) as Buffer; const buffer = await downloadMediaMessage(msg, 'buffer', {}) as Buffer;
const mime = mimetype || 'image/jpeg'; let outFilename: string;
const extMap: Record<string, string> = { if (fileName) {
'image/jpeg': '.jpg', // Documents have a filename — use it with a unique prefix to avoid collisions
'image/png': '.png', const prefix = `wa_${Date.now()}_${randomBytes(4).toString('hex')}_`;
'image/gif': '.gif', outFilename = prefix + fileName;
'image/webp': '.webp', } else {
}; const mime = mimetype || 'application/octet-stream';
const ext = extMap[mime] || '.jpg'; // 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, outFilename);
const filepath = join(mediaDir, filename);
await writeFile(filepath, buffer); await writeFile(filepath, buffer);
return filepath; return filepath;
} catch (err) { } catch (err) {
console.error('Failed to download image:', err); console.error('Failed to download media:', err);
return null; return null;
} }
} }
@@ -191,14 +200,14 @@ export class WhatsAppClient {
return message.imageMessage.caption || ''; return message.imageMessage.caption || '';
} }
// Video with caption // Video with optional caption
if (message.videoMessage?.caption) { if (message.videoMessage) {
return `[Video] ${message.videoMessage.caption}`; return message.videoMessage.caption || '';
} }
// Document with caption // Document with optional caption
if (message.documentMessage?.caption) { if (message.documentMessage) {
return `[Document] ${message.documentMessage.caption}`; return message.documentMessage.caption || '';
} }
// Voice/Audio message // Voice/Audio message

View File

@@ -2,6 +2,7 @@
import asyncio import asyncio
import json import json
import mimetypes
from collections import OrderedDict from collections import OrderedDict
from loguru import logger 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) 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]" 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 [] media_paths = data.get("media") or []
# For image messages without caption, provide descriptive content # Build content tags matching Telegram's pattern: [image: /path] or [file: /path]
if not content and media_paths: if media_paths:
content = "[image]" 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( await self._handle_message(
sender_id=sender_id, sender_id=sender_id,