diff --git a/packages/provider-meta/__tests__/provider.test.ts b/packages/provider-meta/__tests__/provider.test.ts index 4fd0e41cf..3f24105d8 100644 --- a/packages/provider-meta/__tests__/provider.test.ts +++ b/packages/provider-meta/__tests__/provider.test.ts @@ -1074,87 +1074,54 @@ describe('#MetaProvider', () => { }) describe('#sendPresenceUpdate', () => { - test('should send typing_on status by default', async () => { + test('should send typing_indicator with incoming message_id', async () => { // Arrange - const fakeRecipient = '1234567890' - jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true }) - - // Act - await metaProvider.sendPresenceUpdate(fakeRecipient) - - // Assert - expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({ - messaging_product: 'whatsapp', - recipient_type: 'individual', - to: fakeRecipient, - type: 'typing_on', - }) - }) - - test('should send typing_on status when explicitly specified', async () => { - // Arrange - const fakeRecipient = '1234567890' + const fakeMessageId = 'wamid.HBgLMTIzNDU2Nzg5MA==' jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true }) // Act - await metaProvider.sendPresenceUpdate(fakeRecipient, 'typing_on') + await metaProvider.sendPresenceUpdate(fakeMessageId) // Assert expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({ messaging_product: 'whatsapp', - recipient_type: 'individual', - to: fakeRecipient, - type: 'typing_on', - }) - }) - - test('should send typing_off status when specified', async () => { - // Arrange - const fakeRecipient = '1234567890' - jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true }) - - // Act - await metaProvider.sendPresenceUpdate(fakeRecipient, 'typing_off') - - // Assert - expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({ - messaging_product: 'whatsapp', - recipient_type: 'individual', - to: fakeRecipient, - type: 'typing_off', + status: 'read', + message_id: fakeMessageId, + typing_indicator: { + type: 'text', + }, }) }) }) describe('#typing', () => { - test('should send typing_on when called without duration', async () => { + test('should call sendPresenceUpdate with messageId when called without duration', async () => { // Arrange - const fakeRecipient = '1234567890' + const fakeMessageId = 'wamid.HBgLMTIzNDU2Nzg5MA==' jest.spyOn(metaProvider, 'sendPresenceUpdate').mockResolvedValue({ success: true }) // Act - await metaProvider.typing(fakeRecipient) + await metaProvider.typing(fakeMessageId) // Assert expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledTimes(1) - expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledWith(fakeRecipient, 'typing_on') + expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledWith(fakeMessageId) }) - test('should send typing_on then typing_off when called with duration', async () => { + test('should call sendPresenceUpdate then wait when called with duration', async () => { // Arrange - const fakeRecipient = '1234567890' + const fakeMessageId = 'wamid.HBgLMTIzNDU2Nzg5MA==' jest.useFakeTimers() jest.spyOn(metaProvider, 'sendPresenceUpdate').mockResolvedValue({ success: true }) // Act - const typingPromise = metaProvider.typing(fakeRecipient, 1000) + const typingPromise = metaProvider.typing(fakeMessageId, 1000) jest.runAllTimers() await typingPromise // Assert - expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledTimes(2) - expect(metaProvider.sendPresenceUpdate).toHaveBeenNthCalledWith(1, fakeRecipient, 'typing_on') - expect(metaProvider.sendPresenceUpdate).toHaveBeenNthCalledWith(2, fakeRecipient, 'typing_off') + expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledTimes(1) + expect(metaProvider.sendPresenceUpdate).toHaveBeenCalledWith(fakeMessageId) jest.useRealTimers() }) diff --git a/packages/provider-meta/src/interface/meta.ts b/packages/provider-meta/src/interface/meta.ts index dee0b0a50..2491d5734 100644 --- a/packages/provider-meta/src/interface/meta.ts +++ b/packages/provider-meta/src/interface/meta.ts @@ -54,6 +54,6 @@ export interface MetaInterface { sendFile: (to: string, mediaInput: string | null, caption: string, context: string | null) => Promise sendAudio: (to: string, fileOpus: string, context: string | null) => void markAsRead: (wa_id: string) => Promise - sendPresenceUpdate: (to: string, status?: 'typing_on' | 'typing_off') => Promise - typing: (to: string, ms?: number) => Promise + sendPresenceUpdate: (messageId: string) => Promise + typing: (messageId: string, ms?: number) => Promise } diff --git a/packages/provider-meta/src/meta/provider.ts b/packages/provider-meta/src/meta/provider.ts index 710ca974d..2ea12fdbf 100644 --- a/packages/provider-meta/src/meta/provider.ts +++ b/packages/provider-meta/src/meta/provider.ts @@ -1027,44 +1027,42 @@ class MetaProvider extends ProviderClass implements MetaInterface } /** - * Send presence update to simulate typing indicator - * @param to - Recipient phone number - * @param status - Presence status: 'typing_on' to show typing indicator, 'typing_off' to hide it + * Send presence update to simulate typing indicator using Meta Cloud API format. + * Requires the message_id of the incoming message that triggered this response. + * The typing indicator lasts up to 25 seconds or until a message is sent. + * @param messageId - The wamid of the incoming message (from the user) * @returns Promise with the API response * @example - * // Show typing indicator - * await provider.sendPresenceUpdate('1234567890') - * - * // Hide typing indicator - * await provider.sendPresenceUpdate('1234567890', 'typing_off') + * await provider.sendPresenceUpdate('wamid.HBgLMTIzNDU2Nzg5MA==') */ - sendPresenceUpdate = async (to: string, status: 'typing_on' | 'typing_off' = 'typing_on') => { - to = parseMetaNumber(to) + sendPresenceUpdate = async (messageId: string) => { const body = { messaging_product: 'whatsapp', - recipient_type: 'individual', - to, - type: status, + status: 'read', + message_id: messageId, + typing_indicator: { + type: 'text', + }, } return this.sendMessageToApi(body) } /** - * Show a typing indicator to the recipient - * @param to - Recipient phone number - * @param ms - Optional duration in milliseconds after which the typing indicator is hidden automatically + * Show a typing indicator to the recipient. + * Requires the message_id of the incoming message that triggered this response. + * @param messageId - The wamid of the incoming message (from the user) + * @param ms - Optional duration in milliseconds to wait (typing indicator disappears automatically after 25s or when a message is sent) * @example - * // Show typing indicator indefinitely - * await provider.typing('1234567890') + * // Show typing indicator + * await provider.typing('wamid.HBgLMTIzNDU2Nzg5MA==') * - * // Show typing indicator for 3 seconds then hide it - * await provider.typing('1234567890', 3000) + * // Show typing indicator and wait 3 seconds before continuing + * await provider.typing('wamid.HBgLMTIzNDU2Nzg5MA==', 3000) */ - typing = async (to: string, ms?: number): Promise => { - await this.sendPresenceUpdate(to, 'typing_on') + typing = async (messageId: string, ms?: number): Promise => { + await this.sendPresenceUpdate(messageId) if (ms) { await new Promise((resolve) => setTimeout(resolve, ms)) - await this.sendPresenceUpdate(to, 'typing_off') } }