feat: Add agent_reply_time_window in API channels (#4857)
This commit is contained in:
parent
f0db8545cb
commit
1bb0371c1d
10 changed files with 116 additions and 36 deletions
|
@ -80,7 +80,7 @@
|
||||||
@click="toggleMessageSignature"
|
@click="toggleMessageSignature"
|
||||||
/>
|
/>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showWhatsappTemplatesButton"
|
v-if="hasWhatsappTemplates"
|
||||||
v-tooltip.top-end="'Whatsapp Templates'"
|
v-tooltip.top-end="'Whatsapp Templates'"
|
||||||
icon="whatsapp"
|
icon="whatsapp"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
|
@ -275,9 +275,6 @@ export default {
|
||||||
showMessageSignatureButton() {
|
showMessageSignatureButton() {
|
||||||
return !this.isPrivate && this.isAnEmailChannel;
|
return !this.isPrivate && this.isAnEmailChannel;
|
||||||
},
|
},
|
||||||
showWhatsappTemplatesButton() {
|
|
||||||
return !this.isOnPrivateNote && this.hasWhatsappTemplates;
|
|
||||||
},
|
|
||||||
sendWithSignature() {
|
sendWithSignature() {
|
||||||
const { send_with_signature: isEnabled } = this.uiSettings;
|
const { send_with_signature: isEnabled } = this.uiSettings;
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="view-box fill-height">
|
<div class="view-box fill-height">
|
||||||
<banner
|
<banner
|
||||||
v-if="!currentChat.can_reply && !isAWhatsappChannel"
|
v-if="!currentChat.can_reply"
|
||||||
color-scheme="alert"
|
color-scheme="alert"
|
||||||
:banner-message="$t('CONVERSATION.CANNOT_REPLY')"
|
:banner-message="replyWindowBannerMessage"
|
||||||
:href-link="facebookReplyPolicy"
|
:href-link="replyWindowLink"
|
||||||
:href-link-text="$t('CONVERSATION.24_HOURS_WINDOW')"
|
:href-link-text="replyWindowLinkText"
|
||||||
/>
|
|
||||||
|
|
||||||
<banner
|
|
||||||
v-if="!currentChat.can_reply && isAWhatsappChannel"
|
|
||||||
color-scheme="alert"
|
|
||||||
:banner-message="$t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY')"
|
|
||||||
:href-link="twilioWhatsAppReplyPolicy"
|
|
||||||
:href-link-text="$t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW')"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<banner
|
<banner
|
||||||
|
@ -159,7 +151,6 @@ export default {
|
||||||
hasSelectedTweetId() {
|
hasSelectedTweetId() {
|
||||||
return !!this.selectedTweetId;
|
return !!this.selectedTweetId;
|
||||||
},
|
},
|
||||||
|
|
||||||
tweetBannerText() {
|
tweetBannerText() {
|
||||||
return !this.selectedTweetId
|
return !this.selectedTweetId
|
||||||
? this.$t('CONVERSATION.SELECT_A_TWEET_TO_REPLY')
|
? this.$t('CONVERSATION.SELECT_A_TWEET_TO_REPLY')
|
||||||
|
@ -237,12 +228,6 @@ export default {
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
facebookReplyPolicy() {
|
|
||||||
return REPLY_POLICY.FACEBOOK;
|
|
||||||
},
|
|
||||||
twilioWhatsAppReplyPolicy() {
|
|
||||||
return REPLY_POLICY.TWILIO_WHATSAPP;
|
|
||||||
},
|
|
||||||
isRightOrLeftIcon() {
|
isRightOrLeftIcon() {
|
||||||
if (this.isContactPanelOpen) {
|
if (this.isContactPanelOpen) {
|
||||||
return 'arrow-chevron-right';
|
return 'arrow-chevron-right';
|
||||||
|
@ -254,6 +239,41 @@ export default {
|
||||||
const { contact_last_seen_at: contactLastSeenAt } = this.currentChat;
|
const { contact_last_seen_at: contactLastSeenAt } = this.currentChat;
|
||||||
return contactLastSeenAt;
|
return contactLastSeenAt;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
replyWindowBannerMessage() {
|
||||||
|
if (this.isAWhatsappChannel) {
|
||||||
|
return this.$t('CONVERSATION.TWILIO_WHATSAPP_CAN_REPLY');
|
||||||
|
}
|
||||||
|
if (this.isAPIInbox) {
|
||||||
|
const { additional_attributes: additionalAttributes = {} } = this.inbox;
|
||||||
|
if (additionalAttributes) {
|
||||||
|
const {
|
||||||
|
agent_reply_time_window_message: agentReplyTimeWindowMessage,
|
||||||
|
} = additionalAttributes;
|
||||||
|
return agentReplyTimeWindowMessage;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return this.$t('CONVERSATION.CANNOT_REPLY');
|
||||||
|
},
|
||||||
|
replyWindowLink() {
|
||||||
|
if (this.isAWhatsappChannel) {
|
||||||
|
return REPLY_POLICY.FACEBOOK;
|
||||||
|
}
|
||||||
|
if (!this.isAPIInbox) {
|
||||||
|
return REPLY_POLICY.TWILIO_WHATSAPP;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
replyWindowLinkText() {
|
||||||
|
if (this.isAWhatsappChannel) {
|
||||||
|
return this.$t('CONVERSATION.24_HOURS_WINDOW');
|
||||||
|
}
|
||||||
|
if (!this.isAPIInbox) {
|
||||||
|
return this.$t('CONVERSATION.TWILIO_WHATSAPP_24_HOURS_WINDOW');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -26,8 +26,22 @@ class Channel::Api < ApplicationRecord
|
||||||
|
|
||||||
has_secure_token :identifier
|
has_secure_token :identifier
|
||||||
has_secure_token :hmac_token
|
has_secure_token :hmac_token
|
||||||
|
validate :ensure_valid_agent_reply_time_window
|
||||||
|
|
||||||
def name
|
def name
|
||||||
'API'
|
'API'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def messaging_window_enabled?
|
||||||
|
additional_attributes.present? && additional_attributes['agent_reply_time_window'].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_valid_agent_reply_time_window
|
||||||
|
return if additional_attributes['agent_reply_time_window'].blank?
|
||||||
|
return if additional_attributes['agent_reply_time_window'].to_i.positive?
|
||||||
|
|
||||||
|
errors.add(:agent_reply_time_window, 'agent_reply_time_window must be greater than 0')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Channel::FacebookPage < ApplicationRecord
|
||||||
'Facebook'
|
'Facebook'
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_24_hour_messaging_window?
|
def messaging_window_enabled?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Channel::TwilioSms < ApplicationRecord
|
||||||
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
|
medium == 'sms' ? 'Twilio SMS' : 'Whatsapp'
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_24_hour_messaging_window?
|
def messaging_window_enabled?
|
||||||
medium == 'whatsapp'
|
medium == 'whatsapp'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@ class Channel::Whatsapp < ApplicationRecord
|
||||||
{ 'D360-API-KEY' => provider_config['api_key'], 'Content-Type' => 'application/json' }
|
{ 'D360-API-KEY' => provider_config['api_key'], 'Content-Type' => 'application/json' }
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_24_hour_messaging_window?
|
def messaging_window_enabled?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Channelable
|
||||||
has_one :inbox, as: :channel, dependent: :destroy_async
|
has_one :inbox, as: :channel, dependent: :destroy_async
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_24_hour_messaging_window?
|
def messaging_window_enabled?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,23 +93,24 @@ class Conversation < ApplicationRecord
|
||||||
delegate :auto_resolve_duration, to: :account
|
delegate :auto_resolve_duration, to: :account
|
||||||
|
|
||||||
def can_reply?
|
def can_reply?
|
||||||
|
channel = inbox&.channel
|
||||||
|
|
||||||
return can_reply_on_instagram? if additional_attributes['type'] == 'instagram_direct_message'
|
return can_reply_on_instagram? if additional_attributes['type'] == 'instagram_direct_message'
|
||||||
|
|
||||||
return true unless inbox&.channel&.has_24_hour_messaging_window?
|
return true unless channel&.messaging_window_enabled?
|
||||||
|
|
||||||
return false if last_incoming_message.nil?
|
messaging_window = inbox.api? ? channel.additional_attributes['agent_reply_time_window'].to_i : 24
|
||||||
|
last_message_in_messaging_window?(messaging_window)
|
||||||
last_message_less_than_24_hrs?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_incoming_message
|
def last_incoming_message
|
||||||
messages&.incoming&.last
|
messages&.incoming&.last
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_message_less_than_24_hrs?
|
def last_message_in_messaging_window?(time)
|
||||||
return false if last_incoming_message.nil?
|
return false if last_incoming_message.nil?
|
||||||
|
|
||||||
Time.current < last_incoming_message.created_at + 24.hours
|
Time.current < last_incoming_message.created_at + time.hours
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_reply_on_instagram?
|
def can_reply_on_instagram?
|
||||||
|
@ -120,7 +121,7 @@ class Conversation < ApplicationRecord
|
||||||
if global_config['ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT']
|
if global_config['ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT']
|
||||||
Time.current < last_incoming_message.created_at + 7.days
|
Time.current < last_incoming_message.created_at + 7.days
|
||||||
else
|
else
|
||||||
last_message_less_than_24_hrs?
|
last_message_in_messaging_window?(24)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe Channel::TwilioSms do
|
||||||
let!(:whatsapp_channel) { create(:channel_twilio_sms, medium: :whatsapp) }
|
let!(:whatsapp_channel) { create(:channel_twilio_sms, medium: :whatsapp) }
|
||||||
|
|
||||||
it 'returns true' do
|
it 'returns true' do
|
||||||
expect(whatsapp_channel.has_24_hour_messaging_window?).to eq true
|
expect(whatsapp_channel.messaging_window_enabled?).to eq true
|
||||||
expect(whatsapp_channel.name).to eq 'Whatsapp'
|
expect(whatsapp_channel.name).to eq 'Whatsapp'
|
||||||
expect(whatsapp_channel.medium).to eq 'whatsapp'
|
expect(whatsapp_channel.medium).to eq 'whatsapp'
|
||||||
end
|
end
|
||||||
|
@ -17,7 +17,7 @@ RSpec.describe Channel::TwilioSms do
|
||||||
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms) }
|
let!(:sms_channel) { create(:channel_twilio_sms, medium: :sms) }
|
||||||
|
|
||||||
it 'returns false' do
|
it 'returns false' do
|
||||||
expect(sms_channel.has_24_hour_messaging_window?).to eq false
|
expect(sms_channel.messaging_window_enabled?).to eq false
|
||||||
expect(sms_channel.name).to eq 'Twilio SMS'
|
expect(sms_channel.name).to eq 'Twilio SMS'
|
||||||
expect(sms_channel.medium).to eq 'sms'
|
expect(sms_channel.medium).to eq 'sms'
|
||||||
end
|
end
|
||||||
|
|
|
@ -545,6 +545,54 @@ RSpec.describe Conversation, type: :model do
|
||||||
end
|
end
|
||||||
end
|
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: Time.now - 13.hours
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(api_channel.additional_attributes['agent_reply_time_window']).to eq nil
|
||||||
|
expect(conversation.can_reply?).to eq 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: Time.now - 13.hours
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(api_channel_with_limit.additional_attributes['agent_reply_time_window']).to eq '12'
|
||||||
|
expect(conversation.can_reply?).to eq 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 eq true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#delete conversation' do
|
describe '#delete conversation' do
|
||||||
|
|
Loading…
Reference in a new issue