mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-16 04:02:54 -06:00
Merge branch 'EvolutionAPI:main' into main
This commit is contained in:
commit
001849eeaa
@ -1,4 +1,4 @@
|
|||||||
# 1.5.0 (homolog)
|
# 1.5.0 (2023-08-18 12:47)
|
||||||
|
|
||||||
### Feature
|
### Feature
|
||||||
|
|
||||||
@ -10,6 +10,7 @@
|
|||||||
* Added rabbitmq to send events
|
* Added rabbitmq to send events
|
||||||
* Added Typebot integration
|
* Added Typebot integration
|
||||||
* Added proxy endpoint
|
* Added proxy endpoint
|
||||||
|
* Added send and date_time in webhook data
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -19,6 +20,7 @@
|
|||||||
* Update Dockerfile
|
* Update Dockerfile
|
||||||
* If you pass empty events in create instance and set webhook it is understood as all
|
* If you pass empty events in create instance and set webhook it is understood as all
|
||||||
* Fixed issue that did not output base64 averages
|
* Fixed issue that did not output base64 averages
|
||||||
|
* Messages sent by the api now arrive in chatwoot
|
||||||
|
|
||||||
### Integrations
|
### Integrations
|
||||||
|
|
||||||
|
@ -24,7 +24,11 @@ export const initAMQP = () => {
|
|||||||
|
|
||||||
const exchangeName = 'evolution_exchange';
|
const exchangeName = 'evolution_exchange';
|
||||||
|
|
||||||
channel.assertExchange(exchangeName, 'topic', { durable: false });
|
channel.assertExchange(exchangeName, 'topic', {
|
||||||
|
durable: true,
|
||||||
|
autoDelete: false,
|
||||||
|
});
|
||||||
|
|
||||||
amqpChannel = channel;
|
amqpChannel = channel;
|
||||||
|
|
||||||
logger.info('AMQP initialized');
|
logger.info('AMQP initialized');
|
||||||
|
@ -27,7 +27,9 @@ function bootstrap() {
|
|||||||
cors({
|
cors({
|
||||||
origin(requestOrigin, callback) {
|
origin(requestOrigin, callback) {
|
||||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||||
!requestOrigin ? (requestOrigin = '*') : undefined;
|
if (ORIGIN.includes('*')) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
}
|
}
|
||||||
|
@ -1001,6 +1001,18 @@ export const typebotStatusSchema: JSONSchema7 = {
|
|||||||
...isNotEmpty('remoteJid', 'status'),
|
...isNotEmpty('remoteJid', 'status'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const typebotStartSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
remoteJid: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
typebot: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['remoteJid', 'url', 'typebot'],
|
||||||
|
...isNotEmpty('remoteJid', 'url', 'typebot'),
|
||||||
|
};
|
||||||
|
|
||||||
export const proxySchema: JSONSchema7 = {
|
export const proxySchema: JSONSchema7 = {
|
||||||
$id: v4(),
|
$id: v4(),
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -38,4 +38,9 @@ export class TypebotController {
|
|||||||
logger.verbose('requested changeStatus from ' + instance.instanceName + ' instance');
|
logger.verbose('requested changeStatus from ' + instance.instanceName + ' instance');
|
||||||
return this.typebotService.changeStatus(instance, data);
|
return this.typebotService.changeStatus(instance, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async startTypebot(instance: InstanceDto, data: any) {
|
||||||
|
logger.verbose('requested startTypebot from ' + instance.instanceName + ' instance');
|
||||||
|
return this.typebotService.startTypebot(instance, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { RequestHandler, Router } from 'express';
|
import { RequestHandler, Router } from 'express';
|
||||||
|
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
import { instanceNameSchema, typebotSchema, typebotStatusSchema } from '../../validate/validate.schema';
|
import {
|
||||||
|
instanceNameSchema,
|
||||||
|
typebotSchema,
|
||||||
|
typebotStartSchema,
|
||||||
|
typebotStatusSchema,
|
||||||
|
} from '../../validate/validate.schema';
|
||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { TypebotDto } from '../dto/typebot.dto';
|
import { TypebotDto } from '../dto/typebot.dto';
|
||||||
@ -47,7 +52,7 @@ export class TypebotRouter extends RouterBroker {
|
|||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.post(this.routerPath('changeStatus'), ...guards, async (req, res) => {
|
.post(this.routerPath('changeStatus'), ...guards, async (req, res) => {
|
||||||
logger.verbose('request received in findTypebot');
|
logger.verbose('request received in changeStatusTypebot');
|
||||||
logger.verbose('request body: ');
|
logger.verbose('request body: ');
|
||||||
logger.verbose(req.body);
|
logger.verbose(req.body);
|
||||||
|
|
||||||
@ -60,6 +65,22 @@ export class TypebotRouter extends RouterBroker {
|
|||||||
execute: (instance, data) => typebotController.changeStatus(instance, data),
|
execute: (instance, data) => typebotController.changeStatus(instance, data),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.post(this.routerPath('start'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in startTypebot');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
const response = await this.dataValidate<InstanceDto>({
|
||||||
|
request: req,
|
||||||
|
schema: typebotStartSchema,
|
||||||
|
ClassRef: InstanceDto,
|
||||||
|
execute: (instance, data) => typebotController.startTypebot(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
res.status(HttpStatus.OK).json(response);
|
res.status(HttpStatus.OK).json(response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,48 @@ export class TypebotService {
|
|||||||
return { typebot: { ...instance, typebot: typebotData } };
|
return { typebot: { ...instance, typebot: typebotData } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async startTypebot(instance: InstanceDto, data: any) {
|
||||||
|
const remoteJid = data.remoteJid;
|
||||||
|
const url = data.url;
|
||||||
|
const typebot = data.typebot;
|
||||||
|
|
||||||
|
const id = Math.floor(Math.random() * 10000000000).toString();
|
||||||
|
|
||||||
|
const reqData = {
|
||||||
|
sessionId: id,
|
||||||
|
startParams: {
|
||||||
|
typebot: data.typebot,
|
||||||
|
prefilledVariables: {
|
||||||
|
remoteJid: data.remoteJid,
|
||||||
|
pushName: data.pushName,
|
||||||
|
instanceName: instance.instanceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log(reqData);
|
||||||
|
|
||||||
|
const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
|
||||||
|
|
||||||
|
await this.sendWAMessage(
|
||||||
|
instance,
|
||||||
|
remoteJid,
|
||||||
|
request.data.messages,
|
||||||
|
request.data.input,
|
||||||
|
request.data.clientSideActions,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
typebot: {
|
||||||
|
...instance,
|
||||||
|
typebot: {
|
||||||
|
url: url,
|
||||||
|
remoteJid: remoteJid,
|
||||||
|
typebot: typebot,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private getTypeMessage(msg: any) {
|
private getTypeMessage(msg: any) {
|
||||||
this.logger.verbose('get type message');
|
this.logger.verbose('get type message');
|
||||||
|
|
||||||
@ -162,13 +204,34 @@ export class TypebotService {
|
|||||||
return request.data;
|
return request.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendWAMessage(instance: InstanceDto, remoteJid: string, messages: any[], input: any[]) {
|
public async sendWAMessage(
|
||||||
processMessages(this.waMonitor.waInstances[instance.instanceName], messages, input).catch((err) => {
|
instance: InstanceDto,
|
||||||
console.error('Erro ao processar mensagens:', err);
|
remoteJid: string,
|
||||||
});
|
messages: any[],
|
||||||
|
input: any[],
|
||||||
|
clientSideActions: any[],
|
||||||
|
) {
|
||||||
|
processMessages(this.waMonitor.waInstances[instance.instanceName], messages, input, clientSideActions).catch(
|
||||||
|
(err) => {
|
||||||
|
console.error('Erro ao processar mensagens:', err);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
async function processMessages(instance, messages, input) {
|
function findItemAndGetSecondsToWait(array, targetId) {
|
||||||
|
if (!array) return null;
|
||||||
|
|
||||||
|
for (const item of array) {
|
||||||
|
if (item.lastBubbleBlockId === targetId) {
|
||||||
|
return item.wait?.secondsToWaitFor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processMessages(instance, messages, input, clientSideActions) {
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
|
const wait = findItemAndGetSecondsToWait(clientSideActions, message.id);
|
||||||
|
|
||||||
if (message.type === 'text') {
|
if (message.type === 'text') {
|
||||||
let formattedText = '';
|
let formattedText = '';
|
||||||
|
|
||||||
@ -209,7 +272,7 @@ export class TypebotService {
|
|||||||
await instance.textMessage({
|
await instance.textMessage({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
options: {
|
options: {
|
||||||
delay: instance.localTypebot.delay_message || 1000,
|
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||||
presence: 'composing',
|
presence: 'composing',
|
||||||
linkPreview: linkPreview,
|
linkPreview: linkPreview,
|
||||||
},
|
},
|
||||||
@ -223,7 +286,7 @@ export class TypebotService {
|
|||||||
await instance.mediaMessage({
|
await instance.mediaMessage({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
options: {
|
options: {
|
||||||
delay: instance.localTypebot.delay_message || 1000,
|
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||||
presence: 'composing',
|
presence: 'composing',
|
||||||
},
|
},
|
||||||
mediaMessage: {
|
mediaMessage: {
|
||||||
@ -237,7 +300,7 @@ export class TypebotService {
|
|||||||
await instance.mediaMessage({
|
await instance.mediaMessage({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
options: {
|
options: {
|
||||||
delay: instance.localTypebot.delay_message || 1000,
|
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||||
presence: 'composing',
|
presence: 'composing',
|
||||||
},
|
},
|
||||||
mediaMessage: {
|
mediaMessage: {
|
||||||
@ -251,7 +314,7 @@ export class TypebotService {
|
|||||||
await instance.audioWhatsapp({
|
await instance.audioWhatsapp({
|
||||||
number: remoteJid.split('@')[0],
|
number: remoteJid.split('@')[0],
|
||||||
options: {
|
options: {
|
||||||
delay: instance.localTypebot.delay_message || 1000,
|
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||||
presence: 'recording',
|
presence: 'recording',
|
||||||
encoding: true,
|
encoding: true,
|
||||||
},
|
},
|
||||||
@ -323,7 +386,7 @@ export class TypebotService {
|
|||||||
pushName: msg.pushName,
|
pushName: msg.pushName,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -346,7 +409,7 @@ export class TypebotService {
|
|||||||
pushName: msg.pushName,
|
pushName: msg.pushName,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -414,7 +477,13 @@ export class TypebotService {
|
|||||||
|
|
||||||
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
|
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
|
||||||
|
|
||||||
await this.sendWAMessage(instance, remoteJid, request.data.messages, request.data.input);
|
await this.sendWAMessage(
|
||||||
|
instance,
|
||||||
|
remoteJid,
|
||||||
|
request.data.messages,
|
||||||
|
request.data.input,
|
||||||
|
request.data.clientSideActions,
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -588,6 +588,9 @@ export class WAStartupService {
|
|||||||
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
||||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||||
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
||||||
|
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
|
||||||
|
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
|
||||||
|
const now = localISOTime;
|
||||||
|
|
||||||
const expose = this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES;
|
const expose = this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES;
|
||||||
const tokenStore = await this.repository.auth.find(this.instanceName);
|
const tokenStore = await this.repository.auth.find(this.instanceName);
|
||||||
@ -598,13 +601,22 @@ export class WAStartupService {
|
|||||||
|
|
||||||
if (amqp) {
|
if (amqp) {
|
||||||
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
|
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
|
||||||
const exchangeName = 'evolution_exchange';
|
const exchangeName = this.instanceName ?? 'evolution_exchange';
|
||||||
|
|
||||||
amqp.assertExchange(exchangeName, 'topic', { durable: false });
|
amqp.assertExchange(exchangeName, 'topic', {
|
||||||
|
durable: true,
|
||||||
|
autoDelete: false,
|
||||||
|
});
|
||||||
|
|
||||||
const queueName = `${this.instanceName}.${event}`;
|
const queueName = `${this.instanceName}.${event}`;
|
||||||
|
|
||||||
amqp.assertQueue(queueName, { durable: false });
|
amqp.assertQueue(queueName, {
|
||||||
|
durable: true,
|
||||||
|
autoDelete: false,
|
||||||
|
arguments: {
|
||||||
|
'x-queue-type': 'quorum',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
amqp.bindQueue(queueName, exchangeName, event);
|
amqp.bindQueue(queueName, exchangeName, event);
|
||||||
|
|
||||||
@ -613,6 +625,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (expose && instanceApikey) {
|
if (expose && instanceApikey) {
|
||||||
@ -635,6 +649,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (expose && instanceApikey) {
|
if (expose && instanceApikey) {
|
||||||
@ -667,6 +683,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
destination: this.localWebhook.url,
|
destination: this.localWebhook.url,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
apikey: (expose && instanceApikey) || null,
|
apikey: (expose && instanceApikey) || null,
|
||||||
};
|
};
|
||||||
@ -686,6 +704,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
destination: this.localWebhook.url,
|
destination: this.localWebhook.url,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -735,6 +755,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
destination: localUrl,
|
destination: localUrl,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -753,6 +775,8 @@ export class WAStartupService {
|
|||||||
instance: this.instance.name,
|
instance: this.instance.name,
|
||||||
data,
|
data,
|
||||||
destination: localUrl,
|
destination: localUrl,
|
||||||
|
date_time: now,
|
||||||
|
sender: this.wuid,
|
||||||
server_url: serverUrl,
|
server_url: serverUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1976,13 +2000,9 @@ export class WAStartupService {
|
|||||||
this.logger.verbose('Sending data to webhook in event SEND_MESSAGE');
|
this.logger.verbose('Sending data to webhook in event SEND_MESSAGE');
|
||||||
await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||||
|
|
||||||
// if (this.localChatwoot.enabled) {
|
if (this.localChatwoot.enabled) {
|
||||||
// this.chatwootService.eventWhatsapp(
|
this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw);
|
||||||
// Events.SEND_MESSAGE,
|
}
|
||||||
// { instanceName: this.instance.name },
|
|
||||||
// messageRaw,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.logger.verbose('Inserting message in database');
|
this.logger.verbose('Inserting message in database');
|
||||||
await this.repository.message.insert(
|
await this.repository.message.insert(
|
||||||
|
Loading…
Reference in New Issue
Block a user