iachat/enterprise/app/jobs/captain/hermes/notify_payment_confirmed_job.rb
Rodribm10 48fad2977b feat(captain/hermes): payload enriquecido + humanizadores + notif Pix proativa
Captain::Hermes::Client (enterprise/app/services/captain/hermes/client.rb):
- text_for_hermes: transcreve audio via Whisper antes de enviar pro Hermes
  (reusa Captain::OpenAiMessageBuilderService)
- image_urls_for_hermes: URLs publicas de imagens da message; plugin
  captain-webhook do Hermes baixa em /tmp/ e popula event.media_urls pra
  vision multimodal (gpt-4o-mini auxiliary)
- contact_history_snapshot: dados eager pro [ctx] (last_reservation_*,
  total_conversations, ultima_suite, etc) — memoria do contato direto no
  prompt sem precisar tool call
- notify_event + build_event_payload: dispara webhook sintetico pro Hermes
  pra eventos do sistema (Pix pago etc) — Valentina manda mensagem
  espontanea sem cliente perguntar

Captain::Payments::ConfirmationService:
- Hook notify_hermes_proactively! enfileira NotifyPaymentConfirmedJob
  apos confirmacao de Pix, somente se inbox estiver no fluxo Hermes
  (Captain interno continua igual sem mudanca)

Captain::Hermes::NotifyPaymentConfirmedJob (NOVO):
- Monta system_message "[SISTEMA: pagamento_confirmado]\n..." e dispara
  webhook pro Hermes Valentina
- Valentina (via SOUL.md) interpreta como evento do Captain e manda
  mensagem celebrativa pro cliente

Captain::Hermes::DelayedReplyJob (NOVO) — humanizadores:
- Liga indicador "digitando..." (composing) via wuzapi
- Aguarda delay configuravel via Captain::Assistant.config['response_delay']
  (modos: none, fixed, typing_simulation com chars_per_second + min/max)
- Posta msg outgoing
- Desliga typing
- Fallback no HermesCallbackController posta direto se class nao carregada

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:15:50 -03:00

57 lines
2.2 KiB
Ruby

# Notifica o Hermes Agent sobre confirmação de pagamento de uma reserva, pra
# que o agente mande mensagem espontânea pro cliente celebrando (sem cliente
# precisar perguntar "já caiu?").
#
# Disparado por Captain::Payments::ConfirmationService (somente quando a
# inbox da reservation está em CAPTAIN_HERMES_INBOX_IDS — coexiste com o
# fluxo Captain interno).
class Captain::Hermes::NotifyPaymentConfirmedJob < ApplicationJob
queue_as :default
retry_on Captain::Hermes::Client::DispatchError, attempts: 3, wait: 5.seconds
def perform(reservation_id) # rubocop:disable Metrics/MethodLength
reservation = Captain::Reservation.find_by(id: reservation_id)
if reservation.blank?
Rails.logger.warn("[Captain::Hermes::NotifyPaymentConfirmedJob] reservation #{reservation_id} not found")
return
end
conversation = reservation.conversation
if conversation.blank?
Rails.logger.info("[Captain::Hermes::NotifyPaymentConfirmedJob] reservation #{reservation_id} has no conversation — skipping")
return
end
unless Captain::Hermes.enabled_for?(conversation.inbox)
Rails.logger.info(
"[Captain::Hermes::NotifyPaymentConfirmedJob] inbox #{conversation.inbox_id} " \
'not Hermes-enabled — skipping (Captain interno cuida)'
)
return
end
Captain::Hermes::Client.new(conversation.inbox).notify_event(
conversation: conversation,
event_type: 'payment_confirmed',
system_message: build_system_message(reservation)
)
end
private
def build_system_message(reservation)
deposit = reservation.metadata.to_h['deposit_amount'].to_f
total = reservation.total_amount.to_f
suite = reservation.suite_identifier.to_s
check_in = reservation.check_in_at&.strftime('%d/%m/%Y às %Hh%M')
[
'[SISTEMA: pagamento_confirmado]',
"Pix da reserva ##{reservation.id} acabou de cair pelo banco.",
"Sinal R$ #{format('%.2f', deposit)} de R$ #{format('%.2f', total)} (#{suite}, check-in #{check_in}).",
'Mande mensagem espontânea celebrando a reserva confirmada e dando próximos passos curtos. Tom íntimo, sem voltar a oferecer outras coisas.'
].join("\n")
end
end