mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 19:48:08 +00:00
chore: Centralize outgoing message reply restrictions for all the channels (#11279)
This commit is contained in:
@@ -218,8 +218,14 @@ export default {
|
|||||||
if (additionalAttributes) {
|
if (additionalAttributes) {
|
||||||
const {
|
const {
|
||||||
agent_reply_time_window_message: agentReplyTimeWindowMessage,
|
agent_reply_time_window_message: agentReplyTimeWindowMessage,
|
||||||
|
agent_reply_time_window: agentReplyTimeWindow,
|
||||||
} = additionalAttributes;
|
} = additionalAttributes;
|
||||||
return agentReplyTimeWindowMessage;
|
return (
|
||||||
|
agentReplyTimeWindowMessage ||
|
||||||
|
this.$t('CONVERSATION.API_HOURS_WINDOW', {
|
||||||
|
hours: agentReplyTimeWindow,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"LOADING_CONVERSATIONS": "Loading Conversations",
|
"LOADING_CONVERSATIONS": "Loading Conversations",
|
||||||
"CANNOT_REPLY": "You cannot reply due to",
|
"CANNOT_REPLY": "You cannot reply due to",
|
||||||
"24_HOURS_WINDOW": "24 hour message window restriction",
|
"24_HOURS_WINDOW": "24 hour message window restriction",
|
||||||
|
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
|
||||||
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
|
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
|
||||||
"ASSIGN_TO_ME": "Assign to me",
|
"ASSIGN_TO_ME": "Assign to me",
|
||||||
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
|
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
|
||||||
|
|||||||
@@ -33,10 +33,6 @@ class Channel::Api < ApplicationRecord
|
|||||||
'API'
|
'API'
|
||||||
end
|
end
|
||||||
|
|
||||||
def messaging_window_enabled?
|
|
||||||
additional_attributes.present? && additional_attributes['agent_reply_time_window'].present?
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def ensure_valid_agent_reply_time_window
|
def ensure_valid_agent_reply_time_window
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ class Channel::FacebookPage < ApplicationRecord
|
|||||||
'Facebook'
|
'Facebook'
|
||||||
end
|
end
|
||||||
|
|
||||||
def messaging_window_enabled?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_contact_inbox(instagram_id, name)
|
def create_contact_inbox(instagram_id, name)
|
||||||
@contact_inbox = ::ContactInboxWithContactBuilder.new({
|
@contact_inbox = ::ContactInboxWithContactBuilder.new({
|
||||||
source_id: instagram_id,
|
source_id: instagram_id,
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ class Channel::TwilioSms < ApplicationRecord
|
|||||||
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
|
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
|
||||||
end
|
end
|
||||||
|
|
||||||
def messaging_window_enabled?
|
|
||||||
medium == 'whatsapp'
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_message(to:, body:, media_url: nil)
|
def send_message(to:, body:, media_url: nil)
|
||||||
params = send_message_from.merge(to: to, body: body)
|
params = send_message_from.merge(to: to, body: body)
|
||||||
params[:media_url] = media_url if media_url.present?
|
params[:media_url] = media_url if media_url.present?
|
||||||
|
|||||||
@@ -46,10 +46,6 @@ class Channel::Whatsapp < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def messaging_window_enabled?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def mark_message_templates_updated
|
def mark_message_templates_updated
|
||||||
# rubocop:disable Rails/SkipsModelValidations
|
# rubocop:disable Rails/SkipsModelValidations
|
||||||
update_column(:message_templates_last_updated, Time.zone.now)
|
update_column(:message_templates_last_updated, Time.zone.now)
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ module Channelable
|
|||||||
after_update :create_audit_log_entry
|
after_update :create_audit_log_entry
|
||||||
end
|
end
|
||||||
|
|
||||||
def messaging_window_enabled?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_audit_log_entry; end
|
def create_audit_log_entry; end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -115,14 +115,7 @@ class Conversation < ApplicationRecord
|
|||||||
delegate :auto_resolve_duration, to: :account
|
delegate :auto_resolve_duration, to: :account
|
||||||
|
|
||||||
def can_reply?
|
def can_reply?
|
||||||
channel = inbox&.channel
|
Conversations::MessageWindowService.new(self).can_reply?
|
||||||
|
|
||||||
return can_reply_on_instagram? if additional_attributes['type'] == 'instagram_direct_message'
|
|
||||||
|
|
||||||
return true unless channel&.messaging_window_enabled?
|
|
||||||
|
|
||||||
messaging_window = inbox.api? ? channel.additional_attributes['agent_reply_time_window'].to_i : 24
|
|
||||||
last_message_in_messaging_window?(messaging_window)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def language
|
def language
|
||||||
@@ -137,24 +130,6 @@ class Conversation < ApplicationRecord
|
|||||||
messages&.incoming&.last
|
messages&.incoming&.last
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
def can_reply_on_instagram?
|
|
||||||
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
|
|
||||||
else
|
|
||||||
last_message_in_messaging_window?(24)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def toggle_status
|
def toggle_status
|
||||||
# FIXME: implement state machine with aasm
|
# FIXME: implement state machine with aasm
|
||||||
self.status = open? ? :resolved : :open
|
self.status = open? ? :resolved : :open
|
||||||
|
|||||||
64
app/services/conversations/message_window_service.rb
Normal file
64
app/services/conversations/message_window_service.rb
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
class Conversations::MessageWindowService
|
||||||
|
MESSAGING_WINDOW_24_HOURS = 24.hours
|
||||||
|
MESSAGING_WINDOW_7_DAYS = 7.days
|
||||||
|
|
||||||
|
def initialize(conversation)
|
||||||
|
@conversation = conversation
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_reply?
|
||||||
|
return true if messaging_window.blank?
|
||||||
|
|
||||||
|
last_message_in_messaging_window?(messaging_window)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def messaging_window
|
||||||
|
case @conversation.inbox.channel_type
|
||||||
|
when 'Channel::Api'
|
||||||
|
api_messaging_window
|
||||||
|
when 'Channel::FacebookPage'
|
||||||
|
messenger_messaging_window
|
||||||
|
when 'Channel::Instagram'
|
||||||
|
instagram_messaging_window
|
||||||
|
when 'Channel::Whatsapp'
|
||||||
|
MESSAGING_WINDOW_24_HOURS
|
||||||
|
when 'Channel::TwilioSms'
|
||||||
|
twilio_messaging_window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_message_in_messaging_window?(time)
|
||||||
|
return false if last_incoming_message.nil?
|
||||||
|
|
||||||
|
Time.current < last_incoming_message.created_at + time
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_messaging_window
|
||||||
|
return if @conversation.inbox.channel.additional_attributes['agent_reply_time_window'].blank?
|
||||||
|
|
||||||
|
@conversation.inbox.channel.additional_attributes['agent_reply_time_window'].to_i.hours
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check medium of the inbox to determine the messaging window
|
||||||
|
def twilio_messaging_window
|
||||||
|
@conversation.inbox.channel.medium == 'whatsapp' ? MESSAGING_WINDOW_24_HOURS : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def messenger_messaging_window
|
||||||
|
meta_messaging_window('ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT')
|
||||||
|
end
|
||||||
|
|
||||||
|
def instagram_messaging_window
|
||||||
|
meta_messaging_window('ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT')
|
||||||
|
end
|
||||||
|
|
||||||
|
def meta_messaging_window(config_key)
|
||||||
|
GlobalConfigService.load(config_key, nil) ? MESSAGING_WINDOW_7_DAYS : MESSAGING_WINDOW_24_HOURS
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_incoming_message
|
||||||
|
@last_incoming_message ||= @conversation.messages&.incoming&.last
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,28 +3,6 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Channel::TwilioSms do
|
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
|
describe '#validations' do
|
||||||
context 'with phone number blank' do
|
context 'with phone number blank' do
|
||||||
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms, phone_number: nil) }
|
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms, phone_number: nil) }
|
||||||
|
|||||||
@@ -585,116 +585,6 @@ RSpec.describe Conversation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#can_reply?' do
|
|
||||||
describe 'on channels without 24 hour restriction' do
|
|
||||||
let(:conversation) { create(:conversation) }
|
|
||||||
|
|
||||||
it 'returns true' do
|
|
||||||
expect(conversation.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)
|
|
||||||
|
|
||||||
expect(fb_conversation.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/)
|
|
||||||
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 instagram 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
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(conversation.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
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(conversation.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
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(api_channel.additional_attributes['agent_reply_time_window']).to be_nil
|
|
||||||
expect(conversation.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
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(api_channel_with_limit.additional_attributes['agent_reply_time_window']).to eq '12'
|
|
||||||
expect(conversation.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
|
|
||||||
)
|
|
||||||
expect(conversation.can_reply?).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#delete conversation' do
|
describe '#delete conversation' do
|
||||||
include ActiveJob::TestHelper
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
@@ -918,4 +808,25 @@ RSpec.describe Conversation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#can_reply?' do
|
||||||
|
let(:conversation) { create(:conversation) }
|
||||||
|
let(:message_window_service) { instance_double(Conversations::MessageWindowService) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Conversations::MessageWindowService).to receive(:new).with(conversation).and_return(message_window_service)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'delegates to MessageWindowService' do
|
||||||
|
allow(message_window_service).to receive(:can_reply?).and_return(true)
|
||||||
|
expect(conversation.can_reply?).to be true
|
||||||
|
expect(message_window_service).to have_received(:can_reply?)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false when MessageWindowService returns false' do
|
||||||
|
allow(message_window_service).to receive(:can_reply?).and_return(false)
|
||||||
|
expect(conversation.can_reply?).to be false
|
||||||
|
expect(message_window_service).to have_received(:can_reply?)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
627
spec/services/conversations/message_window_service_spec.rb
Normal file
627
spec/services/conversations/message_window_service_spec.rb
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Conversations::MessageWindowService do
|
||||||
|
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
|
||||||
|
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
|
||||||
|
it 'return false if the last message is outgoing' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' do
|
||||||
|
service = described_class.new(conversation)
|
||||||
|
expect(service.can_reply?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and within the messaging window (with in 24 hours)' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and within the messaging window (with in 7 days)' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' do
|
||||||
|
create(
|
||||||
|
:message,
|
||||||
|
account: conversation.account,
|
||||||
|
inbox: facebook_inbox,
|
||||||
|
conversation: conversation,
|
||||||
|
created_at: 5.days.ago
|
||||||
|
)
|
||||||
|
service = described_class.new(conversation)
|
||||||
|
expect(service.can_reply?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return false if the last message is incoming and outside the messaging window (8 days ago )' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if last message is outgoing but previous incoming message is within window' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'considers only the last incoming message for determining time window' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the HUMAN_AGENT is disabled' do
|
||||||
|
with_modified_env ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT: 'false' do
|
||||||
|
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
|
||||||
|
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
|
||||||
|
it 'return false if the last message is outgoing' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' do
|
||||||
|
service = described_class.new(conversation)
|
||||||
|
expect(service.can_reply?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and within the messaging window (with in 24 hours)' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and within the messaging window (with in 7 days)' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return false if the last message is incoming and outside the messaging window (8 days ago)' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if last message is outgoing but previous incoming message is within window' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'considers only the last incoming message for determining time window' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and exactly at the edge of 24-hour window with HUMAN_AGENT disabled' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'true' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the HUMAN_AGENT is disabled' do
|
||||||
|
it 'return false if the last message is outgoing' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'false' do
|
||||||
|
service = described_class.new(conversation)
|
||||||
|
expect(service.can_reply?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return false if the last message is incoming and outside the messaging window (8 days ago)' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'false' do
|
||||||
|
create(
|
||||||
|
:message,
|
||||||
|
account: conversation.account,
|
||||||
|
inbox: instagram_inbox,
|
||||||
|
conversation: conversation,
|
||||||
|
created_at: 9.days.ago
|
||||||
|
)
|
||||||
|
service = described_class.new(conversation)
|
||||||
|
expect(service.can_reply?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return true if the last message is incoming and within the messaging window (24 hours limit)' do
|
||||||
|
with_modified_env ENABLE_INSTAGRAM_CHANNEL_HUMAN_AGENT: 'false' 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
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'on WhatsApp Cloud 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 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
|
||||||
|
|
||||||
|
describe 'on Twilio SMS channels' do
|
||||||
|
let!(:twilio_sms_channel) { create(:channel_twilio_sms) }
|
||||||
|
let!(:twilio_sms_inbox) { create(:inbox, channel: twilio_sms_channel, account: twilio_sms_channel.account) }
|
||||||
|
let!(:conversation) { create(:conversation, inbox: twilio_sms_inbox, account: twilio_sms_channel.account) }
|
||||||
|
|
||||||
|
it 'return true irrespective of the last message time' do
|
||||||
|
create(
|
||||||
|
:message,
|
||||||
|
account: conversation.account,
|
||||||
|
inbox: twilio_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 WhatsApp Twilio channels' do
|
||||||
|
let!(:whatsapp_channel) { create(:channel_twilio_sms, medium: :whatsapp) }
|
||||||
|
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
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user