Merge branch 'develop' into fix-typebot-error-handle

This commit is contained in:
Gabriel Pastori 2023-10-29 12:54:05 -03:00 committed by GitHub
commit 94633484ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1863 additions and 312 deletions

View File

@ -1,8 +1,24 @@
# 1.5.3 (develop) # 1.5.5 (develop)
### Fixed
* Adjusts in proxy
* Adjusts in start session for Typebot
* Added mimetype field when sending media
# 1.5.4 (2023-10-09 20:43)
### Fixed
* Baileys logger typing issue resolved
* Solved problem with duplicate messages in chatwoot
# 1.5.3 (2023-10-06 18:55)
### Feature ### Feature
* Swagger documentation * Swagger documentation
* Added base 64 sending option via webhook
### Fixed ### Fixed
@ -13,6 +29,7 @@
* Adjustment to start typebot, added startSession parameter * Adjustment to start typebot, added startSession parameter
* Chatwoot now receives messages sent via api and typebot * Chatwoot now receives messages sent via api and typebot
* Fixed problem with starting with an input in typebot * Fixed problem with starting with an input in typebot
* Added check to ensure variables are not empty before executing foreach in start typebot
# 1.5.2 (2023-09-28 17:56) # 1.5.2 (2023-09-28 17:56)

View File

@ -1,6 +1,6 @@
FROM node:20.7.0-alpine FROM node:20.7.0-alpine
LABEL version="1.5.3" description="Api to control whatsapp features through http requests." LABEL version="1.5.5" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@agenciadgcode.com" LABEL contact="contato@agenciadgcode.com"

View File

