Skip to content

Commit 0525501

Browse files
committed
feat: whatsapp cloud api
1 parent 3a37fd9 commit 0525501

24 files changed

+4812
-3275
lines changed

Docker/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
5151

5252
WEBSOCKET_ENABLED=false
5353

54+
WA_BUSINESS_TOKEN_WEBHOOK=evolution
55+
WA_BUSINESS_URL=https://graph.facebook.com
56+
WA_BUSINESS_VERSION=v18.0
57+
WA_BUSINESS_LANGUAGE=pt_BR
58+
5459
SQS_ENABLED=false
5560
SQS_ACCESS_KEY_ID=
5661
SQS_SECRET_ACCESS_KEY=

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
6666

6767
ENV WEBSOCKET_ENABLED=false
6868

69+
ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution
70+
ENV WA_BUSINESS_URL=https://graph.facebook.com
71+
ENV WA_BUSINESS_VERSION=v18.0
72+
ENV WA_BUSINESS_LANGUAGE=pt_BR
73+
6974
ENV SQS_ENABLED=false
7075
ENV SQS_ACCESS_KEY_ID=
7176
ENV SQS_SECRET_ACCESS_KEY=

src/config/env.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ export type Websocket = {
8686
ENABLED: boolean;
8787
};
8888

89+
export type WaBusiness = {
90+
TOKEN_WEBHOOK: string;
91+
URL: string;
92+
VERSION: string;
93+
LANGUAGE: string;
94+
};
95+
8996
export type EventsWebhook = {
9097
APPLICATION_STARTUP: boolean;
9198
INSTANCE_CREATE: boolean;
@@ -179,6 +186,7 @@ export interface Env {
179186
RABBITMQ: Rabbitmq;
180187
SQS: Sqs;
181188
WEBSOCKET: Websocket;
189+
WA_BUSINESS: WaBusiness;
182190
LOG: Log;
183191
DEL_INSTANCE: DelInstance;
184192
LANGUAGE: Language;
@@ -286,6 +294,12 @@ export class ConfigService {
286294
WEBSOCKET: {
287295
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
288296
},
297+
WA_BUSINESS: {
298+
TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || '',
299+
URL: process.env.WA_BUSINESS_URL || '',
300+
VERSION: process.env.WA_BUSINESS_VERSION || '',
301+
LANGUAGE: process.env.WA_BUSINESS_LANGUAGE || 'en',
302+
},
289303
LOG: {
290304
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
291305
'ERROR',

src/dev-env.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ SERVER:
1212
DISABLE_MANAGER: false
1313
DISABLE_DOCS: false
1414

15-
1615
CORS:
1716
ORIGIN:
1817
- "*"
@@ -96,6 +95,12 @@ SQS:
9695
WEBSOCKET:
9796
ENABLED: false
9897

98+
WA_BUSINESS:
99+
TOKEN_WEBHOOK: evolution
100+
URL: https://graph.facebook.com
101+
VERSION: v18.0
102+
LANGUAGE: pt_BR
103+
99104
# Global Webhook Settings
100105
# Each instance's Webhook URL and events will be requested at the time it is created
101106
WEBHOOK:
@@ -152,12 +157,12 @@ QRCODE:
152157
COLOR: "#198754"
153158

154159
TYPEBOT:
155-
API_VERSION: 'old' # old | latest
160+
API_VERSION: "old" # old | latest
156161
KEEP_OPEN: false
157162

158163
CHATWOOT:
159164
# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot.
160-
MESSAGE_DELETE: true # false | true
165+
MESSAGE_DELETE: true # false | true
161166
IMPORT:
162167
# This db connection is used to import messages from whatsapp to chatwoot database
163168
DATABASE:
@@ -192,5 +197,4 @@ AUTHENTICATION:
192197
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
193198
SECRET: L=0YWt]b2w[WF>#>:&E`
194199

195-
196-
LANGUAGE: "pt-BR" # pt-BR, en
200+
LANGUAGE: "pt-BR" # pt-BR, en

src/validate/validate.schema.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,25 @@ export const audioMessageSchema: JSONSchema7 = {
277277
required: ['audioMessage', 'number'],
278278
};
279279

280+
export const templateMessageSchema: JSONSchema7 = {
281+
$id: v4(),
282+
type: 'object',
283+
properties: {
284+
number: { ...numberDefinition },
285+
options: { ...optionsSchema },
286+
templateMessage: {
287+
type: 'object',
288+
properties: {
289+
name: { type: 'string' },
290+
language: { type: 'string' },
291+
},
292+
required: ['name', 'language'],
293+
...isNotEmpty('name', 'language'),
294+
},
295+
},
296+
required: ['templateMessage', 'number'],
297+
};
298+
280299
export const buttonMessageSchema: JSONSchema7 = {
281300
$id: v4(),
282301
type: 'object',

src/whatsapp/controllers/instance.controller.ts

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isURL } from 'class-validator';
33
import EventEmitter2 from 'eventemitter2';
44
import { v4 } from 'uuid';
55

6-
import { ConfigService, HttpServer } from '../../config/env.config';
6+
import { ConfigService, HttpServer, WaBusiness } from '../../config/env.config';
77
import { Logger } from '../../config/logger.config';
88
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
99
import { RedisCache } from '../../libs/redis.client';
@@ -12,15 +12,17 @@ import { RepositoryBroker } from '../repository/repository.manager';
1212
import { AuthService, OldToken } from '../services/auth.service';
1313
import { CacheService } from '../services/cache.service';
1414
import { ChatwootService } from '../services/chatwoot.service';
15+
import { IntegrationService } from '../services/integration.service';
1516
import { WAMonitoringService } from '../services/monitor.service';
1617
import { RabbitmqService } from '../services/rabbitmq.service';
1718
import { SettingsService } from '../services/settings.service';
1819
import { SqsService } from '../services/sqs.service';
1920
import { TypebotService } from '../services/typebot.service';
2021
import { WebhookService } from '../services/webhook.service';
2122
import { WebsocketService } from '../services/websocket.service';
22-
import { WAStartupService } from '../services/whatsapp.service';
23-
import { Events, wa } from '../types/wa.types';
23+
import { BaileysStartupService } from '../services/whatsapp.baileys.service';
24+
import { BusinessStartupService } from '../services/whatsapp.business.service';
25+
import { Events, Integration, wa } from '../types/wa.types';
2426

2527
export class InstanceController {
2628
constructor(
@@ -36,6 +38,7 @@ export class InstanceController {
3638
private readonly rabbitmqService: RabbitmqService,
3739
private readonly sqsService: SqsService,
3840
private readonly typebotService: TypebotService,
41+
private readonly integrationService: IntegrationService,
3942
private readonly cache: RedisCache,
4043
private readonly chatwootCache: CacheService,
4144
) {}
@@ -50,6 +53,7 @@ export class InstanceController {
5053
events,
5154
qrcode,
5255
number,
56+
integration,
5357
token,
5458
chatwoot_account_id,
5559
chatwoot_token,
@@ -87,14 +91,31 @@ export class InstanceController {
8791
this.logger.verbose('checking duplicate token');
8892
await this.authService.checkDuplicateToken(token);
8993

94+
if (!token && integration !== Integration.WHATSAPP_BUSINESS) {
95+
throw new BadRequestException('token is required');
96+
}
97+
9098
this.logger.verbose('creating instance');
91-
const instance = new WAStartupService(
92-
this.configService,
93-
this.eventEmitter,
94-
this.repository,
95-
this.cache,
96-
this.chatwootCache,
97-
);
99+
let instance: BaileysStartupService | BusinessStartupService;
100+
if (integration === Integration.WHATSAPP_BUSINESS) {
101+
instance = new BusinessStartupService(
102+
this.configService,
103+
this.eventEmitter,
104+
this.repository,
105+
this.cache,
106+
this.chatwootCache,
107+
);
108+
await this.waMonitor.saveInstance({ integration, instanceName, token, number });
109+
} else {
110+
instance = new BaileysStartupService(
111+
this.configService,
112+
this.eventEmitter,
113+
this.repository,
114+
this.cache,
115+
this.chatwootCache,
116+
);
117+
}
118+
98119
instance.instanceName = instanceName;
99120

100121
const instanceId = v4();
@@ -361,6 +382,23 @@ export class InstanceController {
361382

362383
this.settingsService.create(instance, settings);
363384

385+
let webhook_wa_business = null,
386+
access_token_wa_business = '';
387+
388+
if (integration === Integration.WHATSAPP_BUSINESS) {
389+
if (!number) {
390+
throw new BadRequestException('number is required');
391+
}
392+
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
393+
webhook_wa_business = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
394+
access_token_wa_business = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
395+
}
396+
397+
this.integrationService.create(instance, {
398+
integration,
399+
number,
400+
token,
401+
});
364402
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
365403
let getQrcode: wa.QrCode;
366404

@@ -375,6 +413,9 @@ export class InstanceController {
375413
instance: {
376414
instanceName: instance.instanceName,
377415
instanceId: instanceId,
416+
integration: integration,
417+
webhook_wa_business,
418+
access_token_wa_business,
378419
status: 'created',
379420
},
380421
hash,
@@ -470,6 +511,9 @@ export class InstanceController {
470511
instance: {
471512
instanceName: instance.instanceName,
472513
instanceId: instanceId,
514+
integration: integration,
515+
webhook_wa_business,
516+
access_token_wa_business,
473517
status: 'created',
474518
},
475519
hash,

src/whatsapp/controllers/sendMessage.controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
SendReactionDto,
1515
SendStatusDto,
1616
SendStickerDto,
17+
SendTemplateDto,
1718
SendTextDto,
1819
} from '../dto/sendMessage.dto';
1920
import { WAMonitoringService } from '../services/monitor.service';
@@ -28,6 +29,11 @@ export class SendMessageController {
2829
return await this.waMonitor.waInstances[instanceName].textMessage(data);
2930
}
3031

32+
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
33+
logger.verbose('requested sendList from ' + instanceName + ' instance');
34+
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
35+
}
36+
3137
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
3238
logger.verbose('requested sendMedia from ' + instanceName + ' instance');
3339

src/whatsapp/dto/chat.dto.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ export class NumberBusiness {
3131
message?: string;
3232
description?: string;
3333
email?: string;
34+
websites?: string[];
3435
website?: string[];
3536
address?: string;
37+
about?: string;
38+
vertical?: string;
39+
profilehandle?: string;
3640
}
3741

3842
export class ProfileNameDto {

src/whatsapp/dto/instance.dto.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export class InstanceDto {
33
instanceId?: string;
44
qrcode?: boolean;
55
number?: string;
6+
integration?: string;
67
token?: string;
78
webhook?: string;
89
webhook_by_events?: boolean;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class IntegrationDto {
2+
integration: string;
3+
number: string;
4+
token: string;
5+
}

0 commit comments

Comments
 (0)