From a08bbab9dcdac7a0fe15df034feb08cf7b0b6628 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Tue, 20 Jun 2023 16:47:26 -0300 Subject: [PATCH] feat: route to send Sticker --- CHANGELOG.md | 1 + package.json | 1 + src/validate/validate.schema.ts | 18 +++++++ .../controllers/sendMessage.controller.ts | 8 +++ src/whatsapp/dto/sendMessage.dto.ts | 6 +++ src/whatsapp/routers/sendMessage.router.ts | 12 +++++ src/whatsapp/services/whatsapp.service.ts | 46 +++++++++++++++++- temp/sticker.webp | Bin 0 -> 8624 bytes temp/temp-sticker.png | Bin 0 -> 6538 bytes 9 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 temp/sticker.webp create mode 100644 temp/temp-sticker.png diff --git a/CHANGELOG.md b/CHANGELOG.md index f03ec516..8c513bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Now the api key can be exposed in fetch instances if the EXPOSE_IN_FETCH_INSTANCES variable is set to true * Added option to generate qrcode as soon as the instance is created * The created instance token can now also be optionally defined manually in the creation endpoint +* Route to send Sticker ### Fixed diff --git a/package.json b/package.json index 21b675d0..791a9e6f 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "qrcode": "^1.5.1", "qrcode-terminal": "^0.12.0", "redis": "^4.6.5", + "sharp": "^0.30.7", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 554301e8..d95f9268 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -210,6 +210,24 @@ export const mediaMessageSchema: JSONSchema7 = { required: ['mediaMessage', 'number'], }; +export const stickerMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + stickerMessage: { + type: 'object', + properties: { + image: { type: 'string' }, + }, + required: ['image'], + ...isNotEmpty('image'), + }, + }, + required: ['stickerMessage', 'number'], +}; + export const audioMessageSchema: JSONSchema7 = { $id: v4(), type: 'object', diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts index df26cf40..b985d5e7 100644 --- a/src/whatsapp/controllers/sendMessage.controller.ts +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -11,6 +11,7 @@ import { SendMediaDto, SendPollDto, SendReactionDto, + SendStickerDto, SendTextDto, } from '../dto/sendMessage.dto'; import { WAMonitoringService } from '../services/monitor.service'; @@ -32,6 +33,13 @@ export class SendMessageController { throw new BadRequestException('Owned media must be a url or base64'); } + public async sendSticker({ instanceName }: InstanceDto, data: SendStickerDto) { + if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) { + return await this.waMonitor.waInstances[instanceName].mediaSticker(data); + } + throw new BadRequestException('Owned media must be a url or base64'); + } + public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) { if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) { return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data); diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index dd545250..273d8aae 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -62,6 +62,12 @@ export class MediaMessage { export class SendMediaDto extends Metadata { mediaMessage: MediaMessage; } +class Sticker { + image: string; +} +export class SendStickerDto extends Metadata { + stickerMessage: Sticker; +} class Audio { audio: string; diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts index cd7a7dde..b2400f7f 100644 --- a/src/whatsapp/routers/sendMessage.router.ts +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -9,6 +9,7 @@ import { mediaMessageSchema, pollMessageSchema, reactionMessageSchema, + stickerMessageSchema, textMessageSchema, } from '../../validate/validate.schema'; import { @@ -21,6 +22,7 @@ import { SendMediaDto, SendPollDto, SendReactionDto, + SendStickerDto, SendTextDto, } from '../dto/sendMessage.dto'; import { sendMessageController } from '../whatsapp.module'; @@ -131,6 +133,16 @@ export class MessageRouter extends RouterBroker { sendMessageController.sendLinkPreview(instance, data), }); + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendSticker'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: stickerMessageSchema, + ClassRef: SendStickerDto, + execute: (instance, data) => sendMessageController.sendSticker(instance, data), + }); + return res.status(HttpStatus.CREATED).json(response); }); } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 437b7721..cb5d67e6 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -83,6 +83,7 @@ import { SendTextDto, SendPollDto, SendLinkPreviewDto, + SendStickerDto, } from '../dto/sendMessage.dto'; import { arrayUnique, isBase64, isURL } from 'class-validator'; import { @@ -118,6 +119,8 @@ import { WebhookRaw } from '../models/webhook.model'; import { dbserver } from '../../db/db.connect'; import NodeCache from 'node-cache'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; +import { promisify } from 'util'; +import sharp from 'sharp'; export class WAStartupService { constructor( @@ -783,7 +786,6 @@ export class WAStartupService { }, 'messages.update': async (args: WAMessageUpdate[], database: Database) => { - console.log('messages.update args: ', args); const status: Record = { 0: 'ERROR', 1: 'PENDING', @@ -1050,7 +1052,12 @@ export class WAStartupService { quoted, }; - if (!message['audio'] && !message['poll'] && !message['linkPreview']) { + if ( + !message['audio'] && + !message['poll'] && + !message['linkPreview'] && + !message['sticker'] + ) { if (!message['audio']) { return await this.client.sendMessage( sender, @@ -1195,6 +1202,41 @@ export class WAStartupService { } } + private async convertToWebP(image: string) { + try { + let imagePath: string; + const outputPath = `${join(process.cwd(), 'temp', 'sticker.webp')}`; + + if (isBase64(image)) { + const base64Data = image.replace(/^data:image\/(jpeg|png|gif);base64,/, ''); + const imageBuffer = Buffer.from(base64Data, 'base64'); + imagePath = `${join(process.cwd(), 'temp', 'temp-sticker.png')}`; + await sharp(imageBuffer).toFile(imagePath); + } else { + const response = await axios.get(image, { responseType: 'arraybuffer' }); + const imageBuffer = Buffer.from(response.data, 'binary'); + imagePath = `${join(process.cwd(), 'temp', 'temp-sticker.png')}`; + await sharp(imageBuffer).toFile(imagePath); + } + await sharp(imagePath).webp().toFile(outputPath); + + return outputPath; + } catch (error) { + console.error('Erro ao converter a imagem para WebP:', error); + } + } + + public async mediaSticker(data: SendStickerDto) { + const convert = await this.convertToWebP(data.stickerMessage.image); + return await this.sendMessageWithTyping( + data.number, + { + sticker: { url: convert }, + }, + data?.options, + ); + } + public async mediaMessage(data: SendMediaDto) { const generate = await this.prepareMediaMessage(data.mediaMessage); diff --git a/temp/sticker.webp b/temp/sticker.webp new file mode 100644 index 0000000000000000000000000000000000000000..b04e82dadfd7a8998a7be87e1109c51b2c542d3b GIT binary patch literal 8624 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 W61~;YGtq;v3I4)={|)}%{eJ+zQV8S# literal 0 HcmV?d00001 diff --git a/temp/temp-sticker.png b/temp/temp-sticker.png new file mode 100644 index 0000000000000000000000000000000000000000..fde5f86f997c4101d02a139cfeb9e83fc7eb3176 GIT binary patch literal 6538 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 literal 0 HcmV?d00001