feat: mark recorded audio messages (#36)
* feat: mark recorded audio * refactor: cleaner logic for message builder metadata * test: message builder specs * test: baileys service specs * fix: correct method call for checking attachment filename in process_metadata
This commit is contained in:
parent
a52a4cf8ce
commit
b764f3997d
@ -9,6 +9,7 @@ class Messages::MessageBuilder
|
|||||||
@user = user
|
@user = user
|
||||||
@message_type = params[:message_type] || 'outgoing'
|
@message_type = params[:message_type] || 'outgoing'
|
||||||
@attachments = params[:attachments]
|
@attachments = params[:attachments]
|
||||||
|
@is_recorded_audio = params[:is_recorded_audio]
|
||||||
@automation_rule = content_attributes&.dig(:automation_rule_id)
|
@automation_rule = content_attributes&.dig(:automation_rule_id)
|
||||||
return unless params.instance_of?(ActionController::Parameters)
|
return unless params.instance_of?(ActionController::Parameters)
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ class Messages::MessageBuilder
|
|||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file: uploaded_attachment
|
file: uploaded_attachment
|
||||||
)
|
)
|
||||||
|
attachment.meta = process_metadata(uploaded_attachment)
|
||||||
attachment.file_type = if uploaded_attachment.is_a?(String)
|
attachment.file_type = if uploaded_attachment.is_a?(String)
|
||||||
file_type_by_signed_id(
|
file_type_by_signed_id(
|
||||||
uploaded_attachment
|
uploaded_attachment
|
||||||
@ -78,6 +79,10 @@ class Messages::MessageBuilder
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process_metadata(attachment)
|
||||||
|
{ is_recorded_audio: true } if @is_recorded_audio && attachment.original_filename.in?(@is_recorded_audio)
|
||||||
|
end
|
||||||
|
|
||||||
def process_emails
|
def process_emails
|
||||||
return unless @conversation.inbox&.inbox_type == 'Email'
|
return unless @conversation.inbox&.inbox_type == 'Email'
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export const buildCreatePayload = ({
|
|||||||
contentAttributes,
|
contentAttributes,
|
||||||
echoId,
|
echoId,
|
||||||
files,
|
files,
|
||||||
|
isRecordedAudio,
|
||||||
ccEmails = '',
|
ccEmails = '',
|
||||||
bccEmails = '',
|
bccEmails = '',
|
||||||
toEmails = '',
|
toEmails = '',
|
||||||
@ -22,6 +23,7 @@ export const buildCreatePayload = ({
|
|||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
payload.append('attachments[]', file);
|
payload.append('attachments[]', file);
|
||||||
});
|
});
|
||||||
|
payload.append('is_recorded_audio', JSON.stringify(isRecordedAudio));
|
||||||
payload.append('private', isPrivate);
|
payload.append('private', isPrivate);
|
||||||
payload.append('echo_id', echoId);
|
payload.append('echo_id', echoId);
|
||||||
payload.append('cc_emails', ccEmails);
|
payload.append('cc_emails', ccEmails);
|
||||||
@ -60,6 +62,7 @@ class MessageApi extends ApiClient {
|
|||||||
contentAttributes,
|
contentAttributes,
|
||||||
echo_id: echoId,
|
echo_id: echoId,
|
||||||
files,
|
files,
|
||||||
|
isRecordedAudio,
|
||||||
ccEmails = '',
|
ccEmails = '',
|
||||||
bccEmails = '',
|
bccEmails = '',
|
||||||
toEmails = '',
|
toEmails = '',
|
||||||
@ -74,6 +77,7 @@ class MessageApi extends ApiClient {
|
|||||||
contentAttributes,
|
contentAttributes,
|
||||||
echoId,
|
echoId,
|
||||||
files,
|
files,
|
||||||
|
isRecordedAudio,
|
||||||
ccEmails,
|
ccEmails,
|
||||||
bccEmails,
|
bccEmails,
|
||||||
toEmails,
|
toEmails,
|
||||||
|
|||||||
@ -1012,11 +1012,17 @@ export default {
|
|||||||
|
|
||||||
if (this.attachedFiles && this.attachedFiles.length) {
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
messagePayload.files = [];
|
messagePayload.files = [];
|
||||||
|
messagePayload.isRecordedAudio = [];
|
||||||
this.attachedFiles.forEach(attachment => {
|
this.attachedFiles.forEach(attachment => {
|
||||||
if (this.globalConfig.directUploadsEnabled) {
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
messagePayload.files.push(attachment.blobSignedId);
|
messagePayload.files.push(attachment.blobSignedId);
|
||||||
} else {
|
} else {
|
||||||
messagePayload.files.push(attachment.resource.file);
|
messagePayload.files.push(attachment.resource.file);
|
||||||
|
if (attachment.isRecordedAudio) {
|
||||||
|
messagePayload.isRecordedAudio.push(
|
||||||
|
attachment.resource.file.name
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,7 +86,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachment_message_content
|
def attachment_message_content # rubocop:disable Metrics/MethodLength
|
||||||
attachment = @message.attachments.first
|
attachment = @message.attachments.first
|
||||||
buffer = Base64.strict_encode64(attachment.file.download)
|
buffer = Base64.strict_encode64(attachment.file.download)
|
||||||
|
|
||||||
@ -99,6 +99,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
content[:image] = buffer
|
content[:image] = buffer
|
||||||
when 'audio'
|
when 'audio'
|
||||||
content[:audio] = buffer
|
content[:audio] = buffer
|
||||||
|
content[:ptt] = attachment.meta['is_recorded_audio']
|
||||||
when 'file'
|
when 'file'
|
||||||
content[:document] = buffer
|
content[:document] = buffer
|
||||||
when 'sticker'
|
when 'sticker'
|
||||||
|
|||||||
@ -134,6 +134,14 @@ describe Messages::MessageBuilder do
|
|||||||
expect(message.attachments.first.file_type).to eq 'image'
|
expect(message.attachments.first.file_type).to eq 'image'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates attachment with is_recorded_audio metadata' do
|
||||||
|
params[:is_recorded_audio] = ['avatar.png']
|
||||||
|
|
||||||
|
message = message_builder
|
||||||
|
|
||||||
|
expect(message.attachments.first.meta).to eq({ 'is_recorded_audio' => true })
|
||||||
|
end
|
||||||
|
|
||||||
context 'when DIRECT_UPLOAD_ENABLED' do
|
context 'when DIRECT_UPLOAD_ENABLED' do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
ActionController::Parameters.new({
|
ActionController::Parameters.new({
|
||||||
|
|||||||
@ -92,6 +92,8 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#send_message' do
|
describe '#send_message' do
|
||||||
|
let(:request_path) { "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message" }
|
||||||
|
|
||||||
context 'when message does not have content nor attachments' do
|
context 'when message does not have content nor attachments' do
|
||||||
it 'updates the message with content attribute is_unsupported' do
|
it 'updates the message with content attribute is_unsupported' do
|
||||||
message.update!(content: nil)
|
message.update!(content: nil)
|
||||||
@ -107,7 +109,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
let(:base64_image) { 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' }
|
let(:base64_image) { 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
message.attachments.new(
|
message.attachments.create!(
|
||||||
account_id: message.account_id,
|
account_id: message.account_id,
|
||||||
file_type: 'image',
|
file_type: 'image',
|
||||||
file: {
|
file: {
|
||||||
@ -115,11 +117,10 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
filename: 'image.png'
|
filename: 'image.png'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
message.save!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sends the attachment message' do
|
it 'sends the attachment message' do
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
stub_request(:post, request_path)
|
||||||
.with(
|
.with(
|
||||||
headers: stub_headers(whatsapp_channel),
|
headers: stub_headers(whatsapp_channel),
|
||||||
body: {
|
body: {
|
||||||
@ -140,7 +141,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
|
|
||||||
it 'omits caption if message content is empty' do
|
it 'omits caption if message content is empty' do
|
||||||
message.update!(content: nil)
|
message.update!(content: nil)
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
stub_request(:post, request_path)
|
||||||
.with(
|
.with(
|
||||||
headers: stub_headers(whatsapp_channel),
|
headers: stub_headers(whatsapp_channel),
|
||||||
body: {
|
body: {
|
||||||
@ -158,26 +159,31 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
|
|
||||||
expect(result).to eq('msg_123')
|
expect(result).to eq('msg_123')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when request is unsuccessful' do
|
|
||||||
it 'raises MessageNotSentError' do
|
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
|
||||||
.to_return(
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type' => 'application/json' },
|
|
||||||
body: { 'data' => { 'key' => { 'id' => 'msg_123' } } }.to_json
|
|
||||||
)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
service.send_message(test_send_phone_number, message)
|
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::MessageNotSentError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when message is a text' do
|
context 'when message is an audio file' do
|
||||||
it 'sends the message' do
|
let(:base64_audio) { 'UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA=' }
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
|
||||||
|
before do
|
||||||
|
message.attachments.create!(
|
||||||
|
account_id: message.account_id,
|
||||||
|
file_type: 'audio',
|
||||||
|
file: {
|
||||||
|
io: StringIO.new(Base64.decode64(base64_audio)),
|
||||||
|
filename: 'audio.wav'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sends the audio message' do
|
||||||
|
stub_request(:post, request_path)
|
||||||
|
.with(
|
||||||
|
headers: stub_headers(whatsapp_channel),
|
||||||
|
body: {
|
||||||
|
jid: test_send_jid,
|
||||||
|
messageContent: { fileName: 'audio.wav', caption: message.content, audio: base64_audio }
|
||||||
|
}.to_json
|
||||||
|
)
|
||||||
.to_return(
|
.to_return(
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type' => 'application/json' },
|
headers: { 'Content-Type' => 'application/json' },
|
||||||
@ -189,17 +195,41 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
expect(result).to eq('msg_123')
|
expect(result).to eq('msg_123')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises MessageNotSentError' do
|
it 'sends message with ptt true if message is recorded audio' do
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
message.attachments.first.update!(meta: { is_recorded_audio: true })
|
||||||
|
|
||||||
|
stub_request(:post, request_path)
|
||||||
|
.with(
|
||||||
|
headers: stub_headers(whatsapp_channel),
|
||||||
|
body: {
|
||||||
|
jid: test_send_jid,
|
||||||
|
messageContent: { fileName: 'audio.wav', caption: message.content, audio: base64_audio, ptt: true }
|
||||||
|
}.to_json
|
||||||
|
)
|
||||||
.to_return(
|
.to_return(
|
||||||
status: 400,
|
status: 200,
|
||||||
headers: { 'Content-Type' => 'application/json' },
|
headers: { 'Content-Type' => 'application/json' },
|
||||||
body: { 'data' => { 'key' => { 'id' => 'msg_123' } } }.to_json
|
body: { 'data' => { 'key' => { 'id' => 'msg_123' } } }.to_json
|
||||||
)
|
)
|
||||||
|
|
||||||
expect do
|
result = service.send_message(test_send_phone_number, message)
|
||||||
service.send_message(test_send_phone_number, message)
|
|
||||||
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::MessageNotSentError)
|
expect(result).to eq('msg_123')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when message is a text' do
|
||||||
|
it 'sends the message' do
|
||||||
|
stub_request(:post, request_path)
|
||||||
|
.to_return(
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type' => 'application/json' },
|
||||||
|
body: { 'data' => { 'key' => { 'id' => 'msg_123' } } }.to_json
|
||||||
|
)
|
||||||
|
|
||||||
|
result = service.send_message(test_send_phone_number, message)
|
||||||
|
|
||||||
|
expect(result).to eq('msg_123')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -216,7 +246,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
message = create(:message, inbox: inbox, conversation: conversation, sender: account_user, message_type: 'outgoing', source_id: 'msg_123')
|
message = create(:message, inbox: inbox, conversation: conversation, sender: account_user, message_type: 'outgoing', source_id: 'msg_123')
|
||||||
reaction = create(:message, inbox: inbox, conversation: conversation, sender: account_user, content: '👍',
|
reaction = create(:message, inbox: inbox, conversation: conversation, sender: account_user, content: '👍',
|
||||||
content_attributes: { is_reaction: true, in_reply_to: message.id })
|
content_attributes: { is_reaction: true, in_reply_to: message.id })
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
stub_request(:post, request_path)
|
||||||
.with(
|
.with(
|
||||||
headers: stub_headers(whatsapp_channel),
|
headers: stub_headers(whatsapp_channel),
|
||||||
body: {
|
body: {
|
||||||
@ -242,7 +272,7 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
message = create(:message, inbox: inbox, conversation: conversation, sender: contact, source_id: 'msg_123')
|
message = create(:message, inbox: inbox, conversation: conversation, sender: contact, source_id: 'msg_123')
|
||||||
reaction = create(:message, inbox: inbox, conversation: conversation, sender: account_user, content: '👍',
|
reaction = create(:message, inbox: inbox, conversation: conversation, sender: account_user, content: '👍',
|
||||||
content_attributes: { is_reaction: true, in_reply_to: message.id })
|
content_attributes: { is_reaction: true, in_reply_to: message.id })
|
||||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-message")
|
stub_request(:post, request_path)
|
||||||
.with(
|
.with(
|
||||||
headers: stub_headers(whatsapp_channel),
|
headers: stub_headers(whatsapp_channel),
|
||||||
body: {
|
body: {
|
||||||
@ -264,6 +294,21 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
expect(result).to eq('reaction_123')
|
expect(result).to eq('reaction_123')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when request is unsuccessful' do
|
||||||
|
it 'raises MessageNotSentError' do
|
||||||
|
stub_request(:post, request_path)
|
||||||
|
.to_return(
|
||||||
|
status: 400,
|
||||||
|
headers: { 'Content-Type' => 'application/json' },
|
||||||
|
body: { 'data' => { 'key' => { 'id' => 'msg_123' } } }.to_json
|
||||||
|
)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
service.send_message(test_send_phone_number, message)
|
||||||
|
end.to raise_error(Whatsapp::Providers::WhatsappBaileysService::MessageNotSentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#api_headers' do
|
describe '#api_headers' do
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user