(sock, message, args, context)
| 11 | description: 'Convert image/video to sticker', |
| 12 | usage: '.sticker2 (reply to image/video or send with caption)', |
| 13 | async handler(sock, message, args, context) { |
| 14 | const { chatId, config, channelInfo } = context; |
| 15 | const messageToQuote = message; |
| 16 | let targetMessage = message; |
| 17 | if (message.message?.extendedTextMessage?.contextInfo?.quotedMessage) { |
| 18 | const quotedInfo = message.message.extendedTextMessage.contextInfo; |
| 19 | targetMessage = { |
| 20 | key: { |
| 21 | remoteJid: chatId, |
| 22 | id: quotedInfo.stanzaId, |
| 23 | participant: quotedInfo.participant |
| 24 | }, |
| 25 | message: quotedInfo.quotedMessage |
| 26 | }; |
| 27 | } |
| 28 | const mediaMessage = targetMessage.message?.imageMessage || targetMessage.message?.videoMessage || targetMessage.message?.documentMessage; |
| 29 | if (!mediaMessage) { |
| 30 | await sock.sendMessage(chatId, { |
| 31 | text: 'Please reply to an image/video with .sticker2, or send an image/video with .sticker2 as the caption.', |
| 32 | ...channelInfo |
| 33 | }, { quoted: messageToQuote }); |
| 34 | return; |
| 35 | } |
| 36 | try { |
| 37 | const mediaBuffer = await downloadMediaMessage(targetMessage, 'buffer', {}, { |
| 38 | logger: undefined, |
| 39 | reuploadRequest: sock.updateMediaMessage |
| 40 | }); |
| 41 | if (!mediaBuffer) { |
| 42 | await sock.sendMessage(chatId, { |
| 43 | text: 'Failed to download media. Please try again.', |
| 44 | ...channelInfo |
| 45 | }, { quoted: messageToQuote }); |
| 46 | return; |
| 47 | } |
| 48 | const tmpDir = path.join(process.cwd(), 'temp'); |
| 49 | if (!fs.existsSync(tmpDir)) { |
| 50 | fs.mkdirSync(tmpDir, { recursive: true }); |
| 51 | } |
| 52 | const tempInput = path.join(tmpDir, `temp_${Date.now()}`); |
| 53 | const tempOutput = path.join(tmpDir, `sticker_${Date.now()}.webp`); |
| 54 | fs.writeFileSync(tempInput, mediaBuffer); |
| 55 | const isAnimated = mediaMessage.mimetype?.includes('gif') || |
| 56 | mediaMessage.mimetype?.includes('video') || |
| 57 | mediaMessage.seconds > 0; |
| 58 | const ffmpegCommand = isAnimated |
| 59 | ? `ffmpeg -i "${tempInput}" -vf "scale=512:512:force_original_aspect_ratio=decrease,fps=15,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 75 -compression_level 6 "${tempOutput}"` |
| 60 | : `ffmpeg -i "${tempInput}" -vf "scale=512:512:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 75 -compression_level 6 "${tempOutput}"`; |
| 61 | await new Promise((resolve, reject) => { |
| 62 | exec(ffmpegCommand, (error) => { |
| 63 | if (error) { |
| 64 | console.error('FFmpeg error:', error); |
| 65 | reject(error); |
| 66 | } |
| 67 | else |
| 68 | resolve(undefined); |
| 69 | }); |
| 70 | }); |
nothing calls this directly
no test coverage detected