/** * Sticker Processor Service * Converts images to WhatsApp-compatible stickers (512x512 WebP) */ import sharp from 'sharp' // WhatsApp sticker requirements const STICKER_SIZE = 512 const MAX_STICKER_KB = 100 // WhatsApp limit for static stickers export interface StickerResult { buffer: Buffer mimetype: string } /** * Convert an image buffer to a WhatsApp-compatible sticker * Requirements: * - WebP format * - 512x512 pixels (will resize maintaining aspect ratio and add padding) * - Max 100KB for static stickers */ export async function convertToSticker( inputBuffer: Buffer, inputMimetype: string ): Promise { // Validate input is an image if (!inputMimetype.startsWith('image/')) { throw new Error('Input must be an image') } // Process with sharp - resize to fit 512x512 with transparent padding let quality = 100 let buffer = await sharp(inputBuffer) .resize(STICKER_SIZE, STICKER_SIZE, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } // Transparent background }) .webp({ quality, lossless: false }) .toBuffer() // If too large, reduce quality iteratively while (buffer.length > MAX_STICKER_KB * 1024 && quality > 10) { quality -= 10 buffer = await sharp(inputBuffer) .resize(STICKER_SIZE, STICKER_SIZE, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .webp({ quality, lossless: false }) .toBuffer() } // If still too large after quality reduction, try resizing smaller if (buffer.length > MAX_STICKER_KB * 1024) { let size = STICKER_SIZE while (buffer.length > MAX_STICKER_KB * 1024 && size > 128) { size -= 64 buffer = await sharp(inputBuffer) .resize(size, size, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .webp({ quality: 80, lossless: false }) .toBuffer() } // Resize back to 512x512 for WhatsApp compatibility if (size < STICKER_SIZE) { buffer = await sharp(buffer) .resize(STICKER_SIZE, STICKER_SIZE, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .toBuffer() } } return { buffer, mimetype: 'image/webp' } } /** * Check if an image can be converted to a sticker */ export function canConvertToSticker(mimetype: string): boolean { const supportedTypes = [ 'image/jpeg', 'image/png', 'image/webp', 'image/gif' // Will take first frame ] return supportedTypes.includes(mimetype) }