Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* **[WIDGET-WORKS]** Disable useCachedGroupMetadata for all groups to prevent LID 406 errors
* **[WIDGET-WORKS]** Add HTTP timeout (30s) and maxRedirects (10) to all media download axios calls to prevent indefinite hangs on Azure/ActiveStorage URLs
* **[WIDGET-WORKS]** Fix mimetype detection for Azure Storage URLs - prevent `mimeTypes.lookup()` returning "false" string for URLs without file extensions
* **[WIDGET-WORKS]** Fix LID messages not syncing to Chatwoot during historical sync (syncFullHistory) - batch resolve @lid identifiers to phone numbers using IsOnWhatsapp table before messages.set processing. Unresolved LIDs create temporary contacts (self-healing on next real-time message). Resolves 942 unsynced LID messages across production instances. Disable with `CHATWOOT_LID_HISTORICAL_SYNC_ENABLED=false`

**Tier 2 (Optional - disabled by default):**
* **[WIDGET-WORKS]** `LOG_BAILEYS=debug`: Enhanced Baileys connection.update logging with wsCloseCode/wsCloseReason
Expand Down
68 changes: 68 additions & 0 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,74 @@ export class BaileysStartupService extends ChannelStartupService {
chatwootImport.setRepositoryMessagesCache(instance, messagesRepository);
}

// [WIDGET-WORKS] LID resolution for historical sync (messages.set)
// Context: During historical sync, Baileys provides stripped message keys without remoteJidAlt field.
// This prevents LID→phone resolution that works in real-time (see line 1521-1522).
// Solution: Batch resolve LID JIDs using IsOnWhatsapp table before message processing.
// Unresolved LIDs pass through as-is; self-healing at chatwoot.service.ts:644-666 will merge later.
// Related: PR #2275 upstream fix for real-time @lid handling
if (this.configService.get<Chatwoot>('CHATWOOT').LID_HISTORICAL_SYNC_ENABLED) {
const lidJids = new Set(
messages
.filter((m) => m.key?.remoteJid?.endsWith('@lid'))
.map((m) => m.key.remoteJid)
.filter((jid): jid is string => !!jid),
);

if (lidJids.size > 0) {
this.logger.verbose(
`[messages.set] Found ${lidJids.size} unique LID identifiers, attempting resolution via IsOnWhatsapp table`,
);

try {
const onWhatsappRecords = await this.prismaRepository.isOnWhatsapp.findMany({
where: {
OR: Array.from(lidJids).map((jid) => ({ jidOptions: { contains: jid } })),
},
});

const lidToPhoneMap = new Map<string, string>();
onWhatsappRecords.forEach((record) => {
if (!record.jidOptions || !record.remoteJid.endsWith('@s.whatsapp.net')) {
return;
}
const lidInOptions = record.jidOptions.split(',').find((j) => j.endsWith('@lid'));
if (lidInOptions && lidJids.has(lidInOptions)) {
lidToPhoneMap.set(lidInOptions, record.remoteJid);
}
});

this.logger.verbose(
`[messages.set] Resolved ${lidToPhoneMap.size}/${lidJids.size} LID identifiers to phone numbers`,
);

// Replace LID with phone number where mapping exists
let replacedCount = 0;
messages.forEach((m) => {
if (m.key?.remoteJid?.endsWith('@lid') && lidToPhoneMap.has(m.key.remoteJid)) {
const originalLid = m.key.remoteJid;
m.key.remoteJid = lidToPhoneMap.get(originalLid);
replacedCount++;
}
});

if (replacedCount > 0) {
this.logger.verbose(`[messages.set] Replaced ${replacedCount} message remoteJids from LID to phone`);
}

const unresolvedCount = lidJids.size - lidToPhoneMap.size;
if (unresolvedCount > 0) {
this.logger.verbose(
`[messages.set] ${unresolvedCount} LID identifiers not found in IsOnWhatsapp cache - will create temporary contacts (self-healing on next real-time message)`,
);
}
} catch (error) {
this.logger.error(`[messages.set] Error during LID resolution: ${error?.message}`);
// Continue processing - better to have LID contacts than miss messages entirely
}
}
}

for (const m of messages) {
if (!m.message || !m.key || !m.messageTimestamp) {
continue;
Expand Down
6 changes: 6 additions & 0 deletions src/config/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ export type Chatwoot = {
};
PLACEHOLDER_MEDIA_MESSAGE: boolean;
};
// [WIDGET-WORKS] Enable LID→phone resolution during historical sync (messages.set)
// Resolves @lid identifiers using IsOnWhatsapp table before Chatwoot import
// Default: true (enabled), set to false to disable
LID_HISTORICAL_SYNC_ENABLED: boolean;
};
export type Openai = { ENABLED: boolean; API_KEY_GLOBAL?: string };
export type Dify = { ENABLED: boolean };
Expand Down Expand Up @@ -822,6 +826,8 @@ export class ConfigService {
},
PLACEHOLDER_MEDIA_MESSAGE: process.env?.CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE === 'true',
},
// [WIDGET-WORKS] LID historical sync feature flag (default: true, opt-out with =false)
LID_HISTORICAL_SYNC_ENABLED: process.env?.CHATWOOT_LID_HISTORICAL_SYNC_ENABLED !== 'false',
},
OPENAI: {
ENABLED: process.env?.OPENAI_ENABLED === 'true',
Expand Down