chore: enable reply

This commit is contained in:
Muhsin Keloth
2025-04-10 15:39:20 +05:30
parent dc1b563bd3
commit c58abb03b9
9 changed files with 525 additions and 201 deletions
-4
View File
@@ -33,10 +33,6 @@ class Channel::Api < ApplicationRecord
'API'
end
def messaging_window_enabled?
additional_attributes.present? && additional_attributes['agent_reply_time_window'].present?
end
private
def ensure_valid_agent_reply_time_window
-4
View File
@@ -32,10 +32,6 @@ class Channel::FacebookPage < ApplicationRecord
'Facebook'
end
def messaging_window_enabled?
false
end
def create_contact_inbox(instagram_id, name)
@contact_inbox = ::ContactInboxWithContactBuilder.new({
source_id: instagram_id,
-4
View File
@@ -41,10 +41,6 @@ class Channel::TwilioSms < ApplicationRecord
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
end
def messaging_window_enabled?
medium == 'whatsapp'
end
def send_message(to:, body:, media_url: nil)
params = send_message_from.merge(to: to, body: body)
params[:media_url] = media_url if media_url.present?
-4
View File
@@ -46,10 +46,6 @@ class Channel::Whatsapp < ApplicationRecord
end
end
def messaging_window_enabled?
true
end
def mark_message_templates_updated
# rubocop:disable Rails/SkipsModelValidations
update_column(:message_templates_last_updated, Time.zone.now)
-4
View File
@@ -7,10 +7,6 @@ module Channelable
after_update :create_audit_log_entry
end
def messaging_window_enabled?
false
end
def create_audit_log_entry; end
end
-6
View File
@@ -130,12 +130,6 @@ class Conversation < ApplicationRecord
messages&.incoming&.last
end
def last_message_in_messaging_window?(time)
return false if last_incoming_message.nil?
Time.current < last_incoming_message.created_at + time.hours
end
def toggle_status
# FIXME: implement state machine with aasm
self.status = open? ? :resolved : :open
+20 -21
View File
@@ -4,11 +4,7 @@ class Conversations::CanReplyService
end
def can_reply?
channel = @conversation.inbox&.channel
return can_reply_on_instagram_via_messenger? if instagram_via_messenger?
return can_reply_on_instagram? if @conversation.inbox.instagram_direct?
return true unless channel&.messaging_window_enabled?
return true if messaging_window.blank?
last_message_in_messaging_window?(messaging_window)
end
@@ -16,40 +12,43 @@ class Conversations::CanReplyService
private
def messaging_window
@conversation.inbox.api? ? @conversation.inbox.channel.additional_attributes['agent_reply_time_window'].to_i : 24
case @conversation.inbox.channel_type
when 'Channel::Api'
if @conversation.inbox.channel.additional_attributes['agent_reply_time_window'].present?
@conversation.inbox.channel.additional_attributes['agent_reply_time_window'].to_i.hours
end
when 'Channel::FacebookPage'
messenger_messaging_window
when 'Channel::Instagram'
instagram_messaging_window
when 'Channel::Whatsapp'
24.hours
end
end
def last_message_in_messaging_window?(time)
return false if last_incoming_message.nil?
Time.current < last_incoming_message.created_at + time.hours
Time.current < last_incoming_message.created_at + time
end
def instagram_via_messenger?
@conversation.additional_attributes['type'] == 'instagram_direct_message'
end
def can_reply_on_instagram_via_messenger?
def messenger_messaging_window
global_config = GlobalConfig.get('ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT')
return false if last_incoming_message.nil?
if global_config['ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT']
Time.current < last_incoming_message.created_at + 7.days
7.days
else
last_message_in_messaging_window?(24)
24.hours
end
end
def can_reply_on_instagram?
def instagram_messaging_window
global_config = GlobalConfig.get('ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT')
return false if last_incoming_message.nil?
if global_config['ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT']
Time.current < last_incoming_message.created_at + 7.days
7.days
else
last_message_in_messaging_window?(24)
24.hours
end
end
-22
View File
@@ -3,28 +3,6 @@
require 'rails_helper'
RSpec.describe Channel::TwilioSms do
describe '#has_24_hour_messaging_window?' do
context 'with medium whatsapp' do
let!(:whatsapp_channel) { create(:channel_twilio_sms, medium: :whatsapp) }
it 'returns true' do
expect(whatsapp_channel.messaging_window_enabled?).to be true
expect(whatsapp_channel.name).to eq 'Whatsapp'
expect(whatsapp_channel.medium).to eq 'whatsapp'
end
end
context 'with medium sms' do
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms) }
it 'returns false' do
expect(sms_channel.messaging_window_enabled?).to be false
expect(sms_channel.name).to eq 'Twilio SMS'
expect(sms_channel.medium).to eq 'sms'
end
end
end
describe '#validations' do
context 'with phone number blank' do
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms, phone_number: nil) }
@@ -1,147 +1,520 @@
require 'rails_helper'
RSpec.describe Conversations::CanReplyService do
describe '#can_reply?' do
describe 'on channels without 24 hour restriction' do
let(:conversation) { create(:conversation) }
let(:service) { described_class.new(conversation) }
describe 'on API channels' do
let!(:api_channel) { create(:channel_api, additional_attributes: {}) }
let!(:api_channel_with_limit) { create(:channel_api, additional_attributes: { agent_reply_time_window: '12' }) }
it 'returns true' do
context 'when agent_reply_time_window is not configured' do
it 'return true irrespective of the last message time' do
conversation = create(:conversation, inbox: api_channel.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel.inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(api_channel.additional_attributes['agent_reply_time_window']).to be_nil
expect(service.can_reply?).to be true
end
it 'return true for facebook channels' do
stub_request(:post, /graph.facebook.com/)
facebook_channel = create(:channel_facebook_page)
facebook_inbox = create(:inbox, channel: facebook_channel, account: facebook_channel.account)
fb_conversation = create(:conversation, inbox: facebook_inbox, account: facebook_channel.account)
service = described_class.new(fb_conversation)
expect(service.can_reply?).to be true
expect(facebook_channel.messaging_window_enabled?).to be false
end
end
describe 'on channels with 24 hour restriction' do
before do
stub_request(:post, /graph.facebook.com/)
context 'when agent_reply_time_window is configured' do
it 'return false if it is outside of agent_reply_time_window' do
conversation = create(:conversation, inbox: api_channel_with_limit.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel_with_limit.inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(api_channel_with_limit.additional_attributes['agent_reply_time_window']).to eq '12'
expect(service.can_reply?).to be false
end
let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: facebook_channel.account) }
let!(:conversation) { create(:conversation, inbox: facebook_inbox, account: facebook_channel.account) }
it 'return true if it is inside of agent_reply_time_window' do
conversation = create(:conversation, inbox: api_channel_with_limit.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel_with_limit.inbox,
conversation: conversation
)
service = described_class.new(conversation)
let!(:instagram_channel) { create(:channel_instagram) }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: instagram_channel.account) }
let!(:instagram_conversation) { create(:conversation, inbox: instagram_inbox, account: instagram_channel.account) }
context 'when instagram messenger channel' do
it 'return true with HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create(value: true)
conversation.update(additional_attributes: { type: 'instagram_direct_message' })
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 48.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return false without HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create(value: false)
conversation.update(additional_attributes: { type: 'instagram_direct_message' })
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 48.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
end
context 'when instagram channel' do
it 'return true with HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT').first_or_create(value: true)
create(:message, account: instagram_conversation.account, inbox: instagram_inbox, conversation: instagram_conversation,
created_at: 48.hours.ago)
service = described_class.new(instagram_conversation)
expect(service.can_reply?).to be true
end
it 'return false without HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT').first_or_create(value: false)
create(:message, account: instagram_conversation.account, inbox: instagram_inbox, conversation: instagram_conversation,
created_at: 48.hours.ago)
service = described_class.new(instagram_conversation)
expect(service.can_reply?).to be false
end
end
end
describe 'on API channels' do
let!(:api_channel) { create(:channel_api, additional_attributes: {}) }
let!(:api_channel_with_limit) { create(:channel_api, additional_attributes: { agent_reply_time_window: '12' }) }
context 'when agent_reply_time_window is not configured' do
it 'return true irrespective of the last message time' do
conversation = create(:conversation, inbox: api_channel.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel.inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(api_channel.additional_attributes['agent_reply_time_window']).to be_nil
expect(service.can_reply?).to be true
end
end
context 'when agent_reply_time_window is configured' do
it 'return false if it is outside of agent_reply_time_window' do
conversation = create(:conversation, inbox: api_channel_with_limit.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel_with_limit.inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(api_channel_with_limit.additional_attributes['agent_reply_time_window']).to eq '12'
expect(service.can_reply?).to be false
end
it 'return true if it is inside of agent_reply_time_window' do
conversation = create(:conversation, inbox: api_channel_with_limit.inbox)
create(
:message,
account: conversation.account,
inbox: api_channel_with_limit.inbox,
conversation: conversation
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
expect(service.can_reply?).to be true
end
end
end
describe 'on Facebook channels' do
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: facebook_channel.account) }
let!(:conversation) { create(:conversation, inbox: facebook_inbox, account: facebook_channel.account) }
context 'when the HUMAN_AGENT is enabled' do
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create(value: true)
it 'return false if the last message is outgoing' do
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if the last message is incoming and within the messaging window (with in 24 hours)' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return true if the last message is incoming and within the messaging window (with in 7 days)' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 6.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return false if the last message is incoming and outside the messaging window (8 days ago )' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 8.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if last message is outgoing but previous incoming message is within window' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
message_type: :incoming,
created_at: 6.hours.ago
)
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
message_type: :outgoing,
created_at: 1.hour.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'considers only the last incoming message for determining time window' do
# Old message outside window
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 10.days.ago
)
# Recent message within window
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 6.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return true if the last message is incoming and exactly at the edge of 24-hour window with HUMAN_AGENT disabled' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 24.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
context 'when the HUMAN_AGENT is disabled' do
before do
config = InstallationConfig.find_or_create_by(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT')
config.update(value: false)
end
it 'return false if the last message is outgoing' do
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return false if the last message is incoming and outside the messaging window ( 8 days ago )' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 4.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if the last message is incoming and within the messaging window (24 hours limit)' do
create(
:message,
account: conversation.account,
inbox: facebook_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
end
describe 'on WhatsApp channels' do
let!(:whatsapp_channel) { create(:channel_whatsapp, provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false) }
let!(:whatsapp_inbox) { create(:inbox, channel: whatsapp_channel, account: whatsapp_channel.account) }
let!(:conversation) { create(:conversation, inbox: whatsapp_inbox, account: whatsapp_channel.account) }
it 'return false if the last message is outgoing' do
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if the last message is incoming and within the messaging window (with in 24 hours)' do
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return false if the last message is incoming and outside the messaging window (24 hours limit)' do
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
created_at: 25.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if last message is outgoing but previous incoming message is within window' do
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
message_type: :incoming,
created_at: 6.hours.ago
)
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
message_type: :outgoing,
created_at: 1.hour.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'considers only the last incoming message for determining time window' do
# Old message outside window
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
created_at: 10.days.ago
)
# Recent message within window
create(
:message,
account: conversation.account,
inbox: whatsapp_inbox,
conversation: conversation,
created_at: 6.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
describe 'on Instagram channels' do
let!(:instagram_channel) { create(:channel_instagram) }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: instagram_channel.account) }
let!(:conversation) { create(:conversation, inbox: instagram_inbox, account: instagram_channel.account) }
context 'when the HUMAN_AGENT is enabled' do
InstallationConfig.where(name: 'ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT').first_or_create(value: true)
it 'return false if the last message is outgoing' do
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if the last message is incoming and within the messaging window (with in 24 hours)' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return true if the last message is incoming and within the messaging window (with in 7 days)' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 6.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return false if the last message is incoming and outside the messaging window (8 days ago)' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 8.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if last message is outgoing but previous incoming message is within window' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
message_type: :incoming,
created_at: 6.hours.ago
)
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
message_type: :outgoing,
created_at: 1.hour.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'considers only the last incoming message for determining time window' do
# Old message outside window
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 10.days.ago
)
# Recent message within window
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 6.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
it 'return true if the last message is incoming and exactly at the edge of 24-hour window with HUMAN_AGENT disabled' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 24.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
context 'when the HUMAN_AGENT is disabled' do
before do
config = InstallationConfig.find_or_create_by(name: 'ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT')
config.update(value: false)
end
it 'return false if the last message is outgoing' do
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return false if the last message is incoming and outside the messaging window (8 days ago)' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 4.days.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be false
end
it 'return true if the last message is incoming and within the messaging window (24 hours limit)' do
create(
:message,
account: conversation.account,
inbox: instagram_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
end
describe 'on Web widget channels' do
let!(:widget_channel) { create(:channel_widget) }
let!(:widget_inbox) { create(:inbox, channel: widget_channel, account: widget_channel.account) }
let!(:conversation) { create(:conversation, inbox: widget_inbox, account: widget_channel.account) }
it 'return true irrespective of the last message time' do
create(
:message,
account: conversation.account,
inbox: widget_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
describe 'on SMS channels' do
let!(:sms_channel) { create(:channel_sms) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: sms_channel.account) }
let!(:conversation) { create(:conversation, inbox: sms_inbox, account: sms_channel.account) }
it 'return true irrespective of the last message time' do
create(
:message,
account: conversation.account,
inbox: sms_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
describe 'on Telegram channels' do
let!(:telegram_channel) { create(:channel_telegram) }
let!(:telegram_inbox) { create(:inbox, channel: telegram_channel, account: telegram_channel.account) }
let!(:conversation) { create(:conversation, inbox: telegram_inbox, account: telegram_channel.account) }
it 'return true irrespective of the last message time' do
create(
:message,
account: conversation.account,
inbox: telegram_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
describe 'on Email channels' do
let!(:email_channel) { create(:channel_email) }
let!(:email_inbox) { create(:inbox, channel: email_channel, account: email_channel.account) }
let!(:conversation) { create(:conversation, inbox: email_inbox, account: email_channel.account) }
it 'return true irrespective of the last message time' do
create(
:message,
account: conversation.account,
inbox: email_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
describe 'on Line channels' do
let!(:line_channel) { create(:channel_line) }
let!(:line_inbox) { create(:inbox, channel: line_channel, account: line_channel.account) }
let!(:conversation) { create(:conversation, inbox: line_inbox, account: line_channel.account) }
it 'return true irrespective of the last message time' do
create(
:message,
account: conversation.account,
inbox: line_inbox,
conversation: conversation,
created_at: 13.hours.ago
)
service = described_class.new(conversation)
expect(service.can_reply?).to be true
end
end
end