From 32f66c8fe07aabdc920bdfd94ca6f895fc682ad0 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Thu, 4 Dec 2025 12:33:22 -0600 Subject: [PATCH] Fix: fetchMessageHistory con evento SSE y logs de debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar logs detallados en /api/debug/history/fetch para diagnosticar - Emitir evento SSE 'history.synced' cuando llegan mensajes del historial - Frontend ahora escucha el evento SSE en vez de timeout fijo de 3 segundos - Agregar script y documentación para referencia de Baileys message history --- app/composables/useRealtime.ts | 6 +- app/pages/messages/index.vue | 39 +- docs/baileys-message-history-reference.md | 1684 +++++++++++++++++++++ scripts/scrape-baileys-message-history.ts | 253 ++++ server/api/debug/history/fetch.post.ts | 20 +- server/api/events/stream.get.ts | 3 + server/services/baileys/manager.ts | 9 + 7 files changed, 2004 insertions(+), 10 deletions(-) create mode 100644 docs/baileys-message-history-reference.md create mode 100644 scripts/scrape-baileys-message-history.ts diff --git a/app/composables/useRealtime.ts b/app/composables/useRealtime.ts index 24ebac2..861031d 100644 --- a/app/composables/useRealtime.ts +++ b/app/composables/useRealtime.ts @@ -8,6 +8,7 @@ export interface RealtimeEvents { 'message.status': (data: { instanceId: string; messageId: string; status: string }) => void 'instance.status': (data: { instanceId: string; status: string; phoneNumber?: string }) => void 'instance.qr': (data: { instanceId: string; qr: string; qrDataUrl: string }) => void + 'history.synced': (data: { instanceId: string; chatsCount: number; contactsCount: number; messagesCount: number; syncType: any }) => void } export const useRealtime = () => { @@ -49,11 +50,12 @@ export const useRealtime = () => { // Listen for specific event types const eventTypes = [ 'message.received', - 'message.sent', + 'message.sent', 'message.status', 'instance.status', 'instance.qr', - 'instance.pairing' + 'instance.pairing', + 'history.synced' ] eventTypes.forEach(eventType => { diff --git a/app/pages/messages/index.vue b/app/pages/messages/index.vue index e27b8e9..423ffd3 100644 --- a/app/pages/messages/index.vue +++ b/app/pages/messages/index.vue @@ -395,10 +395,10 @@ const fetchWhatsAppHistory = async () => { if (!selectedInstance.value?.value || !selectedChat.value) return loadingWhatsAppHistory.value = true - try { - const instanceId = selectedInstance.value.value - const chatId = selectedChat.value.id + const instanceId = selectedInstance.value.value + const chatId = selectedChat.value.id + try { // Get the oldest message from local DB const oldest = await $fetch<{ hasMessages: boolean @@ -406,6 +406,23 @@ const fetchWhatsAppHistory = async () => { timestamp: number | null }>(`/api/messages/${instanceId}/${chatId}/oldest`) + // Create a promise that resolves when history.synced event arrives + const historyPromise = new Promise<{ messagesCount: number; timeout?: boolean }>((resolve) => { + // Set up listener for history.synced event + const cleanup = on('history.synced', (data) => { + if (data.instanceId === instanceId) { + cleanup() // Remove listener + resolve({ messagesCount: data.messagesCount }) + } + }) + + // Timeout de seguridad (15 segundos) + setTimeout(() => { + cleanup() // Remove listener + resolve({ messagesCount: 0, timeout: true }) + }, 15000) + }) + // Request history from WhatsApp if (!oldest.hasMessages) { // No messages in DB, request without reference @@ -429,10 +446,18 @@ const fetchWhatsAppHistory = async () => { }) } - // Wait for history sync to complete and reload - await new Promise(r => setTimeout(r, 3000)) - await reloadMessages() - hasMoreMessages.value = true + // Wait for history sync event or timeout + const result = await historyPromise + + if (result.timeout) { + console.log('[History] Timeout waiting for history sync - WhatsApp may not have more messages') + } else if (result.messagesCount > 0) { + console.log(`[History] Received ${result.messagesCount} messages from history sync`) + await reloadMessages() + hasMoreMessages.value = true + } else { + console.log('[History] No messages received from history sync') + } } catch (e) { console.error('Error fetching WhatsApp history:', e) } finally { diff --git a/docs/baileys-message-history-reference.md b/docs/baileys-message-history-reference.md new file mode 100644 index 0000000..bbb3255 --- /dev/null +++ b/docs/baileys-message-history-reference.md @@ -0,0 +1,1684 @@ +# Baileys Message History API Reference + +> Documentation for fetching and managing message history in WhatsApp Nucleo +> Source: https://baileys.wiki +> Generated: 2025-12-04T18:06:29.611Z + +This document contains the Baileys API documentation specifically for: +- Fetching message history from chats +- History sync functionality +- Message events and types + +## Table of Contents + +### Message Types +- [WAMessage](#type-wamessage) +- [WAMessageKey](#type-wamessagekey) +- [WAMessageCursor](#type-wamessagecursor) +- [WAMessageUpdate](#type-wamessageupdate) +- [MinimalMessage](#type-minimalmessage) +- [RecentMessage](#interface-recentmessage) +- [RecentMessageKey](#interface-recentmessagekey) +- [LastMessageList](#type-lastmessagelist) + +### Chat Types +- [Chat](#type-chat) +- [ChatModification](#type-chatmodification) +- [ChatMutation](#type-chatmutation) + +### History Functions +- [downloadAndProcessHistorySyncNotification](#function-downloadandprocesshistorysyncnotification) +- [processHistoryMessage](#function-processhistorymessage) +- [getHistoryMsg](#function-gethistorymsg) +- [downloadHistory](#function-downloadhistory) + +### Socket +- [WASocket](#type-wasocket) +- [SocketConfig](#type-socketconfig) +- [UserFacingSocketConfig](#type-userfacingsocketconfig) + +### Events +- [BaileysEventMap](#type-baileyseventmap) +- [BaileysEventEmitter](#interface-baileyseventemitter) +- [MessageUpsertType](#type-messageupserttype) + +### Message Utils +- [extractMessageContent](#function-extractmessagecontent) +- [getContentType](#function-getcontenttype) +- [normalizeMessageContent](#function-normalizemessagecontent) + +### Constants +- [PROCESSABLE_HISTORY_TYPES](#variable-processable_history_types) + +## Quick Reference: How to Fetch Messages + +### Using fetchMessageHistory (WASocket method) + +```typescript +// The WASocket has a fetchMessageHistory method: +sock.fetchMessageHistory( + count: number, // Number of messages to fetch + oldestMsgKey: WAMessageKey, // Key of the oldest message you have + oldestMsgTimestamp: number // Timestamp of the oldest message +) +``` + +### Listening to History Sync Events + +```typescript +sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => { + // messages: WAMessage[] - reverse chronologically sorted + // chats: Chat[] - chat metadata + // isLatest: boolean - if this is the most recent sync +}) +``` + +### Configuration Options + +```typescript +const sock = makeWASocket({ + // ... other config + syncFullHistory: true, // Request full history from phone + shouldSyncHistoryMessage: (msg) => true, // Control which messages to sync + getMessage: async (key) => { + // Implement to fetch message from your store + // Required for message retries + } +}) +``` + + + +# Message Types + + +--- + +## Type: WAMessage + +**Source:** https://baileys.wiki/docs/api/type-aliases/WAMessage + +On this page + +**WAMessage**: `IWebMessageInfo` & `object` + +Defined in: src/Types/Message.ts:11 + +## Type declaration​ + +### category?​ + +`optional` **category**: `string` + +### key​ + +**key**: `WAMessageKey` + +### messageStubParameters?​ + +`optional` **messageStubParameters**: `any` + +### retryCount?​ + +`optional` **retryCount**: `number` + +- Type declaration +category? +- key +- messageStubParameters? +- retryCount? + + +--- + +## Type: WAMessageKey + +**Source:** https://baileys.wiki/docs/api/type-aliases/WAMessageKey + +On this page + +**WAMessageKey**: `IMessageKey` & `object` + +Defined in: src/Types/Message.ts:20 + +## Type declaration​ + +### addressingMode?​ + +`optional` **addressingMode**: `string` + +### isViewOnce?​ + +`optional` **isViewOnce**: `boolean` + +### participantAlt?​ + +`optional` **participantAlt**: `string` + +### remoteJidAlt?​ + +`optional` **remoteJidAlt**: `string` + +### server_id?​ + +`optional` **server_id**: `string` + +- Type declaration +addressingMode? +- isViewOnce? +- participantAlt? +- remoteJidAlt? +- server_id? + + +--- + +## Type: WAMessageCursor + +**Source:** https://baileys.wiki/docs/api/type-aliases/WAMessageCursor + +**WAMessageCursor**: { `before`: `WAMessageKey` | `undefined`; } | { `after`: `WAMessageKey` | `undefined`; } + +Defined in: src/Types/Message.ts:370 + + +--- + +## Type: WAMessageUpdate + +**Source:** https://baileys.wiki/docs/api/type-aliases/WAMessageUpdate + +On this page + +**WAMessageUpdate**: `object` + +Defined in: src/Types/Message.ts:368 + +## Type declaration​ + +### key​ + +**key**: `WAMessageKey` + +### update​ + +**update**: `Partial`<`WAMessage`> + +- Type declaration +key +- update + + +--- + +## Type: MinimalMessage + +**Source:** https://baileys.wiki/docs/api/type-aliases/MinimalMessage + +**MinimalMessage**: `Pick`<`WAMessage`, `"key"` | `"messageTimestamp"`> + +Defined in: src/Types/Message.ts:380 + + +--- + +## Interface: RecentMessage + +**Source:** https://baileys.wiki/docs/api/interfaces/RecentMessage + +On this page + +Defined in: src/Utils/message-retry-manager.ts:18 + +## Properties​ + +### message​ + +**message**: `IMessage` + +Defined in: src/Utils/message-retry-manager.ts:19 + +### timestamp​ + +**timestamp**: `number` + +Defined in: src/Utils/message-retry-manager.ts:20 + +- Properties +message +- timestamp + + +--- + +## Interface: RecentMessageKey + +**Source:** https://baileys.wiki/docs/api/interfaces/RecentMessageKey + +On this page + +Defined in: src/Utils/message-retry-manager.ts:13 + +## Properties​ + +### id​ + +**id**: `string` + +Defined in: src/Utils/message-retry-manager.ts:15 + +### to​ + +**to**: `string` + +Defined in: src/Utils/message-retry-manager.ts:14 + +- Properties +id +- to + + +--- + +## Type: LastMessageList + +**Source:** https://baileys.wiki/docs/api/type-aliases/LastMessageList + +**LastMessageList**: `MinimalMessage`[] | `ISyncActionMessageRange` + +Defined in: src/Types/Chat.ts:85 + +the last messages in a chat, sorted reverse-chronologically. That is, the latest message should be first in the chat +for MD modifications, the last message in the array (i.e. the earlist message) must be the last message recv in the chat + + +# Chat Types + + +--- + +## Type: Chat + +**Source:** https://baileys.wiki/docs/api/type-aliases/Chat + +On this page + +**Chat**: `IConversation` & `object` + +Defined in: src/Types/Chat.ts:59 + +## Type declaration​ + +### lastMessageRecvTimestamp?​ + +`optional` **lastMessageRecvTimestamp**: `number` + +unix timestamp of when the last message was received in the chat + +- Type declaration +lastMessageRecvTimestamp? + + +--- + +## Type: ChatModification + +**Source:** https://baileys.wiki/docs/api/type-aliases/ChatModification + +On this page + +**ChatModification**: { `archive`: `boolean`; `lastMessages`: `LastMessageList`; } | { `pushNameSetting`: `string`; } | { `pin`: `boolean`; } | { `mute`: `number` | `null`; } | { `clear`: `boolean`; `lastMessages`: `LastMessageList`; } | { `deleteForMe`: { `deleteMedia`: `boolean`; `key`: `WAMessageKey`; `timestamp`: `number`; }; } | { `star`: { `messages`: `object`[]; `star`: `boolean`; }; } | { `lastMessages`: `LastMessageList`; `markRead`: `boolean`; } | { `delete`: `true`; `lastMessages`: `LastMessageList`; } | { `contact`: `IContactAction` | `null`; } | { `disableLinkPreviews`: `IPrivacySettingDisableLinkPreviewsAction`; } | { `addLabel`: `LabelActionBody`; } | { `addChatLabel`: `ChatLabelAssociationActionBody`; } | { `removeChatLabel`: `ChatLabelAssociationActionBody`; } | { `addMessageLabel`: `MessageLabelAssociationActionBody`; } | { `removeMessageLabel`: `MessageLabelAssociationActionBody`; } | { `quickReply`: `QuickReplyAction`; } + +Defined in: src/Types/Chat.ts:87 + +## Type declaration​ + +{ `archive`: `boolean`; `lastMessages`: `LastMessageList`; } + +### archive​ + +**archive**: `boolean` + +### lastMessages​ + +**lastMessages**: `LastMessageList` + +{ `pushNameSetting`: `string`; } + +### pushNameSetting​ + +**pushNameSetting**: `string` + +{ `pin`: `boolean`; } + +### pin​ + +**pin**: `boolean` + +{ `mute`: `number` | `null`; } + +### mute​ + +**mute**: `number` | `null` + +mute for duration, or provide timestamp of mute to remove + +{ `clear`: `boolean`; `lastMessages`: `LastMessageList`; } + +### clear​ + +**clear**: `boolean` + +### lastMessages​ + +**lastMessages**: `LastMessageList` + +{ `deleteForMe`: { `deleteMedia`: `boolean`; `key`: `WAMessageKey`; `timestamp`: `number`; }; } + +### deleteForMe​ + +**deleteForMe**: `object` + +#### deleteForMe.deleteMedia​ + +**deleteMedia**: `boolean` + +#### deleteForMe.key​ + +**key**: `WAMessageKey` + +#### deleteForMe.timestamp​ + +**timestamp**: `number` + +{ `star`: { `messages`: `object`[]; `star`: `boolean`; }; } + +### star​ + +**star**: `object` + +#### star.messages​ + +**messages**: `object`[] + +#### star.star​ + +**star**: `boolean` + +{ `lastMessages`: `LastMessageList`; `markRead`: `boolean`; } + +### lastMessages​ + +**lastMessages**: `LastMessageList` + +### markRead​ + +**markRead**: `boolean` + +{ `delete`: `true`; `lastMessages`: `LastMessageList`; } + +### delete​ + +**delete**: `true` + +### lastMessages​ + +**lastMessages**: `LastMessageList` + +{ `contact`: `IContactAction` | `null`; } + +### contact​ + +**contact**: `IContactAction` | `null` + +{ `disableLinkPreviews`: `IPrivacySettingDisableLinkPreviewsAction`; } + +### disableLinkPreviews​ + +**disableLinkPreviews**: `IPrivacySettingDisableLinkPreviewsAction` + +{ `addLabel`: `LabelActionBody`; } + +### addLabel​ + +**addLabel**: `LabelActionBody` + +{ `addChatLabel`: `ChatLabelAssociationActionBody`; } + +### addChatLabel​ + +**addChatLabel**: `ChatLabelAssociationActionBody` + +{ `removeChatLabel`: `ChatLabelAssociationActionBody`; } + +### removeChatLabel​ + +**removeChatLabel**: `ChatLabelAssociationActionBody` + +{ `addMessageLabel`: `MessageLabelAssociationActionBody`; } + +### addMessageLabel​ + +**addMessageLabel**: `MessageLabelAssociationActionBody` + +{ `removeMessageLabel`: `MessageLabelAssociationActionBody`; } + +### removeMessageLabel​ + +**removeMessageLabel**: `MessageLabelAssociationActionBody` + +{ `quickReply`: `QuickReplyAction`; } + +### quickReply​ + +**quickReply**: `QuickReplyAction` + +- Type declaration +archive +- lastMessages +- pushNameSetting +- pin +- mute +- clear +- lastMessages +- deleteForMe +- star +- lastMessages +- markRead +- delete +- lastMessages +- contact +- disableLinkPreviews +- addLabel +- addChatLabel +- removeChatLabel +- addMessageLabel +- removeMessageLabel +- quickReply + + +--- + +## Type: ChatMutation + +**Source:** https://baileys.wiki/docs/api/type-aliases/ChatMutation + +On this page + +**ChatMutation**: `object` + +Defined in: src/Types/Chat.ts:46 + +## Type declaration​ + +### index​ + +**index**: `string`[] + +### syncAction​ + +**syncAction**: `ISyncActionData` + +- Type declaration +index +- syncAction + + +# History Functions + + +--- + +## Function: downloadAndProcessHistorySyncNotification + +**Source:** https://baileys.wiki/docs/api/functions/downloadAndProcessHistorySyncNotification + +On this page + +**downloadAndProcessHistorySyncNotification**(`msg`, `options`): `Promise`<{ `chats`: `Chat`[]; `contacts`: `Contact`[]; `messages`: `WAMessage`[]; `progress`: `undefined` | `null` | `number`; `syncType`: `undefined` | `null` | `HistorySyncType`; }> + +Defined in: src/Utils/history.ts:95 + +## Parameters​ + +### msg​ + +`IHistorySyncNotification` + +### options​ + +`RequestInit` + +## Returns​ + +`Promise`<{ `chats`: `Chat`[]; `contacts`: `Contact`[]; `messages`: `WAMessage`[]; `progress`: `undefined` | `null` | `number`; `syncType`: `undefined` | `null` | `HistorySyncType`; }> + +- Parameters +msg +- options + +- Returns + + +--- + +## Function: processHistoryMessage + +**Source:** https://baileys.wiki/docs/api/functions/processHistoryMessage + +On this page + +**processHistoryMessage**(`item`): `object` + +Defined in: src/Utils/history.ts:28 + +## Parameters​ + +### item​ + +`IHistorySync` + +## Returns​ + +`object` + +### chats​ + +**chats**: `Chat`[] + +### contacts​ + +**contacts**: `Contact`[] + +### messages​ + +**messages**: `WAMessage`[] + +### progress​ + +**progress**: `undefined` | `null` | `number` = `item.progress` + +### syncType​ + +**syncType**: `undefined` | `null` | `HistorySyncType` = `item.syncType` + +- Parameters +item + +- Returns +chats +- contacts +- messages +- progress +- syncType + + +--- + +## Function: getHistoryMsg + +**Source:** https://baileys.wiki/docs/api/functions/getHistoryMsg + +On this page + +**getHistoryMsg**(`message`): `IHistorySyncNotification` + +Defined in: src/Utils/history.ts:109 + +## Parameters​ + +### message​ + +`IMessage` + +## Returns​ + +`IHistorySyncNotification` + +- Parameters +message + +- Returns + + +--- + +## Function: downloadHistory + +**Source:** https://baileys.wiki/docs/api/functions/downloadHistory + +On this page + +**downloadHistory**(`msg`, `options`): `Promise`<`HistorySync`> + +Defined in: src/Utils/history.ts:12 + +## Parameters​ + +### msg​ + +`IHistorySyncNotification` + +### options​ + +`RequestInit` + +## Returns​ + +`Promise`<`HistorySync`> + +- Parameters +msg +- options + +- Returns + + +# Socket + + +--- + +## Type: WASocket + +**Source:** https://baileys.wiki/docs/api/type-aliases/WASocket + +**WASocket**: `ReturnType`<*typeof* `makeWASocket`> + +Defined in: src/index.ts:11 + + +--- + +## Type: SocketConfig + +**Source:** https://baileys.wiki/docs/api/type-aliases/SocketConfig + +On this page + +**SocketConfig**: `object` + +Defined in: src/Types/Socket.ts:32 + +## Type declaration​ + +### agent?​ + +`optional` **agent**: `Agent` + +proxy agent + +### appStateMacVerification​ + +**appStateMacVerification**: `object` + +verify app state MACs + +#### appStateMacVerification.patch​ + +**patch**: `boolean` + +#### appStateMacVerification.snapshot​ + +**snapshot**: `boolean` + +### auth​ + +**auth**: `AuthenticationState` + +provide an auth state object to maintain the auth state + +### browser​ + +**browser**: `WABrowserDescription` + +override browser config + +### cachedGroupMetadata()​ + +**cachedGroupMetadata**: (`jid`) => `Promise`<`GroupMetadata` | `undefined`> + +cached group metadata, use to prevent redundant requests to WA & speed up msg sending + +#### Parameters​ + +jid​ + +`string` + +#### Returns​ + +`Promise`<`GroupMetadata` | `undefined`> + +### callOfferCache?​ + +`optional` **callOfferCache**: `CacheStore` + +cache to store call offers + +### connectTimeoutMs​ + +**connectTimeoutMs**: `number` + +Fails the connection if the socket times out in this interval + +### countryCode​ + +**countryCode**: `string` + +alphanumeric country code (USA -> US) for the number used + +### customUploadHosts​ + +**customUploadHosts**: `MediaConnInfo`[`"hosts"`] + +custom upload hosts to upload media to + +### defaultQueryTimeoutMs​ + +**defaultQueryTimeoutMs**: `number` | `undefined` + +Default timeout for queries, undefined for no timeout + +### emitOwnEvents​ + +**emitOwnEvents**: `boolean` + +should events be emitted for actions done by this socket connection + +### enableAutoSessionRecreation​ + +**enableAutoSessionRecreation**: `boolean` + +Enable automatic session recreation for failed messages + +### enableRecentMessageCache​ + +**enableRecentMessageCache**: `boolean` + +Enable recent message caching for retry handling + +### fetchAgent?​ + +`optional` **fetchAgent**: `Agent` + +agent used for fetch requests -- uploading/downloading media + +### fireInitQueries​ + +**fireInitQueries**: `boolean` + +Should baileys fire init queries automatically, default true + +### generateHighQualityLinkPreview​ + +**generateHighQualityLinkPreview**: `boolean` + +generate a high quality link preview, +entails uploading the jpegThumbnail to WA + +### getMessage()​ + +**getMessage**: (`key`) => `Promise`<`IMessage` | `undefined`> + +fetch a message from your store +implement this so that messages failed to send +(solves the "this message can take a while" issue) can be retried + +#### Parameters​ + +key​ + +`WAMessageKey` + +#### Returns​ + +`Promise`<`IMessage` | `undefined`> + +### keepAliveIntervalMs​ + +**keepAliveIntervalMs**: `number` + +ping-pong interval for WS connection + +### linkPreviewImageThumbnailWidth​ + +**linkPreviewImageThumbnailWidth**: `number` + +width for link preview images + +### logger​ + +**logger**: `ILogger` + +logger + +### makeSignalRepository()​ + +**makeSignalRepository**: (`auth`, `logger`, `pnToLIDFunc`?) => `SignalRepositoryWithLIDStore` + +#### Parameters​ + +auth​ + +`SignalAuthState` + +logger​ + +`ILogger` + +pnToLIDFunc?​ + +(`jids`) => `Promise`<`LIDMapping`[] | `undefined`> + +#### Returns​ + +`SignalRepositoryWithLIDStore` + +### markOnlineOnConnect​ + +**markOnlineOnConnect**: `boolean` + +marks the client as online whenever the socket successfully connects + +### maxMsgRetryCount​ + +**maxMsgRetryCount**: `number` + +max retry count + +### mediaCache?​ + +`optional` **mediaCache**: `CacheStore` + +provide a cache to store media, so does not have to be re-uploaded + +### mobile?​ + +`optional` **mobile**: `boolean` + +should baileys use the mobile api instead of the multi device api + +#### Deprecated​ + +This feature has been removed + +### msgRetryCounterCache?​ + +`optional` **msgRetryCounterCache**: `CacheStore` + +map to store the retry counts for failed messages; +used to determine whether to retry a message or not + +### options​ + +**options**: `RequestInit` + +options for HTTP fetch requests + +### patchMessageBeforeSending()​ + +**patchMessageBeforeSending**: (`msg`, `recipientJids`?) => `Promise`<`PatchedMessageWithRecipientJID`[] | `PatchedMessageWithRecipientJID`> | `PatchedMessageWithRecipientJID`[] | `PatchedMessageWithRecipientJID` + +Optionally patch the message before sending out + +#### Parameters​ + +msg​ + +`IMessage` + +recipientJids?​ + +`string`[] + +#### Returns​ + +`Promise`<`PatchedMessageWithRecipientJID`[] | `PatchedMessageWithRecipientJID`> | `PatchedMessageWithRecipientJID`[] | `PatchedMessageWithRecipientJID` + +### placeholderResendCache?​ + +`optional` **placeholderResendCache**: `CacheStore` + +cache to track placeholder resends + +### printQRInTerminal?​ + +`optional` **printQRInTerminal**: `boolean` + +should the QR be printed in the terminal + +#### Deprecated​ + +This feature has been removed + +### qrTimeout?​ + +`optional` **qrTimeout**: `number` + +time to wait for the generation of the next QR in ms + +### retryRequestDelayMs​ + +**retryRequestDelayMs**: `number` + +time to wait between sending new retry requests + +### shouldIgnoreJid()​ + +**shouldIgnoreJid**: (`jid`) => `boolean` | `undefined` + +Returns if a jid should be ignored, +no event for that jid will be triggered. +Messages from that jid will also not be decrypted + +#### Parameters​ + +jid​ + +`string` + +#### Returns​ + +`boolean` | `undefined` + +### shouldSyncHistoryMessage()​ + +**shouldSyncHistoryMessage**: (`msg`) => `boolean` + +manage history processing with this control; by default will sync up everything + +#### Parameters​ + +msg​ + +`IHistorySyncNotification` + +#### Returns​ + +`boolean` + +### syncFullHistory​ + +**syncFullHistory**: `boolean` + +Should Baileys ask the phone for full history, will be received async + +### transactionOpts​ + +**transactionOpts**: `TransactionCapabilityOptions` + +transaction capability options for SignalKeyStore + +### userDevicesCache?​ + +`optional` **userDevicesCache**: `PossiblyExtendedCacheStore` + +provide a cache to store a user's device list + +### version​ + +**version**: `WAVersion` + +version to connect with + +### waWebSocketUrl​ + +**waWebSocketUrl**: `string` | `URL` + +the WS url to connect to WA + +- Type declaration +agent? +- appStateMacVerification +- auth +- browser +- cachedGroupMetadata() +- callOfferCache? +- connectTimeoutMs +- countryCode +- customUploadHosts +- defaultQueryTimeoutMs +- emitOwnEvents +- enableAutoSessionRecreation +- enableRecentMessageCache +- fetchAgent? +- fireInitQueries +- generateHighQualityLinkPreview +- getMessage() +- keepAliveIntervalMs +- linkPreviewImageThumbnailWidth +- logger +- makeSignalRepository() +- markOnlineOnConnect +- maxMsgRetryCount +- mediaCache? +- mobile? +- msgRetryCounterCache? +- options +- patchMessageBeforeSending() +- placeholderResendCache? +- printQRInTerminal? +- qrTimeout? +- retryRequestDelayMs +- shouldIgnoreJid() +- shouldSyncHistoryMessage() +- syncFullHistory +- transactionOpts +- userDevicesCache? +- version +- waWebSocketUrl + + +--- + +## Type: UserFacingSocketConfig + +**Source:** https://baileys.wiki/docs/api/type-aliases/UserFacingSocketConfig + +On this page + +**UserFacingSocketConfig**: `Partial`<`SocketConfig`> & `object` + +Defined in: src/Types/index.ts:17 + +## Type declaration​ + +### auth​ + +**auth**: `AuthenticationState` + +- Type declaration +auth + + +# Events + + +--- + +## Type: BaileysEventMap + +**Source:** https://baileys.wiki/docs/api/type-aliases/BaileysEventMap + +On this page + +**BaileysEventMap**: `object` + +Defined in: src/Types/Events.ts:20 + +## Type declaration​ + +#### blocklist.set​ + +**set**: `object` + +#### blocklist.set.blocklist​ + +**blocklist**: `string`[] + +#### blocklist.update​ + +**update**: `object` + +#### blocklist.update.blocklist​ + +**blocklist**: `string`[] + +#### blocklist.update.type​ + +**type**: `"add"` | `"remove"` + +### call​ + +**call**: `WACallEvent`[] + +Receive an update on a call, including when the call was received, rejected, accepted + +#### chats.delete​ + +**delete**: `string`[] + +delete chats with given ID + +#### chats.update​ + +**update**: `ChatUpdate`[] + +update the given chats + +#### chats.upsert​ + +**upsert**: `Chat`[] + +upsert chats + +#### connection.update​ + +**update**: `Partial`<`ConnectionState`> + +connection state has been updated -- WS closed, opened, connecting etc. + +#### contacts.update​ + +**update**: `Partial`<`Contact`>[] + +#### contacts.upsert​ + +**upsert**: `Contact`[] + +#### creds.update​ + +**update**: `Partial`<`AuthenticationCreds`> + +credentials updated -- some metadata, keys or something + +#### group-participants.update​ + +**update**: `object` + +apply an action to participants in a group + +#### group-participants.update.action​ + +**action**: `ParticipantAction` + +#### group-participants.update.author​ + +**author**: `string` + +#### group-participants.update.authorPn?​ + +`optional` **authorPn**: `string` + +#### group-participants.update.id​ + +**id**: `string` + +#### group-participants.update.participants​ + +**participants**: `GroupParticipant`[] + +#### group.join-request​ + +**join-request**: `object` + +#### group.join-request.action​ + +**action**: `RequestJoinAction` + +#### group.join-request.author​ + +**author**: `string` + +#### group.join-request.authorPn?​ + +`optional` **authorPn**: `string` + +#### group.join-request.id​ + +**id**: `string` + +#### group.join-request.method​ + +**method**: `RequestJoinMethod` + +#### group.join-request.participant​ + +**participant**: `string` + +#### group.join-request.participantPn?​ + +`optional` **participantPn**: `string` + +#### groups.update​ + +**update**: `Partial`<`GroupMetadata`>[] + +#### groups.upsert​ + +**upsert**: `GroupMetadata`[] + +#### labels.association​ + +**association**: `object` + +#### labels.association.association​ + +**association**: `LabelAssociation` + +#### labels.association.type​ + +**type**: `"add"` | `"remove"` + +#### labels.edit​ + +**edit**: `Label` + +#### lid-mapping.update​ + +**update**: `object` + +#### lid-mapping.update.lid​ + +**lid**: `string` + +#### lid-mapping.update.pn​ + +**pn**: `string` + +#### message-receipt.update​ + +**update**: `MessageUserReceiptUpdate`[] + +#### messages.delete​ + +**delete**: { `keys`: `WAMessageKey`[]; } | { `all`: `true`; `jid`: `string`; } + +#### messages.media-update​ + +**media-update**: `object`[] + +#### messages.reaction​ + +**reaction**: `object`[] + +message was reacted to. If reaction was removed -- then "reaction.text" will be falsey + +#### messages.update​ + +**update**: `WAMessageUpdate`[] + +#### messages.upsert​ + +**upsert**: `object` + +add/update the given messages. If they were received while the connection was online, +the update will have type: "notify" +if requestId is provided, then the messages was received from the phone due to it being unavailable + +#### messages.upsert.messages​ + +**messages**: `WAMessage`[] + +#### messages.upsert.requestId?​ + +`optional` **requestId**: `string` + +#### messages.upsert.type​ + +**type**: `MessageUpsertType` + +#### messaging-history.set​ + +**set**: `object` + +set chats (history sync), everything is reverse chronologically sorted + +#### messaging-history.set.chats​ + +**chats**: `Chat`[] + +#### messaging-history.set.contacts​ + +**contacts**: `Contact`[] + +#### messaging-history.set.isLatest?​ + +`optional` **isLatest**: `boolean` + +#### messaging-history.set.messages​ + +**messages**: `WAMessage`[] + +#### messaging-history.set.peerDataRequestSessionId?​ + +`optional` **peerDataRequestSessionId**: `string` | `null` + +#### messaging-history.set.progress?​ + +`optional` **progress**: `number` | `null` + +#### messaging-history.set.syncType?​ + +`optional` **syncType**: `HistorySyncType` | `null` + +#### newsletter-participants.update​ + +**update**: `object` + +#### newsletter-participants.update.action​ + +**action**: `string` + +#### newsletter-participants.update.author​ + +**author**: `string` + +#### newsletter-participants.update.id​ + +**id**: `string` + +#### newsletter-participants.update.new_role​ + +**new_role**: `string` + +#### newsletter-participants.update.user​ + +**user**: `string` + +#### newsletter-settings.update​ + +**update**: `object` + +#### newsletter-settings.update.id​ + +**id**: `string` + +#### newsletter-settings.update.update​ + +**update**: `any` + +#### newsletter.reaction​ + +**reaction**: `object` + +Newsletter-related events + +#### newsletter.reaction.id​ + +**id**: `string` + +#### newsletter.reaction.reaction​ + +**reaction**: `object` + +#### newsletter.reaction.reaction.code?​ + +`optional` **code**: `string` + +#### newsletter.reaction.reaction.count?​ + +`optional` **count**: `number` + +#### newsletter.reaction.reaction.removed?​ + +`optional` **removed**: `boolean` + +#### newsletter.reaction.server_id​ + +**server_id**: `string` + +#### newsletter.view​ + +**view**: `object` + +#### newsletter.view.count​ + +**count**: `number` + +#### newsletter.view.id​ + +**id**: `string` + +#### newsletter.view.server_id​ + +**server_id**: `string` + +#### presence.update​ + +**update**: `object` + +presence of contact in a chat updated + +#### presence.update.id​ + +**id**: `string` + +#### presence.update.presences​ + +**presences**: `object` + +Index Signature​ + +[`participant`: `string`]: `PresenceData` + +- Type declaration +call + + +--- + +## Interface: BaileysEventEmitter + +**Source:** https://baileys.wiki/docs/api/interfaces/BaileysEventEmitter + +On this page + +Defined in: src/Types/Events.ts:127 + +## Methods​ + +### emit()​ + +**emit**<`T`>(`event`, `arg`): `boolean` + +Defined in: src/Types/Events.ts:131 + +#### Type Parameters​ + +• **T** *extends* keyof `BaileysEventMap` + +#### Parameters​ + +event​ + +`T` + +arg​ + +`BaileysEventMap`[`T`] + +#### Returns​ + +`boolean` + +### off()​ + +**off**<`T`>(`event`, `listener`): `void` + +Defined in: src/Types/Events.ts:129 + +#### Type Parameters​ + +• **T** *extends* keyof `BaileysEventMap` + +#### Parameters​ + +event​ + +`T` + +listener​ + +(`arg`) => `void` + +#### Returns​ + +`void` + +### on()​ + +**on**<`T`>(`event`, `listener`): `void` + +Defined in: src/Types/Events.ts:128 + +#### Type Parameters​ + +• **T** *extends* keyof `BaileysEventMap` + +#### Parameters​ + +event​ + +`T` + +listener​ + +(`arg`) => `void` + +#### Returns​ + +`void` + +### removeAllListeners()​ + +**removeAllListeners**<`T`>(`event`): `void` + +Defined in: src/Types/Events.ts:130 + +#### Type Parameters​ + +• **T** *extends* keyof `BaileysEventMap` + +#### Parameters​ + +event​ + +`T` + +#### Returns​ + +`void` + +- Methods +emit() +- off() +- on() +- removeAllListeners() + + +--- + +## Type: MessageUpsertType + +**Source:** https://baileys.wiki/docs/api/type-aliases/MessageUpsertType + +**MessageUpsertType**: `"append"` | `"notify"` + +Defined in: src/Types/Message.ts:364 + +Type of message upsert + +- notify => notify the user, this message was just received + +- append => append the message to the chat history, no notification required + + +# Message Utils + + +--- + +## Function: extractMessageContent + +**Source:** https://baileys.wiki/docs/api/functions/extractMessageContent + +On this page + +**extractMessageContent**(`content`): `undefined` | `IMessage` + +Defined in: src/Utils/messages.ts:762 + +Extract the true message content from a message +Eg. extracts the inner message from a disappearing message/view once message + +## Parameters​ + +### content​ + +`undefined` | `null` | `IMessage` + +## Returns​ + +`undefined` | `IMessage` + +- Parameters +content + +- Returns + + +--- + +## Function: getContentType + +**Source:** https://baileys.wiki/docs/api/functions/getContentType + +On this page + +**getContentType**(`content`): `undefined` | keyof IMessage + +Defined in: src/Utils/messages.ts:715 + +Get the key to access the true type of content + +## Parameters​ + +### content​ + +`undefined` | `IMessage` + +## Returns​ + +`undefined` | keyof IMessage + +- Parameters +content + +- Returns + + +--- + +## Function: normalizeMessageContent + +**Source:** https://baileys.wiki/docs/api/functions/normalizeMessageContent + +On this page + +**normalizeMessageContent**(`content`): `undefined` | `IMessage` + +Defined in: src/Utils/messages.ts:729 + +Normalizes ephemeral, view once messages to regular message content +Eg. image messages in ephemeral messages, in view once messages etc. + +## Parameters​ + +### content​ + +`undefined` | `null` | `IMessage` + +## Returns​ + +`undefined` | `IMessage` + +- Parameters +content + +- Returns + + +# Constants + + +--- + +## Variable: PROCESSABLE_HISTORY_TYPES + +**Source:** https://baileys.wiki/docs/api/variables/PROCESSABLE_HISTORY_TYPES + +`const` **PROCESSABLE_HISTORY_TYPES**: `HistorySyncType`[] + +Defined in: src/Defaults/index.ts:37 diff --git a/scripts/scrape-baileys-message-history.ts b/scripts/scrape-baileys-message-history.ts new file mode 100644 index 0000000..30366e1 --- /dev/null +++ b/scripts/scrape-baileys-message-history.ts @@ -0,0 +1,253 @@ +/** + * Script to scrape Baileys message history documentation from baileys.wiki + * Focused on fetching messages and chat history functionality + */ + +import * as fs from 'fs' + +const BASE_URL = 'https://baileys.wiki/docs/api' + +interface DocSection { + name: string + path: string + type: 'interface' | 'type' | 'function' | 'variable' | 'class' | 'enum' + category: string +} + +// Sections specifically related to message history +const SECTIONS: DocSection[] = [ + // Core message types + { name: 'WAMessage', path: '/type-aliases/WAMessage', type: 'type', category: 'Message Types' }, + { name: 'WAMessageKey', path: '/type-aliases/WAMessageKey', type: 'type', category: 'Message Types' }, + { name: 'WAMessageCursor', path: '/type-aliases/WAMessageCursor', type: 'type', category: 'Message Types' }, + { name: 'WAMessageUpdate', path: '/type-aliases/WAMessageUpdate', type: 'type', category: 'Message Types' }, + { name: 'MinimalMessage', path: '/type-aliases/MinimalMessage', type: 'type', category: 'Message Types' }, + { name: 'RecentMessage', path: '/interfaces/RecentMessage', type: 'interface', category: 'Message Types' }, + { name: 'RecentMessageKey', path: '/interfaces/RecentMessageKey', type: 'interface', category: 'Message Types' }, + { name: 'LastMessageList', path: '/type-aliases/LastMessageList', type: 'type', category: 'Message Types' }, + + // Chat types (messages are in chats) + { name: 'Chat', path: '/type-aliases/Chat', type: 'type', category: 'Chat Types' }, + { name: 'ChatModification', path: '/type-aliases/ChatModification', type: 'type', category: 'Chat Types' }, + { name: 'ChatMutation', path: '/type-aliases/ChatMutation', type: 'type', category: 'Chat Types' }, + + // History sync functions + { name: 'downloadAndProcessHistorySyncNotification', path: '/functions/downloadAndProcessHistorySyncNotification', type: 'function', category: 'History Functions' }, + { name: 'processHistoryMessage', path: '/functions/processHistoryMessage', type: 'function', category: 'History Functions' }, + { name: 'getHistoryMsg', path: '/functions/getHistoryMsg', type: 'function', category: 'History Functions' }, + { name: 'downloadHistory', path: '/functions/downloadHistory', type: 'function', category: 'History Functions' }, + + // Socket configuration for history + { name: 'WASocket', path: '/type-aliases/WASocket', type: 'type', category: 'Socket' }, + { name: 'SocketConfig', path: '/type-aliases/SocketConfig', type: 'type', category: 'Socket' }, + { name: 'UserFacingSocketConfig', path: '/type-aliases/UserFacingSocketConfig', type: 'type', category: 'Socket' }, + + // Events related to messages/history + { name: 'BaileysEventMap', path: '/type-aliases/BaileysEventMap', type: 'type', category: 'Events' }, + { name: 'BaileysEventEmitter', path: '/interfaces/BaileysEventEmitter', type: 'interface', category: 'Events' }, + { name: 'MessageUpsertType', path: '/type-aliases/MessageUpsertType', type: 'type', category: 'Events' }, + + // Message content extraction + { name: 'extractMessageContent', path: '/functions/extractMessageContent', type: 'function', category: 'Message Utils' }, + { name: 'getContentType', path: '/functions/getContentType', type: 'function', category: 'Message Utils' }, + { name: 'normalizeMessageContent', path: '/functions/normalizeMessageContent', type: 'function', category: 'Message Utils' }, + + // Variables related to history + { name: 'PROCESSABLE_HISTORY_TYPES', path: '/variables/PROCESSABLE_HISTORY_TYPES', type: 'variable', category: 'Constants' }, +] + +async function fetchPage(url: string): Promise { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Failed to fetch ${url}: ${response.status}`) + } + return response.text() +} + +function extractContent(html: string): string { + // Remove script tags and style tags + html = html.replace(/]*>[\s\S]*?<\/script>/gi, '') + html = html.replace(/]*>[\s\S]*?<\/style>/gi, '') + html = html.replace(/]*>[\s\S]*?<\/nav>/gi, '') + html = html.replace(/]*>[\s\S]*?<\/footer>/gi, '') + html = html.replace(/]*>[\s\S]*?<\/header>/gi, '') + html = html.replace(/]*>[\s\S]*?<\/aside>/gi, '') + + // Extract main/article content + let mainMatch = html.match(/]*>([\s\S]*?)<\/main>/i) + if (!mainMatch) { + mainMatch = html.match(/]*>([\s\S]*?)<\/article>/i) + } + const content = mainMatch ? mainMatch[1] : html + + // Convert HTML to markdown-like text + let text = content + // Headers + .replace(/]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n') + .replace(/]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n') + .replace(/]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n') + .replace(/]*>([\s\S]*?)<\/h4>/gi, '\n#### $1\n') + // Code blocks + .replace(/]*>]*>([\s\S]*?)<\/code><\/pre>/gi, '\n```typescript\n$1\n```\n') + .replace(/]*>([\s\S]*?)<\/code>/gi, '`$1`') + // Lists + .replace(/]*>([\s\S]*?)<\/li>/gi, '- $1\n') + .replace(/]*>/gi, '\n') + .replace(/<\/ul>/gi, '\n') + // Paragraphs + .replace(/]*>([\s\S]*?)<\/p>/gi, '\n$1\n') + // Links - keep only text for cleaner output + .replace(/]*>([\s\S]*?)<\/a>/gi, '$1') + // Bold/Italic + .replace(/]*>([\s\S]*?)<\/strong>/gi, '**$1**') + .replace(/]*>([\s\S]*?)<\/em>/gi, '*$1*') + // Line breaks + .replace(//gi, '\n') + // Divs and spans + .replace(/]*>/gi, '\n') + .replace(/<\/div>/gi, '\n') + .replace(/]*>/gi, '') + .replace(/<\/span>/gi, '') + // Tables (simplified) + .replace(/]*>/gi, '\n') + .replace(/<\/table>/gi, '\n') + .replace(/]*>/gi, '') + .replace(/<\/tr>/gi, '\n') + .replace(/]*>([\s\S]*?)<\/td>/gi, '| $1 ') + .replace(/]*>([\s\S]*?)<\/th>/gi, '| **$1** ') + // Remove remaining tags + .replace(/<[^>]+>/g, '') + // Decode HTML entities + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/ /g, ' ') + // Clean up whitespace + .replace(/\n\s*\n\s*\n/g, '\n\n') + .replace(/^\s+|\s+$/g, '') + + return text +} + +async function scrapeSection(section: DocSection): Promise { + const url = `${BASE_URL}${section.path}` + console.log(`Fetching ${section.name}...`) + + try { + const html = await fetchPage(url) + const content = extractContent(html) + + return ` +--- + +## ${section.type.charAt(0).toUpperCase() + section.type.slice(1)}: ${section.name} + +**Source:** ${url} + +${content} +` + } catch (error) { + console.error(`Error fetching ${section.name}:`, (error as Error).message) + return ` +--- + +## ${section.type.charAt(0).toUpperCase() + section.type.slice(1)}: ${section.name} + +**Source:** ${url} + +*Error: Could not fetch documentation* +` + } +} + +async function main() { + console.log('Starting Baileys message history documentation scrape...\n') + + // Group sections by category + const categories = [...new Set(SECTIONS.map(s => s.category))] + + const markdown: string[] = [ + `# Baileys Message History API Reference + +> Documentation for fetching and managing message history in WhatsApp Nucleo +> Source: https://baileys.wiki +> Generated: ${new Date().toISOString()} + +This document contains the Baileys API documentation specifically for: +- Fetching message history from chats +- History sync functionality +- Message events and types + +## Table of Contents + +${categories.map(cat => { + const items = SECTIONS.filter(s => s.category === cat) + return `### ${cat} +${items.map(s => `- [${s.name}](#${s.type}-${s.name.toLowerCase()})`).join('\n')}` +}).join('\n\n')} + +## Quick Reference: How to Fetch Messages + +### Using fetchMessageHistory (WASocket method) + +\`\`\`typescript +// The WASocket has a fetchMessageHistory method: +sock.fetchMessageHistory( + count: number, // Number of messages to fetch + oldestMsgKey: WAMessageKey, // Key of the oldest message you have + oldestMsgTimestamp: number // Timestamp of the oldest message +) +\`\`\` + +### Listening to History Sync Events + +\`\`\`typescript +sock.ev.on('messaging-history.set', ({ chats, contacts, messages, isLatest }) => { + // messages: WAMessage[] - reverse chronologically sorted + // chats: Chat[] - chat metadata + // isLatest: boolean - if this is the most recent sync +}) +\`\`\` + +### Configuration Options + +\`\`\`typescript +const sock = makeWASocket({ + // ... other config + syncFullHistory: true, // Request full history from phone + shouldSyncHistoryMessage: (msg) => true, // Control which messages to sync + getMessage: async (key) => { + // Implement to fetch message from your store + // Required for message retries + } +}) +\`\`\` + +` + ] + + // Process each category + for (const category of categories) { + markdown.push(`\n# ${category}\n`) + + const sections = SECTIONS.filter(s => s.category === category) + for (const section of sections) { + const content = await scrapeSection(section) + markdown.push(content) + + // Small delay to be nice to the server + await new Promise(r => setTimeout(r, 300)) + } + } + + // Write to file + const outputPath = './docs/baileys-message-history-reference.md' + fs.writeFileSync(outputPath, markdown.join('\n')) + + console.log(`\nDocumentation saved to ${outputPath}`) + console.log(`Total sections: ${SECTIONS.length}`) +} + +main().catch(console.error) diff --git a/server/api/debug/history/fetch.post.ts b/server/api/debug/history/fetch.post.ts index c15bcfc..e84ffc1 100644 --- a/server/api/debug/history/fetch.post.ts +++ b/server/api/debug/history/fetch.post.ts @@ -26,13 +26,31 @@ export default defineEventHandler(async (event) => { throw createError({ statusCode: 400, message: 'Instance not connected' }) } + // Verificar que el método existe + if (typeof socket.fetchMessageHistory !== 'function') { + console.error('[History] fetchMessageHistory method not available on socket') + console.error('[History] Available methods:', Object.keys(socket).filter(k => typeof (socket as any)[k] === 'function').join(', ')) + throw createError({ statusCode: 500, message: 'fetchMessageHistory not available on this Baileys version' }) + } + + // Log de parámetros + console.log('[History] Requesting history with params:', { + instanceId, + count, + oldestMsgKey: oldestMsgKey ? JSON.stringify(oldestMsgKey) : 'undefined', + oldestMsgTimestamp: oldestMsgTimestamp || 'undefined' + }) + try { - await socket.fetchMessageHistory(count, oldestMsgKey, oldestMsgTimestamp) + const result = await socket.fetchMessageHistory(count, oldestMsgKey, oldestMsgTimestamp) + console.log('[History] fetchMessageHistory returned:', result) return { success: true, + result, message: `Requested ${count} messages from history. Check messaging-history.set event for results.` } } catch (error) { + console.error('[History] fetchMessageHistory error:', error) throw createError({ statusCode: 500, message: `Failed to fetch message history: ${(error as Error).message}` diff --git a/server/api/events/stream.get.ts b/server/api/events/stream.get.ts index b46b1cf..a9a9188 100644 --- a/server/api/events/stream.get.ts +++ b/server/api/events/stream.get.ts @@ -46,6 +46,9 @@ export default defineEventHandler(async (event) => { }, 'presence.update': (data: any) => { res.write(`event: presence.update\ndata: ${JSON.stringify(data)}\n\n`) + }, + 'history.synced': (data: any) => { + res.write(`event: history.synced\ndata: ${JSON.stringify(data)}\n\n`) } } diff --git a/server/services/baileys/manager.ts b/server/services/baileys/manager.ts index e5a68a4..a83e18c 100644 --- a/server/services/baileys/manager.ts +++ b/server/services/baileys/manager.ts @@ -473,6 +473,15 @@ class BaileysManager extends EventEmitter { } console.log(`[BaileysManager] History sync processed`) + + // Emitir evento SSE al frontend para notificar que llegó el historial + this.emit('history.synced', { + instanceId, + chatsCount: chats?.length || 0, + contactsCount: contacts?.length || 0, + messagesCount: messages?.length || 0, + syncType + }) }) }