Skip to content

Commit 2534ec2

Browse files
committed
Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop
2 parents 933a28d + b1b07b7 commit 2534ec2

File tree

2 files changed

+110
-43
lines changed

2 files changed

+110
-43
lines changed

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 87 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,10 +1251,10 @@ export class BaileysStartupService extends ChannelStartupService {
12511251
}
12521252
}
12531253

1254-
const messageRaw = this.prepareMessage(received);
1254+
const messageRaw = this.prepareMessage(received) as any;
12551255

12561256
if (messageRaw.messageType === 'pollUpdateMessage') {
1257-
const pollCreationKey = messageRaw.message.pollUpdateMessage.pollCreationMessageKey;
1257+
const pollCreationKey = (messageRaw.message as any).pollUpdateMessage.pollCreationMessageKey;
12581258
const pollMessage = (await this.getMessage(pollCreationKey, true)) as proto.IWebMessageInfo;
12591259
const pollMessageSecret = (await this.getMessage(pollCreationKey)) as any;
12601260

@@ -1263,7 +1263,7 @@ export class BaileysStartupService extends ChannelStartupService {
12631263
(pollMessage.message as any).pollCreationMessage?.options ||
12641264
(pollMessage.message as any).pollCreationMessageV3?.options ||
12651265
[];
1266-
const pollVote = messageRaw.message.pollUpdateMessage.vote;
1266+
const pollVote = (messageRaw.message as any).pollUpdateMessage.vote;
12671267

12681268
const voterJid = received.key.fromMe
12691269
? this.instance.wuid
@@ -1343,14 +1343,14 @@ export class BaileysStartupService extends ChannelStartupService {
13431343
})
13441344
.map((option) => option.optionName);
13451345

1346-
messageRaw.message.pollUpdateMessage.vote.selectedOptions = selectedOptionNames;
1346+
(messageRaw.message as any).pollUpdateMessage.vote.selectedOptions = selectedOptionNames;
13471347

13481348
const pollUpdates = pollOptions.map((option) => ({
13491349
name: option.optionName,
13501350
voters: selectedOptionNames.includes(option.optionName) ? [successfulVoterJid] : [],
13511351
}));
13521352

1353-
messageRaw.pollUpdates = pollUpdates;
1353+
(messageRaw as any).pollUpdates = pollUpdates;
13541354
}
13551355
}
13561356

@@ -1398,13 +1398,14 @@ export class BaileysStartupService extends ChannelStartupService {
13981398
});
13991399

14001400
if (openAiDefaultSettings && openAiDefaultSettings.openaiCredsId && openAiDefaultSettings.speechToText) {
1401-
messageRaw.message.speechToText = `[audio] ${await this.openaiService.speechToText(received, this)}`;
1401+
(messageRaw.message as any).speechToText =
1402+
`[audio] ${await this.openaiService.speechToText(received, this)}`;
14021403
}
14031404
}
14041405

14051406
if (this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE) {
14061407
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1407-
const { pollUpdates, ...messageData } = messageRaw;
1408+
const { pollUpdates, ...messageData } = messageRaw as any;
14081409
const msg = await this.prismaRepository.message.create({ data: messageData });
14091410

14101411
const { remoteJid } = received.key;
@@ -1480,7 +1481,7 @@ export class BaileysStartupService extends ChannelStartupService {
14801481

14811482
const mediaUrl = await s3Service.getObjectUrl(fullName);
14821483

1483-
messageRaw.message.mediaUrl = mediaUrl;
1484+
(messageRaw.message as any).mediaUrl = mediaUrl;
14841485

14851486
await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw });
14861487
}
@@ -1502,7 +1503,7 @@ export class BaileysStartupService extends ChannelStartupService {
15021503
);
15031504

