From a76044201004de8a3a2c5383676dc223ddf74930 Mon Sep 17 00:00:00 2001 From: "Cayo P. R. Oliveira" Date: Fri, 16 May 2025 14:13:00 -0300 Subject: [PATCH] fix: mark message read edge case (#46) * feat: enhance message_content_attributes to handle reaction messages * fix: update external_created_at to use raw message timestamp directly * fix: update readableTime to use externalCreatedAt if available * feat: enhance timestamp formatting with localization support for pt-BR * feat: add note to message_content_attributes about external_created_at timestamp * fix: add missing newline at end of conversation.json files * test: add additional cases for messageTimestamp formatting * chore: remove outdated note about external_created_at in message_content_attributes * refactor: remove out of scope task changes * feat: add test for setting external_created_at in content_attributes on new message * fix: use last_seen_at message query conditions only if present * test: add SQL execution tests for messages_read with last_seen_at conditions * fix: ensure SQL notifications are unsubscribed after message read events * refactor: streamline message query in messages_read method * test: update messages_read specs to test expected behavior * refactor: simplify readableTime computation by removing unnecessary externalCreatedAt check * fix: update readableTime computation to use externalCreatedAt if available * test: enhance messages_read specs to use a consistent event object * test: refactor spec for creating message with external_created_at in messages.upsert event * Refactor incoming message specs for clarity and consistency - Consolidated raw_message and params definitions using let blocks for better readability. - Updated tests to directly manipulate raw_message and params within individual examples. - Ensured consistent naming and structure across different message types. - Improved assertions to reflect changes in message attributes and expectations. * refactor: streamline messages.update event handling and improve test clarity * test: refactor messages.upsert event specs to use a consistent timestamp * test: enhance reaction message handling in incoming_message_baileys_service_spec --- .../components-next/message/MessageMeta.vue | 5 +- app/listeners/channel_listener.rb | 6 +- .../incoming_message_baileys_service.rb | 15 +- spec/listeners/channel_listener_spec.rb | 39 +- .../incoming_message_baileys_service_spec.rb | 419 ++++++------------ 5 files changed, 183 insertions(+), 301 deletions(-) diff --git a/app/javascript/dashboard/components-next/message/MessageMeta.vue b/app/javascript/dashboard/components-next/message/MessageMeta.vue index e0602cb89..c0b1bd149 100644 --- a/app/javascript/dashboard/components-next/message/MessageMeta.vue +++ b/app/javascript/dashboard/components-next/message/MessageMeta.vue @@ -32,7 +32,10 @@ const { } = useMessageContext(); const readableTime = computed(() => - messageTimestamp(createdAt.value, 'LLL d, h:mm a') + messageTimestamp( + contentAttributes?.value?.externalCreatedAt ?? createdAt.value, + 'LLL d, h:mm a' + ) ); const showStatusIndicator = computed(() => { diff --git a/app/listeners/channel_listener.rb b/app/listeners/channel_listener.rb index b3461fb8b..438dae941 100644 --- a/app/listeners/channel_listener.rb +++ b/app/listeners/channel_listener.rb @@ -28,9 +28,9 @@ class ChannelListener < BaseListener channel = conversation.inbox.channel return unless channel.respond_to?(:send_read_messages) - messages = conversation.messages.where(message_type: :incoming) - .where('updated_at > ?', last_seen_at) - .where.not(status: :read) + messages = conversation.messages.where(message_type: :incoming).where.not(status: :read) + + messages = messages.where('updated_at > ?', last_seen_at) if last_seen_at.present? channel.send_read_messages(messages, conversation: conversation) if messages.any? end diff --git a/app/services/whatsapp/incoming_message_baileys_service.rb b/app/services/whatsapp/incoming_message_baileys_service.rb index 45b98ad44..d7bfa83b4 100644 --- a/app/services/whatsapp/incoming_message_baileys_service.rb +++ b/app/services/whatsapp/incoming_message_baileys_service.rb @@ -171,16 +171,17 @@ class Whatsapp::IncomingMessageBaileysService < Whatsapp::IncomingMessageBaseSer end def message_content_attributes + content_attributes = { + external_created_at: @raw_message[:messageTimestamp] + } if message_type == 'reaction' - { - in_reply_to_external_id: @raw_message.dig(:message, :reactionMessage, :key, :id), - is_reaction: true - } + content_attributes[:in_reply_to_external_id] = @raw_message.dig(:message, :reactionMessage, :key, :id) + content_attributes[:is_reaction] = true elsif message_type == 'unsupported' - { - is_unsupported: true - } + content_attributes[:is_unsupported] = true end + + content_attributes end def incoming? diff --git a/spec/listeners/channel_listener_spec.rb b/spec/listeners/channel_listener_spec.rb index 32549c524..b0574246f 100644 --- a/spec/listeners/channel_listener_spec.rb +++ b/spec/listeners/channel_listener_spec.rb @@ -95,12 +95,13 @@ describe ChannelListener do let(:channel) { create(:channel_whatsapp, sync_templates: false, validate_provider_config: false) } let(:conversation) { create(:conversation, inbox: create(:inbox, channel: channel)) } let(:last_seen_at) { 1.day.ago } + let(:event) { Events::Base.new(Events::Types::MESSAGES_READ, Time.zone.now, conversation: conversation, last_seen_at: last_seen_at) } it 'sends read messages to the channel' do - sent_message = create(:message, conversation: conversation, message_type: :incoming, status: :sent) create(:message, conversation: conversation, message_type: :incoming, status: :read) + sent_message = create(:message, conversation: conversation, message_type: :incoming, status: :sent) + allow(channel).to receive(:send_read_messages).with([sent_message], conversation: conversation) - event = Events::Base.new(Events::Types::MESSAGES_READ, Time.zone.now, conversation: conversation, last_seen_at: last_seen_at) listener.messages_read(event) @@ -111,9 +112,41 @@ describe ChannelListener do create(:channel_api, inbox: conversation.inbox) expect do - listener.messages_read(Events::Base.new(Events::Types::MESSAGES_READ, Time.zone.now, conversation: conversation, last_seen_at: last_seen_at)) + listener.messages_read(event) end.not_to raise_error end + + it 'skips the event if there are no unread messages' do + create(:message, conversation: conversation, message_type: :incoming, status: :read) + + allow(channel).to receive(:send_read_messages) + + listener.messages_read(event) + + expect(channel).not_to have_received(:send_read_messages) + end + + it 'filters messages ignoring last_seen_at' do + old_message = create(:message, conversation: conversation, message_type: :incoming, status: :sent, updated_at: last_seen_at - 1.day) + recent_message = create(:message, conversation: conversation, message_type: :incoming, status: :sent, updated_at: Time.zone.now) + + allow(channel).to receive(:send_read_messages).with([old_message, recent_message], conversation: conversation) + + listener.messages_read(Events::Base.new(Events::Types::MESSAGES_READ, Time.zone.now, conversation: conversation, last_seen_at: nil)) + + expect(channel).to have_received(:send_read_messages) + end + + it 'filters messages based on last_seen_at' do + create(:message, conversation: conversation, message_type: :incoming, status: :sent, updated_at: last_seen_at - 1.day) + recent_message = create(:message, conversation: conversation, message_type: :incoming, status: :sent, updated_at: Time.zone.now) + + allow(channel).to receive(:send_read_messages).with([recent_message], conversation: conversation) + + listener.messages_read(event) + + expect(channel).to have_received(:send_read_messages) + end end def build_typing_event(event_name, conversation:, is_private: false) diff --git a/spec/services/whatsapp/incoming_message_baileys_service_spec.rb b/spec/services/whatsapp/incoming_message_baileys_service_spec.rb index a50d56e63..eda36b309 100644 --- a/spec/services/whatsapp/incoming_message_baileys_service_spec.rb +++ b/spec/services/whatsapp/incoming_message_baileys_service_spec.rb @@ -145,79 +145,65 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when processing messages.upsert event' do - context 'when message type is unsupported' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { unsupported: 'message' }, - pushName: 'John Doe' + let(:timestamp) { Time.current.to_i } + let(:raw_message) do + { + key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, + pushName: 'John Doe', + messageTimestamp: timestamp, + message: { conversation: 'Hello from Baileys' } + } + end + let(:params) do + { + webhookVerifyToken: webhook_verify_token, + event: 'messages.upsert', + data: { + type: 'notify', + messages: [raw_message] } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - } - } - end + } + end + + it 'creates message with external_created_at' do + described_class.new(inbox: inbox, params: params).perform + + conversation = inbox.conversations.last + message = conversation.messages.last + + expect(message).to be_present + expect(message.content_attributes[:external_created_at]).to eq(timestamp) + end + + context 'when message type is unsupported' do + it 'creates message with is_unsupported' do + raw_message[:message] = { unsupported: 'message' } - it 'creates an unsupported message' do described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last message = conversation.messages.last expect(message).to be_present - expect(message.is_unsupported).to be(true) + expect(message.content_attributes[:is_unsupported]).to be(true) end end context 'when message is protocol message' do - let(:raw_message) do - { key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { protocolMessage: { type: 1 } }, - pushName: 'John Doe' } - end - let(:params) do - { webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - } } - end - it 'does not create contact inbox nor message' do + raw_message[:message] = { protocolMessage: { type: 1 } } + described_class.new(inbox: inbox, params: params).perform - expect(inbox.contact_inboxes.count).to be(0) - expect(inbox.messages.count).to be(0) + expect(inbox.messages).to be_empty + expect(inbox.contact_inboxes).to be_empty end end context 'when message is not from a user' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: 'status@broadcast', participant: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { extendedTextMessage: { text: 'message' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - } - } - end - it 'does not create a conversation' do + raw_message[:key][:remoteJid] = 'status@broadcast' + described_class.new(inbox: inbox, params: params).perform expect(inbox.conversations).to be_empty @@ -225,27 +211,6 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is text' do - let(:phone_number) { '5511912345678' } - let(:jid) { "#{phone_number}@s.whatsapp.net" } - let(:content_message) { 'Hello from Baileys' } - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: jid, fromMe: false }, - pushName: 'John Doe', - message: { conversation: content_message } - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - } - } - end - context 'when has key conversation' do # rubocop:disable RSpec/NestedGroups it 'creates an incoming message' do described_class.new(inbox: inbox, params: params).perform @@ -254,47 +219,46 @@ describe Whatsapp::IncomingMessageBaileysService do message = conversation.messages.last expect(message).to be_present - expect(message.content).to eq(content_message) + expect(message.content).to eq('Hello from Baileys') expect(message.sender).to be_present expect(message.sender.name).to eq('John Doe') expect(message.message_type).to eq('incoming') end it 'creates an outgoing message' do - raw_message_outgoing = raw_message.merge(key: { id: 'msg_123', remoteJid: jid, fromMe: true }) - params_outgoing = params.merge(data: { type: 'notify', messages: [raw_message_outgoing] }) + raw_message[:key][:fromMe] = true create(:account_user, account: inbox.account) - described_class.new(inbox: inbox, params: params_outgoing).perform + described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last message = conversation.messages.last expect(message).to be_present - expect(message.content).to eq(content_message) - expect(conversation.contact.name).to eq(phone_number) + expect(message.content).to eq('Hello from Baileys') + expect(conversation.contact.name).to eq('5511912345678') expect(message.message_type).to eq('outgoing') end it 'creates an outgoing self message' do self_jid = "#{whatsapp_channel.phone_number.delete('+')}@s.whatsapp.net" - raw_message_outgoing = raw_message.merge(key: { id: 'msg_123', remoteJid: self_jid, fromMe: true }) - params_outgoing = params.merge(data: { type: 'notify', messages: [raw_message_outgoing] }) + raw_message[:key][:remoteJid] = self_jid + raw_message[:key][:fromMe] = true create(:account_user, account: inbox.account) - described_class.new(inbox: inbox, params: params_outgoing).perform + described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last message = conversation.messages.last expect(message).to be_present - expect(message.content).to eq(content_message) + expect(message.content).to eq('Hello from Baileys') expect(conversation.contact.name).to eq('John Doe') expect(message.message_type).to eq('outgoing') end it 'updates the contact name when name is the phone number and message has a pushName' do - create(:contact, account: inbox.account, name: phone_number) + create(:contact, account: inbox.account, name: '5511912345678') described_class.new(inbox: inbox, params: params).perform @@ -304,7 +268,7 @@ describe Whatsapp::IncomingMessageBaileysService do it 'updates the contact name when name is the phone number and message has a verifiedBizName' do raw_message[:verifiedBizName] = 'Verified John' - create(:contact, account: inbox.account, name: phone_number) + create(:contact, account: inbox.account, name: '5511912345678') described_class.new(inbox: inbox, params: params).perform @@ -313,14 +277,13 @@ describe Whatsapp::IncomingMessageBaileysService do end it 'creates contact with phone number as name on outgoing message' do - raw_message_outgoing = raw_message.merge(key: { id: 'msg_123', remoteJid: jid, fromMe: true }) - params_outgoing = params.merge(data: { type: 'notify', messages: [raw_message_outgoing] }) + raw_message[:key][:fromMe] = true create(:account_user, account: inbox.account) - described_class.new(inbox: inbox, params: params_outgoing).perform + described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last - expect(conversation.contact.name).to eq(phone_number) + expect(conversation.contact.name).to eq('5511912345678') end it 'creates a message on an existing conversation' do @@ -364,15 +327,9 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when is a extendedTextMessage that has key text' do # rubocop:disable RSpec/NestedGroups - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { extendedTextMessage: { text: 'Hello from Baileys' } }, - pushName: 'John Doe' - } - end - it 'creates an incoming message' do + raw_message[:message] = { extendedTextMessage: { text: 'Hello from Baileys' } } + described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last @@ -387,33 +344,22 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is reaction' do - let(:phone_number) { '5511912345678' } - let(:jid) { "#{phone_number}@s.whatsapp.net" } - let(:raw_message) do - { - key: { id: 'reaction_123', remoteJid: jid, fromMe: false }, - message: { reactionMessage: { key: { remoteJid: jid, fromMe: true, id: 'msg_123' }, text: '👍' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - } - } - end let!(:message) do - contact = create(:contact, account: inbox.account, name: phone_number) - contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: phone_number) + contact = create(:contact, account: inbox.account, name: '5511912345678') + contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678') conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox) create(:message, inbox: inbox, conversation: conversation, source_id: 'msg_123') end it 'creates the reaction' do + raw_message[:key][:id] = 'reaction_123' + raw_message[:message] = { + reactionMessage: { + key: { remoteJid: '5511912345678@s.whatsapp.net', fromMe: true, id: 'msg_123' }, + text: '👍' + } + } + described_class.new(inbox: inbox, params: params).perform reaction = message.conversation.messages.last @@ -423,7 +369,13 @@ describe Whatsapp::IncomingMessageBaileysService do end it 'does not create the reaction if content is empty' do - raw_message[:message][:reactionMessage][:text] = '' + raw_message[:key][:id] = 'reaction_123' + raw_message[:message] = { + reactionMessage: { + key: { remoteJid: '5511912345678@s.whatsapp.net', fromMe: true, id: 'msg_123' }, + text: '' + } + } described_class.new(inbox: inbox, params: params).perform @@ -432,30 +384,14 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is image' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { imageMessage: { caption: 'Hello from Baileys', mimetype: 'image/png' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - }, - extra: { - media: { - 'msg_123' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' - } + it 'creates the message with caption' do + raw_message[:message] = { imageMessage: { caption: 'Hello from Baileys', mimetype: 'image/png' } } + params[:extra] = { + media: { + 'msg_123' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' } } - end - it 'creates the message with caption' do described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last message = conversation.messages.last @@ -465,6 +401,13 @@ describe Whatsapp::IncomingMessageBaileysService do end it 'creates message attachment' do + raw_message[:message] = { imageMessage: { caption: 'Hello from Baileys', mimetype: 'image/png' } } + params[:extra] = { + media: { + 'msg_123' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' + } + } + freeze_time described_class.new(inbox: inbox, params: params).perform @@ -483,31 +426,16 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is video' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { videoMessage: { caption: 'Hello from Baileys', mimetype: 'video/mp4' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - }, - extra: { - media: { - 'msg_123' => 'AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAABL21kYXQAAAGzABAHAAABthGBxgj238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2E4HGCkbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237AAABswAQBwAAAbYVgcYLltyRtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfsAAAGzABAHAAABtheBxhPbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2GYHGJG3JG238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt+/AAADHW1vb3YAAABsbXZoZAAAAAAAAAAAAAAAAAAAA+gAAAPoAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAJHdHJhawAAAFx0a2hkAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAPoAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAABAAAAAQAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAAAD6AAAAAAAAQAAAAABv21kaWEAAAAgbWRoZAAAAAAAAAAAAAAAAAAAKAAAACgAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAWptaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEqc3RibAAAAGJzdHNkAAAAAAAAACVhdmMxAAAAAAAAAAAAAAAAAACFc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' # rubocop:disable Layout/LineLength - } + it 'creates the message with caption' do + raw_message[:message] = { videoMessage: { caption: 'Hello from Baileys', mimetype: 'video/mp4' } } + params[:extra] = { + media: { + 'msg_123' => 'AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAABL21kYXQAAAGzABAHAAABthGBxgj238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2E4HGCkbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237AAABswAQBwAAAbYVgcYLltyRtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfsAAAGzABAHAAABtheBxhPbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2GYHGJG3JG238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt+/AAADHW1vb3YAAABsbXZoZAAAAAAAAAAAAAAAAAAAA+gAAAPoAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAJHdHJhawAAAFx0a2hkAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAPoAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAABAAAAAQAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAAAD6AAAAAAAAQAAAAABv21kaWEAAAAgbWRoZAAAAAAAAAAAAAAAAAAAKAAAACgAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAWptaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEqc3RibAAAAGJzdHNkAAAAAAAAACVhdmMxAAAAAAAAAAAAAAAAAACFc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' # rubocop:disable Layout/LineLength } } - end - it 'creates the message with caption' do described_class.new(inbox: inbox, params: params).perform + conversation = inbox.conversations.last message = conversation.messages.last @@ -516,6 +444,13 @@ describe Whatsapp::IncomingMessageBaileysService do end it 'creates message attachment' do + raw_message[:message] = { videoMessage: { caption: 'Hello from Baileys', mimetype: 'video/mp4' } } + params[:extra] = { + media: { + 'msg_123' => 'AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAABL21kYXQAAAGzABAHAAABthGBxgj238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2E4HGCkbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237AAABswAQBwAAAbYVgcYLltyRtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfsAAAGzABAHAAABtheBxhPbckbbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G237wAAAbMAEAcAAAG2GYHGJG3JG238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt/G238bbfxtt+/AAADHW1vb3YAAABsbXZoZAAAAAAAAAAAAAAAAAAAA+gAAAPoAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAJHdHJhawAAAFx0a2hkAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAPoAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAABAAAAAQAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAAAD6AAAAAAAAQAAAAABv21kaWEAAAAgbWRoZAAAAAAAAAAAAAAAAAAAKAAAACgAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAWptaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEqc3RibAAAAGJzdHNkAAAAAAAAACVhdmMxAAAAAAAAAAAAAAAAAACFc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' # rubocop:disable Layout/LineLength + } + } + freeze_time described_class.new(inbox: inbox, params: params).perform @@ -534,31 +469,14 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is file' do - let(:filename) { 'file.pdf' } - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { documentMessage: { fileName: filename } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - }, - extra: { - media: { - 'msg_123' => 'JVBERi0xLjQKJVRlc3QgUERGCjEgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL091dGxpbmVzIDIgMCBSIC9QYWdlcyAzIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvT3V0bGluZXMgL0NvdW50IDAgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFs0IDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgCiAgIC9QYXJlbnQgMyAwIFIgCiAgIC9NZWRpYUJveCBbMCAwIDMwMCAyMDBdIAogICAvQ29udGVudHMgNSAwIFIgCiAgIC9SZXNvdXJjZXMgPDwgL0ZvbnQgPDwgL0YxIDw8IC9UeXBlIC9Gb250IC9TdWJ0eXBlIC9UeXBlMSAvQmFzZUZvbnQgL0hlbHZldGljYSA+PiA+PiA+PgplbmRvYmoKNSAwIG9iago8PCAvTGVuZ3RoIDUzID4+CnN0cmVhbQpCVAovRjEgMTIgVGYKNzIgMTUwIFRkCihIZWxsbyBQREYhKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNyAwMDAwMCBuIAowMDAwMDAwMDg0IDAwMDAwIG4gCjAwMDAwMDAxMzQgMDAwMDAgbiAKMDAwMDAwMDE5MyAwMDAwMCBuIAowMDAwMDAwMzkxIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKNDk0CiUlRU9GCg==' # rubocop:disable Layout/LineLength - } + it 'creates message attachment' do + raw_message[:message] = { documentMessage: { fileName: 'file.pdf' } } + params[:extra] = { + media: { + 'msg_123' => 'JVBERi0xLjQKJVRlc3QgUERGCjEgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL091dGxpbmVzIDIgMCBSIC9QYWdlcyAzIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvT3V0bGluZXMgL0NvdW50IDAgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFs0IDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgCiAgIC9QYXJlbnQgMyAwIFIgCiAgIC9NZWRpYUJveCBbMCAwIDMwMCAyMDBdIAogICAvQ29udGVudHMgNSAwIFIgCiAgIC9SZXNvdXJjZXMgPDwgL0ZvbnQgPDwgL0YxIDw8IC9UeXBlIC9Gb250IC9TdWJ0eXBlIC9UeXBlMSAvQmFzZUZvbnQgL0hlbHZldGljYSA+PiA+PiA+PgplbmRvYmoKNSAwIG9iago8PCAvTGVuZ3RoIDUzID4+CnN0cmVhbQpCVAovRjEgMTIgVGYKNzIgMTUwIFRkCihIZWxsbyBQREYhKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNyAwMDAwMCBuIAowMDAwMDAwMDg0IDAwMDAwIG4gCjAwMDAwMDAxMzQgMDAwMDAgbiAKMDAwMDAwMDE5MyAwMDAwMCBuIAowMDAwMDAwMzkxIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKNDk0CiUlRU9GCg==' # rubocop:disable Layout/LineLength } } - end - it 'creates message attachment' do described_class.new(inbox: inbox, params: params).perform conversation = inbox.conversations.last @@ -569,36 +487,20 @@ describe Whatsapp::IncomingMessageBaileysService do expect(attachment.file_type).to eq('file') expect(attachment.file).to be_present - expect(attachment.file.filename.to_s).to eq(filename) + expect(attachment.file.filename.to_s).to eq('file.pdf') expect(attachment.file.content_type).to eq('application/pdf') end end context 'when message type is audio' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { audioMessage: { mimetype: 'audio/opus' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - }, - extra: { - media: { - 'msg_123' => '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' # rubocop:disable Layout/LineLength - } + it 'creates message attachment' do + raw_message[:message] = { audioMessage: { mimetype: 'audio/opus' } } + params[:extra] = { + media: { + 'msg_123' => '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' # rubocop:disable Layout/LineLength } } - end - it 'creates message attachment' do freeze_time described_class.new(inbox: inbox, params: params).perform @@ -617,30 +519,14 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message type is sticker' do - let(:raw_message) do - { - key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: false }, - message: { stickerMessage: { mimetype: 'image/png' } }, - pushName: 'John Doe' - } - end - let(:params) do - { - webhookVerifyToken: webhook_verify_token, - event: 'messages.upsert', - data: { - type: 'notify', - messages: [raw_message] - }, - extra: { - media: { - 'msg_123' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' - } + it 'creates message attachment' do + raw_message[:message] = { stickerMessage: { mimetype: 'image/png' } } + params[:extra] = { + media: { + 'msg_123' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' } } - end - it 'creates message attachment' do freeze_time described_class.new(inbox: inbox, params: params).perform @@ -660,23 +546,21 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when processing messages.update event' do - context 'when message is not found' do - let(:message_id) { 'msg_123' } - let(:update_payload) do - { - key: { id: message_id }, - update: { - status: 2 - } - } - end + let!(:message) { create(:message, source_id: 'msg_123', status: 'sent') } + let(:update_payload) do + { key: { id: 'msg_123' }, update: { status: 3 } } + end + let(:params) do + { + webhookVerifyToken: webhook_verify_token, + event: 'messages.update', + data: [update_payload] + } + end + context 'when message is not found' do it 'raises MessageNotFoundError' do - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } + update_payload[:key][:id] = 'no_message_id' expect do described_class.new(inbox: inbox, params: params).perform @@ -685,34 +569,14 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when message is found' do - let(:message_id) { 'msg_123' } - let!(:message) { create(:message, source_id: message_id, status: 'sent') } - it 'updates the message status' do - update_payload = { key: { id: message_id }, update: { status: 3 } } - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } - described_class.new(inbox: inbox, params: params).perform expect(message.reload.status).to eq('delivered') end it 'updates the message content' do - update_payload = { - key: { id: message_id }, - update: { - message: { editedMessage: { message: { conversation: 'New message content' } } } - } - } - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } + update_payload[:update] = { message: { editedMessage: { message: { conversation: 'New message content' } } } } described_class.new(inbox: inbox, params: params).perform @@ -724,16 +588,8 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when the status transition is not allowed (message already read)' do - let(:message_id) { 'msg_123' } - let!(:message) { create(:message, source_id: message_id, status: 'read') } - it 'does not update the status' do - update_payload = { key: { id: message_id }, update: { status: 3 } } - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } + message.update!(status: 'read') described_class.new(inbox: inbox, params: params).perform @@ -742,16 +598,9 @@ describe Whatsapp::IncomingMessageBaileysService do end context 'when update unsupported status' do - let(:message_id) { 'msg_123' } - let!(:message) { create(:message, source_id: message_id) } # rubocop:disable RSpec/LetSetup - it 'logs warning for unsupported played status' do - update_payload = { key: { id: message_id }, update: { status: 5 } } - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } + update_payload[:update][:status] = 5 + allow(Rails.logger).to receive(:warn).with('Baileys unsupported message update status: PLAYED(5)') described_class.new(inbox: inbox, params: params).perform @@ -760,12 +609,8 @@ describe Whatsapp::IncomingMessageBaileysService do end it 'logs warning for unsupported status' do - update_payload = { key: { id: message_id }, update: { status: 6 } } - params = { - webhookVerifyToken: webhook_verify_token, - event: 'messages.update', - data: [update_payload] - } + update_payload[:update][:status] = 6 + allow(Rails.logger).to receive(:warn).with('Baileys unsupported message update status: 6') described_class.new(inbox: inbox, params: params).perform