feat(captain-memory): add SilenceDetectorJob with 10min cron

This commit is contained in:
Rodribm10 2026-04-19 00:55:15 -03:00
parent 1646f66a97
commit 833e76856e
3 changed files with 91 additions and 0 deletions

View File

@ -101,3 +101,9 @@ landing_hosts_promotion_sync_scheduler_job:
cron: '0 * * * *'
class: 'LandingHosts::PromotionSyncSchedulerJob'
queue: scheduled_jobs
# every 10 minutes - detects silent conversations for memory extraction
captain_contact_memory_silence_detector_job:
cron: '*/10 * * * *'
class: 'Captain::ContactMemories::SilenceDetectorJob'
queue: scheduled_jobs

View File

@ -0,0 +1,33 @@
class Captain::ContactMemories::SilenceDetectorJob < ApplicationJob
queue_as :scheduled_jobs
SILENCE_THRESHOLD = 30.minutes
def perform
Account.where("custom_attributes->>'captain_contact_memory_extraction_enabled' = 'true'").find_each do |account|
elegible_conversation_ids(account).each do |conv_id|
Captain::ContactMemories::ExtractFromConversationJob.perform_later(conv_id)
end
end
end
private
def elegible_conversation_ids(account)
Conversation
.where(account_id: account.id)
.joins(:messages)
.where.not(id: already_extracted_ids(account))
.group('conversations.id')
.having('MAX(messages.created_at) < ?', SILENCE_THRESHOLD.ago)
.pluck(:id)
end
def already_extracted_ids(account)
Captain::ContactMemory
.where(account_id: account.id)
.where.not(source_conversation_id: nil)
.distinct
.pluck(:source_conversation_id)
end
end

View File

@ -0,0 +1,52 @@
require 'rails_helper'
RSpec.describe Captain::ContactMemories::SilenceDetectorJob do
let(:account) { create(:account, custom_attributes: { 'captain_contact_memory_extraction_enabled' => true }) }
let(:contact) { create(:contact, account: account) }
# Widget inboxes auto-generate template messages (pre-chat form, etc.) with current
# timestamps when an incoming message is created. To deterministically test "silence",
# we update all messages in the conversation to an old created_at via update_all
# (bypassing callbacks) after creation.
def age_all_messages(conversation, age)
conversation.messages.update_all(created_at: age) # rubocop:disable Rails/SkipsModelValidations
end
it 'enqueues extraction for conversations silent > 30 minutes' do
conv = create(:conversation, account: account, contact: contact)
create(:message, conversation: conv, account: account)
age_all_messages(conv, 35.minutes.ago)
expect { described_class.perform_now }
.to have_enqueued_job(Captain::ContactMemories::ExtractFromConversationJob).with(conv.id)
end
it 'ignores conversations with recent activity' do
conv = create(:conversation, account: account, contact: contact)
create(:message, conversation: conv, account: account)
age_all_messages(conv, 5.minutes.ago)
expect { described_class.perform_now }
.not_to have_enqueued_job(Captain::ContactMemories::ExtractFromConversationJob)
end
it 'ignores conversations already extracted' do
conv = create(:conversation, account: account, contact: contact)
create(:message, conversation: conv, account: account)
age_all_messages(conv, 35.minutes.ago)
create(:captain_contact_memory, account: account, contact: contact, source_conversation_id: conv.id)
expect { described_class.perform_now }
.not_to have_enqueued_job(Captain::ContactMemories::ExtractFromConversationJob)
end
it 'ignores accounts with flag off' do
account.update!(custom_attributes: { 'captain_contact_memory_extraction_enabled' => false })
conv = create(:conversation, account: account, contact: contact)
create(:message, conversation: conv, account: account)
age_all_messages(conv, 35.minutes.ago)
expect { described_class.perform_now }
.not_to have_enqueued_job(Captain::ContactMemories::ExtractFromConversationJob)
end
end