mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-26 15:17:44 -06:00
@@ -5,7 +5,7 @@ import { createReadStream, readFileSync, unlinkSync, writeFileSync } from 'fs';
|
||||
import mimeTypes from 'mime-types';
|
||||
import path from 'path';
|
||||
|
||||
import { ConfigService, HttpServer } from '../../config/env.config';
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { ROOT_DIR } from '../../config/path.config';
|
||||
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||
@@ -205,7 +205,14 @@ export class ChatwootService {
|
||||
this.logger.verbose('find contact in chatwoot and create if not exists');
|
||||
const contact =
|
||||
(await this.findContact(instance, '123456')) ||
|
||||
((await this.createContact(instance, '123456', inboxId, false, 'EvolutionAPI')) as any);
|
||||
((await this.createContact(
|
||||
instance,
|
||||
'123456',
|
||||
inboxId,
|
||||
false,
|
||||
'EvolutionAPI',
|
||||
'https://evolution-api.com/files/evolution-api-favicon.png',
|
||||
)) as any);
|
||||
|
||||
if (!contact) {
|
||||
this.logger.warn('contact not found');
|
||||
@@ -237,10 +244,10 @@ export class ChatwootService {
|
||||
|
||||
this.logger.verbose('create message for init instance in chatwoot');
|
||||
|
||||
let contentMsg = '/init';
|
||||
let contentMsg = 'init';
|
||||
|
||||
if (number) {
|
||||
contentMsg = `/init:${number}`;
|
||||
contentMsg = `init:${number}`;
|
||||
}
|
||||
|
||||
const message = await client.messages.create({
|
||||
@@ -269,6 +276,7 @@ export class ChatwootService {
|
||||
isGroup: boolean,
|
||||
name?: string,
|
||||
avatar_url?: string,
|
||||
jid?: string,
|
||||
) {
|
||||
this.logger.verbose('create contact to instance: ' + instance.instanceName);
|
||||
|
||||
@@ -286,6 +294,7 @@ export class ChatwootService {
|
||||
inbox_id: inboxId,
|
||||
name: name || phoneNumber,
|
||||
phone_number: `+${phoneNumber}`,
|
||||
identifier: jid,
|
||||
avatar_url: avatar_url,
|
||||
};
|
||||
} else {
|
||||
@@ -437,6 +446,7 @@ export class ChatwootService {
|
||||
false,
|
||||
body.pushName,
|
||||
picture_url.profilePictureUrl || null,
|
||||
body.key.participant,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -452,6 +462,7 @@ export class ChatwootService {
|
||||
if (findContact) {
|
||||
contact = findContact;
|
||||
} else {
|
||||
const jid = isGroup ? null : body.key.remoteJid;
|
||||
contact = await this.createContact(
|
||||
instance,
|
||||
chatId,
|
||||
@@ -459,6 +470,7 @@ export class ChatwootService {
|
||||
isGroup,
|
||||
nameContact,
|
||||
picture_url.profilePictureUrl || null,
|
||||
jid,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -472,6 +484,7 @@ export class ChatwootService {
|
||||
contact = findContact;
|
||||
}
|
||||
} else {
|
||||
const jid = isGroup ? null : body.key.remoteJid;
|
||||
contact = await this.createContact(
|
||||
instance,
|
||||
chatId,
|
||||
@@ -479,6 +492,7 @@ export class ChatwootService {
|
||||
isGroup,
|
||||
nameContact,
|
||||
picture_url.profilePictureUrl || null,
|
||||
jid,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -578,7 +592,7 @@ export class ChatwootService {
|
||||
}
|
||||
|
||||
this.logger.verbose('find inbox by name');
|
||||
const findByName = inbox.payload.find((inbox) => inbox.name === instance.instanceName);
|
||||
const findByName = inbox.payload.find((inbox) => inbox.name === instance.instanceName.split('-cwId-')[0]);
|
||||
|
||||
if (!findByName) {
|
||||
this.logger.warn('inbox not found');
|
||||
@@ -996,39 +1010,6 @@ export class ChatwootService {
|
||||
await waInstance?.client?.logout('Log out instance: ' + instance.instanceName);
|
||||
await waInstance?.client?.ws?.close();
|
||||
}
|
||||
|
||||
if (command.includes('new_instance')) {
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
const apiKey = this.configService.get('AUTHENTICATION').API_KEY.KEY;
|
||||
|
||||
const data = {
|
||||
instanceName: command.split(':')[1],
|
||||
qrcode: true,
|
||||
chatwoot_account_id: this.provider.account_id,
|
||||
chatwoot_token: this.provider.token,
|
||||
chatwoot_url: this.provider.url,
|
||||
chatwoot_sign_msg: this.provider.sign_msg,
|
||||
chatwoot_reopen_conversation: this.provider.reopen_conversation,
|
||||
chatwoot_conversation_pending: this.provider.conversation_pending,
|
||||
};
|
||||
|
||||
if (command.split(':')[2]) {
|
||||
data['number'] = command.split(':')[2];
|
||||
}
|
||||
|
||||
const config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: `${urlServer}/instance/create`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
apikey: apiKey,
|
||||
},
|
||||
data: data,
|
||||
};
|
||||
|
||||
await axios.request(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (body.message_type === 'outgoing' && body?.conversation?.messages?.length && chatId !== '123456') {
|
||||
@@ -1055,7 +1036,7 @@ export class ChatwootService {
|
||||
if (senderName === null || senderName === undefined) {
|
||||
formatText = messageReceived;
|
||||
} else {
|
||||
formatText = this.provider.sign_msg ? `*${senderName}:*\n\n${messageReceived}` : messageReceived;
|
||||
formatText = this.provider.sign_msg ? `*${senderName}:*\n${messageReceived}` : messageReceived;
|
||||
}
|
||||
|
||||
for (const message of body.conversation.messages) {
|
||||
@@ -1342,7 +1323,7 @@ export class ChatwootService {
|
||||
|
||||
if (!body.key.fromMe) {
|
||||
this.logger.verbose('message is not from me');
|
||||
content = `**${participantName}**\n\n${bodyMessage}`;
|
||||
content = `**${participantName}:**\n\n${bodyMessage}`;
|
||||
} else {
|
||||
this.logger.verbose('message is from me');
|
||||
content = `${bodyMessage}`;
|
||||
@@ -1478,7 +1459,7 @@ export class ChatwootService {
|
||||
this.logger.verbose('event qrcode.updated');
|
||||
if (body.statusCode === 500) {
|
||||
this.logger.verbose('qrcode error');
|
||||
const erroQRcode = `🚨 QRCode generation limit reached, to generate a new QRCode, send the /init message again.`;
|
||||
const erroQRcode = `🚨 QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.`;
|
||||
|
||||
this.logger.verbose('send message to chatwoot');
|
||||
return await this.createBotMessage(instance, erroQRcode, 'incoming');
|
||||
@@ -1515,50 +1496,4 @@ export class ChatwootService {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async newInstance(data: any) {
|
||||
try {
|
||||
const instanceName = data.instanceName;
|
||||
const qrcode = true;
|
||||
const number = data.number;
|
||||
const accountId = data.accountId;
|
||||
const chatwootToken = data.token;
|
||||
const chatwootUrl = data.url;
|
||||
const signMsg = true;
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
const apiKey = this.configService.get('AUTHENTICATION').API_KEY.KEY;
|
||||
|
||||
const requestData = {
|
||||
instanceName,
|
||||
qrcode,
|
||||
chatwoot_account_id: accountId,
|
||||
chatwoot_token: chatwootToken,
|
||||
chatwoot_url: chatwootUrl,
|
||||
chatwoot_sign_msg: signMsg,
|
||||
};
|
||||
|
||||
if (number) {
|
||||
requestData['number'] = number;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const config = {
|
||||
method: 'post',
|
||||
maxBodyLength: Infinity,
|
||||
url: `${urlServer}/instance/create`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
apikey: apiKey,
|
||||
},
|
||||
data: requestData,
|
||||
};
|
||||
|
||||
// await axios.request(config);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import { join } from 'path';
|
||||
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { RedisCache } from '../../db/redis.client';
|
||||
import { NotFoundException } from '../../exceptions';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
import { RedisCache } from '../../libs/redis.client';
|
||||
import {
|
||||
AuthModel,
|
||||
ChatwootModel,
|
||||
@@ -94,7 +94,7 @@ export class WAMonitoringService {
|
||||
if (findChatwoot && findChatwoot.enabled) {
|
||||
chatwoot = {
|
||||
...findChatwoot,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${key}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -335,11 +335,14 @@ export class WAMonitoringService {
|
||||
this.logger.verbose('checking instances without connection');
|
||||
this.eventEmitter.on('no.connection', async (instanceName) => {
|
||||
try {
|
||||
this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
|
||||
this.waInstances[instanceName] = undefined;
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
await this.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
|
||||
|
||||
this.logger.verbose('request cleaning up instance: ' + instanceName);
|
||||
this.cleaningUp(instanceName);
|
||||
this.logger.verbose('close connection instance: ' + instanceName);
|
||||
this.waInstances[instanceName]?.client?.ws?.close();
|
||||
|
||||
this.waInstances[instanceName].instance.qrcode = { count: 0 };
|
||||
this.waInstances[instanceName].stateConnection.state = 'close';
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
localError: 'noConnection',
|
||||
|
||||
33
src/whatsapp/services/proxy.service.ts
Normal file
33
src/whatsapp/services/proxy.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ProxyDto } from '../dto/proxy.dto';
|
||||
import { ProxyRaw } from '../models';
|
||||
import { WAMonitoringService } from './monitor.service';
|
||||
|
||||
export class ProxyService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger(ProxyService.name);
|
||||
|
||||
public create(instance: InstanceDto, data: ProxyDto) {
|
||||
this.logger.verbose('create proxy: ' + instance.instanceName);
|
||||
this.waMonitor.waInstances[instance.instanceName].setProxy(data);
|
||||
|
||||
return { proxy: { ...instance, proxy: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<ProxyRaw> {
|
||||
try {
|
||||
this.logger.verbose('find proxy: ' + instance.instanceName);
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findProxy();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Proxy not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return { enabled: false, proxy: '' };
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/whatsapp/services/rabbitmq.service.ts
Normal file
33
src/whatsapp/services/rabbitmq.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RabbitmqDto } from '../dto/rabbitmq.dto';
|
||||
import { RabbitmqRaw } from '../models';
|
||||
import { WAMonitoringService } from './monitor.service';
|
||||
|
||||
export class RabbitmqService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger(RabbitmqService.name);
|
||||
|
||||
public create(instance: InstanceDto, data: RabbitmqDto) {
|
||||
this.logger.verbose('create rabbitmq: ' + instance.instanceName);
|
||||
this.waMonitor.waInstances[instance.instanceName].setRabbitmq(data);
|
||||
|
||||
return { rabbitmq: { ...instance, rabbitmq: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<RabbitmqRaw> {
|
||||
try {
|
||||
this.logger.verbose('find rabbitmq: ' + instance.instanceName);
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findRabbitmq();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Rabbitmq not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return { enabled: false, events: [] };
|
||||
}
|
||||
}
|
||||
}
|
||||
421
src/whatsapp/services/typebot.service.ts
Normal file
421
src/whatsapp/services/typebot.service.ts
Normal file
@@ -0,0 +1,421 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { Session, TypebotDto } from '../dto/typebot.dto';
|
||||
import { MessageRaw } from '../models';
|
||||
import { WAMonitoringService } from './monitor.service';
|
||||
|
||||
export class TypebotService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger(TypebotService.name);
|
||||
|
||||
public create(instance: InstanceDto, data: TypebotDto) {
|
||||
this.logger.verbose('create typebot: ' + instance.instanceName);
|
||||
this.waMonitor.waInstances[instance.instanceName].setTypebot(data);
|
||||
|
||||
return { typebot: { ...instance, typebot: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<TypebotDto> {
|
||||
try {
|
||||
this.logger.verbose('find typebot: ' + instance.instanceName);
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findTypebot();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Typebot not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return { enabled: false, url: '', typebot: '', expire: 0, sessions: [] };
|
||||
}
|
||||
}
|
||||
|
||||
public async changeStatus(instance: InstanceDto, data: any) {
|
||||
const remoteJid = data.remoteJid;
|
||||
const status = data.status;
|
||||
|
||||
const findData = await this.find(instance);
|
||||
|
||||
const session = findData.sessions.find((session) => session.remoteJid === remoteJid);
|
||||
|
||||
if (session) {
|
||||
if (status === 'closed') {
|
||||
findData.sessions.splice(findData.sessions.indexOf(session), 1);
|
||||
|
||||
const typebotData = {
|
||||
enabled: true,
|
||||
url: findData.url,
|
||||
typebot: findData.typebot,
|
||||
expire: findData.expire,
|
||||
keyword_finish: findData.keyword_finish,
|
||||
delay_message: findData.delay_message,
|
||||
unknown_message: findData.unknown_message,
|
||||
sessions: findData.sessions,
|
||||
};
|
||||
|
||||
this.create(instance, typebotData);
|
||||
|
||||
return { typebot: { ...instance, typebot: typebotData } };
|
||||
}
|
||||
|
||||
findData.sessions.map((session) => {
|
||||
if (session.remoteJid === remoteJid) {
|
||||
session.status = status;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const typebotData = {
|
||||
enabled: true,
|
||||
url: findData.url,
|
||||
typebot: findData.typebot,
|
||||
expire: findData.expire,
|
||||
keyword_finish: findData.keyword_finish,
|
||||
delay_message: findData.delay_message,
|
||||
unknown_message: findData.unknown_message,
|
||||
sessions: findData.sessions,
|
||||
};
|
||||
|
||||
this.create(instance, typebotData);
|
||||
|
||||
return { typebot: { ...instance, typebot: typebotData } };
|
||||
}
|
||||
|
||||
private getTypeMessage(msg: any) {
|
||||
this.logger.verbose('get type message');
|
||||
|
||||
const types = {
|
||||
conversation: msg.conversation,
|
||||
extendedTextMessage: msg.extendedTextMessage?.text,
|
||||
};
|
||||
|
||||
this.logger.verbose('type message: ' + types);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private getMessageContent(types: any) {
|
||||
this.logger.verbose('get message content');
|
||||
const typeKey = Object.keys(types).find((key) => types[key] !== undefined);
|
||||
|
||||
const result = typeKey ? types[typeKey] : undefined;
|
||||
|
||||
this.logger.verbose('message content: ' + result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getConversationMessage(msg: any) {
|
||||
this.logger.verbose('get conversation message');
|
||||
|
||||
const types = this.getTypeMessage(msg);
|
||||
|
||||
const messageContent = this.getMessageContent(types);
|
||||
|
||||
this.logger.verbose('conversation message: ' + messageContent);
|
||||
|
||||
return messageContent;
|
||||
}
|
||||
|
||||
public async createNewSession(instance: InstanceDto, data: any) {
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
|
||||
|
||||
if (request.data.sessionId) {
|
||||
data.sessions.push({
|
||||
remoteJid: data.remoteJid,
|
||||
sessionId: `${id}-${request.data.sessionId}`,
|
||||
status: 'opened',
|
||||
createdAt: Date.now(),
|
||||
updateAt: Date.now(),
|
||||
});
|
||||
|
||||
const typebotData = {
|
||||
enabled: true,
|
||||
url: data.url,
|
||||
typebot: data.typebot,
|
||||
expire: data.expire,
|
||||
keyword_finish: data.keyword_finish,
|
||||
delay_message: data.delay_message,
|
||||
unknown_message: data.unknown_message,
|
||||
sessions: data.sessions,
|
||||
};
|
||||
|
||||
this.create(instance, typebotData);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
async function processMessages(instance, messages, input) {
|
||||
for (const message of messages) {
|
||||
if (message.type === 'text') {
|
||||
let formattedText = '';
|
||||
|
||||
let linkPreview = false;
|
||||
|
||||
for (const richText of message.content.richText) {
|
||||
for (const element of richText.children) {
|
||||
let text = '';
|
||||
if (element.text) {
|
||||
text = element.text;
|
||||
}
|
||||
|
||||
if (element.bold) {
|
||||
text = `*${text}*`;
|
||||
}
|
||||
|
||||
if (element.italic) {
|
||||
text = `_${text}_`;
|
||||
}
|
||||
|
||||
if (element.underline) {
|
||||
text = `~${text}~`;
|
||||
}
|
||||
|
||||
if (element.url) {
|
||||
const linkText = element.children[0].text;
|
||||
text = `[${linkText}](${element.url})`;
|
||||
linkPreview = true;
|
||||
}
|
||||
|
||||
formattedText += text;
|
||||
}
|
||||
formattedText += '\n';
|
||||
}
|
||||
|
||||
formattedText = formattedText.replace(/\n$/, '');
|
||||
|
||||
await instance.textMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
linkPreview: linkPreview,
|
||||
},
|
||||
textMessage: {
|
||||
text: formattedText,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'image') {
|
||||
await instance.mediaMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
},
|
||||
mediaMessage: {
|
||||
mediatype: 'image',
|
||||
media: message.content.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'video') {
|
||||
await instance.mediaMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
presence: 'composing',
|
||||
},
|
||||
mediaMessage: {
|
||||
mediatype: 'video',
|
||||
media: message.content.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'audio') {
|
||||
await instance.audioWhatsapp({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: instance.localTypebot.delay_message || 1000,
|
||||
presence: 'recording',
|
||||
encoding: true,
|
||||
},
|
||||
audioMessage: {
|
||||
audio: message.content.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (input) {
|
||||
if (input.type === 'choice input') {
|
||||
let formattedText = '';
|
||||
|
||||
const items = input.items;
|
||||
|
||||
for (const item of items) {
|
||||
formattedText += `▶️ ${item.content}\n`;
|
||||
}
|
||||
|
||||
formattedText = formattedText.replace(/\n$/, '');
|
||||
|
||||
await instance.textMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: 1200,
|
||||
presence: 'composing',
|
||||
linkPreview: false,
|
||||
},
|
||||
textMessage: {
|
||||
text: formattedText,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: MessageRaw) {
|
||||
const url = (await this.find(instance)).url;
|
||||
const typebot = (await this.find(instance)).typebot;
|
||||
const sessions = ((await this.find(instance)).sessions as Session[]) ?? [];
|
||||
const expire = (await this.find(instance)).expire;
|
||||
const keyword_finish = (await this.find(instance)).keyword_finish;
|
||||
const delay_message = (await this.find(instance)).delay_message;
|
||||
const unknown_message = (await this.find(instance)).unknown_message;
|
||||
|
||||
const session = sessions.find((session) => session.remoteJid === remoteJid);
|
||||
|
||||
if (session && expire && expire > 0) {
|
||||
const now = Date.now();
|
||||
|
||||
const diff = now - session.updateAt;
|
||||
|
||||
const diffInMinutes = Math.floor(diff / 1000 / 60);
|
||||
|
||||
if (diffInMinutes > expire) {
|
||||
sessions.splice(sessions.indexOf(session), 1);
|
||||
|
||||
const data = await this.createNewSession(instance, {
|
||||
url: url,
|
||||
typebot: typebot,
|
||||
expire: expire,
|
||||
keyword_finish: keyword_finish,
|
||||
delay_message: delay_message,
|
||||
unknown_message: unknown_message,
|
||||
sessions: sessions,
|
||||
remoteJid: remoteJid,
|
||||
pushName: msg.pushName,
|
||||
});
|
||||
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (session && session.status !== 'opened') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
const data = await this.createNewSession(instance, {
|
||||
url: url,
|
||||
typebot: typebot,
|
||||
expire: expire,
|
||||
keyword_finish: keyword_finish,
|
||||
delay_message: delay_message,
|
||||
unknown_message: unknown_message,
|
||||
sessions: sessions,
|
||||
remoteJid: remoteJid,
|
||||
pushName: msg.pushName,
|
||||
});
|
||||
|
||||
await this.sendWAMessage(instance, remoteJid, data.messages, data.input);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sessions.map((session) => {
|
||||
if (session.remoteJid === remoteJid) {
|
||||
session.updateAt = Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
const typebotData = {
|
||||
enabled: true,
|
||||
url: url,
|
||||
typebot: typebot,
|
||||
expire: expire,
|
||||
keyword_finish: keyword_finish,
|
||||
delay_message: delay_message,
|
||||
unknown_message: unknown_message,
|
||||
sessions,
|
||||
};
|
||||
|
||||
this.create(instance, typebotData);
|
||||
|
||||
const content = this.getConversationMessage(msg.message);
|
||||
|
||||
if (!content) {
|
||||
if (unknown_message) {
|
||||
this.waMonitor.waInstances[instance.instanceName].textMessage({
|
||||
number: remoteJid.split('@')[0],
|
||||
options: {
|
||||
delay: delay_message || 1000,
|
||||
presence: 'composing',
|
||||
},
|
||||
textMessage: {
|
||||
text: unknown_message,
|
||||
},
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.toLowerCase() === keyword_finish.toLowerCase()) {
|
||||
sessions.splice(sessions.indexOf(session), 1);
|
||||
|
||||
const typebotData = {
|
||||
enabled: true,
|
||||
url: url,
|
||||
typebot: typebot,
|
||||
expire: expire,
|
||||
keyword_finish: keyword_finish,
|
||||
delay_message: delay_message,
|
||||
unknown_message: unknown_message,
|
||||
sessions,
|
||||
};
|
||||
|
||||
this.create(instance, typebotData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const reqData = {
|
||||
message: content,
|
||||
sessionId: session.sessionId.split('-')[1],
|
||||
};
|
||||
|
||||
const request = await axios.post(url + '/api/v1/sendMessage', reqData);
|
||||
|
||||
await this.sendWAMessage(instance, remoteJid, request.data.messages, request.data.input);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
33
src/whatsapp/services/websocket.service.ts
Normal file
33
src/whatsapp/services/websocket.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { WebsocketDto } from '../dto/websocket.dto';
|
||||
import { WebsocketRaw } from '../models';
|
||||
import { WAMonitoringService } from './monitor.service';
|
||||
|
||||
export class WebsocketService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger(WebsocketService.name);
|
||||
|
||||
public create(instance: InstanceDto, data: WebsocketDto) {
|
||||
this.logger.verbose('create websocket: ' + instance.instanceName);
|
||||
this.waMonitor.waInstances[instance.instanceName].setWebsocket(data);
|
||||
|
||||
return { websocket: { ...instance, websocket: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<WebsocketRaw> {
|
||||
try {
|
||||
this.logger.verbose('find websocket: ' + instance.instanceName);
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findWebsocket();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Websocket not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return { enabled: false, events: [] };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import { getMIMEType } from 'node-mime-types';
|
||||
import { release } from 'os';
|
||||
import { join } from 'path';
|
||||
import P from 'pino';
|
||||
import { ProxyAgent } from 'proxy-agent';
|
||||
import qrcode, { QRCodeToDataURLOptions } from 'qrcode';
|
||||
import qrcodeTerminal from 'qrcode-terminal';
|
||||
import sharp from 'sharp';
|
||||
@@ -60,18 +61,22 @@ import {
|
||||
QrCode,
|
||||
Redis,
|
||||
Webhook,
|
||||
Websocket,
|
||||
} from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { INSTANCE_DIR, ROOT_DIR } from '../../config/path.config';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { RedisCache } from '../../db/redis.client';
|
||||
import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../exceptions';
|
||||
import { getAMQP } from '../../libs/amqp.server';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
import { RedisCache } from '../../libs/redis.client';
|
||||
import { getIO } from '../../libs/socket.server';
|
||||
import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db';
|
||||
import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db';
|
||||
import {
|
||||
ArchiveChatDto,
|
||||
DeleteMessage,
|
||||
getBase64FromMediaMessageDto,
|
||||
LastMessage,
|
||||
NumberBusiness,
|
||||
OnWhatsAppDto,
|
||||
PrivacySettingDto,
|
||||
@@ -108,12 +113,13 @@ import {
|
||||
SendTextDto,
|
||||
StatusMessage,
|
||||
} from '../dto/sendMessage.dto';
|
||||
import { SettingsRaw } from '../models';
|
||||
import { ProxyRaw, RabbitmqRaw, SettingsRaw, TypebotRaw } from '../models';
|
||||
import { ChatRaw } from '../models/chat.model';
|
||||
import { ChatwootRaw } from '../models/chatwoot.model';
|
||||
import { ContactRaw } from '../models/contact.model';
|
||||
import { MessageRaw, MessageUpdateRaw } from '../models/message.model';
|
||||
import { WebhookRaw } from '../models/webhook.model';
|
||||
import { WebsocketRaw } from '../models/websocket.model';
|
||||
import { ContactQuery } from '../repository/contact.repository';
|
||||
import { MessageQuery } from '../repository/message.repository';
|
||||
import { MessageUpQuery } from '../repository/messageUp.repository';
|
||||
@@ -121,6 +127,7 @@ import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types';
|
||||
import { waMonitor } from '../whatsapp.module';
|
||||
import { ChatwootService } from './chatwoot.service';
|
||||
import { TypebotService } from './typebot.service';
|
||||
|
||||
export class WAStartupService {
|
||||
constructor(
|
||||
@@ -135,12 +142,16 @@ export class WAStartupService {
|
||||
}
|
||||
|
||||
private readonly logger = new Logger(WAStartupService.name);
|
||||
private readonly instance: wa.Instance = {};
|
||||
public readonly instance: wa.Instance = {};
|
||||
public client: WASocket;
|
||||
private readonly localWebhook: wa.LocalWebHook = {};
|
||||
private readonly localChatwoot: wa.LocalChatwoot = {};
|
||||
private readonly localSettings: wa.LocalSettings = {};
|
||||
private stateConnection: wa.StateConnection = { state: 'close' };
|
||||
private readonly localWebsocket: wa.LocalWebsocket = {};
|
||||
private readonly localRabbitmq: wa.LocalRabbitmq = {};
|
||||
public readonly localTypebot: wa.LocalTypebot = {};
|
||||
private readonly localProxy: wa.LocalProxy = {};
|
||||
public stateConnection: wa.StateConnection = { state: 'close' };
|
||||
public readonly storePath = join(ROOT_DIR, 'store');
|
||||
private readonly msgRetryCounterCache: CacheStore = new NodeCache();
|
||||
private readonly userDevicesCache: CacheStore = new NodeCache();
|
||||
@@ -151,6 +162,8 @@ export class WAStartupService {
|
||||
|
||||
private chatwootService = new ChatwootService(waMonitor, this.configService);
|
||||
|
||||
private typebotService = new TypebotService(waMonitor);
|
||||
|
||||
public set instanceName(name: string) {
|
||||
this.logger.verbose(`Initializing instance '${name}'`);
|
||||
if (!name) {
|
||||
@@ -409,9 +422,168 @@ export class WAStartupService {
|
||||
return data;
|
||||
}
|
||||
|
||||
private async loadWebsocket() {
|
||||
this.logger.verbose('Loading websocket');
|
||||
const data = await this.repository.websocket.find(this.instanceName);
|
||||
|
||||
this.localWebsocket.enabled = data?.enabled;
|
||||
this.logger.verbose(`Websocket enabled: ${this.localWebsocket.enabled}`);
|
||||
|
||||
this.localWebsocket.events = data?.events;
|
||||
this.logger.verbose(`Websocket events: ${this.localWebsocket.events}`);
|
||||
|
||||
this.logger.verbose('Websocket loaded');
|
||||
}
|
||||
|
||||
public async setWebsocket(data: WebsocketRaw) {
|
||||
this.logger.verbose('Setting websocket');
|
||||
await this.repository.websocket.create(data, this.instanceName);
|
||||
this.logger.verbose(`Websocket events: ${data.events}`);
|
||||
Object.assign(this.localWebsocket, data);
|
||||
this.logger.verbose('Websocket set');
|
||||
}
|
||||
|
||||
public async findWebsocket() {
|
||||
this.logger.verbose('Finding websocket');
|
||||
const data = await this.repository.websocket.find(this.instanceName);
|
||||
|
||||
if (!data) {
|
||||
this.logger.verbose('Websocket not found');
|
||||
throw new NotFoundException('Websocket not found');
|
||||
}
|
||||
|
||||
this.logger.verbose(`Websocket events: ${data.events}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
private async loadRabbitmq() {
|
||||
this.logger.verbose('Loading rabbitmq');
|
||||
const data = await this.repository.rabbitmq.find(this.instanceName);
|
||||
|
||||
this.localRabbitmq.enabled = data?.enabled;
|
||||
this.logger.verbose(`Rabbitmq enabled: ${this.localRabbitmq.enabled}`);
|
||||
|
||||
this.localRabbitmq.events = data?.events;
|
||||
this.logger.verbose(`Rabbitmq events: ${this.localRabbitmq.events}`);
|
||||
|
||||
this.logger.verbose('Rabbitmq loaded');
|
||||
}
|
||||
|
||||
public async setRabbitmq(data: RabbitmqRaw) {
|
||||
this.logger.verbose('Setting rabbitmq');
|
||||
await this.repository.rabbitmq.create(data, this.instanceName);
|
||||
this.logger.verbose(`Rabbitmq events: ${data.events}`);
|
||||
Object.assign(this.localRabbitmq, data);
|
||||
this.logger.verbose('Rabbitmq set');
|
||||
}
|
||||
|
||||
public async findRabbitmq() {
|
||||
this.logger.verbose('Finding rabbitmq');
|
||||
const data = await this.repository.rabbitmq.find(this.instanceName);
|
||||
|
||||
if (!data) {
|
||||
this.logger.verbose('Rabbitmq not found');
|
||||
throw new NotFoundException('Rabbitmq not found');
|
||||
}
|
||||
|
||||
this.logger.verbose(`Rabbitmq events: ${data.events}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
private async loadTypebot() {
|
||||
this.logger.verbose('Loading typebot');
|
||||
const data = await this.repository.typebot.find(this.instanceName);
|
||||
|
||||
this.localTypebot.enabled = data?.enabled;
|
||||
this.logger.verbose(`Typebot enabled: ${this.localTypebot.enabled}`);
|
||||
|
||||
this.localTypebot.url = data?.url;
|
||||
this.logger.verbose(`Typebot url: ${this.localTypebot.url}`);
|
||||
|
||||
this.localTypebot.typebot = data?.typebot;
|
||||
this.logger.verbose(`Typebot typebot: ${this.localTypebot.typebot}`);
|
||||
|
||||
this.localTypebot.expire = data?.expire;
|
||||
this.logger.verbose(`Typebot expire: ${this.localTypebot.expire}`);
|
||||
|
||||
this.localTypebot.keyword_finish = data?.keyword_finish;
|
||||
this.logger.verbose(`Typebot keyword_finish: ${this.localTypebot.keyword_finish}`);
|
||||
|
||||
this.localTypebot.delay_message = data?.delay_message;
|
||||
this.logger.verbose(`Typebot delay_message: ${this.localTypebot.delay_message}`);
|
||||
|
||||
this.localTypebot.unknown_message = data?.unknown_message;
|
||||
this.logger.verbose(`Typebot unknown_message: ${this.localTypebot.unknown_message}`);
|
||||
|
||||
this.localTypebot.sessions = data?.sessions;
|
||||
|
||||
this.logger.verbose('Typebot loaded');
|
||||
}
|
||||
|
||||
public async setTypebot(data: TypebotRaw) {
|
||||
this.logger.verbose('Setting typebot');
|
||||
await this.repository.typebot.create(data, this.instanceName);
|
||||
this.logger.verbose(`Typebot typebot: ${data.typebot}`);
|
||||
this.logger.verbose(`Typebot expire: ${data.expire}`);
|
||||
this.logger.verbose(`Typebot keyword_finish: ${data.keyword_finish}`);
|
||||
this.logger.verbose(`Typebot delay_message: ${data.delay_message}`);
|
||||
this.logger.verbose(`Typebot unknown_message: ${data.unknown_message}`);
|
||||
Object.assign(this.localTypebot, data);
|
||||
this.logger.verbose('Typebot set');
|
||||
}
|
||||
|
||||
public async findTypebot() {
|
||||
this.logger.verbose('Finding typebot');
|
||||
const data = await this.repository.typebot.find(this.instanceName);
|
||||
|
||||
if (!data) {
|
||||
this.logger.verbose('Typebot not found');
|
||||
throw new NotFoundException('Typebot not found');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private async loadProxy() {
|
||||
this.logger.verbose('Loading proxy');
|
||||
const data = await this.repository.proxy.find(this.instanceName);
|
||||
|
||||
this.localProxy.enabled = data?.enabled;
|
||||
this.logger.verbose(`Proxy enabled: ${this.localProxy.enabled}`);
|
||||
|
||||
this.localProxy.proxy = data?.proxy;
|
||||
this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy}`);
|
||||
|
||||
this.logger.verbose('Proxy loaded');
|
||||
}
|
||||
|
||||
public async setProxy(data: ProxyRaw) {
|
||||
this.logger.verbose('Setting proxy');
|
||||
await this.repository.proxy.create(data, this.instanceName);
|
||||
this.logger.verbose(`Proxy proxy: ${data.proxy}`);
|
||||
Object.assign(this.localProxy, data);
|
||||
this.logger.verbose('Proxy set');
|
||||
|
||||
this.client?.ws?.close();
|
||||
}
|
||||
|
||||
public async findProxy() {
|
||||
this.logger.verbose('Finding proxy');
|
||||
const data = await this.repository.proxy.find(this.instanceName);
|
||||
|
||||
if (!data) {
|
||||
this.logger.verbose('Proxy not found');
|
||||
throw new NotFoundException('Proxy not found');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async sendDataWebhook<T = any>(event: Events, data: T, local = true) {
|
||||
const webhookGlobal = this.configService.get<Webhook>('WEBHOOK');
|
||||
const webhookLocal = this.localWebhook.events;
|
||||
const websocketLocal = this.localWebsocket.events;
|
||||
const rabbitmqLocal = this.localRabbitmq.events;
|
||||
const serverUrl = this.configService.get<HttpServer>('SERVER').URL;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
||||
@@ -420,6 +592,59 @@ export class WAStartupService {
|
||||
const tokenStore = await this.repository.auth.find(this.instanceName);
|
||||
const instanceApikey = tokenStore?.apikey || 'Apikey not found';
|
||||
|
||||
if (this.localRabbitmq.enabled) {
|
||||
const amqp = getAMQP();
|
||||
|
||||
if (amqp) {
|
||||
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', { durable: false });
|
||||
|
||||
const queueName = `${this.instanceName}.${event}`;
|
||||
|
||||
amqp.assertQueue(queueName, { durable: false });
|
||||
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
|
||||
const message = {
|
||||
event,
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
if (expose && instanceApikey) {
|
||||
message['apikey'] = instanceApikey;
|
||||
}
|
||||
|
||||
amqp.publish(exchangeName, event, Buffer.from(JSON.stringify(message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.configService.get<Websocket>('WEBSOCKET').ENABLED && this.localWebsocket.enabled) {
|
||||
this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name);
|
||||
if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) {
|
||||
this.logger.verbose('Sending data to websocket on event: ' + event);
|
||||
const io = getIO();
|
||||
|
||||
const message = {
|
||||
event,
|
||||
instance: this.instance.name,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
if (expose && instanceApikey) {
|
||||
message['apikey'] = instanceApikey;
|
||||
}
|
||||
|
||||
this.logger.verbose('Sending data to socket.io in channel: ' + this.instance.name);
|
||||
io.of(`/${this.instance.name}`).emit(event, message);
|
||||
}
|
||||
}
|
||||
|
||||
const globalApiKey = this.configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
|
||||
|
||||
if (local) {
|
||||
@@ -585,23 +810,6 @@ export class WAStartupService {
|
||||
statusReason: DisconnectReason.connectionClosed,
|
||||
});
|
||||
|
||||
this.logger.verbose('Sending data to webhook in event STATUS_INSTANCE');
|
||||
this.sendDataWebhook(Events.STATUS_INSTANCE, {
|
||||
instance: this.instance.name,
|
||||
status: 'removed',
|
||||
});
|
||||
|
||||
if (this.localChatwoot.enabled) {
|
||||
this.chatwootService.eventWhatsapp(
|
||||
Events.STATUS_INSTANCE,
|
||||
{ instanceName: this.instance.name },
|
||||
{
|
||||
instance: this.instance.name,
|
||||
status: 'removed',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.verbose('endSession defined as true');
|
||||
this.endSession = true;
|
||||
|
||||
@@ -612,11 +820,13 @@ export class WAStartupService {
|
||||
this.logger.verbose('Incrementing QR code count');
|
||||
this.instance.qrcode.count++;
|
||||
|
||||
const color = this.configService.get<QrCode>('QRCODE').COLOR;
|
||||
|
||||
const optsQrcode: QRCodeToDataURLOptions = {
|
||||
margin: 3,
|
||||
scale: 4,
|
||||
errorCorrectionLevel: 'H',
|
||||
color: { light: '#ffffff', dark: '#198754' },
|
||||
color: { light: '#ffffff', dark: color },
|
||||
};
|
||||
|
||||
if (this.phoneNumber) {
|
||||
@@ -827,6 +1037,10 @@ export class WAStartupService {
|
||||
this.loadWebhook();
|
||||
this.loadChatwoot();
|
||||
this.loadSettings();
|
||||
this.loadWebsocket();
|
||||
this.loadRabbitmq();
|
||||
this.loadTypebot();
|
||||
this.loadProxy();
|
||||
|
||||
this.instance.authState = await this.defineAuthState();
|
||||
|
||||
@@ -836,7 +1050,18 @@ export class WAStartupService {
|
||||
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
|
||||
this.logger.verbose('Browser: ' + JSON.stringify(browser));
|
||||
|
||||
let options;
|
||||
|
||||
if (this.localProxy.enabled) {
|
||||
this.logger.verbose('Proxy enabled');
|
||||
options = {
|
||||
agent: new ProxyAgent(this.localProxy.proxy as any),
|
||||
fetchAgent: new ProxyAgent(this.localProxy.proxy as any),
|
||||
};
|
||||
}
|
||||
|
||||
const socketConfig: UserFacingSocketConfig = {
|
||||
...options,
|
||||
auth: {
|
||||
creds: this.instance.authState.state.creds,
|
||||
keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })),
|
||||
@@ -1134,6 +1359,14 @@ export class WAStartupService {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.localTypebot.enabled && messageRaw.key.remoteJid.includes('@s.whatsapp.net')) {
|
||||
await this.typebotService.sendTypebot(
|
||||
{ instanceName: this.instance.name },
|
||||
messageRaw.key.remoteJid,
|
||||
messageRaw,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.verbose('Inserting message in database');
|
||||
await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE);
|
||||
|
||||
@@ -1945,6 +2178,14 @@ export class WAStartupService {
|
||||
this.logger.verbose('File name: ' + mediaMessage.fileName);
|
||||
}
|
||||
|
||||
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
|
||||
mediaMessage.fileName = 'image.png';
|
||||
}
|
||||
|
||||
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
|
||||
mediaMessage.fileName = 'video.mp4';
|
||||
}
|
||||
|
||||
let mimetype: string;
|
||||
|
||||
if (isURL(mediaMessage.media)) {
|
||||
@@ -2346,20 +2587,55 @@ export class WAStartupService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getLastMessage(number: string) {
|
||||
const messages = await this.fetchMessages({
|
||||
where: {
|
||||
key: {
|
||||
remoteJid: number,
|
||||
},
|
||||
owner: this.instance.name,
|
||||
},
|
||||
});
|
||||
|
||||
let lastMessage = messages.pop();
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.messageTimestamp >= lastMessage.messageTimestamp) {
|
||||
lastMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
return lastMessage as unknown as LastMessage;
|
||||
}
|
||||
|
||||
public async archiveChat(data: ArchiveChatDto) {
|
||||
this.logger.verbose('Archiving chat');
|
||||
try {
|
||||
data.lastMessage.messageTimestamp = data.lastMessage?.messageTimestamp ?? Date.now();
|
||||
let last_message = data.lastMessage;
|
||||
let number = data.chat;
|
||||
|
||||
if (!last_message && number) {
|
||||
last_message = await this.getLastMessage(number);
|
||||
} else {
|
||||
last_message = data.lastMessage;
|
||||
last_message.messageTimestamp = last_message?.messageTimestamp ?? Date.now();
|
||||
number = last_message?.key?.remoteJid;
|
||||
}
|
||||
|
||||
if (!last_message || Object.keys(last_message).length === 0) {
|
||||
throw new NotFoundException('Last message not found');
|
||||
}
|
||||
|
||||
await this.client.chatModify(
|
||||
{
|
||||
archive: data.archive,
|
||||
lastMessages: [data.lastMessage],
|
||||
lastMessages: [last_message],
|
||||
},
|
||||
data.lastMessage.key.remoteJid,
|
||||
this.createJid(number),
|
||||
);
|
||||
|
||||
return {
|
||||
chatId: data.lastMessage.key.remoteJid,
|
||||
chatId: number,
|
||||
archived: true,
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user