Created
July 9, 2025 21:11
-
-
Save igortrinidad/b6036259c285c8876551a2281649e906 to your computer and use it in GitHub Desktop.
downloadMediaMessage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { WAMessage, downloadMediaMessage as downloadMediaSource } from '@whiskeysockets/baileys' | |
import { saveMediaLocally } from '#helpers/general/saveMediaLocally' | |
import { deepSearchKey } from '@igortrindade/lazyfy' | |
import mime from 'mime-types' | |
import logger from '@adonisjs/core/services/logger' | |
import { MediaDownloadResult } from './types.js' | |
const getMediaExtension = (message: WAMessage): { mimetype: string, extension: string } => { | |
const mimetype = deepSearchKey(message, 'mimetype') as string | |
let extension = mime.extension(mimetype) | |
if(extension === 'oga') { | |
extension = 'ogg' // Normalize OGA to OGG | |
} | |
return { mimetype, extension } | |
} | |
// Fallback method 1: Retry with delay | |
const downloadWithRetry = async (message: WAMessage, maxRetries = 3) => { | |
for (let i = 0; i < maxRetries; i++) { | |
try { | |
// Add delay between retries | |
if (i > 0) { | |
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))) | |
} | |
const buffer = await downloadMediaSource(message, 'buffer', {}) | |
if (buffer && buffer.length > 0) { | |
return buffer | |
} | |
} catch (error) { | |
logger.warn({ error, retry: i + 1 }, `Download retry ${i + 1} failed`) | |
if (i === maxRetries - 1) { | |
throw error | |
} | |
} | |
} | |
throw new Error('All retry attempts failed') | |
} | |
// Fallback method 2: Extract media info and save metadata | |
const saveMediaMetadata = async (message: WAMessage, errorDetails: string) => { | |
try { | |
const timestamp = new Date().getTime() | |
const mediaInfo = { | |
timestamp, | |
messageId: message.key?.id, | |
participant: message.key?.participant, | |
remoteJid: message.key?.remoteJid, | |
mediaType: message.message && Object.keys(message.message)[0], | |
error: errorDetails, | |
message: message | |
} | |
const metadataPath = await saveMediaLocally( | |
Buffer.from(JSON.stringify(mediaInfo, null, 2)), | |
`${timestamp}_media_metadata.json`, | |
'whatsapp_manager_events/failed_media_downloads' | |
) | |
return metadataPath | |
} catch (error) { | |
logger.error({ error }, 'Failed to save media metadata') | |
return null | |
} | |
} | |
// Fallback method 3: Try to extract base64 data from message | |
const extractBase64Media = async (message: WAMessage): Promise<Buffer | null> => { | |
try { | |
// Check if there's base64 data in the message | |
const base64Data = deepSearchKey(message, 'jpegThumbnail') || | |
deepSearchKey(message, 'thumbnailDirectPath') || | |
deepSearchKey(message, 'mediaData') | |
if (base64Data && typeof base64Data === 'string') { | |
return Buffer.from(base64Data, 'base64') | |
} | |
return null | |
} catch (error) { | |
logger.warn({ error }, 'Failed to extract base64 media') | |
return null | |
} | |
} | |
export const downloadMediaMessage = async (message: WAMessage): Promise<MediaDownloadResult> => { | |
const startTime = Date.now() | |
let attempts = 0 | |
let lastError: Error | null = null | |
try { | |
attempts++ | |
// Primary method: Standard download | |
const buffer = await downloadMediaSource(message, 'buffer', {}) | |
if (!buffer || buffer.length === 0) { | |
throw new Error('Downloaded buffer is empty or null') | |
} | |
const { mimetype, extension } = getMediaExtension(message) | |
const local_path = await saveMediaLocally( | |
buffer, | |
`${new Date().getTime()}_media_.${extension}`, | |
'whatsapp_manager_events/messages_upsert' | |
) | |
return { mimetype, extension, local_path, success: true, method: 'primary' } | |
} catch (primaryError) { | |
lastError = primaryError as Error | |
logger.warn({ | |
error: primaryError, | |
messageId: message.key?.id, | |
attempt: attempts | |
}, 'Primary download failed, trying fallback methods') | |
// Fallback method 1: Try different download options | |
try { | |
attempts++ | |
const buffer = await downloadWithRetry(message) | |
if (buffer && buffer.length > 0) { | |
const { mimetype, extension } = getMediaExtension(message) | |
const local_path = await saveMediaLocally( | |
buffer, | |
`${new Date().getTime()}_media_fallback1_.${extension}`, | |
'whatsapp_manager_events/messages_upsert' | |
) | |
return { mimetype, extension, local_path, success: true, method: 'fallback1' } | |
} | |
} catch (fallback1Error) { | |
lastError = fallback1Error as Error | |
logger.warn({ | |
error: fallback1Error, | |
messageId: message.key?.id, | |
attempt: attempts | |
}, 'Fallback method 1 failed') | |
} | |
// Fallback method 2: Try to extract base64 data | |
try { | |
attempts++ | |
const buffer = await extractBase64Media(message) | |
if (buffer && buffer.length > 0) { | |
const { mimetype, extension } = getMediaExtension(message) | |
const local_path = await saveMediaLocally( | |
buffer, | |
`${new Date().getTime()}_media_fallback2_.${extension}`, | |
'whatsapp_manager_events/messages_upsert' | |
) | |
return { mimetype, extension, local_path, success: true, method: 'fallback2_base64' } | |
} | |
} catch (fallback2Error) { | |
lastError = fallback2Error as Error | |
logger.warn({ | |
error: fallback2Error, | |
messageId: message.key?.id, | |
attempt: attempts | |
}, 'Fallback method 2 failed') | |
} | |
// Last resort: Save metadata for manual recovery | |
logger.error({ | |
error: lastError, | |
messageId: message.key?.id, | |
totalAttempts: attempts, | |
duration: Date.now() - startTime | |
}, 'All download methods failed, saving metadata') | |
const metadataPath = await saveMediaMetadata(message, lastError?.message || 'Unknown error') | |
return { | |
mimetype: null, | |
extension: null, | |
local_path: null, | |
success: false, | |
method: 'metadata_only', | |
metadataPath: metadataPath || undefined, | |
error: lastError?.message || 'Unknown error', | |
attempts | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment