mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-08-29 02:36:11 -06:00
fix: implementa resolução automática de contatos @lid
This commit is contained in:
parent
9cdb897a0f
commit
4ee4ee3a8b
@ -670,6 +670,8 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
this.eventHandler();
|
this.eventHandler();
|
||||||
|
|
||||||
|
this.startLidCleanupScheduler();
|
||||||
|
|
||||||
this.client.ws.on('CB:call', (packet) => {
|
this.client.ws.on('CB:call', (packet) => {
|
||||||
console.log('CB:call', packet);
|
console.log('CB:call', packet);
|
||||||
const payload = { event: 'CB:call', packet: packet };
|
const payload = { event: 'CB:call', packet: packet };
|
||||||
@ -1049,8 +1051,12 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
try {
|
try {
|
||||||
for (const received of messages) {
|
for (const received of messages) {
|
||||||
if (received.key.remoteJid?.includes('@lid') && received.key.senderPn) {
|
if (received.key.remoteJid?.includes('@lid') && received.key.senderPn) {
|
||||||
|
this.logger.verbose(`Processing @lid message: ${received.key.remoteJid} -> ${received.key.senderPn}`);
|
||||||
|
|
||||||
(received.key as { previousRemoteJid?: string | null }).previousRemoteJid = received.key.remoteJid;
|
(received.key as { previousRemoteJid?: string | null }).previousRemoteJid = received.key.remoteJid;
|
||||||
received.key.remoteJid = received.key.senderPn;
|
received.key.remoteJid = received.key.senderPn;
|
||||||
|
|
||||||
|
await this.updateContactFromLid(received.key.previousRemoteJid, received.key.remoteJid);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
received?.messageStubParameters?.some?.((param) =>
|
received?.messageStubParameters?.some?.((param) =>
|
||||||
@ -1446,16 +1452,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const findMessage = await this.prismaRepository.message.findFirst({
|
|
||||||
where: { instanceId: this.instanceId, key: { path: ['id'], equals: key.id } },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!findMessage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message: any = {
|
const message: any = {
|
||||||
messageId: findMessage.id,
|
|
||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
remoteJid: key?.remoteJid,
|
remoteJid: key?.remoteJid,
|
||||||
fromMe: key.fromMe,
|
fromMe: key.fromMe,
|
||||||
@ -1465,6 +1462,16 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
instanceId: this.instanceId,
|
instanceId: this.instanceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let findMessage: any;
|
||||||
|
const configDatabaseData = this.configService.get<Database>('DATABASE').SAVE_DATA;
|
||||||
|
if (configDatabaseData.HISTORIC || configDatabaseData.NEW_MESSAGE) {
|
||||||
|
findMessage = await this.prismaRepository.message.findFirst({
|
||||||
|
where: { instanceId: this.instanceId, key: { path: ['id'], equals: key.id } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (findMessage) message.messageId = findMessage.id;
|
||||||
|
}
|
||||||
|
|
||||||
if (update.message === null && update.status === undefined) {
|
if (update.message === null && update.status === undefined) {
|
||||||
this.sendDataWebhook(Events.MESSAGES_DELETE, key);
|
this.sendDataWebhook(Events.MESSAGES_DELETE, key);
|
||||||
|
|
||||||
@ -1480,7 +1487,9 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else if (update.status !== undefined && status[update.status] !== findMessage.status) {
|
}
|
||||||
|
|
||||||
|
if (findMessage && update.status !== undefined && status[update.status] !== findMessage.status) {
|
||||||
if (!key.fromMe && key.remoteJid) {
|
if (!key.fromMe && key.remoteJid) {
|
||||||
readChatToUpdate[key.remoteJid] = true;
|
readChatToUpdate[key.remoteJid] = true;
|
||||||
|
|
||||||
@ -3438,17 +3447,20 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
where: { id: message.id },
|
where: { id: message.id },
|
||||||
data: { key: { ...existingKey, deleted: true }, status: 'DELETED' },
|
data: { key: { ...existingKey, deleted: true }, status: 'DELETED' },
|
||||||
});
|
});
|
||||||
const messageUpdate: any = {
|
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE) {
|
||||||
messageId: message.id,
|
const messageUpdate: any = {
|
||||||
keyId: messageId,
|
messageId: message.id,
|
||||||
remoteJid: response.key.remoteJid,
|
keyId: messageId,
|
||||||
fromMe: response.key.fromMe,
|
remoteJid: response.key.remoteJid,
|
||||||
participant: response.key?.remoteJid,
|
fromMe: response.key.fromMe,
|
||||||
status: 'DELETED',
|
participant: response.key?.remoteJid,
|
||||||
instanceId: this.instanceId,
|
status: 'DELETED',
|
||||||
};
|
instanceId: this.instanceId,
|
||||||
await this.prismaRepository.messageUpdate.create({ data: messageUpdate });
|
};
|
||||||
|
await this.prismaRepository.messageUpdate.create({ data: messageUpdate });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (!message) return response;
|
||||||
await this.prismaRepository.message.deleteMany({ where: { id: message.id } });
|
await this.prismaRepository.message.deleteMany({ where: { id: message.id } });
|
||||||
}
|
}
|
||||||
this.sendDataWebhook(Events.MESSAGES_DELETE, {
|
this.sendDataWebhook(Events.MESSAGES_DELETE, {
|
||||||
@ -3780,6 +3792,10 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
private async formatUpdateMessage(data: UpdateMessageDto) {
|
private async formatUpdateMessage(data: UpdateMessageDto) {
|
||||||
try {
|
try {
|
||||||
|
if (!this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
const msg: any = await this.getMessage(data.key, true);
|
const msg: any = await this.getMessage(data.key, true);
|
||||||
|
|
||||||
if (msg?.messageType === 'conversation' || msg?.messageType === 'extendedTextMessage') {
|
if (msg?.messageType === 'conversation' || msg?.messageType === 'extendedTextMessage') {
|
||||||
@ -3813,13 +3829,15 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const oldMessage: any = await this.getMessage(data.key, true);
|
const oldMessage: any = await this.getMessage(data.key, true);
|
||||||
if (!oldMessage) throw new NotFoundException('Message not found');
|
if (this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE) {
|
||||||
if (oldMessage?.key?.remoteJid !== jid) {
|
if (!oldMessage) throw new NotFoundException('Message not found');
|
||||||
throw new BadRequestException('RemoteJid does not match');
|
if (oldMessage?.key?.remoteJid !== jid) {
|
||||||
}
|
throw new BadRequestException('RemoteJid does not match');
|
||||||
if (oldMessage?.messageTimestamp > Date.now() + 900000) {
|
}
|
||||||
// 15 minutes in milliseconds
|
if (oldMessage?.messageTimestamp > Date.now() + 900000) {
|
||||||
throw new BadRequestException('Message is older than 15 minutes');
|
// 15 minutes in milliseconds
|
||||||
|
throw new BadRequestException('Message is older than 15 minutes');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageSent = await this.client.sendMessage(jid, { ...(options as any), edit: data.key });
|
const messageSent = await this.client.sendMessage(jid, { ...(options as any), edit: data.key });
|
||||||
@ -3837,7 +3855,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const messageId = messageSent.message?.protocolMessage?.key?.id;
|
const messageId = messageSent.message?.protocolMessage?.key?.id;
|
||||||
if (messageId) {
|
if (messageId && this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE) {
|
||||||
let message = await this.prismaRepository.message.findFirst({
|
let message = await this.prismaRepository.message.findFirst({
|
||||||
where: { key: { path: ['id'], equals: messageId } },
|
where: { key: { path: ['id'], equals: messageId } },
|
||||||
});
|
});
|
||||||
@ -3849,6 +3867,7 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
if ((message.key.valueOf() as any)?.deleted) {
|
if ((message.key.valueOf() as any)?.deleted) {
|
||||||
new BadRequestException('You cannot edit deleted messages');
|
new BadRequestException('You cannot edit deleted messages');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldMessage.messageType === 'conversation' || oldMessage.messageType === 'extendedTextMessage') {
|
if (oldMessage.messageType === 'conversation' || oldMessage.messageType === 'extendedTextMessage') {
|
||||||
oldMessage.message.conversation = data.text;
|
oldMessage.message.conversation = data.text;
|
||||||
} else {
|
} else {
|
||||||
@ -3862,16 +3881,19 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
messageTimestamp: Math.floor(Date.now() / 1000), // Convert to int32 by dividing by 1000 to get seconds
|
messageTimestamp: Math.floor(Date.now() / 1000), // Convert to int32 by dividing by 1000 to get seconds
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const messageUpdate: any = {
|
|
||||||
messageId: message.id,
|
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE) {
|
||||||
keyId: messageId,
|
const messageUpdate: any = {
|
||||||
remoteJid: messageSent.key.remoteJid,
|
messageId: message.id,
|
||||||
fromMe: messageSent.key.fromMe,
|
keyId: messageId,
|
||||||
participant: messageSent.key?.remoteJid,
|
remoteJid: messageSent.key.remoteJid,
|
||||||
status: 'EDITED',
|
fromMe: messageSent.key.fromMe,
|
||||||
instanceId: this.instanceId,
|
participant: messageSent.key?.remoteJid,
|
||||||
};
|
status: 'EDITED',
|
||||||
await this.prismaRepository.messageUpdate.create({ data: messageUpdate });
|
instanceId: this.instanceId,
|
||||||
|
};
|
||||||
|
await this.prismaRepository.messageUpdate.create({ data: messageUpdate });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3941,6 +3963,226 @@ export class BaileysStartupService extends ChannelStartupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza contatos que foram criados com @lid para o JID real
|
||||||
|
* Isso resolve problemas de mensagens não chegando no iPhone
|
||||||
|
* Funciona com ou sem banco de dados
|
||||||
|
*/
|
||||||
|
private async updateContactFromLid(lidJid: string, realJid: string) {
|
||||||
|
try {
|
||||||
|
// Verificar se o banco de dados está habilitado
|
||||||
|
const db = this.configService.get<Database>('DATABASE');
|
||||||
|
const cache = this.configService.get<CacheConf>('CACHE');
|
||||||
|
|
||||||
|
if (db.SAVE_DATA.CONTACTS) {
|
||||||
|
// Com banco de dados - usar Prisma
|
||||||
|
try {
|
||||||
|
// Buscar contato com @lid
|
||||||
|
const lidContact = await this.prismaRepository.contact.findFirst({
|
||||||
|
where: {
|
||||||
|
remoteJid: lidJid,
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lidContact) {
|
||||||
|
// Atualizar para o JID real
|
||||||
|
await this.prismaRepository.contact.update({
|
||||||
|
where: { id: lidContact.id },
|
||||||
|
data: { remoteJid: realJid },
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.verbose(`Updated contact from @lid: ${lidJid} -> ${realJid}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Também atualizar mensagens com @lid
|
||||||
|
const lidMessages = await this.prismaRepository.message.findMany({
|
||||||
|
where: {
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
key: {
|
||||||
|
path: ['remoteJid'],
|
||||||
|
equals: lidJid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lidMessages.length > 0) {
|
||||||
|
for (const message of lidMessages) {
|
||||||
|
const key = message.key as any;
|
||||||
|
key.remoteJid = realJid;
|
||||||
|
|
||||||
|
await this.prismaRepository.message.update({
|
||||||
|
where: { id: message.id },
|
||||||
|
data: { key: key },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose(`Updated ${lidMessages.length} messages from @lid: ${lidJid} -> ${realJid}`);
|
||||||
|
}
|
||||||
|
} catch (dbError) {
|
||||||
|
this.logger.warn(`Database operation failed, falling back to cache: ${dbError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sem banco de dados - usar cache e arquivos locais
|
||||||
|
if (cache?.REDIS?.ENABLED) {
|
||||||
|
// Atualizar no cache Redis
|
||||||
|
try {
|
||||||
|
const cacheKey = `contact:${this.instanceId}:${lidJid}`;
|
||||||
|
const realContactKey = `contact:${this.instanceId}:${realJid}`;
|
||||||
|
|
||||||
|
// Buscar dados do contato @lid no cache
|
||||||
|
const lidContactData = await this.cache.hGet(this.instanceId, cacheKey);
|
||||||
|
if (lidContactData) {
|
||||||
|
// Atualizar para o JID real no cache
|
||||||
|
await this.cache.hSet(this.instanceId, realContactKey, lidContactData);
|
||||||
|
await this.cache.hDelete(this.instanceId, cacheKey);
|
||||||
|
|
||||||
|
this.logger.verbose(`Updated Redis cache contact from @lid: ${lidJid} -> ${realJid}`);
|
||||||
|
}
|
||||||
|
} catch (cacheError) {
|
||||||
|
this.logger.warn(`Redis cache operation failed: ${cacheError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atualizar arquivos locais se necessário
|
||||||
|
if (this.instance.authState) {
|
||||||
|
try {
|
||||||
|
// Atualizar o estado de autenticação local
|
||||||
|
const authState = this.instance.authState as any;
|
||||||
|
if (authState.store && authState.store.contacts) {
|
||||||
|
// Atualizar contatos no store local
|
||||||
|
const contacts = authState.store.contacts;
|
||||||
|
if (contacts[lidJid]) {
|
||||||
|
contacts[realJid] = contacts[lidJid];
|
||||||
|
delete contacts[lidJid];
|
||||||
|
|
||||||
|
this.logger.verbose(`Updated local auth state contact from @lid: ${lidJid} -> ${realJid}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (localError) {
|
||||||
|
this.logger.warn(`Local auth state update failed: ${localError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info(`Successfully processed @lid update: ${lidJid} -> ${realJid}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error updating contact from @lid:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limpa contatos @lid órfãos e faz manutenção periódica
|
||||||
|
* Executa automaticamente para resolver problemas de mensagens não chegando
|
||||||
|
*/
|
||||||
|
private async cleanupOrphanedLidContacts() {
|
||||||
|
try {
|
||||||
|
this.logger.verbose('Starting cleanup of orphaned @lid contacts...');
|
||||||
|
|
||||||
|
const db = this.configService.get<Database>('DATABASE');
|
||||||
|
const cache = this.configService.get<CacheConf>('CACHE');
|
||||||
|
|
||||||
|
if (db.SAVE_DATA.CONTACTS) {
|
||||||
|
// Com banco: buscar todos os contatos @lid
|
||||||
|
try {
|
||||||
|
const lidContacts = await this.prismaRepository.contact.findMany({
|
||||||
|
where: {
|
||||||
|
remoteJid: { contains: '@lid' },
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.verbose(`Found ${lidContacts.length} @lid contacts to cleanup`);
|
||||||
|
|
||||||
|
for (const contact of lidContacts) {
|
||||||
|
// Tentar resolver o JID real através do WhatsApp
|
||||||
|
try {
|
||||||
|
// Usar o cliente WhatsApp para verificar se o contato existe
|
||||||
|
const contactInfo = await this.client.contactsUpsert([
|
||||||
|
{ id: contact.remoteJid, name: contact.pushName || 'Unknown' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (contactInfo && contactInfo[0] && !contactInfo[0].id.includes('@lid')) {
|
||||||
|
// Contato foi resolvido, atualizar
|
||||||
|
await this.updateContactFromLid(contact.remoteJid, contactInfo[0].id);
|
||||||
|
} else {
|
||||||
|
// Contato não pode ser resolvido, remover
|
||||||
|
this.logger.warn(`Removing orphaned @lid contact: ${contact.remoteJid}`);
|
||||||
|
await this.prismaRepository.contact.delete({
|
||||||
|
where: { id: contact.id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (contactError) {
|
||||||
|
this.logger.warn(`Could not resolve contact ${contact.remoteJid}: ${contactError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (dbError) {
|
||||||
|
this.logger.warn(`Database cleanup failed: ${dbError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limpeza de cache Redis
|
||||||
|
if (cache?.REDIS?.ENABLED) {
|
||||||
|
try {
|
||||||
|
const keys = await this.cache.keys('*@lid*');
|
||||||
|
this.logger.verbose(`Found ${keys.length} @lid keys in Redis cache`);
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
// Tentar resolver e atualizar
|
||||||
|
const contactData = await this.cache.hGet(this.instanceId, key);
|
||||||
|
if (contactData) {
|
||||||
|
this.logger.verbose(`Processing Redis cache key: ${key}`);
|
||||||
|
for (const key of keys) {
|
||||||
|
const contactData = await this.cache.hGet(this.instanceId, key);
|
||||||
|
if (contactData) {
|
||||||
|
try {
|
||||||
|
// Extrai o JID @lid da chave do cache
|
||||||
|
const lidJid = key.split(':').pop();
|
||||||
|
// Usa o Baileys para tentar resolver o JID real
|
||||||
|
const contactInfo = await this.client.contactsUpsert([
|
||||||
|
{ id: lidJid, name: contactData.pushName || 'Unknown' }
|
||||||
|
]);
|
||||||
|
if (contactInfo && contactInfo[0] && !contactInfo[0].id.includes('@lid')) {
|
||||||
|
// Atualiza o cache para o JID real
|
||||||
|
const realContactKey = `contact:${this.instanceId}:${contactInfo[0].id}`;
|
||||||
|
await this.cache.hSet(this.instanceId, realContactKey, contactData);
|
||||||
|
await this.cache.hDelete(this.instanceId, key);
|
||||||
|
this.logger.verbose(`Updated Redis cache contact from @lid: ${key} -> ${realContactKey}`);
|
||||||
|
}
|
||||||
|
} catch (resolveError) {
|
||||||
|
this.logger.warn(`Could not resolve contact in cache: ${key} - ${resolveError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (cacheError) {
|
||||||
|
this.logger.warn(`Redis cleanup failed: ${cacheError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info('Completed cleanup of orphaned @lid contacts');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error during @lid cleanup:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicia o processo de limpeza periódica de @lid
|
||||||
|
* Executa a cada 5 minutos para manter o sistema limpo
|
||||||
|
*/
|
||||||
|
private startLidCleanupScheduler() {
|
||||||
|
// Limpeza inicial
|
||||||
|
setTimeout(() => this.cleanupOrphanedLidContacts(), 30000); // 30 segundos após inicialização
|
||||||
|
|
||||||
|
// Limpeza periódica a cada 5 minutos
|
||||||
|
setInterval(() => {
|
||||||
|
this.cleanupOrphanedLidContacts();
|
||||||
|
}, 5 * 60 * 1000); // 5 minutos
|
||||||
|
|
||||||
|
this.logger.info('Started periodic @lid cleanup scheduler (every 5 minutes)');
|
||||||
|
}
|
||||||
|
|
||||||
private getGroupMetadataCache = async (groupJid: string) => {
|
private getGroupMetadataCache = async (groupJid: string) => {
|
||||||
if (!isJidGroup(groupJid)) return null;
|
if (!isJidGroup(groupJid)) return null;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user