feat: /api/sendSticker

Co-authored-by: nickxs1 <lucasendlichgomes@hotmail.com>

fix #1287
closes #2240
This commit is contained in:
devlikepro committed 2026-08-31 10:44:22 +07:00
1 parent a06a72901a
commit 1df6ec68db
12 files changed
+140 -3

No files matched your search

+42 -1
View File
@@ -9,7 +9,7 @@ import {
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { ApiBody, ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
import {
GetChatMessagesFilter,
@@ -37,6 +37,7 @@ import {
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextQuery,
MessageTextRequest,
MessageVideoRequest,
@@ -145,6 +146,46 @@ export class ChattingController {
return whatsapp.sendVideo(request);
}
@Post('/sendSticker')
@ApiOperation({
summary: 'Send a sticker',
description:
'The image will not be converted. It must already be in WebP format. PNG and JPEG are not supported.',
})
@ApiBody({
type: MessageStickerRequest,
examples: {
url: {
summary: 'From URL',
value: {
session: 'default',
chatId: '11111111111@c.us',
reply_to: null,
file: {
url: 'https://www.gstatic.com/webp/gallery/1.webp',
},
},
},
base64webp: {
summary: 'From base64 WebP',
value: {
session: 'default',
chatId: '11111111111@c.us',
reply_to: null,
file: {
mimetype: 'image/webp',
data: 'your-base64-data-of-webp',
},
},
},
},
})
@CheckPolicies(CanSession(Action.Send, FromBody('session')))
async sendSticker(@Body() request: MessageStickerRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
return whatsapp.sendSticker(request);
}
@Post('/send/link-custom-preview')
@ApiOperation({
summary: 'Send a text message with a CUSTOM link preview.',
+21
View File
@@ -17,6 +17,7 @@ import {
SendPollInput,
SendPollVoteInput,
SendSeenInput,
SendStickerInput,
SendTextInput,
SendVideoInput,
SendVoiceInput,
@@ -126,6 +127,26 @@ export class SendTools extends McpController {
});
}
@Tool('send-sticker', {
title: 'Send a sticker',
description:
'Send a sticker to a chat. The file must already be in WebP format. ' +
FileNote,
inputSchema: SendStickerInput,
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
},
})
async sendSticker(data: z.infer<typeof SendStickerInput>) {
return this.textRequest({
method: 'POST',
url: '/api/sendSticker',
data: data,
});
}
@Tool('send-link-custom-preview', {
title: 'Send a text message with a custom link preview',
description:
+2
View File
@@ -13,6 +13,7 @@ import {
MessagePollVoteRequest,
MessageReactionRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
@@ -27,6 +28,7 @@ export const SendImageInput = DtoToZod(MessageImageRequest);
export const SendFileInput = DtoToZod(MessageFileRequest);
export const SendVoiceInput = DtoToZod(MessageVoiceRequest);
export const SendVideoInput = DtoToZod(MessageVideoRequest);
export const SendStickerInput = DtoToZod(MessageStickerRequest);
export const SendLinkCustomPreviewInput = DtoToZod(
MessageLinkCustomPreviewRequest,
);
+5
View File
@@ -87,6 +87,7 @@ import {
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
@@ -685,6 +686,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendSticker(request: MessageStickerRequest) {
throw new NotImplementedByEngineError();
}
sendButtons(request: SendButtonsRequest) {
throw new NotImplementedByEngineError();
}
+2 -1
View File
@@ -28,7 +28,8 @@ export namespace messages {
AUDIO = 1,
VIDEO = 2,
DOCUMENT = 3,
PTV = 4
PTV = 4,
STICKER = 5
}
export enum Presence {
AVAILABLE = 0,
+2 -1
View File
@@ -23151,7 +23151,8 @@ proto.messages.MediaType = {
AUDIO: 1,
VIDEO: 2,
DOCUMENT: 3,
PTV: 4
PTV: 4,
STICKER: 5
};
/**
@@ -103,6 +103,7 @@ import {
MessageReactionRequest,
MessageReplyRequest,
MessageTextRequest,
MessageStickerRequest,
MessageVideoRequest,
MessageVoiceRequest,
SendSeenRequest,
@@ -1371,6 +1372,8 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
if (!media.mimetype) {
media.mimetype = await detectMimetype(media.content as Buffer);
}
} else if (type === messages.MediaType.STICKER) {
media.mimetype = media.mimetype || WAMimeType.STICKER;
}
if (request.convert) {
@@ -1494,6 +1497,11 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return await this.sendMedia(type, request);
}
@Activity()
async sendSticker(request: MessageStickerRequest) {
return await this.sendMedia(messages.MediaType.STICKER, request);
}
@Activity()
async sendLinkCustomPreview(
request: MessageLinkCustomPreviewRequest,
@@ -138,6 +138,7 @@ import {
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
@@ -1389,6 +1390,18 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return this.sock.sendMessage(chatId, message, options);
}
@Activity()
async sendSticker(request: MessageStickerRequest) {
const message: any = await this.fileToMessage(request.file, 'sticker');
message.mimetype = message.mimetype || WAMimeType.STICKER;
const chatId = await this.hooks.wid.chat.promise(
request.chatId,
'sendSticker',
);
const options = await this.getMessageOptions(request);
return this.sock.sendMessage(chatId, message, options);
}
@Activity()
async sendLinkCustomPreview(
request: MessageLinkCustomPreviewRequest,
@@ -91,6 +91,7 @@ import {
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextRequest,
MessageVideoRequest,
SendSeenRequest,
@@ -1146,6 +1147,22 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return this.whatsapp.sendMessage(chatId, media, options);
}
@Activity()
async sendSticker(request: MessageStickerRequest) {
const media = await this.fileToMedia(request.file);
media.mimetype = media.mimetype || WAMimeType.STICKER;
let options = await this.getMessageOptions(request);
options = {
...options,
sendMediaAsSticker: true,
};
const chatId = await this.hooks.wid.chat.promise(
request.chatId,
'sendSticker',
);
return this.whatsapp.sendMessage(chatId, media, options);
}
private async convertVideo(media: MessageMedia) {
let content: Buffer<ArrayBufferLike> = Buffer.from(media.data, 'base64');
content = await this.mediaConverter.video(content);
+22
View File
@@ -46,6 +46,7 @@ import {
MessageReplyRequest,
MessageFileRequest,
MessageStarRequest,
MessageStickerRequest,
MessageTextRequest,
MessageVideoRequest,
MessageVoiceRequest,
@@ -193,6 +194,7 @@ import type {
DocumentMessageOptions,
FileMessageOptions,
ImageMessageOptions,
StickerMessageOptions,
VideoMessageOptions,
} from '@wppconnect/wa-js/dist/chat';
@@ -803,6 +805,26 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
return await this.sendMedia(chatId, media, options);
}
@Activity()
async sendSticker(request: MessageStickerRequest) {
const quotedMessageId = this.getReplyToMessageId(request as any);
const content = await this.fileToBuffer(request.file);
const mimetype = request.file.mimetype || WAMimeType.STICKER;
const media = WPPMedia(content, mimetype);
const options: StickerMessageOptions = {
type: 'sticker',
filename: request.file.filename,
mimetype: mimetype,
quotedMsg: quotedMessageId,
waitForAck: false,
};
const chatId = await this.hooks.wid.chat.promise(
request.chatId,
'sendSticker',
);
return await this.sendMedia(chatId, media, options);
}
@Activity()
async sendImageStatus(status: ImageStatus) {
this.checkStatusRequest(status);
+1
View File
@@ -2,4 +2,5 @@ export enum WAMimeType {
VOICE = 'audio/ogg; codecs=opus',
VIDEO = 'video/mp4',
IMAGE = 'image/jpeg',
STICKER = 'image/webp',
}
+5
View File
@@ -381,6 +381,11 @@ export class MessageVideoRequest extends ChatRequest {
convert: boolean;
}
export class MessageStickerRequest extends FileRequest {
@ReplyToProperty()
reply_to?: string;
}
export class MessageLinkPreviewRequest extends ChatRequest {
@GeneratedMessageIdProperty()
id?: string;