diff --git a/CHANGELOG.md b/CHANGELOG.md index 466047d1..8eb40063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Add indexes to improve performance in Evolution * Add logical or permanent message deletion based on env config * Add support for fetching multiple instances by key +* Update instance.controller.ts to filter by instanceName # 2.1.2 (2024-10-06 10:09) diff --git a/src/api/controllers/sendMessage.controller.ts b/src/api/controllers/sendMessage.controller.ts index 31ea5c92..ac40562c 100644 --- a/src/api/controllers/sendMessage.controller.ts +++ b/src/api/controllers/sendMessage.controller.ts @@ -7,6 +7,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPtvDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -39,6 +40,13 @@ export class SendMessageController { throw new BadRequestException('Owned media must be a url or base64'); } + public async sendPtv({ instanceName }: InstanceDto, data: SendPtvDto, file?: any) { + if (file || isURL(data?.video) || isBase64(data?.video)) { + return await this.waMonitor.waInstances[instanceName].ptvMessage(data, file); + } + throw new BadRequestException('Owned media must be a url or base64'); + } + public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto, file?: any) { if (file || isURL(data.sticker) || isBase64(data.sticker)) { return await this.waMonitor.waInstances[instanceName].mediaSticker(data, file); diff --git a/src/api/dto/sendMessage.dto.ts b/src/api/dto/sendMessage.dto.ts index c9a5e9cd..c1f16aec 100644 --- a/src/api/dto/sendMessage.dto.ts +++ b/src/api/dto/sendMessage.dto.ts @@ -70,7 +70,7 @@ export class SendPollDto extends Metadata { messageSecret?: Uint8Array; } -export type MediaType = 'image' | 'document' | 'video' | 'audio'; +export type MediaType = 'image' | 'document' | 'video' | 'audio' | 'ptv'; export class SendMediaDto extends Metadata { mediatype: MediaType; @@ -82,6 +82,10 @@ export class SendMediaDto extends Metadata { media: string; } +export class SendPtvDto extends Metadata { + video: string; +} + export class SendStickerDto extends Metadata { sticker: string; } diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 0105a14c..684b4123 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -42,6 +42,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPtvDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -309,7 +310,7 @@ export class BaileysStartupService extends ChannelStartupService { qrcodeTerminal.generate(qr, { small: true }, (qrcode) => this.logger.log( `\n{ instance: ${this.instance.name} pairingCode: ${this.instance.qrcode.pairingCode}, qrcodeCount: ${this.instance.qrcode.count} }\n` + - qrcode, + qrcode, ), ); @@ -913,18 +914,18 @@ export class BaileysStartupService extends ChannelStartupService { const messagesRepository = new Set( chatwootImport.getRepositoryMessagesCache(instance) ?? - ( - await this.prismaRepository.message.findMany({ - select: { key: true }, - where: { instanceId: this.instanceId }, - }) - ).map((message) => { - const key = message.key as { - id: string; - }; + ( + await this.prismaRepository.message.findMany({ + select: { key: true }, + where: { instanceId: this.instanceId }, + }) + ).map((message) => { + const key = message.key as { + id: string; + }; - return key.id; - }), + return key.id; + }), ); if (chatwootImport.getRepositoryMessagesCache(instance) === null) { @@ -2052,6 +2053,7 @@ export class BaileysStartupService extends ChannelStartupService { messageSent?.message?.imageMessage || messageSent?.message?.videoMessage || messageSent?.message?.stickerMessage || + messageSent?.message?.ptvMessage || messageSent?.message?.documentMessage || messageSent?.message?.documentWithCaptionMessage || messageSent?.message?.audioMessage; @@ -2397,9 +2399,11 @@ export class BaileysStartupService extends ChannelStartupService { private async prepareMediaMessage(mediaMessage: MediaMessage) { try { + const type = mediaMessage.mediatype === 'ptv' ? 'video' : mediaMessage.mediatype; + const prepareMedia = await prepareWAMessageMedia( { - [mediaMessage.mediatype]: isURL(mediaMessage.media) + [type]: isURL(mediaMessage.media) ? { url: mediaMessage.media } : Buffer.from(mediaMessage.media, 'base64'), } as any, @@ -2453,6 +2457,10 @@ export class BaileysStartupService extends ChannelStartupService { } } + if (mediaMessage.mediatype === 'ptv') { + prepareMedia[mediaType] = prepareMedia[type + 'Message']; + } + prepareMedia[mediaType].caption = mediaMessage?.caption; prepareMedia[mediaType].mimetype = mimetype; prepareMedia[mediaType].fileName = mediaMessage.fileName; @@ -2564,6 +2572,37 @@ export class BaileysStartupService extends ChannelStartupService { return mediaSent; } + public async ptvMessage(data: SendPtvDto, file?: any, isIntegration = false) { + const mediaData: SendMediaDto = { + number: data.number, + media: data.video, + mediatype: 'ptv', + delay: data?.delay, + quoted: data?.quoted, + mentionsEveryOne: data?.mentionsEveryOne, + mentioned: data?.mentioned, + }; + + if (file) mediaData.media = file.buffer.toString('base64'); + + const generate = await this.prepareMediaMessage(mediaData); + + const mediaSent = await this.sendMessageWithTyping( + data.number, + { ...generate.message }, + { + delay: data?.delay, + presence: 'composing', + quoted: data?.quoted, + mentionsEveryOne: data?.mentionsEveryOne, + mentioned: data?.mentioned, + }, + isIntegration, + ); + + return mediaSent; + } + public async processAudioMp4(audio: string) { let inputStream: PassThrough; diff --git a/src/api/routes/sendMessage.router.ts b/src/api/routes/sendMessage.router.ts index 73f17713..cd073dba 100644 --- a/src/api/routes/sendMessage.router.ts +++ b/src/api/routes/sendMessage.router.ts @@ -7,6 +7,7 @@ import { SendLocationDto, SendMediaDto, SendPollDto, + SendPtvDto, SendReactionDto, SendStatusDto, SendStickerDto, @@ -22,6 +23,7 @@ import { locationMessageSchema, mediaMessageSchema, pollMessageSchema, + ptvMessageSchema, reactionMessageSchema, statusMessageSchema, stickerMessageSchema, @@ -71,6 +73,18 @@ export class MessageRouter extends RouterBroker { return res.status(HttpStatus.CREATED).json(response); }) + .post(this.routerPath('sendPtv'), ...guards, upload.single('file'), async (req, res) => { + const bodyData = req.body; + + const response = await this.dataValidate({ + request: req, + schema: ptvMessageSchema, + ClassRef: SendPtvDto, + execute: (instance) => sendMessageController.sendPtv(instance, bodyData, req.file as any), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) .post(this.routerPath('sendWhatsAppAudio'), ...guards, upload.single('file'), async (req, res) => { const bodyData = req.body; diff --git a/src/validate/message.schema.ts b/src/validate/message.schema.ts index ef0d6c7a..afb4046a 100644 --- a/src/validate/message.schema.ts +++ b/src/validate/message.schema.ts @@ -122,6 +122,32 @@ export const mediaMessageSchema: JSONSchema7 = { required: ['number', 'mediatype'], }; +export const ptvMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + video: { type: 'string' }, + delay: { + type: 'integer', + description: 'Enter a value in milliseconds', + }, + quoted: { ...quotedOptionsSchema }, + everyOne: { type: 'boolean', enum: [true, false] }, + mentioned: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + pattern: '^\\d+', + description: '"mentioned" must be an array of numeric strings', + }, + }, + }, + required: ['number'], +}; + export const audioMessageSchema: JSONSchema7 = { $id: v4(), type: 'object',