feat: route to send Sticker

This commit is contained in:
Davidson Gomes 2023-06-20 16:47:26 -03:00
parent 84f6394f1f
commit a08bbab9dc
9 changed files with 90 additions and 2 deletions

View File

@ -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 * 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 * 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 * The created instance token can now also be optionally defined manually in the creation endpoint
* Route to send Sticker
### Fixed ### Fixed

View File

@ -67,6 +67,7 @@
"qrcode": "^1.5.1", "qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0", "qrcode-terminal": "^0.12.0",
"redis": "^4.6.5", "redis": "^4.6.5",
"sharp": "^0.30.7",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -210,6 +210,24 @@ export const mediaMessageSchema: JSONSchema7 = {
required: ['mediaMessage', 'number'], 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 = { export const audioMessageSchema: JSONSchema7 = {
$id: v4(), $id: v4(),
type: 'object', type: 'object',

View File

@ -11,6 +11,7 @@ import {
SendMediaDto, SendMediaDto,
SendPollDto, SendPollDto,
SendReactionDto, SendReactionDto,
SendStickerDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
@ -32,6 +33,13 @@ export class SendMessageController {
throw new BadRequestException('Owned media must be a url or base64'); 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) { public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) {
if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) { if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data); return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);

View File

@ -62,6 +62,12 @@ export class MediaMessage {
export class SendMediaDto extends Metadata { export class SendMediaDto extends Metadata {
mediaMessage: MediaMessage; mediaMessage: MediaMessage;
} }
class Sticker {
image: string;
}
export class SendStickerDto extends Metadata {
stickerMessage: Sticker;
}
class Audio { class Audio {
audio: string; audio: string;

View File

@ -9,6 +9,7 @@ import {
mediaMessageSchema, mediaMessageSchema,
pollMessageSchema, pollMessageSchema,
reactionMessageSchema, reactionMessageSchema,
stickerMessageSchema,
textMessageSchema, textMessageSchema,
} from '../../validate/validate.schema'; } from '../../validate/validate.schema';
import { import {
@ -21,6 +22,7 @@ import {
SendMediaDto, SendMediaDto,
SendPollDto, SendPollDto,
SendReactionDto, SendReactionDto,
SendStickerDto,
SendTextDto, SendTextDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { sendMessageController } from '../whatsapp.module'; import { sendMessageController } from '../whatsapp.module';
@ -131,6 +133,16 @@ export class MessageRouter extends RouterBroker {
sendMessageController.sendLinkPreview(instance, data), sendMessageController.sendLinkPreview(instance, data),
}); });
return res.status(HttpStatus.CREATED).json(response);
})
.post(this.routerPath('sendSticker'), ...guards, async (req, res) => {
const response = await this.dataValidate<SendStickerDto>({
request: req,
schema: stickerMessageSchema,
ClassRef: SendStickerDto,
execute: (instance, data) => sendMessageController.sendSticker(instance, data),
});
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
}); });
} }

View File

@ -83,6 +83,7 @@ import {
SendTextDto, SendTextDto,
SendPollDto, SendPollDto,
SendLinkPreviewDto, SendLinkPreviewDto,
SendStickerDto,
} from '../dto/sendMessage.dto'; } from '../dto/sendMessage.dto';
import { arrayUnique, isBase64, isURL } from 'class-validator'; import { arrayUnique, isBase64, isURL } from 'class-validator';
import { import {
@ -118,6 +119,8 @@ import { WebhookRaw } from '../models/webhook.model';
import { dbserver } from '../../db/db.connect'; import { dbserver } from '../../db/db.connect';
import NodeCache from 'node-cache'; import NodeCache from 'node-cache';
import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db';
import { promisify } from 'util';
import sharp from 'sharp';
export class WAStartupService { export class WAStartupService {
constructor( constructor(
@ -783,7 +786,6 @@ export class WAStartupService {
}, },
'messages.update': async (args: WAMessageUpdate[], database: Database) => { 'messages.update': async (args: WAMessageUpdate[], database: Database) => {
console.log('messages.update args: ', args);
const status: Record<number, wa.StatusMessage> = { const status: Record<number, wa.StatusMessage> = {
0: 'ERROR', 0: 'ERROR',
1: 'PENDING', 1: 'PENDING',
@ -1050,7 +1052,12 @@ export class WAStartupService {
quoted, quoted,
}; };
if (!message['audio'] && !message['poll'] && !message['linkPreview']) { if (
!message['audio'] &&
!message['poll'] &&
!message['linkPreview'] &&
!message['sticker']
) {
if (!message['audio']) { if (!message['audio']) {
return await this.client.sendMessage( return await this.client.sendMessage(
sender, 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) { public async mediaMessage(data: SendMediaDto) {
const generate = await this.prepareMediaMessage(data.mediaMessage); const generate = await this.prepareMediaMessage(data.mediaMessage);

BIN
temp/sticker.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
temp/temp-sticker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB