mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-24 06:07:45 -06:00
Merge remote-tracking branch 'upstream/develop' into bugfix/media-upload-failed-on-all-hosts
# Conflicts: # package-lock.json # src/utils/makeProxyAgent.ts
This commit is contained in:
@@ -65,78 +65,118 @@ interface ISaveOnWhatsappCacheParams {
|
||||
lid?: 'lid' | undefined;
|
||||
}
|
||||
|
||||
function normalizeJid(jid: string | null | undefined): string | null {
|
||||
if (!jid) return null;
|
||||
return jid.startsWith('+') ? jid.slice(1) : jid;
|
||||
}
|
||||
|
||||
export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) {
|
||||
if (configService.get<Database>('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) {
|
||||
for (const item of data) {
|
||||
const remoteJid = item.remoteJid.startsWith('+') ? item.remoteJid.slice(1) : item.remoteJid;
|
||||
if (!configService.get<Database>('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Buscar registro existente PRIMEIRO para preservar dados
|
||||
const allJids = [remoteJid];
|
||||
|
||||
const altJid =
|
||||
item.remoteJidAlt && item.remoteJidAlt.includes('@lid')
|
||||
? item.remoteJidAlt.startsWith('+')
|
||||
? item.remoteJidAlt.slice(1)
|
||||
: item.remoteJidAlt
|
||||
: null;
|
||||
|
||||
if (altJid) {
|
||||
allJids.push(altJid);
|
||||
// Processa todos os itens em paralelo para melhor performance
|
||||
const processingPromises = data.map(async (item) => {
|
||||
try {
|
||||
const remoteJid = normalizeJid(item.remoteJid);
|
||||
if (!remoteJid) {
|
||||
logger.warn('[saveOnWhatsappCache] Item skipped, missing remoteJid.');
|
||||
return;
|
||||
}
|
||||
|
||||
const expandedJids = allJids.flatMap((jid) => getAvailableNumbers(jid));
|
||||
const altJidNormalized = normalizeJid(item.remoteJidAlt);
|
||||
const lidAltJid = altJidNormalized && altJidNormalized.includes('@lid') ? altJidNormalized : null;
|
||||
|
||||
const baseJids = [remoteJid]; // Garante que o remoteJid esteja na lista inicial
|
||||
if (lidAltJid) {
|
||||
baseJids.push(lidAltJid);
|
||||
}
|
||||
|
||||
const expandedJids = baseJids.flatMap((jid) => getAvailableNumbers(jid));
|
||||
|
||||
// 1. Busca entrada por jidOptions e também remoteJid
|
||||
// Às vezes acontece do remoteJid atual NÃO ESTAR no jidOptions ainda, ocasionando o erro:
|
||||
// 'Unique constraint failed on the fields: (`remoteJid`)'
|
||||
// Isso acontece principalmente em grupos que possuem o número do criador no ID (ex.: '559911223345-1234567890@g.us')
|
||||
const existingRecord = await prismaRepository.isOnWhatsapp.findFirst({
|
||||
where: {
|
||||
OR: expandedJids.map((jid) => ({ jidOptions: { contains: jid } })),
|
||||
OR: [
|
||||
...expandedJids.map((jid) => ({ jidOptions: { contains: jid } })),
|
||||
{ remoteJid: remoteJid }, // TODO: Descobrir o motivo que causa o remoteJid não estar (às vezes) incluso na lista de jidOptions
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
logger.verbose(`Register exists: ${existingRecord ? existingRecord.remoteJid : 'não not found'}`);
|
||||
|
||||
const finalJidOptions = [...expandedJids];
|
||||
|
||||
if (existingRecord?.jidOptions) {
|
||||
const existingJids = existingRecord.jidOptions.split(',');
|
||||
// TODO: Adicionar JIDs existentes que não estão na lista atual
|
||||
existingJids.forEach((jid) => {
|
||||
if (!finalJidOptions.includes(jid)) {
|
||||
finalJidOptions.push(jid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Se tiver remoteJidAlt com @lid novo, adicionar
|
||||
if (altJid && !finalJidOptions.includes(altJid)) {
|
||||
finalJidOptions.push(altJid);
|
||||
}
|
||||
|
||||
const uniqueNumbers = Array.from(new Set(finalJidOptions));
|
||||
|
||||
logger.verbose(
|
||||
`Saving: remoteJid=${remoteJid}, jidOptions=${uniqueNumbers.join(',')}, lid=${item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null}`,
|
||||
`[saveOnWhatsappCache] Register exists for [${expandedJids.join(',')}]? => ${existingRecord ? existingRecord.remoteJid : 'Not found'}`,
|
||||
);
|
||||
|
||||
// 2. Unifica todos os JIDs usando um Set para garantir valores únicos
|
||||
const finalJidOptions = new Set(expandedJids);
|
||||
|
||||
if (lidAltJid) {
|
||||
finalJidOptions.add(lidAltJid);
|
||||
}
|
||||
|
||||
if (existingRecord?.jidOptions) {
|
||||
existingRecord.jidOptions.split(',').forEach((jid) => finalJidOptions.add(jid));
|
||||
}
|
||||
|
||||
// 3. Prepara o payload final
|
||||
// Ordena os JIDs para garantir consistência na string final
|
||||
const sortedJidOptions = [...finalJidOptions].sort();
|
||||
const newJidOptionsString = sortedJidOptions.join(',');
|
||||
const newLid = item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null;
|
||||
|
||||
const dataPayload = {
|
||||
remoteJid: remoteJid,
|
||||
jidOptions: newJidOptionsString,
|
||||
lid: newLid,
|
||||
};
|
||||
|
||||
// 4. Decide entre Criar ou Atualizar
|
||||
if (existingRecord) {
|
||||
// Compara a string de JIDs ordenada existente com a nova
|
||||
const existingJidOptionsString = existingRecord.jidOptions
|
||||
? existingRecord.jidOptions.split(',').sort().join(',')
|
||||
: '';
|
||||
|
||||
const isDataSame =
|
||||
existingRecord.remoteJid === dataPayload.remoteJid &&
|
||||
existingJidOptionsString === dataPayload.jidOptions &&
|
||||
existingRecord.lid === dataPayload.lid;
|
||||
|
||||
if (isDataSame) {
|
||||
logger.verbose(`[saveOnWhatsappCache] Data for ${remoteJid} is already up-to-date. Skipping update.`);
|
||||
return; // Pula para o próximo item
|
||||
}
|
||||
|
||||
// Os dados são diferentes, então atualiza
|
||||
logger.verbose(
|
||||
`[saveOnWhatsappCache] Register exists, updating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}, lid=${dataPayload.lid}`,
|
||||
);
|
||||
await prismaRepository.isOnWhatsapp.update({
|
||||
where: { id: existingRecord.id },
|
||||
data: {
|
||||
remoteJid: remoteJid,
|
||||
jidOptions: uniqueNumbers.join(','),
|
||||
lid: item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null,
|
||||
},
|
||||
data: dataPayload,
|
||||
});
|
||||
} else {
|
||||
// Cria nova entrada
|
||||
logger.verbose(
|
||||
`[saveOnWhatsappCache] Register does not exist, creating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}, lid=${dataPayload.lid}`,
|
||||
);
|
||||
await prismaRepository.isOnWhatsapp.create({
|
||||
data: {
|
||||
remoteJid: remoteJid,
|
||||
jidOptions: uniqueNumbers.join(','),
|
||||
lid: item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null,
|
||||
},
|
||||
data: dataPayload,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// Loga o erro mas não para a execução dos outros promises
|
||||
logger.error(`[saveOnWhatsappCache] Error processing item for ${item.remoteJid}: `);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Espera todas as operações paralelas terminarem
|
||||
await Promise.allSettled(processingPromises);
|
||||
}
|
||||
|
||||
export async function getOnWhatsappCache(remoteJids: string[]) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { prismaRepository } from '@api/server.module';
|
||||
import { CacheService } from '@api/services/cache.service';
|
||||
import { CacheConf, configService } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { INSTANCE_DIR } from '@config/path.config';
|
||||
import { AuthenticationState, BufferJSON, initAuthCreds, WAProto as proto } from 'baileys';
|
||||
import fs from 'fs/promises';
|
||||
@@ -73,12 +74,15 @@ async function fileExists(file: string): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
const logger = new Logger('useMultiFileAuthStatePrisma');
|
||||
|
||||
export default async function useMultiFileAuthStatePrisma(
|
||||
sessionId: string,
|
||||
cache: CacheService,
|
||||
): Promise<{
|
||||
state: AuthenticationState;
|
||||
saveCreds: () => Promise<void>;
|
||||
removeCreds: () => Promise<void>;
|
||||
}> {
|
||||
const localFolder = path.join(INSTANCE_DIR, sessionId);
|
||||
const localFile = (key: string) => path.join(localFolder, fixFileName(key) + '.json');
|
||||
@@ -142,6 +146,26 @@ export default async function useMultiFileAuthStatePrisma(
|
||||
}
|
||||
}
|
||||
|
||||
async function removeCreds(): Promise<any> {
|
||||
const cacheConfig = configService.get<CacheConf>('CACHE');
|
||||
|
||||
// Redis
|
||||
try {
|
||||
if (cacheConfig.REDIS.ENABLED) {
|
||||
await cache.delete(sessionId);
|
||||
logger.info({ action: 'redis.delete', sessionId });
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn({ action: 'redis.delete', sessionId, err });
|
||||
}
|
||||
|
||||
logger.info({ action: 'auth.key.delete', sessionId });
|
||||
|
||||
await deleteAuthKey(sessionId);
|
||||
}
|
||||
|
||||
let creds = await readData('creds');
|
||||
if (!creds) {
|
||||
creds = initAuthCreds();
|
||||
@@ -183,5 +207,7 @@ export default async function useMultiFileAuthStatePrisma(
|
||||
saveCreds: () => {
|
||||
return writeData(creds, 'creds');
|
||||
},
|
||||
|
||||
removeCreds,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,11 @@ import { Logger } from '@config/logger.config';
|
||||
import { AuthenticationCreds, AuthenticationState, BufferJSON, initAuthCreds, proto, SignalDataTypeMap } from 'baileys';
|
||||
import { isNotEmpty } from 'class-validator';
|
||||
|
||||
export type AuthState = { state: AuthenticationState; saveCreds: () => Promise<void> };
|
||||
export type AuthState = {
|
||||
state: AuthenticationState;
|
||||
saveCreds: () => Promise<void>;
|
||||
removeCreds: () => Promise<void>;
|
||||
};
|
||||
|
||||
export class AuthStateProvider {
|
||||
constructor(private readonly providerFiles: ProviderFiles) {}
|
||||
@@ -86,6 +90,18 @@ export class AuthStateProvider {
|
||||
return response;
|
||||
};
|
||||
|
||||
const removeCreds = async () => {
|
||||
const [response, error] = await this.providerFiles.removeSession(instance);
|
||||
if (error) {
|
||||
// this.logger.error(['removeData', error?.message, error?.stack]);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info({ action: 'remove.session', instance, response });
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||
|
||||
return {
|
||||
@@ -126,6 +142,10 @@ export class AuthStateProvider {
|
||||
saveCreds: async () => {
|
||||
return await writeData(creds, 'creds');
|
||||
},
|
||||
|
||||
removeCreds,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const logger = new Logger('useMultiFileAuthStatePrisma');
|
||||
|
||||
@@ -8,6 +8,7 @@ export async function useMultiFileAuthStateRedisDb(
|
||||
): Promise<{
|
||||
state: AuthenticationState;
|
||||
saveCreds: () => Promise<void>;
|
||||
removeCreds: () => Promise<void>;
|
||||
}> {
|
||||
const logger = new Logger('useMultiFileAuthStateRedisDb');
|
||||
|
||||
@@ -36,6 +37,16 @@ export async function useMultiFileAuthStateRedisDb(
|
||||
}
|
||||
};
|
||||
|
||||
async function removeCreds(): Promise<any> {
|
||||
try {
|
||||
logger.warn({ action: 'redis.delete', instanceName });
|
||||
|
||||
return await cache.delete(instanceName);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||
|
||||
return {
|
||||
@@ -76,5 +87,7 @@ export async function useMultiFileAuthStateRedisDb(
|
||||
saveCreds: async () => {
|
||||
return await writeData(creds, 'creds');
|
||||
},
|
||||
|
||||
removeCreds,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user