fix(captain): send WhatsApp reply context to Hermes
This commit is contained in:
parent
358114d04d
commit
572b9ccd10
@ -71,10 +71,13 @@ class Captain::Hermes::Client
|
||||
contact_attrs = contact&.custom_attributes.to_h.with_indifferent_access
|
||||
cpf_digits = contact_attrs[:cpf].to_s.gsub(/\D/, '')
|
||||
history = contact_history_snapshot(contact, conversation)
|
||||
reply_context_builder = Captain::Hermes::ReplyContextBuilder.new(message: message, conversation: conversation)
|
||||
reply_context = reply_context_builder.perform
|
||||
|
||||
{
|
||||
message: content_override.presence || text_for_hermes(message),
|
||||
message: reply_context_builder.wrap_message(content_override.presence || text_for_hermes(message)),
|
||||
image_urls: image_urls_for_hermes(message),
|
||||
reply_context: reply_context,
|
||||
contact_name: contact&.name,
|
||||
contact_first_name: contact&.name.to_s.split.first,
|
||||
contact_id: conversation.contact_id,
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
class Captain::Hermes::ReplyContextBuilder
|
||||
def initialize(message:, conversation:)
|
||||
@message = message
|
||||
@conversation = conversation
|
||||
end
|
||||
|
||||
def perform
|
||||
return nil if reply_to_external_id.blank?
|
||||
|
||||
{
|
||||
external_id: reply_to_external_id,
|
||||
found: quoted_message.present?,
|
||||
quoted_message: quoted_message_snapshot
|
||||
}.compact
|
||||
end
|
||||
|
||||
def wrap_message(current_text)
|
||||
return current_text if reply_context.blank?
|
||||
|
||||
"#{formatted_reply_context}\n\n[RESPOSTA ATUAL DO CLIENTE]\n#{current_text}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :message, :conversation
|
||||
|
||||
def reply_context
|
||||
@reply_context ||= perform
|
||||
end
|
||||
|
||||
def formatted_reply_context
|
||||
return missing_reply_context unless reply_context[:found]
|
||||
|
||||
quoted = reply_context[:quoted_message]
|
||||
quoted_content = quoted[:content].presence || quoted[:attachment_summary].presence || '[mensagem sem texto]'
|
||||
|
||||
<<~TEXT.strip
|
||||
[CONTEXTO DE RESPOSTA DO WHATSAPP]
|
||||
O cliente respondeu citando uma mensagem anterior.
|
||||
Interprete a resposta atual como referência direta a essa mensagem citada.
|
||||
Mensagem citada (#{quoted[:sender_label]}, #{quoted[:created_at]}): #{quoted_content}
|
||||
TEXT
|
||||
end
|
||||
|
||||
def missing_reply_context
|
||||
<<~TEXT.strip
|
||||
[CONTEXTO DE RESPOSTA DO WHATSAPP]
|
||||
O cliente respondeu citando uma mensagem anterior, mas o Chatwoot não encontrou o conteúdo da mensagem citada.
|
||||
ID externo citado: #{reply_context[:external_id]}
|
||||
TEXT
|
||||
end
|
||||
|
||||
def reply_to_external_id
|
||||
@reply_to_external_id ||= message.in_reply_to_external_id.presence ||
|
||||
message.content_attributes.to_h['in_reply_to_external_id'].presence ||
|
||||
message.content_attributes.to_h[:in_reply_to_external_id].presence
|
||||
end
|
||||
|
||||
def quoted_message
|
||||
@quoted_message ||= conversation.messages.find_by(source_id: reply_to_external_id)
|
||||
end
|
||||
|
||||
def quoted_message_snapshot
|
||||
return nil if quoted_message.blank?
|
||||
|
||||
{
|
||||
id: quoted_message.id,
|
||||
external_id: quoted_message.source_id,
|
||||
message_type: quoted_message.message_type,
|
||||
sender_label: sender_label,
|
||||
sender_name: quoted_message.sender&.available_name,
|
||||
content: quoted_message_content,
|
||||
attachment_summary: attachment_summary,
|
||||
created_at: quoted_message.created_at&.iso8601
|
||||
}.compact
|
||||
end
|
||||
|
||||
def sender_label
|
||||
return 'cliente' if quoted_message.incoming?
|
||||
return 'atendente/Hermes' if quoted_message.outgoing?
|
||||
|
||||
'sistema'
|
||||
end
|
||||
|
||||
def quoted_message_content
|
||||
quoted_message.content.to_s.truncate(1200)
|
||||
end
|
||||
|
||||
def attachment_summary
|
||||
return nil if quoted_message.attachments.blank?
|
||||
|
||||
types = quoted_message.attachments.filter_map(&:file_type)
|
||||
"anexos: #{types.join(', ')}"
|
||||
end
|
||||
end
|
||||
100
spec/enterprise/services/captain/hermes/client_spec.rb
Normal file
100
spec/enterprise/services/captain/hermes/client_spec.rb
Normal file
@ -0,0 +1,100 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Captain::Hermes::Client do
|
||||
describe '#build_payload' do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:contact) { create(:contact, account: account, name: 'Cliente Teste') }
|
||||
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: inbox) }
|
||||
let(:conversation) do
|
||||
create(:conversation, account: account, inbox: inbox, contact: contact, contact_inbox: contact_inbox)
|
||||
end
|
||||
let(:client) { described_class.new(inbox) }
|
||||
|
||||
it 'includes quoted WhatsApp message context in the Hermes-visible message' do
|
||||
quoted_message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content: 'A suite Alexa esta disponivel por R$ 199.',
|
||||
source_id: 'wamid.quoted-message'
|
||||
)
|
||||
reply = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: inbox,
|
||||
conversation: conversation,
|
||||
message_type: :incoming,
|
||||
content: 'Pode reservar essa',
|
||||
source_id: 'wamid.reply-message',
|
||||
content_attributes: { in_reply_to_external_id: quoted_message.source_id }
|
||||
)
|
||||
|
||||
payload = client.send(:build_payload, message: reply, conversation: conversation)
|
||||
|
||||
expect(payload[:reply_context]).to include(
|
||||
external_id: quoted_message.source_id,
|
||||
found: true
|
||||
)
|
||||
expect(payload[:reply_context][:quoted_message]).to include(
|
||||
id: quoted_message.id,
|
||||
message_type: 'outgoing',
|
||||
sender_label: 'atendente/Hermes',
|
||||
content: 'A suite Alexa esta disponivel por R$ 199.'
|
||||
)
|
||||
expect(payload[:message]).to include('[CONTEXTO DE RESPOSTA DO WHATSAPP]')
|
||||
expect(payload[:message]).to include('A suite Alexa esta disponivel por R$ 199.')
|
||||
expect(payload[:message]).to include('[RESPOSTA ATUAL DO CLIENTE]')
|
||||
expect(payload[:message]).to include('Pode reservar essa')
|
||||
end
|
||||
|
||||
it 'keeps the combined incoming text while adding quote context' do
|
||||
quoted_message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content: 'Temos opcoes com hidro e garagem privativa.',
|
||||
source_id: 'wamid.quoted-options'
|
||||
)
|
||||
reply = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: inbox,
|
||||
conversation: conversation,
|
||||
message_type: :incoming,
|
||||
content: 'Essa',
|
||||
content_attributes: { in_reply_to_external_id: quoted_message.source_id }
|
||||
)
|
||||
|
||||
payload = client.send(
|
||||
:build_payload,
|
||||
message: reply,
|
||||
conversation: conversation,
|
||||
content_override: "Quero ver as suites\nEssa"
|
||||
)
|
||||
|
||||
expect(payload[:message]).to include('Temos opcoes com hidro e garagem privativa.')
|
||||
expect(payload[:message]).to include("Quero ver as suites\nEssa")
|
||||
end
|
||||
|
||||
it 'does not add reply context when the message is not a WhatsApp reply' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: inbox,
|
||||
conversation: conversation,
|
||||
message_type: :incoming,
|
||||
content: 'Oi, tem suite disponivel?'
|
||||
)
|
||||
|
||||
payload = client.send(:build_payload, message: message, conversation: conversation)
|
||||
|
||||
expect(payload[:reply_context]).to be_nil
|
||||
expect(payload[:message]).to eq('Oi, tem suite disponivel?')
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue
Block a user