From fbccf2eb2aaef0328fc1e2ddae84a29a1292ca03 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 29 Oct 2024 10:00:32 -0300 Subject: [PATCH] feat: send pix button --- package.json | 1 + .../whatsapp/whatsapp.baileys.service.ts | 94 +++++++++++++++++++ src/api/types/wa.types.ts | 2 +- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e7fb2a77..f7e77194 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "jsonschema": "^1.4.1", "link-preview-js": "^3.0.4", "long": "^5.2.3", + "mediainfo.js": "^0.3.2", "mime": "^3.0.0", "minio": "^8.0.1", "multer": "^1.4.5-lts.1", diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 3aac7a61..85146af3 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -141,9 +141,70 @@ import qrcodeTerminal from 'qrcode-terminal'; import sharp from 'sharp'; import { PassThrough } from 'stream'; import { v4 } from 'uuid'; +import { Readable } from 'stream'; const groupMetadataCache = new CacheService(new CacheEngine(configService, 'groups').getEngine()); +// Adicione a função getVideoDuration no início do arquivo +async function getVideoDuration(input: Buffer | string | Readable): Promise { + const MediaInfoFactory = (await import('mediainfo.js')).default; + const mediainfo = await MediaInfoFactory({ format: 'JSON' }); + + let fileSize: number; + let readChunk: (size: number, offset: number) => Promise; + + if (Buffer.isBuffer(input)) { + fileSize = input.length; + readChunk = async (size: number, offset: number): Promise => { + return input.slice(offset, offset + size); + }; + } else if (typeof input === 'string') { + const fs = await import('fs'); + const stat = await fs.promises.stat(input); + fileSize = stat.size; + const fd = await fs.promises.open(input, 'r'); + + readChunk = async (size: number, offset: number): Promise => { + const buffer = Buffer.alloc(size); + await fd.read(buffer, 0, size, offset); + return buffer; + }; + + try { + const result = await mediainfo.analyzeData(() => fileSize, readChunk); + const jsonResult = JSON.parse(result); + + const generalTrack = jsonResult.media.track.find((t: any) => t['@type'] === 'General'); + const duration = generalTrack.Duration; + + return Math.round(parseFloat(duration)); + } finally { + await fd.close(); + } + } else if (input instanceof Readable) { + const chunks: Buffer[] = []; + for await (const chunk of input) { + chunks.push(chunk); + } + const data = Buffer.concat(chunks); + fileSize = data.length; + + readChunk = async (size: number, offset: number): Promise => { + return data.slice(offset, offset + size); + }; + } else { + throw new Error('Tipo de entrada não suportado'); + } + + const result = await mediainfo.analyzeData(() => fileSize, readChunk); + const jsonResult = JSON.parse(result); + + const generalTrack = jsonResult.media.track.find((t: any) => t['@type'] === 'General'); + const duration = generalTrack.Duration; + + return Math.round(parseFloat(duration)); +} + export class BaileysStartupService extends ChannelStartupService { constructor( public readonly configService: ConfigService, @@ -1101,6 +1162,7 @@ export class BaileysStartupService extends ChannelStartupService { received?.message?.stickerMessage || received?.message?.documentMessage || received?.message?.documentWithCaptionMessage || + received?.message?.ptvMessage || received?.message?.audioMessage; if (this.localSettings.readMessages && received.key.id !== 'status@broadcast') { @@ -2097,6 +2159,7 @@ export class BaileysStartupService extends ChannelStartupService { messageSent?.message?.ptvMessage || messageSent?.message?.documentMessage || messageSent?.message?.documentWithCaptionMessage || + messageSent?.message?.ptvMessage || messageSent?.message?.audioMessage; if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) { @@ -2500,6 +2563,37 @@ export class BaileysStartupService extends ChannelStartupService { if (mediaMessage.mediatype === 'ptv') { prepareMedia[mediaType] = prepareMedia[type + 'Message']; + mimetype = 'video/mp4'; + + if (!prepareMedia[mediaType]) { + throw new Error('Failed to prepare video message'); + } + + try { + let mediaInput; + if (isURL(mediaMessage.media)) { + mediaInput = mediaMessage.media; + } else { + const mediaBuffer = Buffer.from(mediaMessage.media, 'base64'); + if (!mediaBuffer || mediaBuffer.length === 0) { + throw new Error('Invalid media buffer'); + } + mediaInput = mediaBuffer; + } + + const duration = await getVideoDuration(mediaInput); + if (!duration || duration <= 0) { + throw new Error('Invalid media duration'); + } + + this.logger.verbose(`Video duration: ${duration} seconds`); + prepareMedia[mediaType].seconds = duration; + + } catch (error) { + this.logger.error('Error getting video duration:'); + this.logger.error(error); + throw new Error(`Failed to get video duration: ${error.message}`); + } } prepareMedia[mediaType].caption = mediaMessage?.caption; diff --git a/src/api/types/wa.types.ts b/src/api/types/wa.types.ts index 6d4523f2..72c183b2 100644 --- a/src/api/types/wa.types.ts +++ b/src/api/types/wa.types.ts @@ -131,7 +131,7 @@ export declare namespace wa { export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED'; } -export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage']; +export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage', 'ptvMessage']; export const MessageSubtype = [ 'ephemeralMessage',