mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-09 01:49:37 -06:00
refactor: update TypeScript build process and dependencies
- Changed the build command in package.json to use TypeScript compiler (tsc) with noEmit option. - Added @swc/core and @swc/helpers as development dependencies for improved performance. refactor: clean up WhatsApp Baileys service - Removed unused properties and interfaces related to message keys. - Simplified message handling logic by removing redundant checks and conditions. - Updated message timestamp handling for consistency. - Improved readability and maintainability by restructuring code and removing commented-out sections. refactor: optimize Chatwoot service - Streamlined database queries by reusing PostgreSQL client connection. - Enhanced conversation creation logic with better cache handling. - Removed unnecessary methods and improved existing ones for clarity. - Updated message sending logic to handle file streams instead of buffers. fix: improve translation loading mechanism - Simplified translation file loading by removing environment variable checks. - Ensured translations are loaded from a consistent path within the project structure.
This commit is contained in:
parent
c041986e26
commit
4b043cb4b8
2389
package-lock.json
generated
2389
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -56,7 +56,7 @@
|
|||||||
"eslint --fix"
|
"eslint --fix"
|
||||||
],
|
],
|
||||||
"src/**/*.ts": [
|
"src/**/*.ts": [
|
||||||
"sh -c 'npm run build'"
|
"sh -c 'tsc --noEmit'"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@ -126,6 +126,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^19.8.1",
|
"@commitlint/cli": "^19.8.1",
|
||||||
"@commitlint/config-conventional": "^19.8.1",
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
|
"@swc/core": "^1.13.5",
|
||||||
|
"@swc/helpers": "^0.5.17",
|
||||||
"@types/compression": "^1.7.5",
|
"@types/compression": "^1.7.5",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^4.17.18",
|
"@types/express": "^4.17.18",
|
||||||
|
|||||||
@ -152,13 +152,7 @@ import { v4 } from 'uuid';
|
|||||||
import { BaileysMessageProcessor } from './baileysMessage.processor';
|
import { BaileysMessageProcessor } from './baileysMessage.processor';
|
||||||
import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys';
|
import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys';
|
||||||
|
|
||||||
export interface ExtendedMessageKey extends WAMessageKey {
|
|
||||||
senderPn?: string;
|
|
||||||
previousRemoteJid?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExtendedIMessageKey extends proto.IMessageKey {
|
export interface ExtendedIMessageKey extends proto.IMessageKey {
|
||||||
senderPn?: string;
|
|
||||||
remoteJidAlt?: string;
|
remoteJidAlt?: string;
|
||||||
participantAlt?: string;
|
participantAlt?: string;
|
||||||
server_id?: string;
|
server_id?: string;
|
||||||
@ -1004,10 +998,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.key.remoteJid?.includes('@lid') && (m.key as ExtendedIMessageKey).senderPn) {
|
|
||||||
m.key.remoteJid = (m.key as ExtendedIMessageKey).senderPn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Long.isLong(m?.messageTimestamp)) {
|
if (Long.isLong(m?.messageTimestamp)) {
|
||||||
m.messageTimestamp = m.messageTimestamp?.toNumber();
|
m.messageTimestamp = m.messageTimestamp?.toNumber();
|
||||||
}
|
}
|
||||||
@ -1069,16 +1059,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
settings: any,
|
settings: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
// Garantir que localChatwoot está carregado antes de processar mensagens
|
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && !this.localChatwoot?.enabled) {
|
|
||||||
await this.loadChatwoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const received of messages) {
|
for (const received of messages) {
|
||||||
if (received.key.remoteJid?.includes('@lid') && (received.key as ExtendedMessageKey).senderPn) {
|
|
||||||
(received.key as ExtendedMessageKey).previousRemoteJid = received.key.remoteJid;
|
|
||||||
received.key.remoteJid = (received.key as ExtendedMessageKey).senderPn;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
received?.messageStubParameters?.some?.((param) =>
|
received?.messageStubParameters?.some?.((param) =>
|
||||||
[
|
[
|
||||||
@ -1126,9 +1107,9 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage);
|
await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage);
|
||||||
const oldMessage = await this.getMessage(editedMessage.key, true);
|
const oldMessage = await this.getMessage(editedMessage.key, true);
|
||||||
if ((oldMessage as any)?.id) {
|
if ((oldMessage as any)?.id) {
|
||||||
const editedMessageTimestamp = Long.isLong(editedMessage?.timestampMs)
|
const editedMessageTimestamp = Long.isLong(received?.messageTimestamp)
|
||||||
? Math.floor(editedMessage.timestampMs.toNumber() / 1000)
|
? Math.floor(received?.messageTimestamp.toNumber())
|
||||||
: Math.floor((editedMessage.timestampMs as number) / 1000);
|
: Math.floor(received?.messageTimestamp as number);
|
||||||
|
|
||||||
await this.prismaRepository.message.update({
|
await this.prismaRepository.message.update({
|
||||||
where: { id: (oldMessage as any).id },
|
where: { id: (oldMessage as any).id },
|
||||||
@ -1367,10 +1348,6 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageRaw.key.remoteJid?.includes('@lid') && messageRaw.key.remoteJidAlt) {
|
|
||||||
messageRaw.key.remoteJid = messageRaw.key.remoteJidAlt;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log(messageRaw);
|
this.logger.log(messageRaw);
|
||||||
|
|
||||||
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
|
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
|
||||||
@ -1446,25 +1423,18 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.remoteJid?.includes('@lid') && key.remoteJidAlt) {
|
if (update.message !== null && update.status === undefined) continue;
|
||||||
key.remoteJid = key.remoteJidAlt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateKey = `${this.instance.id}_${key.id}_${update.status}`;
|
const updateKey = `${this.instance.id}_${key.id}_${update.status}`;
|
||||||
|
|
||||||
const cached = await this.baileysCache.get(updateKey);
|
const cached = await this.baileysCache.get(updateKey);
|
||||||
|
|
||||||
// Não ignorar mensagens deletadas (messageStubType === 1) mesmo que estejam em cache
|
if (cached) {
|
||||||
const isDeletedMessage = update.messageStubType === 1;
|
|
||||||
|
|
||||||
if (cached && !isDeletedMessage) {
|
|
||||||
this.logger.info(`Message duplicated ignored [avoid deadlock]: ${updateKey}`);
|
this.logger.info(`Message duplicated ignored [avoid deadlock]: ${updateKey}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDeletedMessage) {
|
await this.baileysCache.set(updateKey, true, 30 * 60);
|
||||||
await this.baileysCache.set(updateKey, true, this.UPDATE_CACHE_TTL_SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status[update.status] === 'READ' && key.fromMe) {
|
if (status[update.status] === 'READ' && key.fromMe) {
|
||||||
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
||||||
@ -1494,8 +1464,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
remoteJid: key?.remoteJid,
|
remoteJid: key?.remoteJid,
|
||||||
fromMe: key.fromMe,
|
fromMe: key.fromMe,
|
||||||
participant: key?.remoteJid,
|
participant: key?.participant,
|
||||||
status: status[update.status] ?? 'DELETED',
|
status: status[update.status],
|
||||||
pollUpdates,
|
pollUpdates,
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
@ -1568,22 +1538,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
this.sendDataWebhook(Events.MESSAGES_UPDATE, message);
|
this.sendDataWebhook(Events.MESSAGES_UPDATE, message);
|
||||||
|
|
||||||
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE) {
|
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE)
|
||||||
// Verificar se a mensagem ainda existe antes de criar o update
|
await this.prismaRepository.messageUpdate.create({ data: message });
|
||||||
const messageExists = await this.prismaRepository.message.findFirst({
|
|
||||||
where: {
|
|
||||||
instanceId: message.instanceId,
|
|
||||||
key: {
|
|
||||||
path: ['id'],
|
|
||||||
equals: message.keyId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (messageExists) {
|
|
||||||
await this.prismaRepository.messageUpdate.create({ data: message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingChat = await this.prismaRepository.chat.findFirst({
|
const existingChat = await this.prismaRepository.chat.findFirst({
|
||||||
where: { instanceId: this.instanceId, remoteJid: message.remoteJid },
|
where: { instanceId: this.instanceId, remoteJid: message.remoteJid },
|
||||||
@ -2193,7 +2149,20 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkPreview = options?.linkPreview != false ? undefined : false;
|
let linkPreview: boolean | undefined;
|
||||||
|
let conversationText: string | undefined;
|
||||||
|
|
||||||
|
if (typeof message === 'object' && 'conversation' in message && typeof message['conversation'] === 'string') {
|
||||||
|
conversationText = message['conversation'];
|
||||||
|
if (conversationText.includes('[linkPreview=false]')) {
|
||||||
|
message['conversation'] = conversationText.replace('[linkPreview=false]', '').trim();
|
||||||
|
linkPreview = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkPreview === undefined) {
|
||||||
|
linkPreview = options?.linkPreview != false ? undefined : false;
|
||||||
|
}
|
||||||
|
|
||||||
let quoted: WAMessage;
|
let quoted: WAMessage;
|
||||||
|
|
||||||
@ -3453,18 +3422,13 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const numberJid = numberVerified?.jid || user.jid;
|
const numberJid = numberVerified?.jid || user.jid;
|
||||||
// const lid =
|
|
||||||
// typeof numberVerified?.lid === 'string'
|
|
||||||
// ? numberVerified.lid
|
|
||||||
// : numberJid.includes('@lid')
|
|
||||||
// ? numberJid.split('@')[1]
|
|
||||||
// : undefined;
|
|
||||||
return new OnWhatsAppDto(
|
return new OnWhatsAppDto(
|
||||||
numberJid,
|
numberJid,
|
||||||
!!numberVerified?.exists,
|
!!numberVerified?.exists,
|
||||||
user.number,
|
user.number,
|
||||||
contacts.find((c) => c.remoteJid === numberJid)?.pushName,
|
contacts.find((c) => c.remoteJid === numberJid)?.pushName,
|
||||||
// lid,
|
undefined,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -3616,7 +3580,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
keyId: messageId,
|
keyId: messageId,
|
||||||
remoteJid: response.key.remoteJid,
|
remoteJid: response.key.remoteJid,
|
||||||
fromMe: response.key.fromMe,
|
fromMe: response.key.fromMe,
|
||||||
participant: response.key?.remoteJid,
|
participant: response.key?.participant,
|
||||||
status: 'DELETED',
|
status: 'DELETED',
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
@ -4051,7 +4015,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
keyId: messageId,
|
keyId: messageId,
|
||||||
remoteJid: messageSent.key.remoteJid,
|
remoteJid: messageSent.key.remoteJid,
|
||||||
fromMe: messageSent.key.fromMe,
|
fromMe: messageSent.key.fromMe,
|
||||||
participant: messageSent.key?.remoteJid,
|
participant: messageSent.key?.participant,
|
||||||
status: 'EDITED',
|
status: 'EDITED',
|
||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
@ -4647,9 +4611,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
public async baileysAssertSessions(jids: string[]) {
|
||||||
public async baileysAssertSessions(jids: string[], _force?: boolean) {
|
|
||||||
// Note: _force parameter kept for API compatibility but not used in Baileys 7.0.0-rc.5+
|
|
||||||
const response = await this.client.assertSessions(jids);
|
const response = await this.client.assertSessions(jids);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@ -4854,7 +4816,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
{
|
{
|
||||||
OR: [
|
OR: [
|
||||||
keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {},
|
keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {},
|
||||||
keyFilters?.senderPn ? { key: { path: ['senderPn'], equals: keyFilters?.senderPn } } : {},
|
keyFilters?.remoteJidAlt ? { key: { path: ['remoteJidAlt'], equals: keyFilters?.remoteJidAlt } } : {},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -4884,7 +4846,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
{
|
{
|
||||||
OR: [
|
OR: [
|
||||||
keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {},
|
keyFilters?.remoteJid ? { key: { path: ['remoteJid'], equals: keyFilters?.remoteJid } } : {},
|
||||||
keyFilters?.senderPn ? { key: { path: ['senderPn'], equals: keyFilters?.senderPn } } : {},
|
keyFilters?.remoteJidAlt ? { key: { path: ['remoteJidAlt'], equals: keyFilters?.remoteJidAlt } } : {},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { InstanceDto } from '@api/dto/instance.dto';
|
import { InstanceDto } from '@api/dto/instance.dto';
|
||||||
import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '@api/dto/sendMessage.dto';
|
import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '@api/dto/sendMessage.dto';
|
||||||
import { ExtendedMessageKey } from '@api/integrations/channel/whatsapp/whatsapp.baileys.service';
|
|
||||||
import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto';
|
import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto';
|
||||||
import { postgresClient } from '@api/integrations/chatbot/chatwoot/libs/postgres.client';
|
import { postgresClient } from '@api/integrations/chatbot/chatwoot/libs/postgres.client';
|
||||||
import { chatwootImport } from '@api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper';
|
import { chatwootImport } from '@api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper';
|
||||||
@ -24,7 +23,7 @@ import { Chatwoot as ChatwootModel, Contact as ContactModel, Message as MessageM
|
|||||||
import i18next from '@utils/i18n';
|
import i18next from '@utils/i18n';
|
||||||
import { sendTelemetry } from '@utils/sendTelemetry';
|
import { sendTelemetry } from '@utils/sendTelemetry';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { proto } from 'baileys';
|
import { proto, WAMessageKey } from 'baileys';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
import { Jimp, JimpMime } from 'jimp';
|
import { Jimp, JimpMime } from 'jimp';
|
||||||
@ -74,9 +73,7 @@ export class ChatwootService {
|
|||||||
private readonly cache: CacheService,
|
private readonly cache: CacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private async getPgClient() {
|
private pgClient = postgresClient.getChatwootConnection();
|
||||||
return postgresClient.getChatwootConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getProvider(instance: InstanceDto): Promise<ChatwootModel | null> {
|
private async getProvider(instance: InstanceDto): Promise<ChatwootModel | null> {
|
||||||
const cacheKey = `${instance.instanceName}:getProvider`;
|
const cacheKey = `${instance.instanceName}:getProvider`;
|
||||||
@ -405,8 +402,7 @@ export class ChatwootService {
|
|||||||
if (!uri) return false;
|
if (!uri) return false;
|
||||||
|
|
||||||
const sqlTags = `SELECT id, taggings_count FROM tags WHERE name = $1 LIMIT 1`;
|
const sqlTags = `SELECT id, taggings_count FROM tags WHERE name = $1 LIMIT 1`;
|
||||||
const pgClient = await this.getPgClient();
|
const tagData = (await this.pgClient.query(sqlTags, [nameInbox]))?.rows[0];
|
||||||
const tagData = (await pgClient.query(sqlTags, [nameInbox]))?.rows[0];
|
|
||||||
let tagId = tagData?.id;
|
let tagId = tagData?.id;
|
||||||
const taggingsCount = tagData?.taggings_count || 0;
|
const taggingsCount = tagData?.taggings_count || 0;
|
||||||
|
|
||||||
@ -416,18 +412,18 @@ export class ChatwootService {
|
|||||||
DO UPDATE SET taggings_count = tags.taggings_count + 1
|
DO UPDATE SET taggings_count = tags.taggings_count + 1
|
||||||
RETURNING id`;
|
RETURNING id`;
|
||||||
|
|
||||||
tagId = (await pgClient.query(sqlTag, [nameInbox, taggingsCount + 1]))?.rows[0]?.id;
|
tagId = (await this.pgClient.query(sqlTag, [nameInbox, taggingsCount + 1]))?.rows[0]?.id;
|
||||||
|
|
||||||
const sqlCheckTagging = `SELECT 1 FROM taggings
|
const sqlCheckTagging = `SELECT 1 FROM taggings
|
||||||
WHERE tag_id = $1 AND taggable_type = 'Contact' AND taggable_id = $2 AND context = 'labels' LIMIT 1`;
|
WHERE tag_id = $1 AND taggable_type = 'Contact' AND taggable_id = $2 AND context = 'labels' LIMIT 1`;
|
||||||
|
|
||||||
const taggingExists = (await pgClient.query(sqlCheckTagging, [tagId, contactId]))?.rowCount > 0;
|
const taggingExists = (await this.pgClient.query(sqlCheckTagging, [tagId, contactId]))?.rowCount > 0;
|
||||||
|
|
||||||
if (!taggingExists) {
|
if (!taggingExists) {
|
||||||
const sqlInsertLabel = `INSERT INTO taggings (tag_id, taggable_type, taggable_id, context, created_at)
|
const sqlInsertLabel = `INSERT INTO taggings (tag_id, taggable_type, taggable_id, context, created_at)
|
||||||
VALUES ($1, 'Contact', $2, 'labels', NOW())`;
|
VALUES ($1, 'Contact', $2, 'labels', NOW())`;
|
||||||
|
|
||||||
await pgClient.query(sqlInsertLabel, [tagId, contactId]);
|
await this.pgClient.query(sqlInsertLabel, [tagId, contactId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -592,27 +588,29 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createConversation(instance: InstanceDto, body: any) {
|
public async createConversation(instance: InstanceDto, body: any) {
|
||||||
const isLid = body.key.previousRemoteJid?.includes('@lid') && body.key.senderPn;
|
const isLid = body.key.addressingMode === 'lid' && body.key.remoteJidAlt;
|
||||||
const remoteJid = body.key.remoteJid;
|
const remoteJid = isLid ? body.key.remoteJidAlt : body.key.remoteJid;
|
||||||
const cacheKey = `${instance.instanceName}:createConversation-${remoteJid}`;
|
const cacheKey = `${instance.instanceName}:createConversation-${remoteJid}`;
|
||||||
const lockKey = `${instance.instanceName}:lock:createConversation-${remoteJid}`;
|
const lockKey = `${instance.instanceName}:lock:createConversation-${remoteJid}`;
|
||||||
const maxWaitTime = 5000; // 5 secounds
|
const maxWaitTime = 5000; // 5 seconds
|
||||||
|
const client = await this.clientCw(instance);
|
||||||
|
if (!client) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Processa atualização de contatos já criados @lid
|
// Processa atualização de contatos já criados @lid
|
||||||
if (isLid && body.key.senderPn !== body.key.previousRemoteJid) {
|
if (isLid && body.key.remoteJidAlt !== body.key.remoteJid) {
|
||||||
const contact = await this.findContact(instance, body.key.remoteJid.split('@')[0]);
|
const contact = await this.findContact(instance, body.key.remoteJid.split('@')[0]);
|
||||||
if (contact && contact.identifier !== body.key.senderPn) {
|
if (contact && contact.identifier !== body.key.remoteJidAlt) {
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
`Identifier needs update: (contact.identifier: ${contact.identifier}, body.key.remoteJid: ${body.key.remoteJid}, body.key.senderPn: ${body.key.senderPn}`,
|
`Identifier needs update: (contact.identifier: ${contact.identifier}, body.key.remoteJid: ${body.key.remoteJid}, body.key.remoteJidAlt: ${body.key.remoteJidAlt}`,
|
||||||
);
|
);
|
||||||
const updateContact = await this.updateContact(instance, contact.id, {
|
const updateContact = await this.updateContact(instance, contact.id, {
|
||||||
identifier: body.key.senderPn,
|
identifier: body.key.remoteJidAlt,
|
||||||
phone_number: `+${body.key.senderPn.split('@')[0]}`,
|
phone_number: `+${body.key.remoteJidAlt.split('@')[0]}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updateContact === null) {
|
if (updateContact === null) {
|
||||||
const baseContact = await this.findContact(instance, body.key.senderPn.split('@')[0]);
|
const baseContact = await this.findContact(instance, body.key.remoteJidAlt.split('@')[0]);
|
||||||
if (baseContact) {
|
if (baseContact) {
|
||||||
await this.mergeContacts(baseContact.id, contact.id);
|
await this.mergeContacts(baseContact.id, contact.id);
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
@ -625,7 +623,28 @@ export class ChatwootService {
|
|||||||
this.logger.verbose(`--- Start createConversation ---`);
|
this.logger.verbose(`--- Start createConversation ---`);
|
||||||
this.logger.verbose(`Instance: ${JSON.stringify(instance)}`);
|
this.logger.verbose(`Instance: ${JSON.stringify(instance)}`);
|
||||||
|
|
||||||
// Always check Chatwoot first, cache only as fallback
|
// If it already exists in the cache, return conversationId
|
||||||
|
if (await this.cache.has(cacheKey)) {
|
||||||
|
const conversationId = (await this.cache.get(cacheKey)) as number;
|
||||||
|
this.logger.verbose(`Found conversation to: ${remoteJid}, conversation ID: ${conversationId}`);
|
||||||
|
let conversationExists: conversation | boolean;
|
||||||
|
try {
|
||||||
|
conversationExists = await client.conversations.get({
|
||||||
|
accountId: this.provider.accountId,
|
||||||
|
conversationId: conversationId,
|
||||||
|
});
|
||||||
|
this.logger.verbose(`Conversation exists: ${JSON.stringify(conversationExists)}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error getting conversation: ${error}`);
|
||||||
|
conversationExists = false;
|
||||||
|
}
|
||||||
|
if (!conversationExists) {
|
||||||
|
this.logger.verbose('Conversation does not exist, re-calling createConversation');
|
||||||
|
this.cache.delete(cacheKey);
|
||||||
|
return await this.createConversation(instance, body);
|
||||||
|
}
|
||||||
|
return conversationId;
|
||||||
|
}
|
||||||
|
|
||||||
// If lock already exists, wait until release or timeout
|
// If lock already exists, wait until release or timeout
|
||||||
if (await this.cache.has(lockKey)) {
|
if (await this.cache.has(lockKey)) {
|
||||||
@ -651,12 +670,12 @@ export class ChatwootService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
Double check after lock - REMOVED
|
Double check after lock
|
||||||
This was causing the system to use cached conversations instead of checking Chatwoot
|
Utilizei uma nova verificação para evitar que outra thread execute entre o terminio do while e o set lock
|
||||||
*/
|
*/
|
||||||
|
if (await this.cache.has(cacheKey)) {
|
||||||
const client = await this.clientCw(instance);
|
return (await this.cache.get(cacheKey)) as number;
|
||||||
if (!client) return null;
|
}
|
||||||
|
|
||||||
const isGroup = remoteJid.includes('@g.us');
|
const isGroup = remoteJid.includes('@g.us');
|
||||||
const chatId = isGroup ? remoteJid : remoteJid.split('@')[0];
|
const chatId = isGroup ? remoteJid : remoteJid.split('@')[0];
|
||||||
@ -760,39 +779,34 @@ export class ChatwootService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inboxConversation = null;
|
let inboxConversation = contactConversations.payload.find(
|
||||||
|
(conversation) => conversation.inbox_id == filterInbox.id,
|
||||||
if (this.provider.reopenConversation) {
|
);
|
||||||
inboxConversation = this.findOpenConversation(contactConversations.payload, filterInbox.id);
|
if (inboxConversation) {
|
||||||
|
if (this.provider.reopenConversation) {
|
||||||
|
this.logger.verbose(`Found conversation in reopenConversation mode: ${JSON.stringify(inboxConversation)}`);
|
||||||
|
if (inboxConversation && this.provider.conversationPending && inboxConversation.status !== 'open') {
|
||||||
|
await client.conversations.toggleStatus({
|
||||||
|
accountId: this.provider.accountId,
|
||||||
|
conversationId: inboxConversation.id,
|
||||||
|
data: {
|
||||||
|
status: 'pending',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inboxConversation = contactConversations.payload.find(
|
||||||
|
(conversation) =>
|
||||||
|
conversation && conversation.status !== 'resolved' && conversation.inbox_id == filterInbox.id,
|
||||||
|
);
|
||||||
|
this.logger.verbose(`Found conversation: ${JSON.stringify(inboxConversation)}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (inboxConversation) {
|
if (inboxConversation) {
|
||||||
this.logger.verbose(
|
this.logger.verbose(`Returning existing conversation ID: ${inboxConversation.id}`);
|
||||||
`Found open conversation in reopenConversation mode: ${JSON.stringify(inboxConversation)}`,
|
this.cache.set(cacheKey, inboxConversation.id, 8 * 3600);
|
||||||
);
|
return inboxConversation.id;
|
||||||
} else {
|
|
||||||
inboxConversation = await this.findAndReopenResolvedConversation(
|
|
||||||
client,
|
|
||||||
contactConversations.payload,
|
|
||||||
filterInbox.id,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
inboxConversation = this.findOpenConversation(contactConversations.payload, filterInbox.id);
|
|
||||||
this.logger.verbose(`Found conversation: ${JSON.stringify(inboxConversation)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inboxConversation) {
|
|
||||||
this.logger.verbose(`Returning existing conversation ID: ${inboxConversation.id}`);
|
|
||||||
this.cache.set(cacheKey, inboxConversation.id);
|
|
||||||
return inboxConversation.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await this.cache.has(cacheKey)) {
|
|
||||||
const conversationId = (await this.cache.get(cacheKey)) as number;
|
|
||||||
this.logger.warn(
|
|
||||||
`No active conversations found in Chatwoot, using cached conversation ID: ${conversationId} as fallback`,
|
|
||||||
);
|
|
||||||
return conversationId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@ -823,7 +837,7 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.verbose(`New conversation created of ${remoteJid} with ID: ${conversation.id}`);
|
this.logger.verbose(`New conversation created of ${remoteJid} with ID: ${conversation.id}`);
|
||||||
this.cache.set(cacheKey, conversation.id);
|
this.cache.set(cacheKey, conversation.id, 8 * 3600);
|
||||||
return conversation.id;
|
return conversation.id;
|
||||||
} finally {
|
} finally {
|
||||||
await this.cache.delete(lockKey);
|
await this.cache.delete(lockKey);
|
||||||
@ -835,45 +849,6 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private findOpenConversation(conversations: any[], inboxId: number): any | null {
|
|
||||||
const openConversation = conversations.find(
|
|
||||||
(conversation) => conversation && conversation.status !== 'resolved' && conversation.inbox_id == inboxId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (openConversation) {
|
|
||||||
this.logger.verbose(`Found open conversation: ${JSON.stringify(openConversation)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return openConversation || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async findAndReopenResolvedConversation(
|
|
||||||
client: any,
|
|
||||||
conversations: any[],
|
|
||||||
inboxId: number,
|
|
||||||
): Promise<any | null> {
|
|
||||||
const resolvedConversation = conversations.find(
|
|
||||||
(conversation) => conversation && conversation.status === 'resolved' && conversation.inbox_id == inboxId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (resolvedConversation) {
|
|
||||||
this.logger.verbose(`Found resolved conversation to reopen: ${JSON.stringify(resolvedConversation)}`);
|
|
||||||
if (this.provider.conversationPending && resolvedConversation.status !== 'open') {
|
|
||||||
await client.conversations.toggleStatus({
|
|
||||||
accountId: this.provider.accountId,
|
|
||||||
conversationId: resolvedConversation.id,
|
|
||||||
data: {
|
|
||||||
status: 'pending',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.logger.verbose(`Reopened resolved conversation ID: ${resolvedConversation.id}`);
|
|
||||||
}
|
|
||||||
return resolvedConversation;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getInbox(instance: InstanceDto): Promise<inbox | null> {
|
public async getInbox(instance: InstanceDto): Promise<inbox | null> {
|
||||||
const cacheKey = `${instance.instanceName}:getInbox`;
|
const cacheKey = `${instance.instanceName}:getInbox`;
|
||||||
if (await this.cache.has(cacheKey)) {
|
if (await this.cache.has(cacheKey)) {
|
||||||
@ -921,7 +896,6 @@ export class ChatwootService {
|
|||||||
messageBody?: any,
|
messageBody?: any,
|
||||||
sourceId?: string,
|
sourceId?: string,
|
||||||
quotedMsg?: MessageModel,
|
quotedMsg?: MessageModel,
|
||||||
messageBodyForRetry?: any,
|
|
||||||
) {
|
) {
|
||||||
const client = await this.clientCw(instance);
|
const client = await this.clientCw(instance);
|
||||||
|
|
||||||
@ -930,86 +904,32 @@ export class ChatwootService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const doCreateMessage = async (convId: number) => {
|
const replyToIds = await this.getReplyToIds(messageBody, instance);
|
||||||
const replyToIds = await this.getReplyToIds(messageBody, instance);
|
|
||||||
|
|
||||||
const sourceReplyId = quotedMsg?.chatwootMessageId || null;
|
const sourceReplyId = quotedMsg?.chatwootMessageId || null;
|
||||||
|
|
||||||
const message = await client.messages.create({
|
const message = await client.messages.create({
|
||||||
accountId: this.provider.accountId,
|
accountId: this.provider.accountId,
|
||||||
conversationId: convId,
|
conversationId: conversationId,
|
||||||
data: {
|
data: {
|
||||||
content: content,
|
content: content,
|
||||||
message_type: messageType,
|
message_type: messageType,
|
||||||
attachments: attachments,
|
attachments: attachments,
|
||||||
private: privateMessage || false,
|
private: privateMessage || false,
|
||||||
source_id: sourceId,
|
source_id: sourceId,
|
||||||
content_attributes: {
|
content_attributes: {
|
||||||
...replyToIds,
|
...replyToIds,
|
||||||
},
|
|
||||||
source_reply_id: sourceReplyId ? sourceReplyId.toString() : null,
|
|
||||||
},
|
},
|
||||||
});
|
source_reply_id: sourceReplyId ? sourceReplyId.toString() : null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
this.logger.warn('message not found');
|
this.logger.warn('message not found');
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await doCreateMessage(conversationId);
|
|
||||||
} catch (error) {
|
|
||||||
return this.handleStaleConversationError(
|
|
||||||
error,
|
|
||||||
instance,
|
|
||||||
conversationId,
|
|
||||||
messageBody,
|
|
||||||
messageBodyForRetry,
|
|
||||||
'createMessage',
|
|
||||||
(newConvId) => doCreateMessage(newConvId),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async handleStaleConversationError(
|
return message;
|
||||||
error: any,
|
|
||||||
instance: InstanceDto,
|
|
||||||
conversationId: number,
|
|
||||||
messageBody: any,
|
|
||||||
messageBodyForRetry: any,
|
|
||||||
functionName: string,
|
|
||||||
originalFunction: (newConversationId: number) => Promise<any>,
|
|
||||||
) {
|
|
||||||
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
||||||
this.logger.warn(
|
|
||||||
`Conversation ${conversationId} not found in Chatwoot. Retrying operation from ${functionName}...`,
|
|
||||||
);
|
|
||||||
const bodyForRetry = messageBodyForRetry || messageBody;
|
|
||||||
|
|
||||||
if (!bodyForRetry || !bodyForRetry.key?.remoteJid) {
|
|
||||||
this.logger.error(`Cannot retry ${functionName} without a message body for context.`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { remoteJid } = bodyForRetry.key;
|
|
||||||
const cacheKey = `${instance.instanceName}:createConversation-${remoteJid}`;
|
|
||||||
await this.cache.delete(cacheKey);
|
|
||||||
|
|
||||||
const newConversationId = await this.createConversation(instance, bodyForRetry);
|
|
||||||
if (!newConversationId) {
|
|
||||||
this.logger.error(`Failed to create new conversation for ${remoteJid} during retry.`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log(`Retrying ${functionName} for ${remoteJid} with new conversation ${newConversationId}`);
|
|
||||||
return await originalFunction(newConversationId);
|
|
||||||
} else {
|
|
||||||
this.logger.error(`Error in ${functionName}: ${error}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getOpenConversationByContact(
|
public async getOpenConversationByContact(
|
||||||
@ -1094,7 +1014,7 @@ export class ChatwootService {
|
|||||||
|
|
||||||
private async sendData(
|
private async sendData(
|
||||||
conversationId: number,
|
conversationId: number,
|
||||||
fileData: Buffer | Readable,
|
fileStream: Readable,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
messageType: 'incoming' | 'outgoing' | undefined,
|
messageType: 'incoming' | 'outgoing' | undefined,
|
||||||
content?: string,
|
content?: string,
|
||||||
@ -1102,7 +1022,6 @@ export class ChatwootService {
|
|||||||
messageBody?: any,
|
messageBody?: any,
|
||||||
sourceId?: string,
|
sourceId?: string,
|
||||||
quotedMsg?: MessageModel,
|
quotedMsg?: MessageModel,
|
||||||
messageBodyForRetry?: any,
|
|
||||||
) {
|
) {
|
||||||
if (sourceId && this.isImportHistoryAvailable()) {
|
if (sourceId && this.isImportHistoryAvailable()) {
|
||||||
const messageAlreadySaved = await chatwootImport.getExistingSourceIds([sourceId], conversationId);
|
const messageAlreadySaved = await chatwootImport.getExistingSourceIds([sourceId], conversationId);
|
||||||
@ -1113,67 +1032,54 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const doSendData = async (convId: number) => {
|
const data = new FormData();
|
||||||
const data = new FormData();
|
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
data.append('content', content);
|
data.append('content', content);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.append('message_type', messageType);
|
||||||
|
|
||||||
|
data.append('attachments[]', fileStream, { filename: fileName });
|
||||||
|
|
||||||
|
const sourceReplyId = quotedMsg?.chatwootMessageId || null;
|
||||||
|
|
||||||
|
if (messageBody && instance) {
|
||||||
|
const replyToIds = await this.getReplyToIds(messageBody, instance);
|
||||||
|
|
||||||
|
if (replyToIds.in_reply_to || replyToIds.in_reply_to_external_id) {
|
||||||
|
const content = JSON.stringify({
|
||||||
|
...replyToIds,
|
||||||
|
});
|
||||||
|
data.append('content_attributes', content);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data.append('message_type', messageType);
|
if (sourceReplyId) {
|
||||||
|
data.append('source_reply_id', sourceReplyId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
if (fileData && fileName) {
|
if (sourceId) {
|
||||||
data.append('attachments[]', fileData, { filename: fileName });
|
data.append('source_id', sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceReplyId = quotedMsg?.chatwootMessageId || null;
|
const config = {
|
||||||
|
method: 'post',
|
||||||
if (messageBody && instance) {
|
maxBodyLength: Infinity,
|
||||||
const replyToIds = await this.getReplyToIds(messageBody, instance);
|
url: `${this.provider.url}/api/v1/accounts/${this.provider.accountId}/conversations/${conversationId}/messages`,
|
||||||
|
headers: {
|
||||||
if (replyToIds.in_reply_to || replyToIds.in_reply_to_external_id) {
|
api_access_token: this.provider.token,
|
||||||
const content = JSON.stringify({
|
...data.getHeaders(),
|
||||||
...replyToIds,
|
},
|
||||||
});
|
data: data,
|
||||||
data.append('content_attributes', content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourceReplyId) {
|
|
||||||
data.append('source_reply_id', sourceReplyId.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourceId) {
|
|
||||||
data.append('source_id', sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
method: 'post',
|
|
||||||
maxBodyLength: Infinity,
|
|
||||||
url: `${this.provider.url}/api/v1/accounts/${this.provider.accountId}/conversations/${convId}/messages`,
|
|
||||||
headers: {
|
|
||||||
api_access_token: this.provider.token,
|
|
||||||
...data.getHeaders(),
|
|
||||||
},
|
|
||||||
data: data,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data: responseData } = await axios.request(config);
|
|
||||||
return responseData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await doSendData(conversationId);
|
const { data } = await axios.request(config);
|
||||||
|
|
||||||
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.handleStaleConversationError(
|
this.logger.error(error);
|
||||||
error,
|
|
||||||
instance,
|
|
||||||
conversationId,
|
|
||||||
messageBody,
|
|
||||||
messageBodyForRetry,
|
|
||||||
'sendData',
|
|
||||||
(newConvId) => doSendData(newConvId),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1423,7 +1329,7 @@ export class ChatwootService {
|
|||||||
// Para outros tipos, converter para base64 puro (sem prefixo data URI)
|
// Para outros tipos, converter para base64 puro (sem prefixo data URI)
|
||||||
const base64Media = mediaBuffer.toString('base64');
|
const base64Media = mediaBuffer.toString('base64');
|
||||||
|
|
||||||
const documentExtensions = ['.gif', '.svg', '.tiff', '.tif'];
|
const documentExtensions = ['.gif', '.svg', '.tiff', '.tif', '.dxf', '.dwg'];
|
||||||
const parsedExt = path.parse(fileName)?.ext;
|
const parsedExt = path.parse(fileName)?.ext;
|
||||||
if (type === 'image' && parsedExt && documentExtensions.includes(parsedExt)) {
|
if (type === 'image' && parsedExt && documentExtensions.includes(parsedExt)) {
|
||||||
type = 'document';
|
type = 'document';
|
||||||
@ -1511,7 +1417,7 @@ export class ChatwootService {
|
|||||||
|
|
||||||
// Deletar cada mensagem no WhatsApp
|
// Deletar cada mensagem no WhatsApp
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
const key = message.key as ExtendedMessageKey;
|
const key = message.key as WAMessageKey;
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`[DELETE] Attempting to delete WhatsApp message - keyId: ${key?.id}, remoteJid: ${key?.remoteJid}`,
|
`[DELETE] Attempting to delete WhatsApp message - keyId: ${key?.id}, remoteJid: ${key?.remoteJid}`,
|
||||||
);
|
);
|
||||||
@ -1643,6 +1549,29 @@ export class ChatwootService {
|
|||||||
const senderName = body?.conversation?.messages[0]?.sender?.available_name || body?.sender?.name;
|
const senderName = body?.conversation?.messages[0]?.sender?.available_name || body?.sender?.name;
|
||||||
const waInstance = this.waMonitor.waInstances[instance.instanceName];
|
const waInstance = this.waMonitor.waInstances[instance.instanceName];
|
||||||
|
|
||||||
|
if (body.event === 'message_updated' && body.content_attributes?.deleted) {
|
||||||
|
const message = await this.prismaRepository.message.findFirst({
|
||||||
|
where: {
|
||||||
|
chatwootMessageId: body.id,
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
const key = message.key as WAMessageKey;
|
||||||
|
|
||||||
|
await waInstance?.client.sendMessage(key.remoteJid, { delete: key });
|
||||||
|
|
||||||
|
await this.prismaRepository.message.deleteMany({
|
||||||
|
where: {
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
chatwootMessageId: body.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { message: 'bot' };
|
||||||
|
}
|
||||||
|
|
||||||
const cwBotContact = this.configService.get<Chatwoot>('CHATWOOT').BOT_CONTACT;
|
const cwBotContact = this.configService.get<Chatwoot>('CHATWOOT').BOT_CONTACT;
|
||||||
|
|
||||||
if (chatId === '123456' && body.message_type === 'outgoing') {
|
if (chatId === '123456' && body.message_type === 'outgoing') {
|
||||||
@ -1714,7 +1643,10 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (body.message_type === 'outgoing' && body?.conversation?.messages?.length && chatId !== '123456') {
|
if (body.message_type === 'outgoing' && body?.conversation?.messages?.length && chatId !== '123456') {
|
||||||
if (body?.conversation?.messages[0]?.source_id?.substring(0, 5) === 'WAID:') {
|
if (
|
||||||
|
body?.conversation?.messages[0]?.source_id?.substring(0, 5) === 'WAID:' &&
|
||||||
|
body?.conversation?.messages[0]?.id === body?.id
|
||||||
|
) {
|
||||||
return { message: 'bot' };
|
return { message: 'bot' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1842,7 +1774,7 @@ export class ChatwootService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (lastMessage && !lastMessage.chatwootIsRead) {
|
if (lastMessage && !lastMessage.chatwootIsRead) {
|
||||||
const key = lastMessage.key as ExtendedMessageKey;
|
const key = lastMessage.key as WAMessageKey;
|
||||||
|
|
||||||
waInstance?.markMessageAsRead({
|
waInstance?.markMessageAsRead({
|
||||||
readMessages: [
|
readMessages: [
|
||||||
@ -1900,7 +1832,7 @@ export class ChatwootService {
|
|||||||
chatwootMessageIds: ChatwootMessage,
|
chatwootMessageIds: ChatwootMessage,
|
||||||
instance: InstanceDto,
|
instance: InstanceDto,
|
||||||
) {
|
) {
|
||||||
const key = message.key as ExtendedMessageKey;
|
const key = message.key as WAMessageKey;
|
||||||
|
|
||||||
if (!chatwootMessageIds.messageId || !key?.id) {
|
if (!chatwootMessageIds.messageId || !key?.id) {
|
||||||
this.logger.verbose(
|
this.logger.verbose(
|
||||||
@ -2019,7 +1951,7 @@ export class ChatwootService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const key = message?.key as ExtendedMessageKey;
|
const key = message?.key as WAMessageKey;
|
||||||
|
|
||||||
if (message && key?.id) {
|
if (message && key?.id) {
|
||||||
return {
|
return {
|
||||||
@ -2323,6 +2255,7 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event === 'messages.upsert' || event === 'send.message') {
|
if (event === 'messages.upsert' || event === 'send.message') {
|
||||||
|
this.logger.info(`[${event}] New message received - Instance: ${JSON.stringify(body, null, 2)}`);
|
||||||
if (body.key.remoteJid === 'status@broadcast') {
|
if (body.key.remoteJid === 'status@broadcast') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2406,6 +2339,11 @@ export class ChatwootService {
|
|||||||
|
|
||||||
const fileData = Buffer.from(downloadBase64.base64, 'base64');
|
const fileData = Buffer.from(downloadBase64.base64, 'base64');
|
||||||
|
|
||||||
|
const fileStream = new Readable();
|
||||||
|
fileStream._read = () => {};
|
||||||
|
fileStream.push(fileData);
|
||||||
|
fileStream.push(null);
|
||||||
|
|
||||||
if (body.key.remoteJid.includes('@g.us')) {
|
if (body.key.remoteJid.includes('@g.us')) {
|
||||||
const participantName = body.pushName;
|
const participantName = body.pushName;
|
||||||
const rawPhoneNumber = body.key.participant.split('@')[0];
|
const rawPhoneNumber = body.key.participant.split('@')[0];
|
||||||
@ -2429,7 +2367,7 @@ export class ChatwootService {
|
|||||||
|
|
||||||
const send = await this.sendData(
|
const send = await this.sendData(
|
||||||
getConversation,
|
getConversation,
|
||||||
fileData,
|
fileStream,
|
||||||
nameFile,
|
nameFile,
|
||||||
messageType,
|
messageType,
|
||||||
content,
|
content,
|
||||||
@ -2437,7 +2375,6 @@ export class ChatwootService {
|
|||||||
body,
|
body,
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
quotedMsg,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
@ -2449,7 +2386,7 @@ export class ChatwootService {
|
|||||||
} else {
|
} else {
|
||||||
const send = await this.sendData(
|
const send = await this.sendData(
|
||||||
getConversation,
|
getConversation,
|
||||||
fileData,
|
fileStream,
|
||||||
nameFile,
|
nameFile,
|
||||||
messageType,
|
messageType,
|
||||||
bodyMessage,
|
bodyMessage,
|
||||||
@ -2457,7 +2394,6 @@ export class ChatwootService {
|
|||||||
body,
|
body,
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
quotedMsg,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
@ -2483,7 +2419,6 @@ export class ChatwootService {
|
|||||||
},
|
},
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
quotedMsg,
|
||||||
body,
|
|
||||||
);
|
);
|
||||||
if (!send) {
|
if (!send) {
|
||||||
this.logger.warn('message not sent');
|
this.logger.warn('message not sent');
|
||||||
@ -2517,6 +2452,11 @@ export class ChatwootService {
|
|||||||
});
|
});
|
||||||
const processedBuffer = await img.getBuffer(JimpMime.png);
|
const processedBuffer = await img.getBuffer(JimpMime.png);
|
||||||
|
|
||||||
|
const fileStream = new Readable();
|
||||||
|
fileStream._read = () => {}; // _read is required but you can noop it
|
||||||
|
fileStream.push(processedBuffer);
|
||||||
|
fileStream.push(null);
|
||||||
|
|
||||||
const truncStr = (str: string, len: number) => {
|
const truncStr = (str: string, len: number) => {
|
||||||
if (!str) return '';
|
if (!str) return '';
|
||||||
|
|
||||||
@ -2528,15 +2468,13 @@ export class ChatwootService {
|
|||||||
|
|
||||||
const send = await this.sendData(
|
const send = await this.sendData(
|
||||||
getConversation,
|
getConversation,
|
||||||
processedBuffer,
|
fileStream,
|
||||||
nameFile,
|
nameFile,
|
||||||
messageType,
|
messageType,
|
||||||
`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`,
|
`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`,
|
||||||
instance,
|
instance,
|
||||||
body,
|
body,
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
@ -2578,7 +2516,6 @@ export class ChatwootService {
|
|||||||
body,
|
body,
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
quotedMsg,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
@ -2598,7 +2535,6 @@ export class ChatwootService {
|
|||||||
body,
|
body,
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
quotedMsg,
|
quotedMsg,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
@ -2644,51 +2580,6 @@ export class ChatwootService {
|
|||||||
if (event === 'messages.edit' || event === 'send.message.update') {
|
if (event === 'messages.edit' || event === 'send.message.update') {
|
||||||
const editedMessageContent =
|
const editedMessageContent =
|
||||||
body?.editedMessage?.conversation || body?.editedMessage?.extendedTextMessage?.text;
|
body?.editedMessage?.conversation || body?.editedMessage?.extendedTextMessage?.text;
|
||||||
|
|
||||||
// Se não houver conteúdo editado, verificar se é uma deleção
|
|
||||||
if (!editedMessageContent || editedMessageContent.trim() === '') {
|
|
||||||
// Verificar se é uma mensagem revogada (messageStubType: 1)
|
|
||||||
const messageStubType = body?.update?.messageStubType || body?.messageStubType;
|
|
||||||
this.logger.verbose(
|
|
||||||
`No edited content found - messageStubType: ${messageStubType}, body.update: ${JSON.stringify(body?.update)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (messageStubType === 1) {
|
|
||||||
// É uma mensagem deletada - processar exclusão no Chatwoot
|
|
||||||
this.logger.verbose('Message revoked detected, processing deletion in Chatwoot');
|
|
||||||
|
|
||||||
const message = await this.getMessageByKeyId(instance, body?.key?.id);
|
|
||||||
|
|
||||||
if (message?.chatwootMessageId && message?.chatwootConversationId) {
|
|
||||||
try {
|
|
||||||
await client.messages.delete({
|
|
||||||
accountId: this.provider.accountId,
|
|
||||||
conversationId: message.chatwootConversationId,
|
|
||||||
messageId: message.chatwootMessageId,
|
|
||||||
});
|
|
||||||
this.logger.verbose(`Deleted revoked message ${message.chatwootMessageId} in Chatwoot`);
|
|
||||||
|
|
||||||
// Remover do banco de dados
|
|
||||||
await this.prismaRepository.message.deleteMany({
|
|
||||||
where: {
|
|
||||||
key: {
|
|
||||||
path: ['id'],
|
|
||||||
equals: body.key.id,
|
|
||||||
},
|
|
||||||
instanceId: instance.instanceId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.logger.verbose(`Removed revoked message from database`);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error deleting revoked message: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.verbose('Message deleted, skipping edit notification');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = await this.getMessageByKeyId(instance, body?.key?.id);
|
const message = await this.getMessageByKeyId(instance, body?.key?.id);
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
@ -2696,24 +2587,13 @@ export class ChatwootService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = message.key as ExtendedMessageKey;
|
const key = message.key as WAMessageKey;
|
||||||
|
|
||||||
const messageType = key?.fromMe ? 'outgoing' : 'incoming';
|
const messageType = key?.fromMe ? 'outgoing' : 'incoming';
|
||||||
|
|
||||||
if (message && message.chatwootConversationId && message.chatwootMessageId) {
|
if (message && message.chatwootConversationId && message.chatwootMessageId) {
|
||||||
// Deletar a mensagem original no Chatwoot
|
|
||||||
try {
|
|
||||||
await client.messages.delete({
|
|
||||||
accountId: this.provider.accountId,
|
|
||||||
conversationId: message.chatwootConversationId,
|
|
||||||
messageId: message.chatwootMessageId,
|
|
||||||
});
|
|
||||||
this.logger.verbose(`Deleted original message ${message.chatwootMessageId} for edit`);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error deleting original message for edit: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Criar nova mensagem com formato: "Mensagem editada:\n\nteste1"
|
// Criar nova mensagem com formato: "Mensagem editada:\n\nteste1"
|
||||||
const editedText = `${i18next.t('cw.message.edited')}:\n\n${editedMessageContent}`;
|
const editedText = `\n\n\`${i18next.t('cw.message.edited')}:\`\n\n${editedMessageContent}`;
|
||||||
|
|
||||||
const send = await this.createMessage(
|
const send = await this.createMessage(
|
||||||
instance,
|
instance,
|
||||||
@ -2727,33 +2607,11 @@ export class ChatwootService {
|
|||||||
},
|
},
|
||||||
'WAID:' + body.key.id,
|
'WAID:' + body.key.id,
|
||||||
null,
|
null,
|
||||||
body,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!send) {
|
if (!send) {
|
||||||
this.logger.warn('edited message not sent');
|
this.logger.warn('edited message not sent');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.verbose(`Created edited message in Chatwoot with ID: ${send.id}`);
|
|
||||||
|
|
||||||
// Atualizar o chatwootMessageId no banco para apontar para a nova mensagem
|
|
||||||
// Isso permite que a exclusão funcione após a edição
|
|
||||||
try {
|
|
||||||
await this.prismaRepository.message.update({
|
|
||||||
where: {
|
|
||||||
id: message.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
chatwootMessageId: send.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.logger.verbose(
|
|
||||||
`Updated chatwootMessageId from ${message.chatwootMessageId} to ${send.id} for message ${body.key.id}`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error updating chatwootMessageId after edit: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3017,8 +2875,7 @@ export class ChatwootService {
|
|||||||
and created_at >= now() - interval '6h'
|
and created_at >= now() - interval '6h'
|
||||||
order by created_at desc`;
|
order by created_at desc`;
|
||||||
|
|
||||||
const pgClient = await this.getPgClient();
|
const messagesData = (await this.pgClient.query(sqlMessages))?.rows;
|
||||||
const messagesData = (await pgClient.query(sqlMessages))?.rows;
|
|
||||||
const ids: string[] = messagesData
|
const ids: string[] = messagesData
|
||||||
.filter((message) => !!message.source_id)
|
.filter((message) => !!message.source_id)
|
||||||
.map((message) => message.source_id.replace('WAID:', ''));
|
.map((message) => message.source_id.replace('WAID:', ''));
|
||||||
|
|||||||
@ -112,12 +112,19 @@ class ChatwootImport {
|
|||||||
const bindInsert = [provider.accountId];
|
const bindInsert = [provider.accountId];
|
||||||
|
|
||||||
for (const contact of contactsChunk) {
|
for (const contact of contactsChunk) {
|
||||||
bindInsert.push(contact.pushName);
|
const isGroup = this.isIgnorePhoneNumber(contact.remoteJid);
|
||||||
|
|
||||||
|
const contactName = isGroup ? `${contact.pushName} (GROUP)` : contact.pushName;
|
||||||
|
bindInsert.push(contactName);
|
||||||
const bindName = `$${bindInsert.length}`;
|
const bindName = `$${bindInsert.length}`;
|
||||||
|
|
||||||
bindInsert.push(`+${contact.remoteJid.split('@')[0]}`);
|
let bindPhoneNumber: string;
|
||||||
const bindPhoneNumber = `$${bindInsert.length}`;
|
if (!isGroup) {
|
||||||
|
bindInsert.push(`+${contact.remoteJid.split('@')[0]}`);
|
||||||
|
bindPhoneNumber = `$${bindInsert.length}`;
|
||||||
|
} else {
|
||||||
|
bindPhoneNumber = 'NULL';
|
||||||
|
}
|
||||||
bindInsert.push(contact.remoteJid);
|
bindInsert.push(contact.remoteJid);
|
||||||
const bindIdentifier = `$${bindInsert.length}`;
|
const bindIdentifier = `$${bindInsert.length}`;
|
||||||
|
|
||||||
|
|||||||
@ -3,37 +3,21 @@ import fs from 'fs';
|
|||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
// Make translations base directory configurable via environment variable
|
|
||||||
const envBaseDir = process.env.TRANSLATIONS_BASE_DIR;
|
|
||||||
let baseDir: string;
|
|
||||||
|
|
||||||
if (envBaseDir) {
|
|
||||||
// Use explicitly configured base directory
|
|
||||||
baseDir = envBaseDir;
|
|
||||||
} else {
|
|
||||||
// Fallback to auto-detection if env variable is not set
|
|
||||||
const isProduction = fs.existsSync(path.join(process.cwd(), 'dist'));
|
|
||||||
baseDir = isProduction ? 'dist' : 'src/utils';
|
|
||||||
}
|
|
||||||
|
|
||||||
const translationsPath = path.join(process.cwd(), baseDir, 'translations');
|
|
||||||
|
|
||||||
const languages = ['en', 'pt-BR', 'es'];
|
const languages = ['en', 'pt-BR', 'es'];
|
||||||
|
const translationsPath = path.join(__dirname, 'translations');
|
||||||
const configService: ConfigService = new ConfigService();
|
const configService: ConfigService = new ConfigService();
|
||||||
|
|
||||||
const resources: any = {};
|
const resources: any = {};
|
||||||
|
|
||||||
if (translationsPath) {
|
languages.forEach((language) => {
|
||||||
languages.forEach((language) => {
|
const languagePath = path.join(translationsPath, `${language}.json`);
|
||||||
const languagePath = path.join(translationsPath, `${language}.json`);
|
if (fs.existsSync(languagePath)) {
|
||||||
if (fs.existsSync(languagePath)) {
|
const translationContent = fs.readFileSync(languagePath, 'utf8');
|
||||||
const translationContent = fs.readFileSync(languagePath, 'utf8');
|
resources[language] = {
|
||||||
resources[language] = {
|
translation: JSON.parse(translationContent),
|
||||||
translation: JSON.parse(translationContent),
|
};
|
||||||
};
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
i18next.init({
|
i18next.init({
|
||||||
resources,
|
resources,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user