All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m28s
- Consolidar send.post.ts y send-media.post.ts en un único endpoint /send - Agregar servicio sticker-processor.ts para convertir imágenes a WebP 512x512 - Agregar toggle Imagen/Sticker en MediaPreview para enviar imágenes como stickers - Actualizar MessageInput y página de mensajes para usar endpoint unificado - Instalar dependencia sharp para procesamiento de imágenes
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
/**
|
|
* 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<StickerResult> {
|
|
// 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)
|
|
}
|