Unificar endpoint de envío y agregar soporte para stickers
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m28s
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
This commit is contained in:
95
server/services/media/sticker-processor.ts
Normal file
95
server/services/media/sticker-processor.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
Reference in New Issue
Block a user