Skip to content

Commit a531796

Browse files
committed
feat: send Button Message
1 parent 8d4d445 commit a531796

File tree

6 files changed

+143
-31
lines changed

6 files changed

+143
-31
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Fake Call function
66
* Send List Message
7+
* Send Button Message
78
* Added unreadMessages to chats
89

910
### Fixed

src/api/controllers/sendMessage.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { InstanceDto } from '@api/dto/instance.dto';
22
import {
33
SendAudioDto,
4-
SendButtonDto,
4+
SendButtonsDto,
55
SendContactDto,
66
SendListDto,
77
SendLocationDto,
@@ -55,7 +55,7 @@ export class SendMessageController {
5555
}
5656
}
5757

58-
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) {
58+
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonsDto) {
5959
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
6060
}
6161

src/api/dto/sendMessage.dto.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,22 @@ export class SendAudioDto extends Metadata {
9090
audio: string;
9191
}
9292

93-
class Button {
94-
text: string;
95-
id: string;
93+
export type TypeButton = 'reply' | 'copy' | 'url' | 'call';
94+
95+
export class Button {
96+
type: TypeButton;
97+
displayText: string;
98+
id?: string;
99+
url?: string;
100+
copyCode?: string;
101+
phoneNumber?: string;
96102
}
97-
export class SendButtonDto extends Metadata {
103+
104+
export class SendButtonsDto extends Metadata {
105+
thumbnailUrl?: string;
98106
title: string;
99-
description: string;
100-
footerText?: string;
107+
description?: string;
108+
footer?: string;
101109
buttons: Button[];
102110
}
103111

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ import {
3131
import { SetPresenceDto } from '@api/dto/instance.dto';
3232
import { HandleLabelDto, LabelDto } from '@api/dto/label.dto';
3333
import {
34+
Button,
3435
ContactMessage,
3536
MediaMessage,
3637
Options,
3738
SendAudioDto,
39+
SendButtonsDto,
3840
SendContactDto,
3941
SendListDto,
4042
SendLocationDto,
@@ -45,6 +47,7 @@ import {
4547
SendStickerDto,
4648
SendTextDto,
4749
StatusMessage,
50+
TypeButton,
4851
} from '@api/dto/sendMessage.dto';
4952
import { ProviderFiles } from '@api/provider/sessions';
5053
import { PrismaRepository } from '@api/repository/repository.service';
@@ -2111,8 +2114,97 @@ export class BaileysStartupService extends ChannelStartupService {
21112114
);
21122115
}
21132116

2114-
public async buttonMessage() {
2115-
throw new BadRequestException('Method not available on WhatsApp Baileys');
2117+
private toJSONString(button: Button): string {
2118+
const toString = (obj: any) => JSON.stringify(obj);
2119+
2120+
const json = {
2121+
call: () => toString({ display_text: button.displayText, phone_number: button.phoneNumber }),
2122+
reply: () => toString({ display_text: button.displayText, id: button.id }),
2123+
copy: () => toString({ display_text: button.displayText, copy_code: button.copyCode }),
2124+
url: () =>
2125+
toString({
2126+
display_text: button.displayText,
2127+
url: button.url,
2128+
merchant_url: button.url,
2129+
}),
2130+
};
2131+
2132+
return json[button.type]?.() || '';
2133+
}
2134+
2135+
private readonly mapType = new Map<TypeButton, string>([
2136+
['reply', 'quick_reply'],
2137+
['copy', 'cta_copy'],
2138+
['url', 'cta_url'],
2139+
['call', 'cta_call'],
2140+
]);
2141+
2142+
public async buttonMessage(data: SendButtonsDto) {
2143+
const generate = await (async () => {
2144+
if (data?.thumbnailUrl) {
2145+
return await this.prepareMediaMessage({
2146+
mediatype: 'image',
2147+
media: data.thumbnailUrl,
2148+
});
2149+
}
2150+
})();
2151+
2152+
const buttons = data.buttons.map((value) => {
2153+
return {
2154+
name: this.mapType.get(value.type),
2155+
buttonParamsJson: this.toJSONString(value),
2156+
};
2157+
});
2158+
2159+
const message: proto.IMessage = {
2160+
viewOnceMessage: {
2161+
message: {
2162+
messageContextInfo: {
2163+
deviceListMetadata: {},
2164+
deviceListMetadataVersion: 2,
2165+
},
2166+
interactiveMessage: {
2167+
body: {
2168+
text: (() => {
2169+
let t = '*' + data.title + '*';
2170+
if (data?.description) {
2171+
t += '\n\n';
2172+
t += data.description;
2173+
t += '\n';
2174+
}
2175+
return t;
2176+
})(),
2177+
},
2178+
footer: {
2179+
text: data?.footer,
2180+
},
2181+
header: (() => {
2182+
if (generate?.message?.imageMessage) {
2183+
return {
2184+
hasMediaAttachment: !!generate.message.imageMessage,
2185+
imageMessage: generate.message.imageMessage,
2186+
};
2187+
}
2188+
})(),
2189+
nativeFlowMessage: {
2190+
buttons: buttons,
2191+
messageParamsJson: JSON.stringify({
2192+
from: 'api',
2193+
templateId: v4(),
2194+
}),
2195+
},
2196+
},
2197+
},
2198+
},
2199+
};
2200+
2201+
return await this.sendMessageWithTyping(data.number, message, {
2202+
delay: data?.delay,
2203+
presence: 'composing',
2204+
quoted: data?.quoted,
2205+
mentionsEveryOne: data?.mentionsEveryOne,
2206+
mentioned: data?.mentioned,
2207+
});
21162208
}
21172209

21182210
public async listMessage(data: SendListDto) {

src/api/routes/sendMessage.router.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { RouterBroker } from '@api/abstract/abstract.router';
22
import {
33
SendAudioDto,
4-
SendButtonDto,
4+
SendButtonsDto,
55
SendContactDto,
66
SendListDto,
77
SendLocationDto,
@@ -16,7 +16,7 @@ import {
1616
import { sendMessageController } from '@api/server.module';
1717
import {
1818
audioMessageSchema,
19-
buttonMessageSchema,
19+
buttonsMessageSchema,
2020
contactMessageSchema,
2121
listMessageSchema,
2222
locationMessageSchema,
@@ -159,14 +159,23 @@ export class MessageRouter extends RouterBroker {
159159
return res.status(HttpStatus.CREATED).json(response);
160160
})
161161
.post(this.routerPath('sendButtons'), ...guards, async (req, res) => {
162-
const response = await this.dataValidate<SendButtonDto>({
163-
request: req,
164-
schema: buttonMessageSchema,
165-
ClassRef: SendButtonDto,
166-
execute: (instance, data) => sendMessageController.sendButtons(instance, data),
167-
});
168-
169-
return res.status(HttpStatus.CREATED).json(response);
162+
console.log('Corpo da requisição recebido:', JSON.stringify(req.body, null, 2));
163+
try {
164+
const response = await this.dataValidate<SendButtonsDto>({
165+
request: req,
166+
schema: buttonsMessageSchema,
167+
ClassRef: SendButtonsDto,
168+
execute: (instance, data) => sendMessageController.sendButtons(instance, data),
169+
});
170+
171+
return res.status(HttpStatus.CREATED).json(response);
172+
} catch (error) {
173+
console.error('Erro ao enviar lista:', error);
174+
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
175+
message: 'Erro interno no servidor',
176+
error: error.message,
177+
});
178+
}
170179
});
171180
}
172181

src/validate/message.schema.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -360,31 +360,33 @@ export const listMessageSchema: JSONSchema7 = {
360360
required: ['number', 'title', 'footerText', 'buttonText', 'sections'],
361361
};
362362

363-
export const buttonMessageSchema: JSONSchema7 = {
363+
export const buttonsMessageSchema: JSONSchema7 = {
364364
$id: v4(),
365365
type: 'object',
366366
properties: {
367367
number: { ...numberDefinition },
368+
thumbnailUrl: { type: 'string' },
368369
title: { type: 'string' },
369370
description: { type: 'string' },
370-
footerText: { type: 'string' },
371+
footer: { type: 'string' },
371372
buttons: {
372373
type: 'array',
373-
minItems: 1,
374-
uniqueItems: true,
375374
items: {
376375
type: 'object',
377376
properties: {
378-
text: { type: 'string' },
377+
type: {
378+
type: 'string',
379+
enum: ['reply', 'copy', 'url', 'call'],
380+
},
381+
displayText: { type: 'string' },
379382
id: { type: 'string' },
383+
url: { type: 'string' },
384+
phoneNumber: { type: 'string' },
380385
},
381-
required: ['text', 'id'],
382-
...isNotEmpty('text', 'id'),
386+
required: ['type', 'displayText'],
387+
...isNotEmpty('id', 'url', 'phoneNumber'),
383388
},
384389
},
385-
media: { type: 'string' },
386-
fileName: { type: 'string' },
387-
mediatype: { type: 'string', enum: ['image', 'document', 'video'] },
388390
delay: {
389391
type: 'integer',
390392
description: 'Enter a value in milliseconds',
@@ -402,7 +404,7 @@ export const buttonMessageSchema: JSONSchema7 = {
402404
},
403405
},
404406
},
405-
required: ['number', 'title', 'buttons'],
407+
required: ['number'],
406408
};
407409

408410
export const offerCallSchema: JSONSchema7 = {

0 commit comments

Comments
 (0)