@ -1,6 +1,6 @@
{ {
"name": "evolution-api", "name": "evolution-api",
"version": "1.5.3", "version": "1.5.5",
"description": "Rest api for communication with WhatsApp", "description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js", "main": "./dist/src/main.js",
"scripts": { "scripts": {
@ -46,7 +46,7 @@
"@figuro/chatwoot-sdk": "^1.1.16", "@figuro/chatwoot-sdk": "^1.1.16",
"@hapi/boom": "^10.0.1", "@hapi/boom": "^10.0.1",
"@sentry/node": "^7.59.2", "@sentry/node": "^7.59.2",
"@whiskeysockets/baileys": "^6.5.0", "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys#fix-lids",
"amqplib": "^0.10.3", "amqplib": "^0.10.3",
"axios": "^1.3.5", "axios": "^1.3.5",
"class-validator": "^0.13.2", "class-validator": "^0.13.2",

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@ export class InstanceController {
instanceName, instanceName,
webhook, webhook,
webhook_by_events, webhook_by_events,
webhook_base64,
events, events,
qrcode, qrcode,
number, number,
@ -139,6 +140,7 @@ export class InstanceController {
url: webhook, url: webhook,
events: newEvents, events: newEvents,
webhook_by_events, webhook_by_events,
webhook_base64,
}); });
webhookEvents = (await this.webhookService.find(instance)).events; webhookEvents = (await this.webhookService.find(instance)).events;
@ -297,6 +299,7 @@ export class InstanceController {
webhook: { webhook: {
webhook, webhook,
webhook_by_events, webhook_by_events,
webhook_base64,
events: webhookEvents, events: webhookEvents,
}, },
websocket: { websocket: {
@ -390,6 +393,7 @@ export class InstanceController {
webhook: { webhook: {
webhook, webhook,
webhook_by_events, webhook_by_events,
webhook_base64,
events: webhookEvents, events: webhookEvents,
}, },
websocket: { websocket: {

View File

@ -1,15 +1,21 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { ConfigService } from '../../config/env.config'; import { Auth, ConfigService, HttpServer } from '../../config/env.config';
import { HttpStatus } from '../routers/index.router'; import { HttpStatus } from '../routers/index.router';
import { WAMonitoringService } from '../services/monitor.service'; import { WAMonitoringService } from '../services/monitor.service';
export class ViewsController { export class ViewsController {
constructor(private readonly waMonit: WAMonitoringService, private readonly configService: ConfigService) {} constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {}
public async manager(request: Request, response: Response) { public async manager(request: Request, response: Response) {
try { try {
return response.status(HttpStatus.OK).render('manager'); const token = this.configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const port = this.configService.get<HttpServer>('SERVER').PORT;
const instances = await this.waMonitor.instanceInfo();
console.log('INSTANCES: ', instances);
return response.status(HttpStatus.OK).render('manager', { token, port, instances });
} catch (error) { } catch (error) {
console.log('ERROR: ', error); console.log('ERROR: ', error);
} }

View File

@ -5,6 +5,7 @@ export class InstanceDto {
token?: string; token?: string;
webhook?: string; webhook?: string;
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean;
events?: string[]; events?: string[];
reject_call?: boolean; reject_call?: boolean;
msg_call?: string; msg_call?: string;

View File

@ -61,6 +61,7 @@ export class SendPollDto extends Metadata {
export type MediaType = 'image' | 'document' | 'video' | 'audio'; export type MediaType = 'image' | 'document' | 'video' | 'audio';
export class MediaMessage { export class MediaMessage {
mediatype: MediaType; mediatype: MediaType;
mimetype?: string;
caption?: string; caption?: string;
// for document // for document
fileName?: string; fileName?: string;

View File

@ -3,4 +3,5 @@ export class WebhookDto {
url?: string; url?: string;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean;
} }

View File

@ -48,7 +48,7 @@ const typebotSchema = new Schema<TypebotRaw>({
prefilledVariables: { prefilledVariables: {
remoteJid: { type: String, required: false }, remoteJid: { type: String, required: false },
pushName: { type: String, required: false }, pushName: { type: String, required: false },
additionalData: { type: Schema.Types.Mixed, required: false } additionalData: { type: Schema.Types.Mixed, required: false },
}, },
}, },
], ],

View File

@ -8,6 +8,7 @@ export class WebhookRaw {
enabled?: boolean; enabled?: boolean;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean;
} }
const webhookSchema = new Schema<WebhookRaw>({ const webhookSchema = new Schema<WebhookRaw>({
@ -16,6 +17,7 @@ const webhookSchema = new Schema<WebhookRaw>({
enabled: { type: Boolean, required: true }, enabled: { type: Boolean, required: true },
events: { type: [String], required: true }, events: { type: [String], required: true },
webhook_by_events: { type: Boolean, required: true }, webhook_by_events: { type: Boolean, required: true },
webhook_base64: { type: Boolean, required: true },
}); });
export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook'); export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook');

View File

@ -914,7 +914,7 @@ export class ChatwootService {
}, },
}; };
await waInstance?.audioWhatsapp(data); await waInstance?.audioWhatsapp(data, true);
this.logger.verbose('audio sent'); this.logger.verbose('audio sent');
return; return;
@ -939,7 +939,7 @@ export class ChatwootService {
data.mediaMessage.caption = caption; data.mediaMessage.caption = caption;
} }
await waInstance?.mediaMessage(data); await waInstance?.mediaMessage(data, true);
this.logger.verbose('media sent'); this.logger.verbose('media sent');
return; return;
@ -1074,7 +1074,7 @@ export class ChatwootService {
}, },
}; };
await waInstance?.textMessage(data); await waInstance?.textMessage(data, true);
} }
} }
} }

View File

@ -117,7 +117,7 @@ export class WAMonitoringService {
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL; instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey;
instanceData.instance['chatwoot'] = chatwoot; instanceData.instance['chatwoot'] = chatwoot;
} }
@ -136,7 +136,7 @@ export class WAMonitoringService {
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL; instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey;
instanceData.instance['chatwoot'] = chatwoot; instanceData.instance['chatwoot'] = chatwoot;
} }

View File

