fix: Captain not responding to campaign conversations (#13489)

Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
This commit is contained in:
Shivam Mishra
2026-02-12 10:07:56 +05:30
committed by GitHub
parent c7193c7917
commit 2c2f0547f7
6 changed files with 121 additions and 1 deletions

View File

@@ -2,7 +2,6 @@ class MessageTemplates::HookExecutionService
pattr_initialize [:message!] pattr_initialize [:message!]
def perform def perform
return if conversation.campaign.present?
return if conversation.last_incoming_message.blank? return if conversation.last_incoming_message.blank?
return if message.auto_reply_email? return if message.auto_reply_email?
@@ -21,6 +20,7 @@ class MessageTemplates::HookExecutionService
end end
def should_send_out_of_office_message? def should_send_out_of_office_message?
return false if conversation.campaign.present?
# should not send if its a tweet message # should not send if its a tweet message
return false if conversation.tweet? return false if conversation.tweet?
# should not send for outbound messages # should not send for outbound messages
@@ -37,6 +37,7 @@ class MessageTemplates::HookExecutionService
end end
def should_send_greeting? def should_send_greeting?
return false if conversation.campaign.present?
# should not send if its a tweet message # should not send if its a tweet message
return false if conversation.tweet? return false if conversation.tweet?
@@ -49,6 +50,8 @@ class MessageTemplates::HookExecutionService
# TODO: we should be able to reduce this logic once we have a toggle for email collect messages # TODO: we should be able to reduce this logic once we have a toggle for email collect messages
def should_send_email_collect? def should_send_email_collect?
return false if conversation.campaign.present?
!contact_has_email? && inbox.web_widget? && !email_collect_was_sent? !contact_has_email? && inbox.web_widget? && !email_collect_was_sent?
end end

View File

@@ -93,6 +93,10 @@ class Captain::Conversation::ResponseBuilderJob < ApplicationJob
end end
def send_out_of_office_message_if_applicable def send_out_of_office_message_if_applicable
# Campaign conversations should never receive OOO templates — the campaign itself
# serves as the initial outreach, and OOO would be confusing in that context.
return if @conversation.campaign.present?
::MessageTemplates::Template::OutOfOffice.perform_if_applicable(@conversation) ::MessageTemplates::Template::OutOfOffice.perform_if_applicable(@conversation)
end end

View File

@@ -68,6 +68,10 @@ module Enterprise::MessageTemplates::HookExecutionService
end end
def send_out_of_office_message_after_handoff def send_out_of_office_message_after_handoff
# Campaign conversations should never receive OOO templates — the campaign itself
# serves as the initial outreach, and OOO would be confusing in that context.
return if conversation.campaign.present?
::MessageTemplates::Template::OutOfOffice.perform_if_applicable(conversation) ::MessageTemplates::Template::OutOfOffice.perform_if_applicable(conversation)
end end

View File

@@ -42,6 +42,10 @@ class Captain::Tools::HandoffTool < Captain::Tools::BasePublicTool
end end
def send_out_of_office_message_if_applicable(conversation) def send_out_of_office_message_if_applicable(conversation)
# Campaign conversations should never receive OOO templates — the campaign itself
# serves as the initial outreach, and OOO would be confusing in that context.
return if conversation.campaign.present?
::MessageTemplates::Template::OutOfOffice.perform_if_applicable(conversation) ::MessageTemplates::Template::OutOfOffice.perform_if_applicable(conversation)
end end

View File

@@ -238,6 +238,77 @@ RSpec.describe MessageTemplates::HookExecutionService do
end end
end end
context 'when conversation has a campaign' do
let(:campaign) { create(:campaign, account: account) }
let(:campaign_conversation) { create(:conversation, inbox: inbox, account: account, contact: contact, status: :pending, campaign: campaign) }
it 'schedules captain response job for incoming messages on pending campaign conversations' do
expect(Captain::Conversation::ResponseBuilderJob).to receive(:perform_later).with(campaign_conversation, assistant)
create(:message, conversation: campaign_conversation, message_type: :incoming)
end
it 'does not send greeting template on campaign conversations' do
inbox.update!(greeting_enabled: true, greeting_message: 'Hello! How can we help you?', enable_email_collect: false)
greeting_service = instance_double(MessageTemplates::Template::Greeting)
allow(MessageTemplates::Template::Greeting).to receive(:new).and_return(greeting_service)
allow(greeting_service).to receive(:perform).and_return(true)
create(:message, conversation: campaign_conversation, message_type: :incoming)
expect(MessageTemplates::Template::Greeting).not_to have_received(:new)
end
it 'does not send out of office template on campaign conversations' do
inbox.update!(working_hours_enabled: true, out_of_office_message: 'We are currently closed')
inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
closed_all_day: true,
open_all_day: false
)
out_of_office_service = instance_double(MessageTemplates::Template::OutOfOffice)
allow(MessageTemplates::Template::OutOfOffice).to receive(:new).and_return(out_of_office_service)
allow(out_of_office_service).to receive(:perform).and_return(true)
create(:message, conversation: campaign_conversation, message_type: :incoming)
expect(MessageTemplates::Template::OutOfOffice).not_to have_received(:new)
end
it 'does not send email collect template on campaign conversations' do
contact.update!(email: nil)
inbox.update!(enable_email_collect: true)
email_collect_service = instance_double(MessageTemplates::Template::EmailCollect)
allow(MessageTemplates::Template::EmailCollect).to receive(:new).and_return(email_collect_service)
allow(email_collect_service).to receive(:perform).and_return(true)
create(:message, conversation: campaign_conversation, message_type: :incoming)
expect(MessageTemplates::Template::EmailCollect).not_to have_received(:new)
end
it 'does not send out of office template after handoff on campaign conversations when quota is exceeded' do
account.update!(
limits: { 'captain_responses' => 100 },
custom_attributes: account.custom_attributes.merge('captain_responses_usage' => 100)
)
inbox.update!(
working_hours_enabled: true,
out_of_office_message: 'We are currently closed'
)
inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
closed_all_day: true,
open_all_day: false
)
expect do
create(:message, conversation: campaign_conversation, message_type: :incoming)
end.not_to(change { campaign_conversation.messages.template.count })
end
end
context 'when Captain quota is exceeded and handoff happens' do context 'when Captain quota is exceeded and handoff happens' do
before do before do
account.update!( account.update!(

View File

@@ -111,6 +111,40 @@ describe MessageTemplates::HookExecutionService do
end end
end end
context 'when conversation has a campaign' do
let(:campaign) { create(:campaign) }
it 'does not call ::MessageTemplates::Template::Greeting on campaign conversations' do
contact = create(:contact, email: nil)
conversation = create(:conversation, contact: contact, campaign: campaign)
conversation.inbox.update(greeting_enabled: true, greeting_message: 'Hi, this is a greeting message', enable_email_collect: false)
greeting_service = double
allow(MessageTemplates::Template::Greeting).to receive(:new).and_return(greeting_service)
allow(greeting_service).to receive(:perform).and_return(true)
create(:message, conversation: conversation)
expect(MessageTemplates::Template::Greeting).not_to have_received(:new)
end
it 'does not call ::MessageTemplates::Template::OutOfOffice on campaign conversations' do
contact = create(:contact)
conversation = create(:conversation, contact: contact, campaign: campaign)
conversation.inbox.update(working_hours_enabled: true, out_of_office_message: 'We are out of office')
conversation.inbox.working_hours.today.update!(closed_all_day: true)
out_of_office_service = double
allow(MessageTemplates::Template::OutOfOffice).to receive(:new).and_return(out_of_office_service)
allow(out_of_office_service).to receive(:perform).and_return(true)
create(:message, conversation: conversation)
expect(MessageTemplates::Template::OutOfOffice).not_to have_received(:new)
end
end
context 'when message is an auto reply email' do context 'when message is an auto reply email' do
it 'does not call any template hooks' do it 'does not call any template hooks' do
contact = create(:contact) contact = create(:contact)