15041505
if (buffer) {
1505-
messageRaw.message.base64 = buffer.toString('base64');
1506+
(messageRaw.message as any).base64 = buffer.toString('base64');
15061507
} else {
15071508
// retry to download media
15081509
const buffer = await downloadMediaMessage(
@@ -1513,7 +1514,7 @@ export class BaileysStartupService extends ChannelStartupService {
15131514
);
15141515

15151516
if (buffer) {
1516-
messageRaw.message.base64 = buffer.toString('base64');
1517+
(messageRaw.message as any).base64 = buffer.toString('base64');
15171518
}
15181519
}
15191520
} catch (error) {
@@ -1525,16 +1526,16 @@ export class BaileysStartupService extends ChannelStartupService {
15251526
this.logger.verbose(messageRaw);
15261527

15271528
sendTelemetry(`received.message.${messageRaw.messageType ?? 'unknown'}`);
1528-
if (messageRaw.key.remoteJid?.includes('@lid') && messageRaw.key.remoteJidAlt) {
1529-
messageRaw.key.remoteJid = messageRaw.key.remoteJidAlt;
1529+
if ((messageRaw.key as any).remoteJid?.includes('@lid') && (messageRaw.key as any).remoteJidAlt) {
1530+
(messageRaw.key as any).remoteJid = (messageRaw.key as any).remoteJidAlt;
15301531
}
15311532
console.log(messageRaw);
15321533

15331534
this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
15341535

15351536
await chatbotController.emit({
15361537
instance: { instanceName: this.instance.name, instanceId: this.instanceId },
1537-
remoteJid: messageRaw.key.remoteJid,
1538+
remoteJid: (messageRaw.key as any).remoteJid,
15381539
msg: messageRaw,
15391540
pushName: messageRaw.pushName,
15401541
});
@@ -1563,9 +1564,11 @@ export class BaileysStartupService extends ChannelStartupService {
15631564
await saveOnWhatsappCache([
15641565
{
15651566
remoteJid:
1566-
messageRaw.key.addressingMode === 'lid' ? messageRaw.key.remoteJidAlt : messageRaw.key.remoteJid,
1567-
remoteJidAlt: messageRaw.key.remoteJidAlt,
1568-
lid: messageRaw.key.addressingMode === 'lid' ? 'lid' : null,
1567+
(messageRaw.key as any).addressingMode === 'lid'
1568+
? (messageRaw.key as any).remoteJidAlt
1569+
: (messageRaw.key as any).remoteJid,
1570+
remoteJidAlt: (messageRaw.key as any).remoteJidAlt,
1571+
lid: (messageRaw.key as any).addressingMode === 'lid' ? 'lid' : null,
15691572
},
15701573
]);
15711574
}
@@ -1611,7 +1614,18 @@ export class BaileysStartupService extends ChannelStartupService {
16111614
const readChatToUpdate: Record<string, true> = {}; // {remoteJid: true}
16121615

16131616
for await (const { key, update } of args) {
1614-
if (settings?.groupsIgnore && key.remoteJid?.includes('@g.us')) {
1617+
const keyAny = key as any;
1618+
if (keyAny.remoteJid) {
1619+
keyAny.remoteJid = keyAny.remoteJid.replace(/:.*$/, '');
1620+
}
1621+
if (keyAny.participant) {
1622+
keyAny.participant = keyAny.participant.replace(/:.*$/, '');
1623+
}
1624+
1625+
const normalizedRemoteJid = keyAny.remoteJid;
1626+
const normalizedParticipant = keyAny.participant;
1627+
1628+
if (settings?.groupsIgnore && normalizedRemoteJid?.includes('@g.us')) {
16151629
continue;
16161630
}
16171631

@@ -1662,9 +1676,9 @@ export class BaileysStartupService extends ChannelStartupService {
16621676

16631677
const message: any = {
16641678
keyId: key.id,
1665-
remoteJid: key?.remoteJid,
1679+
remoteJid: normalizedRemoteJid,
16661680
fromMe: key.fromMe,
1667-
participant: key?.participant,
1681+
participant: normalizedParticipant,
16681682
status: status[update.status] ?? 'SERVER_ACK',
16691683
pollUpdates,
16701684
instanceId: this.instanceId,
@@ -1687,18 +1701,48 @@ export class BaileysStartupService extends ChannelStartupService {
16871701

16881702
const searchId = originalMessageId || key.id;
16891703

1690-
const messages = (await this.prismaRepository.$queryRaw`
1691-
SELECT * FROM "Message"
1692-
WHERE "instanceId" = ${this.instanceId}
1693-
AND "key"->>'id' = ${searchId}
1694-
LIMIT 1
1695-
`) as any[];
1696-
findMessage = messages[0] || null;
1704+
let retries = 0;
1705+
const maxRetries = 3;
1706+
const retryDelay = 500; // 500ms delay to avoid blocking for too long
1707+
1708+
while (retries < maxRetries) {
1709+
const messages = (await this.prismaRepository.$queryRaw`
1710+
SELECT * FROM "Message"
1711+
WHERE "instanceId" = ${this.instanceId}
1712+
AND "key"->>'id' = ${searchId}
1713+
LIMIT 1
1714+
`) as any[];
1715+
findMessage = messages[0] || null;
1716+
1717+
if (findMessage?.id) {
1718+
break;
1719+
}
1720+
1721+
retries++;
1722+
if (retries < maxRetries) {
1723+
await delay(retryDelay);
1724+
}
1725+
}
16971726

16981727
if (!findMessage?.id) {
1699-
this.logger.warn(`Original message not found for update. Skipping. Key: ${JSON.stringify(key)}`);
1728+
this.logger.verbose(
1729+
`Original message not found for update after ${maxRetries} retries. Skipping. This is expected for protocol messages or ephemeral events not saved to the database. Key: ${JSON.stringify(key)}`,
1730+
);
17001731
continue;
17011732
}
1733+
1734+
// Sync the incoming key.remoteJid with the stored one.
1735+
// This mutation is safe and necessary because Baileys events might use LIDs while we store Phone JIDs (or vice versa).
1736+
// Normalizing ensuring downstream logic uses the identifier that exists in our database.
1737+
if (findMessage?.key?.remoteJid && key.remoteJid !== findMessage.key.remoteJid) {
1738+
key.remoteJid = findMessage.key.remoteJid;
1739+
}
1740+
if (findMessage?.key?.remoteJid && findMessage.key.remoteJid !== key.remoteJid) {
1741+
this.logger.verbose(
1742+
`Updating key.remoteJid from ${key.remoteJid} to ${findMessage.key.remoteJid} based on stored message`,
1743+
);
1744+
key.remoteJid = findMessage.key.remoteJid;
1745+
}
17021746
message.messageId = findMessage.id;
17031747
}
17041748

@@ -2472,7 +2516,7 @@ export class BaileysStartupService extends ChannelStartupService {
24722516
messageSent.messageTimestamp = messageSent.messageTimestamp?.toNumber();
24732517
}
24742518

2475-
const messageRaw = this.prepareMessage(messageSent);
2519+
const messageRaw = this.prepareMessage(messageSent) as any;
24762520

24772521
const isMedia =
24782522
messageSent?.message?.imageMessage ||
@@ -2494,14 +2538,15 @@ export class BaileysStartupService extends ChannelStartupService {
24942538
);
24952539
}
24962540

2497-
if (this.configService.get<Openai>('OPENAI').ENABLED && messageRaw?.message?.audioMessage) {
2541+
if (this.configService.get<Openai>('OPENAI').ENABLED && (messageRaw as any)?.message?.audioMessage) {
24982542
const openAiDefaultSettings = await this.prismaRepository.openaiSetting.findFirst({
24992543
where: { instanceId: this.instanceId },
25002544
include: { OpenaiCreds: true },
25012545
});
25022546

25032547
if (openAiDefaultSettings && openAiDefaultSettings.openaiCredsId && openAiDefaultSettings.speechToText) {
2504-
messageRaw.message.speechToText = `[audio] ${await this.openaiService.speechToText(messageRaw, this)}`;
2548+
(messageRaw.message as any).speechToText =
2549+
`[audio] ${await this.openaiService.speechToText(messageRaw, this)}`;
25052550
}
25062551
}
25072552

@@ -4712,26 +4757,28 @@ export class BaileysStartupService extends ChannelStartupService {
47124757
return obj;
47134758
}
47144759

4715-
private prepareMessage(message: proto.IWebMessageInfo): any {
4716-
const contentType = getContentType(message.message);
4717-
const contentMsg = message?.message[contentType] as any;
4718-
4719-
const messageRaw = {
4720-
key: message.key, // Save key exactly as it comes from Baileys
4760+
private prepareMessage(message: WAMessage): Message {
4761+
const keyAny = message.key as any;
4762+
const messageRaw: any = {
4763+
key: {
4764+
...message.key,
4765+
remoteJid: keyAny.remoteJid?.replace(/:.*$/, ''),
4766+
participant: keyAny.participant?.replace(/:.*$/, ''),
4767+
},
47214768
pushName:
47224769
message.pushName ||
47234770
(message.key.fromMe
47244771
? 'Você'
47254772
: message?.participant || (message.key?.participant ? message.key.participant.split('@')[0] : null)),
4726-
status: status[message.status],
47274773
message: this.deserializeMessageBuffers({ ...message.message }),
4728-
contextInfo: this.deserializeMessageBuffers(contentMsg?.contextInfo),
4729-
messageType: contentType || 'unknown',
4774+
messageType: getContentType(message.message),
47304775
messageTimestamp: Long.isLong(message.messageTimestamp)
47314776
? message.messageTimestamp.toNumber()
47324777
: (message.messageTimestamp as number),
4778+
source: getDevice(keyAny.id),
47334779
instanceId: this.instanceId,
4734-
source: getDevice(message.key.id),
4780+
status: status[message.status],
4781+
contextInfo: this.deserializeMessageBuffers(message.message?.messageContextInfo),
47354782
};
47364783

47374784
if (!messageRaw.status && message.key.fromMe === false) {

src/utils/onWhatsappCache.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { prismaRepository } from '@api/server.module';
22
import { configService, Database } from '@config/env.config';
33
import { Logger } from '@config/logger.config';
4+
import { Prisma } from '@prisma/client';
45
import dayjs from 'dayjs';
56

67
const logger = new Logger('OnWhatsappCache');
@@ -164,9 +165,28 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) {
164165
logger.verbose(
165166
`[saveOnWhatsappCache] Register does not exist, creating: remoteJid=${remoteJid}, jidOptions=${dataPayload.jidOptions}, lid=${dataPayload.lid}`,
166167
);
167-
await prismaRepository.isOnWhatsapp.create({
168-
data: dataPayload,
169-
});
168+
try {
169+
await prismaRepository.isOnWhatsapp.create({
170+
data: dataPayload,
171+
});
172+
} catch (error: any) {
173+
// Check for unique constraint violation (Prisma error code P2002)
174+
if (
175+
error instanceof Prisma.PrismaClientKnownRequestError &&
176+
error.code === 'P2002' &&
177+
(error.meta?.target as string[])?.includes('remoteJid')
178+
) {
179+
logger.verbose(
180+
`[saveOnWhatsappCache] Race condition detected for ${remoteJid}, updating existing record instead.`,
181+
);
182+
await prismaRepository.isOnWhatsapp.update({
183+
where: { remoteJid: remoteJid },
184+
data: dataPayload,
185+
});
186+
} else {
187+
throw error;
188+
}
189+
}
170190
}
171191
} catch (e) {
172192
// Loga o erro mas não para a execução dos outros promises

0 commit comments

Comments
 (0)