|
| 1 | +import { parseMessageIdSerialized } from '@waha/core/utils/ids'; |
| 2 | +import { WAMessage } from '@waha/structures/responses.dto'; |
1 | 3 | import { WAMessageAckBody } from '@waha/structures/webhooks.dto'; |
2 | 4 | import { distinct, interval } from 'rxjs'; |
3 | 5 |
|
4 | 6 | export function DistinctAck(flushEvery: number = 60_000) { |
5 | | - // only if we haven’t seen this key since the last flush |
| 7 | + // only if we haven't seen this key since the last flush |
6 | 8 | return distinct( |
7 | 9 | (msg: WAMessageAckBody) => `${msg.id}-${msg.ack}-${msg.participant}`, |
8 | 10 | interval(flushEvery), |
9 | 11 | ); |
10 | 12 | } |
| 13 | + |
| 14 | +/** |
| 15 | + * Extracts the unique WhatsApp message ID from a serialized WAHA message ID. |
| 16 | + * Message IDs have format: {fromMe}_{chatId}_{uniqueId}[_{participant}] |
| 17 | + * |
| 18 | + * The uniqueId part is what WhatsApp generates and remains constant even when |
| 19 | + * the same message is delivered with different chat identifiers (LID vs JID). |
| 20 | + * |
| 21 | + * @example |
| 22 | + * "false_13649439626@lid_ABC123" => "ABC123" |
| 23 | + * "false_2010XXXXXXX@c.us_ABC123" => "ABC123" |
| 24 | + */ |
| 25 | +function extractUniqueMessageId(messageId: string): string { |
| 26 | + const key = parseMessageIdSerialized(messageId, true); |
| 27 | + return key.id; |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * Deduplicates messages by their unique WhatsApp message ID. |
| 32 | + * This is needed to prevent duplicate webhooks for the same message, |
| 33 | + * which can happen in GOWS when receiving the first message from a new sender. |
| 34 | + * |
| 35 | + * When a new contact sends their first message, WhatsApp may deliver: |
| 36 | + * 1. Two events with the same full ID but different internal structures |
| 37 | + * 2. Two events with different chat identifiers (LID vs JID) but same unique message ID |
| 38 | + * |
| 39 | + * This function extracts the unique message ID part and deduplicates based on that, |
| 40 | + * along with fromMe flag to avoid conflicts between sent and received messages. |
| 41 | + * |
| 42 | + * @see https://github.com/devlikeapro/waha/issues/1564 |
| 43 | + */ |
| 44 | +export function DistinctMessages(flushEvery: number = 60_000) { |
| 45 | + return distinct((msg: WAMessage) => { |
| 46 | + const uniqueId = extractUniqueMessageId(msg.id); |
| 47 | + // Include fromMe to distinguish sent vs received with same ID |
| 48 | + return `${msg.fromMe}_${uniqueId}`; |
| 49 | + }, interval(flushEvery)); |
| 50 | +} |
0 commit comments