-
Notifications
You must be signed in to change notification settings - Fork 5.1k
fix(baileys): normalize remote JIDs for consistent database lookups #2319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(baileys): normalize remote JIDs for consistent database lookups #2319
Conversation
Reviewer's GuideNormalizes Baileys remoteJid/participant values to strip device suffixes before any DB interaction, and simplifies/strongly-types message preparation to avoid Prisma validation issues and ensure consistent message keys. Sequence diagram for normalized message status update and DB lookupsequenceDiagram
participant BaileysClient
participant BaileysStartupService
participant MessagesUpdateHandler
participant MessageRepository
participant Database
BaileysClient->>BaileysStartupService: messages.update(args)
BaileysStartupService->>MessagesUpdateHandler: iterate args { key, update }
MessagesUpdateHandler->>MessagesUpdateHandler: normalize keyAny.remoteJid
MessagesUpdateHandler->>MessagesUpdateHandler: normalize keyAny.participant
MessagesUpdateHandler->>MessageRepository: findMessageByKey(keyAny)
MessageRepository->>Database: SELECT message WHERE remoteJid, participant
Database-->>MessageRepository: normalized message row
MessageRepository-->>MessagesUpdateHandler: message entity
MessagesUpdateHandler->>MessagesUpdateHandler: build message object with normalized JIDs
MessagesUpdateHandler->>MessageRepository: updateMessageStatus(message)
MessageRepository->>Database: UPDATE status
Database-->>MessageRepository: update ok
MessageRepository-->>BaileysStartupService: status updated
BaileysStartupService-->>BaileysClient: processing complete
Sequence diagram for prepareMessage normalization and persistencesequenceDiagram
participant BaileysClient
participant BaileysStartupService
participant prepareMessage
participant MessageRepository
participant Database
BaileysClient->>BaileysStartupService: incoming WAMessage
BaileysStartupService->>prepareMessage: prepareMessage(message)
prepareMessage->>prepareMessage: keyAny = message.key as any
prepareMessage->>prepareMessage: normalize keyAny.remoteJid
prepareMessage->>prepareMessage: normalize keyAny.participant
prepareMessage->>prepareMessage: build messageRaw with nested key and fromMe
prepareMessage-->>BaileysStartupService: Message entity
BaileysStartupService->>MessageRepository: upsertMessage(Message)
MessageRepository->>Database: INSERT or UPDATE using normalized JIDs
Database-->>MessageRepository: upsert ok
MessageRepository-->>BaileysStartupService: persistence complete
ER diagram for Message storage with normalized JIDserDiagram
INSTANCE {
string id
string name
}
MESSAGE {
string id
string remoteJid
string participant
string keyId
boolean fromMe
string status
string instanceId
string messageType
number messageTimestamp
}
INSTANCE ||--o{ MESSAGE : has_messages
Class diagram for BaileysStartupService message key normalizationclassDiagram
class BaileysStartupService {
+string instanceId
+handleMessagesUpdate(args)
-prepareMessage(message WAMessage) Message
-deserializeMessageBuffers(input any) any
}
class WAMessage {
+MessageKey key
+string pushName
+any message
+number messageTimestamp
}
class MessageKey {
+string id
+string remoteJid
+string participant
+boolean fromMe
}
class Message {
+MessageKey key
+string pushName
+any message
+string messageType
+number messageTimestamp
+string source
+string instanceId
}
class MessageRepository {
+Message findMessageByKey(key MessageKey)
+Message upsertMessage(message Message)
+void updateMessageStatus(message Message)
}
BaileysStartupService --> WAMessage : consumes
BaileysStartupService --> Message : produces
WAMessage --> MessageKey : has
Message --> MessageKey : has
BaileysStartupService --> MessageRepository : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- In the messages.update handler you cast
keytoanyand mutateremoteJid/participantin place; consider normalizing into new local variables or a cloned key object instead of modifying the Baileys key directly to avoid side effects elsewhere that still expect the original JID format. - In
prepareMessageyou removed thedeserializeMessageBufferscalls and the previouspushName/contextInfo/statushandling; if those fields are still used downstream this could be a subtle regression, so it may be safer to preserve the previous deserialization and fallback logic while adding the JID normalization. - The new
prepareMessagenow assignsmessageTimestampdirectly frommessage.messageTimestampwithout handling theLongcase as before, which may change the stored type; consider keeping the explicitLong.isLongnormalization to ensure a consistent numeric timestamp.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the messages.update handler you cast `key` to `any` and mutate `remoteJid`/`participant` in place; consider normalizing into new local variables or a cloned key object instead of modifying the Baileys key directly to avoid side effects elsewhere that still expect the original JID format.
- In `prepareMessage` you removed the `deserializeMessageBuffers` calls and the previous `pushName`/`contextInfo`/`status` handling; if those fields are still used downstream this could be a subtle regression, so it may be safer to preserve the previous deserialization and fallback logic while adding the JID normalization.
- The new `prepareMessage` now assigns `messageTimestamp` directly from `message.messageTimestamp` without handling the `Long` case as before, which may change the stored type; consider keeping the explicit `Long.isLong` normalization to ensure a consistent numeric timestamp.
## Individual Comments
### Comment 1
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:4665-4670` </location>
<code_context>
- private prepareMessage(message: proto.IWebMessageInfo): any {
</code_context>
<issue_to_address>
**issue (bug_risk):** Changing `messageTimestamp` handling may introduce type/serialization issues when it is a Long.
Previously, `messageTimestamp` was always converted to a JS number when it was a `Long`:
```ts
messageTimestamp: Long.isLong(message.messageTimestamp)
? message.messageTimestamp.toNumber()
: (message.messageTimestamp as number),
```
Now it’s passed through unchanged, so callers/DB serializers may receive a `Long` instead of a primitive number. If downstream code or schemas expect a numeric timestamp, this can break queries or JSON serialization. Please keep an explicit normalization (or equivalent guard) while adopting the new `WAMessage`/`Message` types.
</issue_to_address>
### Comment 2
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:4671-4678` </location>
<code_context>
- pushName:
</code_context>
<issue_to_address>
**issue:** Dropping the pushName fallback logic may affect UX where pushName is absent.
Previously this fell back to `'Você'` for outgoing messages and to `message.participant`/the JID local-part when `message.pushName` was missing; now it relies solely on `message.pushName`. If that change wasn’t intentional, consider reintroducing a minimal fallback so UI/logs don’t show blank or `undefined` names.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
📋 Description
This PR addresses critical issues with message handling and persistence caused by
Multi-Device (MD) JID formats in the WhatsApp integration (Baileys).
The core problem was that Baileys often emits message keys with device-specific
suffixes (e.g., :3 for web, :0 for phone), while the system and database expect
normalized JIDs (e.g., [email protected]). This mismatch caused:
match the "dirty" JID.
suffix depending on the flow.
structure in prepareMessage.
Changes:
handler to ensure database lookups and updates always use the canonical JID.
messages are stored cleanly.
the key object instead of the root level.
🔗 Related Issue
Closes # (insira o número da issue se houver)
🧪 Type of Change
not work as expected)
🧪 Testing
Verification Steps:
"Original message not found" warnings in logs.
device suffixes.
📸 Screenshots (if applicable)
(Optional: Add screenshots of clean logs or DB records if available)
✅ Checklist
📝 Additional Notes
This fix is essential for stability in environments where users frequently switch
between devices (Phone/Web/Desktop), ensuring message history and status updates
remain consistent regardless of the source device.
Summary by Sourcery
Normalize WhatsApp Baileys message keys to use canonical JIDs and align stored messages with the expected schema.
Bug Fixes:
Enhancements: