iachat/enterprise/app/jobs/captain/hermes/delayed_reply_job.rb
Rodribm10 c7300dfbcf fix(captain/hermes): nao enviar typing_off explicito
Antes: typing_on -> sleep delay -> create msg DB -> typing_off -> SendReplyJob
envia via wuzapi -> msg chega no celular (2-5s depois).

Resultado visual quebrado: cliente ve "digitando..." sumir antes da msg
chegar, gap visivel.

Agora: typing_on -> sleep -> create msg -> deixa rolar. WhatsApp do
cliente cancela typing automaticamente quando msg eh entregue. Sequencia
fica natural.

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

86 lines
2.9 KiB
Ruby

# Posta a resposta do Hermes na conversa simulando comportamento humano:
# 1. Liga indicador de "digitando..." (composing) via wuzapi
# 2. Aguarda delay configurado pelo assistant (typing_simulation, fixed ou none)
# 3. Posta a mensagem outgoing
#
# Config vive em `Captain::Assistant.config['response_delay']`:
# {
# "mode": "typing_simulation" | "fixed" | "none",
# "chars_per_second": 25, # apenas typing_simulation
# "seconds": 3, # apenas fixed
# "min_seconds": 1.5, # cap inferior pra typing_simulation
# "max_seconds": 8.0 # cap superior pra typing_simulation
# }
#
# Default: none (zero delay, igual antes — defensivo).
class Captain::Hermes::DelayedReplyJob < ApplicationJob
queue_as :default
DEFAULT_CONFIG = {
'mode' => 'none',
'chars_per_second' => 25,
'min_seconds' => 1.5,
'max_seconds' => 8.0
}.freeze
def perform(conversation_id, content)
conversation = Conversation.find_by(id: conversation_id)
if conversation.blank?
Rails.logger.warn("[Captain::Hermes::DelayedReplyJob] conv #{conversation_id} not found")
return
end
delay = compute_delay(conversation, content)
if delay.positive?
send_typing(conversation, 'typing_on')
sleep(delay)
end
create_outgoing_message(conversation, content)
# NÃO mandamos typing_off explícito — WhatsApp cancela o indicador
# automaticamente quando a msg chega no celular. Mandar paused agora
# quebraria visualmente: typing some -> gap de 2-5s ate msg ser
# entregue via SendReplyJob -> msg chega. Deixa o WhatsApp gerenciar.
end
private
def compute_delay(conversation, content)
cfg = DEFAULT_CONFIG.merge(conversation.inbox.captain_assistant&.config.to_h.fetch('response_delay', {}))
case cfg['mode']
when 'fixed' then cfg['seconds'].to_f
when 'typing_simulation'
cps = cfg['chars_per_second'].to_f
cps = 25 if cps <= 0
raw = content.to_s.length / cps
raw.clamp(cfg['min_seconds'].to_f, cfg['max_seconds'].to_f)
else 0.0
end
end
def send_typing(conversation, status)
return unless conversation.inbox.respond_to?(:channel)
return unless conversation.inbox.channel.respond_to?(:toggle_typing_status)
conversation.inbox.channel.toggle_typing_status(status, conversation: conversation)
rescue StandardError => e
Rails.logger.warn("[Captain::Hermes::DelayedReplyJob] toggle_typing_status #{status} failed: #{e.class} - #{e.message}")
end
def create_outgoing_message(conversation, content)
assistant = conversation.inbox.captain_assistant
sender = assistant.presence || User.find_by(id: conversation.assignee_id)
conversation.messages.create!(
message_type: :outgoing,
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
sender: sender,
content: content,
content_attributes: { external_source: 'hermes_callback' }
)
end
end