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
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
* Added rabbitmq to send events
|
||||
* Added Typebot integration
|
||||
* Added proxy endpoint
|
||||
* Added send and date_time in webhook data
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -19,6 +20,7 @@
|
||||
* Update Dockerfile
|
||||
* If you pass empty events in create instance and set webhook it is understood as all
|
||||
* Fixed issue that did not output base64 averages
|
||||
* Messages sent by the api now arrive in chatwoot
|
||||
|
||||
### Integrations
|
||||
|
||||
|
@ -24,7 +24,11 @@ export const initAMQP = () => {
|
||||
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
channel.assertExchange(exchangeName, 'topic', { durable: false });
|
||||
channel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
amqpChannel = channel;
|
||||
|
||||
logger.info('AMQP initialized');
|
||||
|
@ -27,7 +27,9 @@ function bootstrap() {
|
||||
cors({
|
||||
origin(requestOrigin, callback) {
|
||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||
!requestOrigin ? (requestOrigin = '*') : undefined;
|
||||
if (ORIGIN.includes('*')) {
|
||||
return callback(null, true);
|
||||
}
|
||||
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
@ -1001,6 +1001,18 @@ export const typebotStatusSchema: JSONSchema7 = {
|
||||
...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 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
|
@ -38,4 +38,9 @@ export class TypebotController {
|
||||
logger.verbose('requested changeStatus from ' + instance.instanceName + ' instance');
|
||||
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 { 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 { InstanceDto } from '../dto/instance.dto';
|
||||
import { TypebotDto } from '../dto/typebot.dto';
|
||||
@ -47,7 +52,7 @@ export class TypebotRouter extends RouterBroker {
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.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(req.body);
|
||||
|
||||
@ -60,6 +65,22 @@ export class TypebotRouter extends RouterBroker {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -84,6 +84,48 @@ export class TypebotService {
|
||||
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) {
|
||||
this.logger.verbose('get type message');
|
||||
|
||||
@ -162,13 +204,34 @@ export class TypebotService {
|
||||
return request.data;
|
||||
}
|
||||
|
||||
public async sendWAMessage(instance: InstanceDto, remoteJid: string, messages: any[], input: any[]) {
|
||||
processMessages(this.waMonitor.waInstances[instance.instanceName], messages, input).catch((err) => {
|
||||
console.error('Erro ao processar mensagens:', err);
|
||||
});
|
||||
public async sendWAMessage(
|
||||
instance: InstanceDto,
|
||||
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) {
|
||||
const wait = findItemAndGetSecondsToWait(clientSideActions, message.id);
|
||||
|
||||
if (message.type === 'text') {
|
||||
let formattedText = '';
|
||||
|
||||
@ -209,7 +272,7 @@ export class TypebotService {
|
||||
await instance.textMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
linkPreview: linkPreview,
|
||||
},
|
||||
@ -223,7 +286,7 @@ export class TypebotService {
|
||||
await instance.mediaMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
},
|
||||
mediaMessage: {
|
||||
@ -237,7 +300,7 @@ export class TypebotService {
|
||||
await instance.mediaMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
},
|
||||
mediaMessage: {
|
||||
@ -251,7 +314,7 @@ export class TypebotService {
|
||||
await instance.audioWhatsapp({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
delay: wait ? wait * 1000 : instance.localTypebot.delay_message || 1000,
|
||||
presence: 'recording',
|
||||
encoding: true,
|
||||
},
|
||||
@ -323,7 +386,7 @@ export class TypebotService {
|
||||
pushName: msg.pushName,
|
||||
});
|
||||
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -346,7 +409,7 @@ export class TypebotService {
|
||||
pushName: msg.pushName,
|
||||
});
|
||||
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -414,7 +477,13 @@ export class TypebotService {
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -588,6 +588,9 @@ export class WAStartupService {
|
||||
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
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 tokenStore = await this.repository.auth.find(this.instanceName);
|
||||
@ -598,13 +601,22 @@ export class WAStartupService {
|
||||
|
||||
if (amqp) {
|
||||
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}`;
|
||||
|
||||
amqp.assertQueue(queueName, { durable: false });
|
||||
amqp.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
|
||||
@ -613,6 +625,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
};
|
||||
|
||||
if (expose && instanceApikey) {
|
||||
@ -635,6 +649,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
};
|
||||
|
||||
if (expose && instanceApikey) {
|
||||
@ -667,6 +683,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
destination: this.localWebhook.url,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
server_url: serverUrl,
|
||||
apikey: (expose && instanceApikey) || null,
|
||||
};
|
||||
@ -686,6 +704,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
destination: this.localWebhook.url,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
@ -735,6 +755,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
destination: localUrl,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
@ -753,6 +775,8 @@ export class WAStartupService {
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
destination: localUrl,
|
||||
date_time: now,
|
||||
sender: this.wuid,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
@ -1976,13 +2000,9 @@ export class WAStartupService {
|
||||
this.logger.verbose('Sending data to webhook in event SEND_MESSAGE');
|
||||
await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);
|
||||
|
||||
// if (this.localChatwoot.enabled) {
|
||||
// this.chatwootService.eventWhatsapp(
|
||||
// Events.SEND_MESSAGE,
|
||||
// { instanceName: this.instance.name },
|
||||
// messageRaw,
|
||||
// );
|
||||
// }
|
||||
if (this.localChatwoot.enabled) {
|
||||
this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw);
|
||||
}
|
||||
|
||||
this.logger.verbose('Inserting message in database');
|
||||
await this.repository.message.insert(
|
||||
|
Loading…
Reference in New Issue
Block a user