feat: Add agent_reply_time_window in API channels (#4857)

This commit is contained in:
Pranav Raj S 2022-06-14 18:05:37 +05:30 committed by GitHub
parent f0db8545cb
commit 1bb0371c1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 116 additions and 36 deletions

View file

@ -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;

View file

@ -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: {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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