mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-10 18:39:38 -06:00
refactor: remove Evolution Channel provider integration
Remove the Evolution Channel provider from WhatsApp integrations as it is no longer needed. Only Baileys and Meta Business API providers are now supported. Changes: - Deleted Evolution Channel service, controller, and router files - Updated ChannelController factory to remove Evolution provider case - Removed Integration.EVOLUTION from type definitions - Updated channel router to remove Evolution routes - Cleaned up imports and exports in server.module.ts - Updated documentation (CLAUDE.md, AGENTS.md, integration-channel-rules.mdc) Note: EvolutionBot chatbot integration remains unchanged (different component)
This commit is contained in:
parent
3454bec79f
commit
f7f8bf1182
@ -49,10 +49,6 @@ export class ChannelController {
|
||||
return new BusinessStartupService(/* dependencies */);
|
||||
}
|
||||
|
||||
if (instanceData.integration === Integration.EVOLUTION) {
|
||||
return new EvolutionStartupService(/* dependencies */);
|
||||
}
|
||||
|
||||
if (instanceData.integration === Integration.WHATSAPP_BAILEYS) {
|
||||
return new BaileysStartupService(/* dependencies */);
|
||||
}
|
||||
@ -62,43 +58,6 @@ export class ChannelController {
|
||||
}
|
||||
```
|
||||
|
||||
### Extended Channel Controller
|
||||
```typescript
|
||||
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
||||
private readonly logger = new Logger('EvolutionController');
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
const numberId = data.numberId;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Channel Service Pattern
|
||||
|
||||
### Base Channel Service
|
||||
@ -154,64 +113,6 @@ export class ChannelStartupService {
|
||||
}
|
||||
```
|
||||
|
||||
### Extended Channel Service
|
||||
```typescript
|
||||
export class EvolutionStartupService extends ChannelStartupService {
|
||||
constructor(
|
||||
configService: ConfigService,
|
||||
eventEmitter: EventEmitter2,
|
||||
prismaRepository: PrismaRepository,
|
||||
cache: CacheService,
|
||||
chatwootCache: CacheService,
|
||||
) {
|
||||
super(configService, eventEmitter, prismaRepository, cache, chatwootCache);
|
||||
}
|
||||
|
||||
public async sendMessage(data: SendTextDto): Promise<any> {
|
||||
// Evolution-specific message sending logic
|
||||
const response = await this.evolutionApiCall('/send-message', data);
|
||||
return response;
|
||||
}
|
||||
|
||||
public async connectToWhatsapp(data: any): Promise<void> {
|
||||
// Evolution-specific connection logic
|
||||
this.logger.log('Connecting to Evolution API');
|
||||
|
||||
// Set up webhook listeners
|
||||
this.setupWebhookHandlers();
|
||||
|
||||
// Initialize connection
|
||||
await this.initializeConnection(data);
|
||||
}
|
||||
|
||||
private async evolutionApiCall(endpoint: string, data: any): Promise<any> {
|
||||
const config = this.configService.get<Evolution>('EVOLUTION');
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${config.API_URL}${endpoint}`, data, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.instance.token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error(`Evolution API call failed: ${error.message}`);
|
||||
throw new InternalServerErrorException('Evolution API call failed');
|
||||
}
|
||||
}
|
||||
|
||||
private setupWebhookHandlers(): void {
|
||||
// Set up webhook event handlers
|
||||
}
|
||||
|
||||
private async initializeConnection(data: any): Promise<void> {
|
||||
// Initialize connection with Evolution API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Business API Service Pattern
|
||||
|
||||
### Meta Business Service
|
||||
@ -431,29 +332,12 @@ export class ChannelRouter {
|
||||
constructor(configService: any, ...guards: any[]) {
|
||||
this.router = Router();
|
||||
|
||||
this.router.use('/', new EvolutionRouter(configService).router);
|
||||
this.router.use('/', new MetaRouter(configService).router);
|
||||
this.router.use('/baileys', new BaileysRouter(...guards).router);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Specific Channel Router
|
||||
```typescript
|
||||
export class EvolutionRouter extends RouterBroker {
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('webhook'), async (req, res) => {
|
||||
const response = await evolutionController.receiveWebhook(req.body);
|
||||
return res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Types
|
||||
|
||||
### Channel Data Types
|
||||
@ -471,7 +355,6 @@ type ChannelDataType = {
|
||||
export enum Integration {
|
||||
WHATSAPP_BUSINESS = 'WHATSAPP-BUSINESS',
|
||||
WHATSAPP_BAILEYS = 'WHATSAPP-BAILEYS',
|
||||
EVOLUTION = 'EVOLUTION',
|
||||
}
|
||||
```
|
||||
|
||||
@ -502,51 +385,4 @@ public async sendMessage(data: SendTextDto): Promise<any> {
|
||||
|
||||
## Channel Testing Pattern
|
||||
|
||||
### Channel Service Testing
|
||||
```typescript
|
||||
describe('EvolutionStartupService', () => {
|
||||
let service: EvolutionStartupService;
|
||||
let configService: jest.Mocked<ConfigService>;
|
||||
let eventEmitter: jest.Mocked<EventEmitter2>;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockConfig = {
|
||||
get: jest.fn().mockReturnValue({
|
||||
API_URL: 'https://api.evolution.com',
|
||||
}),
|
||||
};
|
||||
|
||||
service = new EvolutionStartupService(
|
||||
mockConfig as any,
|
||||
eventEmitter,
|
||||
prismaRepository,
|
||||
cache,
|
||||
chatwootCache,
|
||||
);
|
||||
});
|
||||
|
||||
describe('sendMessage', () => {
|
||||
it('should send message successfully', async () => {
|
||||
const data = { number: '5511999999999', text: 'Test message' };
|
||||
|
||||
// Mock axios response
|
||||
jest.spyOn(axios, 'post').mockResolvedValue({
|
||||
data: { success: true, messageId: '123' },
|
||||
});
|
||||
|
||||
const result = await service.sendMessage(data);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(axios.post).toHaveBeenCalledWith(
|
||||
expect.stringContaining('/send-message'),
|
||||
data,
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
'Authorization': expect.stringContaining('Bearer'),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
Tests for channel services should mock dependencies and verify behavior for message sending, connection handling, and error cases.
|
||||
@ -14,7 +14,7 @@ This document provides comprehensive guidelines for AI agents (Claude, GPT, Curs
|
||||
- `api/services/` – Business logic (core functionality)
|
||||
- `api/routes/` – Express route definitions (RouterBroker pattern)
|
||||
- `api/integrations/` – External service integrations
|
||||
- `channel/` – WhatsApp providers (Baileys, Business API, Evolution)
|
||||
- `channel/` – WhatsApp providers (Baileys, Business API)
|
||||
- `chatbot/` – AI/Bot integrations (OpenAI, Dify, Typebot, Chatwoot)
|
||||
- `event/` – Event systems (WebSocket, RabbitMQ, SQS, NATS, Pusher)
|
||||
- `storage/` – File storage (S3, MinIO)
|
||||
@ -229,8 +229,7 @@ const result = await this.prismaRepository.instance.findUnique({
|
||||
|
||||
### Channel Integration (WhatsApp Providers)
|
||||
- **Baileys**: WhatsApp Web with QR code authentication
|
||||
- **Business API**: Official Meta WhatsApp Business API
|
||||
- **Evolution API**: Custom WhatsApp integration
|
||||
- **Business API**: Official Meta WhatsApp Business API
|
||||
- **Pattern**: Extend base channel service classes
|
||||
|
||||
### Chatbot Integration
|
||||
|
||||
@ -7,7 +7,6 @@ This file provides comprehensive guidance to Claude AI when working with the Evo
|
||||
**Evolution API** is a powerful, production-ready REST API for WhatsApp communication that supports multiple WhatsApp providers:
|
||||
- **Baileys** (WhatsApp Web) - Open-source WhatsApp Web client
|
||||
- **Meta Business API** - Official WhatsApp Business API
|
||||
- **Evolution API** - Custom WhatsApp integration
|
||||
|
||||
Built with **Node.js 20+**, **TypeScript 5+**, and **Express.js**, it provides extensive integrations with chatbots, CRM systems, and messaging platforms in a **multi-tenant architecture**.
|
||||
|
||||
@ -67,7 +66,7 @@ npm test # Run tests with watch mode
|
||||
### Core Structure
|
||||
- **Multi-tenant SaaS**: Complete instance isolation with per-tenant authentication
|
||||
- **Multi-provider database**: PostgreSQL and MySQL via Prisma ORM with provider-specific schemas and migrations
|
||||
- **WhatsApp integrations**: Baileys, Meta Business API, and Evolution API with unified interface
|
||||
- **WhatsApp integrations**: Baileys and Meta Business API with unified interface
|
||||
- **Event-driven architecture**: EventEmitter2 for internal events + WebSocket, RabbitMQ, SQS, NATS, Pusher for external events
|
||||
- **Microservices pattern**: Modular integrations for chatbots, storage, and external services
|
||||
|
||||
@ -81,7 +80,7 @@ src/
|
||||
│ ├── dto/ # Data Transfer Objects (simple classes)
|
||||
│ ├── guards/ # Authentication/authorization middleware
|
||||
│ ├── integrations/ # External service integrations
|
||||
│ │ ├── channel/ # WhatsApp providers (Baileys, Business API, Evolution)
|
||||
│ │ ├── channel/ # WhatsApp providers (Baileys, Business API)
|
||||
│ │ ├── chatbot/ # AI/Bot integrations (OpenAI, Dify, Typebot, Chatwoot)
|
||||
│ │ ├── event/ # Event systems (WebSocket, RabbitMQ, SQS, NATS, Pusher)
|
||||
│ │ └── storage/ # File storage (S3, MinIO)
|
||||
@ -99,7 +98,6 @@ src/
|
||||
**Channel Integrations** (`src/api/integrations/channel/`):
|
||||
- **Baileys**: WhatsApp Web client with QR code authentication
|
||||
- **Business API**: Official Meta WhatsApp Business API
|
||||
- **Evolution API**: Custom WhatsApp integration
|
||||
- Connection lifecycle management per instance with automatic reconnection
|
||||
|
||||
**Chatbot Integrations** (`src/api/integrations/chatbot/`):
|
||||
|
||||
@ -8,7 +8,6 @@ import { ConfigService } from '@config/env.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
|
||||
import { EvolutionStartupService } from './evolution/evolution.channel.service';
|
||||
import { BusinessStartupService } from './meta/whatsapp.business.service';
|
||||
import { BaileysStartupService } from './whatsapp/whatsapp.baileys.service';
|
||||
|
||||
@ -68,16 +67,6 @@ export class ChannelController {
|
||||
);
|
||||
}
|
||||
|
||||
if (instanceData.integration === Integration.EVOLUTION) {
|
||||
return new EvolutionStartupService(
|
||||
data.configService,
|
||||
data.eventEmitter,
|
||||
data.prismaRepository,
|
||||
data.cache,
|
||||
data.chatwootCache,
|
||||
);
|
||||
}
|
||||
|
||||
if (instanceData.integration === Integration.WHATSAPP_BAILEYS) {
|
||||
return new BaileysStartupService(
|
||||
data.configService,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
|
||||
import { EvolutionRouter } from './evolution/evolution.router';
|
||||
import { MetaRouter } from './meta/meta.router';
|
||||
import { BaileysRouter } from './whatsapp/baileys.router';
|
||||
|
||||
@ -10,7 +9,6 @@ export class ChannelRouter {
|
||||
constructor(configService: any, ...guards: any[]) {
|
||||
this.router = Router();
|
||||
|
||||
this.router.use('/', new EvolutionRouter(configService).router);
|
||||
this.router.use('/', new MetaRouter(configService).router);
|
||||
this.router.use('/baileys', new BaileysRouter(...guards).router);
|
||||
}
|
||||
|
||||
@ -1,888 +0,0 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import {
|
||||
MediaMessage,
|
||||
Options,
|
||||
SendAudioDto,
|
||||
SendButtonsDto,
|
||||
SendMediaDto,
|
||||
SendTextDto,
|
||||
} from '@api/dto/sendMessage.dto';
|
||||
import * as s3Service from '@api/integrations/storage/s3/libs/minio.server';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { chatbotController } from '@api/server.module';
|
||||
import { CacheService } from '@api/services/cache.service';
|
||||
import { ChannelStartupService } from '@api/services/channel.service';
|
||||
import { Events, wa } from '@api/types/wa.types';
|
||||
import { AudioConverter, Chatwoot, ConfigService, Openai, S3 } from '@config/env.config';
|
||||
import { BadRequestException, InternalServerErrorException } from '@exceptions';
|
||||
import { createJid } from '@utils/createJid';
|
||||
import { sendTelemetry } from '@utils/sendTelemetry';
|
||||
import axios from 'axios';
|
||||
import { isBase64, isURL } from 'class-validator';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import FormData from 'form-data';
|
||||
import mimeTypes from 'mime-types';
|
||||
import { join } from 'path';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export class EvolutionStartupService extends ChannelStartupService {
|
||||
constructor(
|
||||
public readonly configService: ConfigService,
|
||||
public readonly eventEmitter: EventEmitter2,
|
||||
public readonly prismaRepository: PrismaRepository,
|
||||
public readonly cache: CacheService,
|
||||
public readonly chatwootCache: CacheService,
|
||||
) {
|
||||
super(configService, eventEmitter, prismaRepository, chatwootCache);
|
||||
|
||||
this.client = null;
|
||||
}
|
||||
|
||||
public client: any;
|
||||
|
||||
public stateConnection: wa.StateConnection = { state: 'open' };
|
||||
|
||||
public phoneNumber: string;
|
||||
public mobile: boolean;
|
||||
|
||||
public get connectionStatus() {
|
||||
return this.stateConnection;
|
||||
}
|
||||
|
||||
public async closeClient() {
|
||||
this.stateConnection = { state: 'close' };
|
||||
}
|
||||
|
||||
public get qrCode(): wa.QrCode {
|
||||
return {
|
||||
pairingCode: this.instance.qrcode?.pairingCode,
|
||||
code: this.instance.qrcode?.code,
|
||||
base64: this.instance.qrcode?.base64,
|
||||
count: this.instance.qrcode?.count,
|
||||
};
|
||||
}
|
||||
|
||||
public async logoutInstance() {
|
||||
await this.closeClient();
|
||||
}
|
||||
|
||||
public setInstance(instance: InstanceDto) {
|
||||
this.logger.setInstance(instance.instanceId);
|
||||
|
||||
this.instance.name = instance.instanceName;
|
||||
this.instance.id = instance.instanceId;
|
||||
this.instance.integration = instance.integration;
|
||||
this.instance.number = instance.number;
|
||||
this.instance.token = instance.token;
|
||||
this.instance.businessId = instance.businessId;
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.STATUS_INSTANCE,
|
||||
{
|
||||
instanceName: this.instance.name,
|
||||
instanceId: this.instance.id,
|
||||
integration: instance.integration,
|
||||
},
|
||||
{
|
||||
instance: this.instance.name,
|
||||
status: 'created',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async profilePicture(number: string) {
|
||||
const jid = createJid(number);
|
||||
|
||||
return {
|
||||
wuid: jid,
|
||||
profilePictureUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
public async getProfileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async profilePictureUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async getProfileStatus() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async connectToWhatsapp(data?: any): Promise<any> {
|
||||
if (!data) {
|
||||
this.loadChatwoot();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.eventHandler(data);
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
throw new InternalServerErrorException(error?.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected async eventHandler(received: any) {
|
||||
try {
|
||||
let messageRaw: any;
|
||||
|
||||
if (received.message) {
|
||||
const key = {
|
||||
id: received.key.id || v4(),
|
||||
remoteJid: received.key.remoteJid,
|
||||
fromMe: received.key.fromMe,
|
||||
profilePicUrl: received.profilePicUrl,
|
||||
};
|
||||
messageRaw = {
|
||||
key,
|
||||
pushName: received.pushName,
|
||||
message: received.message,
|
||||
messageType: received.messageType,
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
|
||||
const isAudio = received?.message?.audioMessage;
|
||||
|
||||
if (this.configService.get<Openai>('OPENAI').ENABLED && isAudio) {
|
||||
const openAiDefaultSettings = await this.prismaRepository.openaiSetting.findFirst({
|
||||
where: {
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
include: {
|
||||
OpenaiCreds: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
openAiDefaultSettings &&
|
||||
openAiDefaultSettings.openaiCredsId &&
|
||||
openAiDefaultSettings.speechToText &&
|
||||
received?.message?.audioMessage
|
||||
) {
|
||||
messageRaw.message.speechToText = `[audio] ${await this.openaiService.speechToText(received, this)}`;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(messageRaw);
|
||||
|
||||
sendTelemetry(`received.message.${messageRaw.messageType ?? 'unknown'}`);
|
||||
|
||||
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
|
||||
|
||||
await chatbotController.emit({
|
||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
remoteJid: messageRaw.key.remoteJid,
|
||||
msg: messageRaw,
|
||||
pushName: messageRaw.pushName,
|
||||
});
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
|
||||
Events.MESSAGES_UPSERT,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
messageRaw,
|
||||
);
|
||||
|
||||
if (chatwootSentMessage?.id) {
|
||||
messageRaw.chatwootMessageId = chatwootSentMessage.id;
|
||||
messageRaw.chatwootInboxId = chatwootSentMessage.id;
|
||||
messageRaw.chatwootConversationId = chatwootSentMessage.id;
|
||||
}
|
||||
}
|
||||
|
||||
await this.prismaRepository.message.create({
|
||||
data: messageRaw,
|
||||
});
|
||||
|
||||
await this.updateContact({
|
||||
remoteJid: messageRaw.key.remoteJid,
|
||||
pushName: messageRaw.pushName,
|
||||
profilePicUrl: received.profilePicUrl,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateContact(data: { remoteJid: string; pushName?: string; profilePicUrl?: string }) {
|
||||
const contactRaw: any = {
|
||||
remoteJid: data.remoteJid,
|
||||
pushName: data?.pushName,
|
||||
instanceId: this.instanceId,
|
||||
profilePicUrl: data?.profilePicUrl,
|
||||
};
|
||||
|
||||
const existingContact = await this.prismaRepository.contact.findFirst({
|
||||
where: {
|
||||
remoteJid: data.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingContact) {
|
||||
await this.prismaRepository.contact.updateMany({
|
||||
where: {
|
||||
remoteJid: data.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
},
|
||||
data: contactRaw,
|
||||
});
|
||||
} else {
|
||||
await this.prismaRepository.contact.create({
|
||||
data: contactRaw,
|
||||
});
|
||||
}
|
||||
|
||||
this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||
await this.chatwootService.eventWhatsapp(
|
||||
Events.CONTACTS_UPDATE,
|
||||
{
|
||||
instanceName: this.instance.name,
|
||||
instanceId: this.instanceId,
|
||||
integration: this.instance.integration,
|
||||
},
|
||||
contactRaw,
|
||||
);
|
||||
}
|
||||
|
||||
const chat = await this.prismaRepository.chat.findFirst({
|
||||
where: { instanceId: this.instanceId, remoteJid: data.remoteJid },
|
||||
});
|
||||
|
||||
if (chat) {
|
||||
const chatRaw: any = {
|
||||
remoteJid: data.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
|
||||
this.sendDataWebhook(Events.CHATS_UPDATE, chatRaw);
|
||||
|
||||
await this.prismaRepository.chat.updateMany({
|
||||
where: { remoteJid: chat.remoteJid },
|
||||
data: chatRaw,
|
||||
});
|
||||
}
|
||||
|
||||
const chatRaw: any = {
|
||||
remoteJid: data.remoteJid,
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
|
||||
this.sendDataWebhook(Events.CHATS_UPSERT, chatRaw);
|
||||
|
||||
await this.prismaRepository.chat.create({
|
||||
data: chatRaw,
|
||||
});
|
||||
}
|
||||
|
||||
protected async sendMessageWithTyping(
|
||||
number: string,
|
||||
message: any,
|
||||
options?: Options,
|
||||
file?: any,
|
||||
isIntegration = false,
|
||||
) {
|
||||
try {
|
||||
let quoted: any;
|
||||
let webhookUrl: any;
|
||||
|
||||
if (options?.quoted) {
|
||||
const m = options?.quoted;
|
||||
|
||||
const msg = m?.key;
|
||||
|
||||
if (!msg) {
|
||||
throw 'Message not found';
|
||||
}
|
||||
|
||||
quoted = msg;
|
||||
}
|
||||
|
||||
if (options.delay) {
|
||||
await new Promise((resolve) => setTimeout(resolve, options.delay));
|
||||
}
|
||||
|
||||
if (options?.webhookUrl) {
|
||||
webhookUrl = options.webhookUrl;
|
||||
}
|
||||
|
||||
let audioFile;
|
||||
|
||||
const messageId = v4();
|
||||
|
||||
let messageRaw: any;
|
||||
|
||||
if (message?.mediaType === 'image') {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
base64: isBase64(message.media) ? message.media : null,
|
||||
mediaUrl: isURL(message.media) ? message.media : null,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'imageMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
} else if (message?.mediaType === 'video') {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
base64: isBase64(message.media) ? message.media : null,
|
||||
mediaUrl: isURL(message.media) ? message.media : null,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'videoMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
} else if (message?.mediaType === 'audio') {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
base64: isBase64(message.media) ? message.media : null,
|
||||
mediaUrl: isURL(message.media) ? message.media : null,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'audioMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
|
||||
const buffer = Buffer.from(message.media, 'base64');
|
||||
audioFile = {
|
||||
buffer,
|
||||
mimetype: 'audio/mp4',
|
||||
originalname: `${messageId}.mp4`,
|
||||
};
|
||||
} else if (message?.mediaType === 'document') {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
base64: isBase64(message.media) ? message.media : null,
|
||||
mediaUrl: isURL(message.media) ? message.media : null,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'documentMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
} else if (message.buttonMessage) {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
...message.buttonMessage,
|
||||
buttons: message.buttonMessage.buttons,
|
||||
footer: message.buttonMessage.footer,
|
||||
body: message.buttonMessage.body,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'buttonMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
} else if (message.listMessage) {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
...message.listMessage,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'listMessage',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
} else {
|
||||
messageRaw = {
|
||||
key: { fromMe: true, id: messageId, remoteJid: number },
|
||||
message: {
|
||||
...message,
|
||||
quoted,
|
||||
},
|
||||
messageType: 'conversation',
|
||||
messageTimestamp: Math.round(new Date().getTime() / 1000),
|
||||
webhookUrl,
|
||||
source: 'unknown',
|
||||
instanceId: this.instanceId,
|
||||
};
|
||||
}
|
||||
|
||||
if (messageRaw.message.contextInfo) {
|
||||
messageRaw.contextInfo = {
|
||||
...messageRaw.message.contextInfo,
|
||||
};
|
||||
}
|
||||
|
||||
if (messageRaw.contextInfo?.stanzaId) {
|
||||
const key: any = {
|
||||
id: messageRaw.contextInfo.stanzaId,
|
||||
};
|
||||
|
||||
const findMessage = await this.prismaRepository.message.findFirst({
|
||||
where: {
|
||||
instanceId: this.instanceId,
|
||||
key,
|
||||
},
|
||||
});
|
||||
|
||||
if (findMessage) {
|
||||
messageRaw.contextInfo.quotedMessage = findMessage.message;
|
||||
}
|
||||
}
|
||||
|
||||
const { base64 } = messageRaw.message;
|
||||
delete messageRaw.message.base64;
|
||||
|
||||
if (base64 || file || audioFile) {
|
||||
if (this.configService.get<S3>('S3').ENABLE) {
|
||||
try {
|
||||
// Verificação adicional para garantir que há conteúdo de mídia real
|
||||
const hasRealMedia = this.hasValidMediaContent(messageRaw);
|
||||
|
||||
if (!hasRealMedia) {
|
||||
this.logger.warn('Message detected as media but contains no valid media content');
|
||||
} else {
|
||||
const fileBuffer = audioFile?.buffer || file?.buffer;
|
||||
const buffer = base64 ? Buffer.from(base64, 'base64') : fileBuffer;
|
||||
|
||||
let mediaType: string;
|
||||
let mimetype = audioFile?.mimetype || file.mimetype;
|
||||
|
||||
if (messageRaw.messageType === 'documentMessage') {
|
||||
mediaType = 'document';
|
||||
mimetype = !mimetype ? 'application/pdf' : mimetype;
|
||||
} else if (messageRaw.messageType === 'imageMessage') {
|
||||
mediaType = 'image';
|
||||
mimetype = !mimetype ? 'image/png' : mimetype;
|
||||
} else if (messageRaw.messageType === 'audioMessage') {
|
||||
mediaType = 'audio';
|
||||
mimetype = !mimetype ? 'audio/mp4' : mimetype;
|
||||
} else if (messageRaw.messageType === 'videoMessage') {
|
||||
mediaType = 'video';
|
||||
mimetype = !mimetype ? 'video/mp4' : mimetype;
|
||||
}
|
||||
|
||||
const fileName = `${messageRaw.key.id}.${mimetype.split('/')[1]}`;
|
||||
|
||||
const size = buffer.byteLength;
|
||||
|
||||
const fullName = join(`${this.instance.id}`, messageRaw.key.remoteJid, mediaType, fileName);
|
||||
|
||||
await s3Service.uploadFile(fullName, buffer, size, {
|
||||
'Content-Type': mimetype,
|
||||
});
|
||||
|
||||
const mediaUrl = await s3Service.getObjectUrl(fullName);
|
||||
|
||||
messageRaw.message.mediaUrl = mediaUrl;
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(['Error on upload file to minio', error?.message, error?.stack]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(messageRaw);
|
||||
|
||||
this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.SEND_MESSAGE,
|
||||
{ instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
messageRaw,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
|
||||
await chatbotController.emit({
|
||||
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
|
||||
remoteJid: messageRaw.key.remoteJid,
|
||||
msg: messageRaw,
|
||||
pushName: messageRaw.pushName,
|
||||
});
|
||||
|
||||
await this.prismaRepository.message.create({
|
||||
data: messageRaw,
|
||||
});
|
||||
|
||||
return messageRaw;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
throw new BadRequestException(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public async textMessage(data: SendTextDto, isIntegration = false) {
|
||||
const res = await this.sendMessageWithTyping(
|
||||
data.number,
|
||||
{
|
||||
conversation: data.text,
|
||||
},
|
||||
{
|
||||
delay: data?.delay,
|
||||
presence: 'composing',
|
||||
quoted: data?.quoted,
|
||||
linkPreview: data?.linkPreview,
|
||||
mentionsEveryOne: data?.mentionsEveryOne,
|
||||
mentioned: data?.mentioned,
|
||||
},
|
||||
null,
|
||||
isIntegration,
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
protected async prepareMediaMessage(mediaMessage: MediaMessage) {
|
||||
try {
|
||||
if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) {
|
||||
const regex = new RegExp(/.*\/(.+?)\./);
|
||||
const arrayMatch = regex.exec(mediaMessage.media);
|
||||
mediaMessage.fileName = arrayMatch[1];
|
||||
}
|
||||
|
||||
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
|
||||
mediaMessage.fileName = 'image.png';
|
||||
}
|
||||
|
||||
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
|
||||
mediaMessage.fileName = 'video.mp4';
|
||||
}
|
||||
|
||||
let mimetype: string | false;
|
||||
|
||||
const prepareMedia: any = {
|
||||
caption: mediaMessage?.caption,
|
||||
fileName: mediaMessage.fileName,
|
||||
mediaType: mediaMessage.mediatype,
|
||||
media: mediaMessage.media,
|
||||
gifPlayback: false,
|
||||
};
|
||||
|
||||
if (isURL(mediaMessage.media)) {
|
||||
mimetype = mimeTypes.lookup(mediaMessage.media);
|
||||
} else {
|
||||
mimetype = mimeTypes.lookup(mediaMessage.fileName);
|
||||
}
|
||||
|
||||
prepareMedia.mimetype = mimetype;
|
||||
|
||||
return prepareMedia;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
throw new InternalServerErrorException(error?.toString() || error);
|
||||
}
|
||||
}
|
||||
|
||||
public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) {
|
||||
const mediaData: SendMediaDto = { ...data };
|
||||
|
||||
if (file) mediaData.media = file.buffer.toString('base64');
|
||||
|
||||
const message = await this.prepareMediaMessage(mediaData);
|
||||
|
||||
const mediaSent = await this.sendMessageWithTyping(
|
||||
data.number,
|
||||
{ ...message },
|
||||
{
|
||||
delay: data?.delay,
|
||||
presence: 'composing',
|
||||
quoted: data?.quoted,
|
||||
linkPreview: data?.linkPreview,
|
||||
mentionsEveryOne: data?.mentionsEveryOne,
|
||||
mentioned: data?.mentioned,
|
||||
},
|
||||
file,
|
||||
isIntegration,
|
||||
);
|
||||
|
||||
return mediaSent;
|
||||
}
|
||||
|
||||
public async processAudio(audio: string, number: string, file: any) {
|
||||
number = number.replace(/\D/g, '');
|
||||
const hash = `${number}-${new Date().getTime()}`;
|
||||
|
||||
const audioConverterConfig = this.configService.get<AudioConverter>('AUDIO_CONVERTER');
|
||||
if (audioConverterConfig.API_URL) {
|
||||
try {
|
||||
this.logger.verbose('Using audio converter API');
|
||||
const formData = new FormData();
|
||||
|
||||
if (file) {
|
||||
formData.append('file', file.buffer, {
|
||||
filename: file.originalname,
|
||||
contentType: file.mimetype,
|
||||
});
|
||||
} else if (isURL(audio)) {
|
||||
formData.append('url', audio);
|
||||
} else {
|
||||
formData.append('base64', audio);
|
||||
}
|
||||
|
||||
formData.append('format', 'mp4');
|
||||
|
||||
const response = await axios.post(audioConverterConfig.API_URL, formData, {
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
apikey: audioConverterConfig.API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response?.data?.audio) {
|
||||
throw new InternalServerErrorException('Failed to convert audio');
|
||||
}
|
||||
|
||||
const prepareMedia: any = {
|
||||
fileName: `${hash}.mp4`,
|
||||
mediaType: 'audio',
|
||||
media: response?.data?.audio,
|
||||
mimetype: 'audio/mpeg',
|
||||
};
|
||||
|
||||
return prepareMedia;
|
||||
} catch (error) {
|
||||
this.logger.error(error?.response?.data || error);
|
||||
throw new InternalServerErrorException(error?.response?.data?.message || error?.toString() || error);
|
||||
}
|
||||
} else {
|
||||
let mimetype: string;
|
||||
|
||||
const prepareMedia: any = {
|
||||
fileName: `${hash}.mp3`,
|
||||
mediaType: 'audio',
|
||||
media: audio,
|
||||
mimetype: 'audio/mpeg',
|
||||
};
|
||||
|
||||
if (isURL(audio)) {
|
||||
mimetype = mimeTypes.lookup(audio).toString();
|
||||
} else {
|
||||
mimetype = mimeTypes.lookup(prepareMedia.fileName).toString();
|
||||
}
|
||||
|
||||
prepareMedia.mimetype = mimetype;
|
||||
|
||||
return prepareMedia;
|
||||
}
|
||||
}
|
||||
|
||||
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
|
||||
const mediaData: SendAudioDto = { ...data };
|
||||
|
||||
if (file?.buffer) {
|
||||
mediaData.audio = file.buffer.toString('base64');
|
||||
} else {
|
||||
console.error('El archivo o buffer no est<73> definido correctamente.');
|
||||
throw new Error('File or buffer is undefined.');
|
||||
}
|
||||
|
||||
const message = await this.processAudio(mediaData.audio, data.number, file);
|
||||
|
||||
const audioSent = await this.sendMessageWithTyping(
|
||||
data.number,
|
||||
{ ...message },
|
||||
{
|
||||
delay: data?.delay,
|
||||
presence: 'composing',
|
||||
quoted: data?.quoted,
|
||||
linkPreview: data?.linkPreview,
|
||||
mentionsEveryOne: data?.mentionsEveryOne,
|
||||
mentioned: data?.mentioned,
|
||||
},
|
||||
file,
|
||||
isIntegration,
|
||||
);
|
||||
|
||||
return audioSent;
|
||||
}
|
||||
|
||||
public async buttonMessage(data: SendButtonsDto, isIntegration = false) {
|
||||
return await this.sendMessageWithTyping(
|
||||
data.number,
|
||||
{
|
||||
buttonMessage: {
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
footer: data.footer,
|
||||
buttons: data.buttons,
|
||||
},
|
||||
},
|
||||
{
|
||||
delay: data?.delay,
|
||||
presence: 'composing',
|
||||
quoted: data?.quoted,
|
||||
mentionsEveryOne: data?.mentionsEveryOne,
|
||||
mentioned: data?.mentioned,
|
||||
},
|
||||
null,
|
||||
isIntegration,
|
||||
);
|
||||
}
|
||||
public async locationMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async listMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async templateMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async contactMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async reactionMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async getBase64FromMediaMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async deleteMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async mediaSticker() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async pollMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async statusMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async reloadConnection() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async whatsappNumber() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async markMessageAsRead() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async archiveChat() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async markChatUnread() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fetchProfile() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async offerCall() {
|
||||
throw new BadRequestException('Method not available on WhatsApp Business API');
|
||||
}
|
||||
public async sendPresence() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async setPresence() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fetchPrivacySettings() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updatePrivacySettings() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fetchBusinessProfile() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateProfileName() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateProfileStatus() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateProfilePicture() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async removeProfilePicture() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async blockUser() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateMessage() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async createGroup() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateGroupPicture() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateGroupSubject() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateGroupDescription() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async findGroup() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fetchAllGroups() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async inviteCode() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async inviteInfo() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async sendInvite() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async acceptInviteCode() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async revokeInviteCode() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async findParticipants() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateGParticipant() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async updateGSetting() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async toggleEphemeral() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async leaveGroup() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fetchLabels() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async handleLabel() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async receiveMobileCode() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
public async fakeCall() {
|
||||
throw new BadRequestException('Method not available on Evolution Channel');
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
|
||||
import { ChannelController, ChannelControllerInterface } from '../channel.controller';
|
||||
|
||||
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
||||
private readonly logger = new Logger('EvolutionController');
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
const numberId = data.numberId;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { evolutionController } from '@api/server.module';
|
||||
import { ConfigService } from '@config/env.config';
|
||||
import { Router } from 'express';
|
||||
|
||||
export class EvolutionRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService) {
|
||||
super();
|
||||
this.router.post(this.routerPath('webhook/evolution', false), async (req, res) => {
|
||||
const { body } = req;
|
||||
const response = await evolutionController.receiveWebhook(body);
|
||||
|
||||
return res.status(200).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
||||
@ -14,7 +14,6 @@ import { SendMessageController } from './controllers/sendMessage.controller';
|
||||
import { SettingsController } from './controllers/settings.controller';
|
||||
import { TemplateController } from './controllers/template.controller';
|
||||
import { ChannelController } from './integrations/channel/channel.controller';
|
||||
import { EvolutionController } from './integrations/channel/evolution/evolution.controller';
|
||||
import { MetaController } from './integrations/channel/meta/meta.controller';
|
||||
import { BaileysController } from './integrations/channel/whatsapp/baileys.controller';
|
||||
import { ChatbotController } from './integrations/chatbot/chatbot.controller';
|
||||
@ -112,7 +111,6 @@ export const chatbotController = new ChatbotController(prismaRepository, waMonit
|
||||
export const channelController = new ChannelController(prismaRepository, waMonitor);
|
||||
|
||||
// channels
|
||||
export const evolutionController = new EvolutionController(prismaRepository, waMonitor);
|
||||
export const metaController = new MetaController(prismaRepository, waMonitor);
|
||||
export const baileysController = new BaileysController(waMonitor);
|
||||
|
||||
|
||||
@ -152,5 +152,4 @@ export const MessageSubtype = [
|
||||
export const Integration = {
|
||||
WHATSAPP_BUSINESS: 'WHATSAPP-BUSINESS',
|
||||
WHATSAPP_BAILEYS: 'WHATSAPP-BAILEYS',
|
||||
EVOLUTION: 'EVOLUTION',
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user