fix: handle extended text message type of incoming message upsert (#9)

* feat: support extended text messages in WhatsApp incoming message service

* fix: correct isEditorHotKeyEnabled's spec

* ci: add baileys-api service to docker-compose configuration

* feat!: update validate_provider_config? endpoint

* feat: add Resend email delivery method and configuration (#11)

* feat: add Resend email delivery method and configuration

* chore: simplify ResendProvider initialization by removing settings parameter

* test: update ResendProvider initialization in tests by removing unnecessary settings parameter

* chore: improvements

* chore: add support for external frontend URL in environment configuration

* chore: ensure trailing slash

* ci: updated docker compose

* feat: update Resend email delivery method to use headers for sender and recipient

* feat: update email templates to use user's name instead of email

* fix: simplify extended text message structure in Baileys service spec

---------

Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
Co-authored-by: Gabriel Jablonski <gabriel@fazer.ai>
This commit is contained in:
Cayo P. R. Oliveira 2025-04-02 14:29:27 -03:00 committed by gabrieljablonski
parent 010575e005
commit 7ee0ec2fdb
3 changed files with 89 additions and 64 deletions

View File

@ -119,8 +119,8 @@ describe('useUISettings', () => {
it('returns correct value for isEditorHotKeyEnabled when editor_message_key is not configured', () => {
getUISettingsMock.value.editor_message_key = undefined;
const { isEditorHotKeyEnabled } = useUISettings();
expect(isEditorHotKeyEnabled('enter')).toBe(false);
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(true);
expect(isEditorHotKeyEnabled('enter')).toBe(true);
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(false);
});
it('handles non-existent keys', () => {

View File

@ -85,7 +85,7 @@ class Whatsapp::IncomingMessageBaileysService < Whatsapp::IncomingMessageBaseSer
def message_type # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
msg = @raw_message[:message]
return 'text' if msg.key?(:conversation)
return 'text' if msg.key?(:conversation) || msg.dig(:extendedTextMessage, :text)
return 'contacts' if msg.key?(:contactMessage)
return 'image' if msg.key?(:imageMessage)
return 'audio' if msg.key?(:audioMessage)
@ -106,9 +106,10 @@ class Whatsapp::IncomingMessageBaileysService < Whatsapp::IncomingMessageBaseSer
sender = is_outgoing ? @inbox.account.account_users.first.user : @contact
sender_type = is_outgoing ? 'User' : 'Contact'
message_type = is_outgoing ? :outgoing : :incoming
content = @raw_message.dig(:message, :conversation) || @raw_message.dig(:message, :extendedTextMessage, :text)
@message = @conversation.messages.create!(
content: @raw_message[:message][:conversation],
content: content,
account_id: @inbox.account_id,
inbox_id: @inbox.id,
source_id: message_id,

View File

@ -192,71 +192,95 @@ describe Whatsapp::IncomingMessageBaileysService do
}
end
it 'creates an incoming message' do
described_class.new(inbox: inbox, params: params).perform
context 'when has key conversation' do # rubocop:disable RSpec/NestedGroups
it 'creates an incoming 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.content).to eq('Hello from Baileys')
expect(message.message_type).to eq('incoming')
expect(message.sender).to be_present
expect(message.sender.name).to eq('John Doe')
conversation = inbox.conversations.last
message = conversation.messages.last
expect(message).to be_present
expect(message.content).to eq('Hello from Baileys')
expect(message.message_type).to eq('incoming')
expect(message.sender).to be_present
expect(message.sender.name).to eq('John Doe')
end
it 'creates an outgoing message' do
raw_message_outgoing = raw_message.merge(
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: true }
)
params_outgoing = params.merge(data: { type: 'notify', messages: [raw_message_outgoing] })
create(:account_user, account: inbox.account)
described_class.new(inbox: inbox, params: params_outgoing).perform
conversation = inbox.conversations.last
message = conversation.messages.last
expect(message).to be_present
expect(message.content).to eq('Hello from Baileys')
expect(message.message_type).to eq('outgoing')
end
it 'creates a message on an existing conversation' do
contact = create(:contact, account: inbox.account, name: 'John Doe')
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
existing_conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
described_class.new(inbox: inbox, params: params).perform
message = existing_conversation.messages.last
expect(message.sender).to eq(contact)
end
it 'does not create a message if it already exists' do
message = create(:message, inbox: inbox, source_id: 'msg_123')
described_class.new(inbox: inbox, params: params).perform
conversation = inbox.conversations.last
messages = conversation.messages
expect(messages).to eq([message])
end
it 'does not create a message if it is already being processed' do
allow(Redis::Alfred).to receive(:get).with(format_message_source_key('msg_123')).and_return(true)
described_class.new(inbox: inbox, params: params).perform
expect(inbox.conversations).to be_empty
end
it 'caches the message source id in Redis and clears it' do
allow(Redis::Alfred).to receive(:setex).with(format_message_source_key('msg_123'), true)
allow(Redis::Alfred).to receive(:delete).with(format_message_source_key('msg_123'))
described_class.new(inbox: inbox, params: params).perform
expect(Redis::Alfred).to have_received(:setex)
expect(Redis::Alfred).to have_received(:delete)
end
end
it 'creates an outgoing message' do
raw_message_outgoing = raw_message.merge(
key: { id: 'msg_123', remoteJid: '5511912345678@s.whatsapp.net', fromMe: true }
)
params_outgoing = params.merge(data: { type: 'notify', messages: [raw_message_outgoing] })
create(:account_user, account: inbox.account)
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
described_class.new(inbox: inbox, params: params_outgoing).perform
it 'creates an incoming 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.content).to eq('Hello from Baileys')
expect(message.message_type).to eq('outgoing')
end
it 'creates a message on an existing conversation' do
contact = create(:contact, account: inbox.account, name: 'John Doe')
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: '5511912345678')
existing_conversation = create(:conversation, inbox: inbox, contact_inbox: contact_inbox)
described_class.new(inbox: inbox, params: params).perform
message = existing_conversation.messages.last
expect(message.sender).to eq(contact)
end
it 'does not create a message if it already exists' do
message = create(:message, inbox: inbox, source_id: 'msg_123')
described_class.new(inbox: inbox, params: params).perform
conversation = inbox.conversations.last
messages = conversation.messages
expect(messages).to eq([message])
end
it 'does not create a message if it is already being processed' do
allow(Redis::Alfred).to receive(:get).with(format_message_source_key('msg_123')).and_return(true)
described_class.new(inbox: inbox, params: params).perform
expect(inbox.conversations).to be_empty
end
it 'caches the message source id in Redis and clears it' do
allow(Redis::Alfred).to receive(:setex).with(format_message_source_key('msg_123'), true)
allow(Redis::Alfred).to receive(:delete).with(format_message_source_key('msg_123'))
described_class.new(inbox: inbox, params: params).perform
expect(Redis::Alfred).to have_received(:setex)
expect(Redis::Alfred).to have_received(:delete)
conversation = inbox.conversations.last
message = conversation.messages.last
expect(message).to be_present
expect(message.content).to eq('Hello from Baileys')
expect(message.message_type).to eq('incoming')
expect(message.sender).to be_present
expect(message.sender.name).to eq('John Doe')
end
end
end
end