@ -47,7 +47,7 @@ export class TypebotService {
findData.sessions.splice(findData.sessions.indexOf(session), 1); findData.sessions.splice(findData.sessions.indexOf(session), 1);
const typebotData = { const typebotData = {
enabled: true, enabled: findData.enabled,
url: findData.url, url: findData.url,
typebot: findData.typebot, typebot: findData.typebot,
expire: findData.expire, expire: findData.expire,
@ -71,7 +71,7 @@ export class TypebotService {
} }
const typebotData = { const typebotData = {
enabled: true, enabled: findData.enabled,
url: findData.url, url: findData.url,
typebot: findData.typebot, typebot: findData.typebot,
expire: findData.expire, expire: findData.expire,
@ -104,7 +104,6 @@ export class TypebotService {
const startSession = data.startSession; const startSession = data.startSession;
const variables = data.variables; const variables = data.variables;
const findTypebot = await this.find(instance); const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const expire = findTypebot.expire; const expire = findTypebot.expire;
const keyword_finish = findTypebot.keyword_finish; const keyword_finish = findTypebot.keyword_finish;
const delay_message = findTypebot.delay_message; const delay_message = findTypebot.delay_message;
@ -116,12 +115,17 @@ export class TypebotService {
instanceName: instance.instanceName, instanceName: instance.instanceName,
}; };
variables.forEach((variable) => { if (variables?.length) {
prefilledVariables[variable.name] = variable.value; variables.forEach((variable: { name: string | number; value: string }) => {
}); prefilledVariables[variable.name] = variable.value;
});
}
if (startSession) { if (startSession) {
const newSessions = await this.clearSessions(instance, remoteJid);
const response = await this.createNewSession(instance, { const response = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
remoteJid: remoteJid, remoteJid: remoteJid,
@ -130,7 +134,7 @@ export class TypebotService {
delay_message: delay_message, delay_message: delay_message,
unknown_message: unknown_message, unknown_message: unknown_message,
listening_from_me: listening_from_me, listening_from_me: listening_from_me,
sessions: sessions, sessions: newSessions,
prefilledVariables: prefilledVariables, prefilledVariables: prefilledVariables,
}); });
@ -270,7 +274,7 @@ export class TypebotService {
}); });
const typebotData = { const typebotData = {
enabled: true, enabled: data.enabled,
url: data.url, url: data.url,
typebot: data.typebot, typebot: data.typebot,
expire: data.expire, expire: data.expire,
@ -287,6 +291,37 @@ export class TypebotService {
return request.data; return request.data;
} }
public async clearSessions(instance: InstanceDto, remoteJid: string) {
const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid);
if (sessionWithRemoteJid.length > 0) {
sessionWithRemoteJid.forEach((session) => {
sessions.splice(sessions.indexOf(session), 1);
});
const typebotData = {
enabled: findTypebot.enabled,
url: findTypebot.url,
typebot: findTypebot.typebot,
expire: findTypebot.expire,
keyword_finish: findTypebot.keyword_finish,
delay_message: findTypebot.delay_message,
unknown_message: findTypebot.unknown_message,
listening_from_me: findTypebot.listening_from_me,
sessions,
};
this.create(instance, typebotData);
return sessions;
}
return sessions;
}
public async sendWAMessage( public async sendWAMessage(
instance: InstanceDto, instance: InstanceDto,
remoteJid: string, remoteJid: string,
@ -457,9 +492,10 @@ export class TypebotService {
const diffInMinutes = Math.floor(diff / 1000 / 60); const diffInMinutes = Math.floor(diff / 1000 / 60);
if (diffInMinutes > expire) { if (diffInMinutes > expire) {
sessions.splice(sessions.indexOf(session), 1); const newSessions = await this.clearSessions(instance, remoteJid);
const data = await this.createNewSession(instance, { const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,
@ -467,7 +503,7 @@ export class TypebotService {
delay_message: delay_message, delay_message: delay_message,
unknown_message: unknown_message, unknown_message: unknown_message,
listening_from_me: listening_from_me, listening_from_me: listening_from_me,
sessions: sessions, sessions: newSessions,
remoteJid: remoteJid, remoteJid: remoteJid,
pushName: msg.pushName, pushName: msg.pushName,
}); });
@ -494,10 +530,10 @@ export class TypebotService {
} }
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) { if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
sessions.splice(sessions.indexOf(session), 1); const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = { const typebotData = {
enabled: true, enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,
@ -505,7 +541,7 @@ export class TypebotService {
delay_message: delay_message, delay_message: delay_message,
unknown_message: unknown_message, unknown_message: unknown_message,
listening_from_me: listening_from_me, listening_from_me: listening_from_me,
sessions, sessions: newSessions,
}; };
this.create(instance, typebotData); this.create(instance, typebotData);
@ -521,7 +557,6 @@ export class TypebotService {
try { try {
const request = await axios.post(url + '/api/v1/sendMessage', reqData); const request = await axios.post(url + '/api/v1/sendMessage', reqData);
console.log('request', request);
await this.sendWAMessage( await this.sendWAMessage(
instance, instance,
remoteJid, remoteJid,
@ -545,6 +580,7 @@ export class TypebotService {
if (!session) { if (!session) {
const data = await this.createNewSession(instance, { const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,
@ -582,7 +618,7 @@ export class TypebotService {
sessions.splice(sessions.indexOf(session), 1); sessions.splice(sessions.indexOf(session), 1);
const typebotData = { const typebotData = {
enabled: true, enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,
@ -606,8 +642,6 @@ export class TypebotService {
let request: any; let request: any;
try { try {
request = await axios.post(url + '/api/v1/sendMessage', reqData); request = await axios.post(url + '/api/v1/sendMessage', reqData);
console.log('request', request);
await this.sendWAMessage( await this.sendWAMessage(
instance, instance,
remoteJid, remoteJid,
@ -630,7 +664,7 @@ export class TypebotService {
}); });
const typebotData = { const typebotData = {
enabled: true, enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,
@ -665,7 +699,7 @@ export class TypebotService {
sessions.splice(sessions.indexOf(session), 1); sessions.splice(sessions.indexOf(session), 1);
const typebotData = { const typebotData = {
enabled: true, enabled: findTypebot.enabled,
url: url, url: url,
typebot: typebot, typebot: typebot,
expire: expire, expire: expire,

View File

@ -26,7 +26,7 @@ export class WebhookService {
return result; return result;
} catch (error) { } catch (error) {
return { enabled: false, url: '', events: [], webhook_by_events: false }; return { enabled: false, url: '', events: [], webhook_by_events: false, webhook_base64: false };
} }
} }
} }

View File

@ -128,9 +128,7 @@ import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types'
import { waMonitor } from '../whatsapp.module'; import { waMonitor } from '../whatsapp.module';
import { ChamaaiService } from './chamaai.service'; import { ChamaaiService } from './chamaai.service';
import { ChatwootService } from './chatwoot.service'; import { ChatwootService } from './chatwoot.service';
//import { SocksProxyAgent } from './socks-proxy-agent';
import { TypebotService } from './typebot.service'; import { TypebotService } from './typebot.service';
export class WAStartupService { export class WAStartupService {
constructor( constructor(
private readonly configService: ConfigService, private readonly configService: ConfigService,
@ -276,6 +274,9 @@ export class WAStartupService {
this.localWebhook.webhook_by_events = data?.webhook_by_events; this.localWebhook.webhook_by_events = data?.webhook_by_events;
this.logger.verbose(`Webhook by events: ${this.localWebhook.webhook_by_events}`); this.logger.verbose(`Webhook by events: ${this.localWebhook.webhook_by_events}`);
this.localWebhook.webhook_base64 = data?.webhook_base64;
this.logger.verbose(`Webhook by webhook_base64: ${this.localWebhook.webhook_base64}`);
this.logger.verbose('Webhook loaded'); this.logger.verbose('Webhook loaded');
} }
@ -581,7 +582,7 @@ export class WAStartupService {
Object.assign(this.localProxy, data); Object.assign(this.localProxy, data);
this.logger.verbose('Proxy set'); this.logger.verbose('Proxy set');
this.client?.ws?.close(); this.reloadConnection();
} }
public async findProxy() { public async findProxy() {
@ -1180,17 +1181,28 @@ export class WAStartupService {
if (this.localProxy.enabled) { if (this.localProxy.enabled) {
this.logger.verbose('Proxy enabled'); this.logger.verbose('Proxy enabled');
options = {
agent: new ProxyAgent(this.localProxy.proxy as any), if (this.localProxy.proxy.includes('proxyscrape')) {
fetchAgent: new ProxyAgent(this.localProxy.proxy as any), const response = await axios.get(this.localProxy.proxy);
}; const text = response.data;
const proxyUrls = text.split('\r\n');
const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length));
const proxyUrl = 'http://' + proxyUrls[rand];
options = {
agent: new ProxyAgent(proxyUrl as any),
};
} else {
options = {
agent: new ProxyAgent(this.localProxy.proxy as any),
};
}
} }
const socketConfig: UserFacingSocketConfig = { const socketConfig: UserFacingSocketConfig = {
...options, ...options,
auth: { auth: {
creds: this.instance.authState.state.creds, creds: this.instance.authState.state.creds,
keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })), keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any),
}, },
logger: P({ level: this.logBaileys }), logger: P({ level: this.logBaileys }),
printQRInTerminal: false, printQRInTerminal: false,
@ -1270,7 +1282,7 @@ export class WAStartupService {
...options, ...options,
auth: { auth: {
creds: this.instance.authState.state.creds, creds: this.instance.authState.state.creds,
keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })), keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any),
}, },
logger: P({ level: this.logBaileys }), logger: P({ level: this.logBaileys }),
printQRInTerminal: false, printQRInTerminal: false,
@ -1508,11 +1520,12 @@ export class WAStartupService {
this.logger.verbose('Event received: messages.upsert'); this.logger.verbose('Event received: messages.upsert');
const received = messages[0]; const received = messages[0];
console.log(received, type);
if ( if (
type !== 'notify' || type !== 'notify' ||
!received?.message || !received?.message ||
received.message?.protocolMessage || received.message?.protocolMessage ||
received.message.senderKeyDistributionMessage || // received.message.senderKeyDistributionMessage ||
received.message?.pollUpdateMessage received.message?.pollUpdateMessage
) { ) {
this.logger.verbose('message rejected'); this.logger.verbose('message rejected');
@ -1528,15 +1541,44 @@ export class WAStartupService {
return; return;
} }
const messageRaw: MessageRaw = { let messageRaw: MessageRaw;
key: received.key,
pushName: received.pushName, if (
message: { ...received.message }, (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) ||
messageType: getContentType(received.message), received?.message.imageMessage
messageTimestamp: received.messageTimestamp as number, ) {
owner: this.instance.name, const buffer = await downloadMediaMessage(
source: getDevice(received.key.id), { key: received.key, message: received?.message },
}; 'buffer',
{},
{
logger: P({ level: 'error' }) as any,
reuploadRequest: this.client.updateMediaMessage,
},
);
messageRaw = {
key: received.key,
pushName: received.pushName,
message: {
...received.message,
base64: buffer ? buffer.toString('base64') : undefined,
},
messageType: getContentType(received.message),
messageTimestamp: received.messageTimestamp as number,
owner: this.instance.name,
source: getDevice(received.key.id),
};
} else {
messageRaw = {
key: received.key,
pushName: received.pushName,
message: { ...received.message },
messageType: getContentType(received.message),
messageTimestamp: received.messageTimestamp as number,
owner: this.instance.name,
source: getDevice(received.key.id),
};
}
if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') {
await this.client.readMessages([received.key]); await this.client.readMessages([received.key]);
@ -1559,7 +1601,11 @@ export class WAStartupService {
); );
} }
if (this.localTypebot.enabled) { const typebotSessionRemoteJid = this.localTypebot.sessions?.find(
(session) => session.remoteJid === received.key.remoteJid,
);
if (this.localTypebot.enabled || typebotSessionRemoteJid) {
if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) {
await this.typebotService.sendTypebot( await this.typebotService.sendTypebot(
{ instanceName: this.instance.name }, { instanceName: this.instance.name },
@ -1901,8 +1947,8 @@ export class WAStartupService {
private createJid(number: string): string { private createJid(number: string): string {
this.logger.verbose('Creating jid with number: ' + number); this.logger.verbose('Creating jid with number: ' + number);
if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) {
this.logger.verbose('Number already contains @g.us or @s.whatsapp.net'); this.logger.verbose('Number already contains @g.us or @s.whatsapp.net or @lid');
return number; return number;
} }
@ -2028,7 +2074,12 @@ export class WAStartupService {
} }
} }
private async sendMessageWithTyping<T = proto.IMessage>(number: string, message: T, options?: Options) { private async sendMessageWithTyping<T = proto.IMessage>(
number: string,
message: T,
options?: Options,
isChatwoot = false,
) {
this.logger.verbose('Sending message with typing'); this.logger.verbose('Sending message with typing');
this.logger.verbose(`Check if number "${number}" is WhatsApp`); this.logger.verbose(`Check if number "${number}" is WhatsApp`);
@ -2186,7 +2237,7 @@ 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 && !isChatwoot) {
this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw); this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw);
} }
@ -2211,7 +2262,7 @@ export class WAStartupService {
} }
// Send Message Controller // Send Message Controller
public async textMessage(data: SendTextDto) { public async textMessage(data: SendTextDto, isChatwoot = false) {
this.logger.verbose('Sending text message'); this.logger.verbose('Sending text message');
return await this.sendMessageWithTyping( return await this.sendMessageWithTyping(
data.number, data.number,
@ -2219,6 +2270,7 @@ export class WAStartupService {
conversation: data.textMessage.text, conversation: data.textMessage.text,
}, },
data?.options, data?.options,
isChatwoot,
); );
} }
@ -2384,7 +2436,6 @@ export class WAStartupService {
mediaMessage.fileName = arrayMatch[1]; mediaMessage.fileName = arrayMatch[1];
this.logger.verbose('File name: ' + mediaMessage.fileName); this.logger.verbose('File name: ' + mediaMessage.fileName);
} }
let mimetype: string;
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) { if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
mediaMessage.fileName = 'image.png'; mediaMessage.fileName = 'image.png';
@ -2394,10 +2445,16 @@ export class WAStartupService {
mediaMessage.fileName = 'video.mp4'; mediaMessage.fileName = 'video.mp4';
} }
if (isURL(mediaMessage.media)) { let mimetype: string;
mimetype = getMIMEType(mediaMessage.media);
if (mediaMessage.mimetype) {
mimetype = mediaMessage.mimetype;
} else { } else {
mimetype = getMIMEType(mediaMessage.fileName); if (isURL(mediaMessage.media)) {
mimetype = getMIMEType(mediaMessage.media);
} else {
mimetype = getMIMEType(mediaMessage.fileName);
}
} }
this.logger.verbose('Mimetype: ' + mimetype); this.logger.verbose('Mimetype: ' + mimetype);
@ -2494,11 +2551,11 @@ export class WAStartupService {
return result; return result;
} }
public async mediaMessage(data: SendMediaDto) { public async mediaMessage(data: SendMediaDto, isChatwoot = false) {
this.logger.verbose('Sending media message'); this.logger.verbose('Sending media message');
const generate = await this.prepareMediaMessage(data.mediaMessage); const generate = await this.prepareMediaMessage(data.mediaMessage);
return await this.sendMessageWithTyping(data.number, { ...generate.message }, data?.options); return await this.sendMessageWithTyping(data.number, { ...generate.message }, data?.options, isChatwoot);
} }
public async processAudio(audio: string, number: string) { public async processAudio(audio: string, number: string) {
@ -2555,7 +2612,7 @@ export class WAStartupService {
}); });
} }
public async audioWhatsapp(data: SendAudioDto) { public async audioWhatsapp(data: SendAudioDto, isChatwoot = false) {
this.logger.verbose('Sending audio whatsapp'); this.logger.verbose('Sending audio whatsapp');
if (!data.options?.encoding && data.options?.encoding !== false) { if (!data.options?.encoding && data.options?.encoding !== false) {
@ -2574,6 +2631,7 @@ export class WAStartupService {
mimetype: 'audio/mp4', mimetype: 'audio/mp4',
}, },
{ presence: 'recording', delay: data?.options?.delay }, { presence: 'recording', delay: data?.options?.delay },
isChatwoot,
); );
fs.unlinkSync(convert); fs.unlinkSync(convert);
@ -2595,6 +2653,7 @@ export class WAStartupService {
mimetype: 'audio/ogg; codecs=opus', mimetype: 'audio/ogg; codecs=opus',
}, },
{ presence: 'recording', delay: data?.options?.delay }, { presence: 'recording', delay: data?.options?.delay },
isChatwoot,
); );
} }
@ -2905,7 +2964,7 @@ export class WAStartupService {
'buffer', 'buffer',
{}, {},
{ {
logger: P({ level: 'error' }), logger: P({ level: 'error' }) as any,
reuploadRequest: this.client.updateMediaMessage, reuploadRequest: this.client.updateMediaMessage,
}, },
); );
@ -3044,7 +3103,7 @@ export class WAStartupService {
await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd); await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd);
this.logger.verbose('Groups add privacy updated'); this.logger.verbose('Groups add privacy updated');
this.client?.ws?.close(); this.reloadConnection();
return { return {
update: 'success', update: 'success',

View File

@ -50,6 +50,7 @@ export declare namespace wa {
url?: string; url?: string;
events?: string[]; events?: string[];
webhook_by_events?: boolean; webhook_by_events?: boolean;
webhook_base64?: boolean;
}; };
export type LocalChatwoot = { export type LocalChatwoot = {

110
views/manager-wip.hbs Normal file
View File

@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="https://evolution-api.com/files/evolution-api-favicon.png" type="image/x-icon">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<title>Instance Manager</title>
</head>
<body>
<div class="container mt-4">
<!-- Botão para abrir o modal de adicionar nova instância -->
<button class="btn btn-primary mb-3" data-toggle="modal" data-target="#actionModal" data-action="add">Nova
Instância</button>
<!-- Tabela de instâncias -->
<table class="table table-bordered">
<thead>
<tr>
<th>Nome da Instância</th>
<th>Status</th>
<th>API Key</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<!-- Iterando sobre as instâncias e preenchendo a tabela -->
{{#each instances}}
<tr>
<td>{{this.instance.instanceName}}</td>
<td>{{this.instance.status}}</td>
<td>{{this.instance.apikey}}</td>
<td>
<!-- Dropdown de ações para cada instância -->
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="actionDropdown"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Ações
</button>
<div class="dropdown-menu" aria-labelledby="actionDropdown">
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#actionModal"
data-action="connect">Connect</a>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#actionModal"
data-action="restart">Restart</a>
<!-- Adicione mais itens de ação aqui -->
<!-- ... -->
</div>
</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<!-- Modal de ações -->
<div class="modal fade" id="actionModal" tabindex="-1" role="dialog" aria-labelledby="actionModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="actionModalLabel">Ação</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Fechar</button>
<button type="button" class="btn btn-primary">Salvar</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js"
integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+"
crossorigin="anonymous"></script>
<script>
$(document).ready(function(){
$('#actionModal').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget);
var action = button.data('action');
console.log(action);
if (action === 'connect') {
} else if (action === 'restart') {
}
});
})
</script>
</body>
</html>

View File

@ -11,7 +11,7 @@
<body> <body>
<iframe src="https://app.smith.dgcode.com.br/app/evolutionapi-public/home-64ca60783615e270291978b4?embed=true" frameborder="0" style="width: 100%; height: 100vh;"></iframe> <iframe src="https://manager.evolution-api.com" frameborder="0" style="width: 100%; height: 100vh;"></iframe>
</body> </body>