From b711bfd2ca3cd2e82d7ee04aa9e1082fc852e1bd Mon Sep 17 00:00:00 2001 From: Clairton Rodrigo Heinzen Date: Wed, 13 Aug 2025 04:27:14 -0300 Subject: [PATCH] feat: Add automation rule event conversation resolved (#9669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description add automation rule event conversation resolved Captura de Tela 2024-06-22 às 21 25 39 --------- Co-authored-by: Sojan Co-authored-by: Shivam Mishra Co-authored-by: Muhsin Keloth --- .../composables/spec/useAutomation.spec.js | 1 + .../dashboard/constants/automation.js | 2 +- .../dashboard/helper/automationHelper.js | 9 +- .../dashboard/i18n/locale/en/automation.json | 1 + .../settings/automation/constants.js | 104 ++++++++++++++++++ app/listeners/automation_rule_listener.rb | 69 +++++------- .../automation_rule_listener_spec.rb | 36 ++++++ .../automation_rule/create_update_payload.yml | 1 + .../definitions/resource/automation_rule.yml | 2 +- swagger/swagger.json | 1 + swagger/tag_groups/application_swagger.json | 1 + swagger/tag_groups/client_swagger.json | 1 + swagger/tag_groups/other_swagger.json | 1 + swagger/tag_groups/platform_swagger.json | 1 + 14 files changed, 184 insertions(+), 46 deletions(-) diff --git a/app/javascript/dashboard/composables/spec/useAutomation.spec.js b/app/javascript/dashboard/composables/spec/useAutomation.spec.js index 46c0bde50..a66f0c7b4 100644 --- a/app/javascript/dashboard/composables/spec/useAutomation.spec.js +++ b/app/javascript/dashboard/composables/spec/useAutomation.spec.js @@ -196,6 +196,7 @@ describe('useAutomation', () => { automationTypes.conversation_created = { conditions: [] }; automationTypes.conversation_updated = { conditions: [] }; automationTypes.conversation_opened = { conditions: [] }; + automationTypes.conversation_resolved = { conditions: [] }; automationHelper.generateCustomAttributeTypes.mockReturnValue([]); automationHelper.generateCustomAttributes.mockReturnValue([]); diff --git a/app/javascript/dashboard/constants/automation.js b/app/javascript/dashboard/constants/automation.js index 399dd6153..a903075b8 100644 --- a/app/javascript/dashboard/constants/automation.js +++ b/app/javascript/dashboard/constants/automation.js @@ -8,7 +8,7 @@ export const DEFAULT_MESSAGE_CREATED_CONDITION = [ }, ]; -export const DEFAULT_CONVERSATION_OPENED_CONDITION = [ +export const DEFAULT_CONVERSATION_CONDITION = [ { attribute_key: 'browser_language', filter_operator: 'equal_to', diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js index c9852814a..3723fd4d5 100644 --- a/app/javascript/dashboard/helper/automationHelper.js +++ b/app/javascript/dashboard/helper/automationHelper.js @@ -5,7 +5,7 @@ import { } from 'dashboard/routes/dashboard/settings/automation/operators'; import { DEFAULT_MESSAGE_CREATED_CONDITION, - DEFAULT_CONVERSATION_OPENED_CONDITION, + DEFAULT_CONVERSATION_CONDITION, DEFAULT_OTHER_CONDITION, DEFAULT_ACTIONS, } from 'dashboard/constants/automation'; @@ -169,8 +169,11 @@ export const getDefaultConditions = eventName => { if (eventName === 'message_created') { return DEFAULT_MESSAGE_CREATED_CONDITION; } - if (eventName === 'conversation_opened') { - return DEFAULT_CONVERSATION_OPENED_CONDITION; + if ( + eventName === 'conversation_opened' || + eventName === 'conversation_resolved' + ) { + return DEFAULT_CONVERSATION_CONDITION; } return DEFAULT_OTHER_CONDITION; }; diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json index cf63de81c..80274f488 100644 --- a/app/javascript/dashboard/i18n/locale/en/automation.json +++ b/app/javascript/dashboard/i18n/locale/en/automation.json @@ -131,6 +131,7 @@ "CONVERSATION_CREATED": "Conversation Created", "CONVERSATION_UPDATED": "Conversation Updated", "MESSAGE_CREATED": "Message Created", + "CONVERSATION_RESOLVED": "Conversation Resolved", "CONVERSATION_OPENED": "Conversation Opened" }, "ACTIONS": { diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js index dfa6163d8..0a6905039 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js @@ -468,6 +468,106 @@ export const AUTOMATIONS = { }, ], }, + conversation_resolved: { + conditions: [ + { + key: 'browser_language', + name: 'BROWSER_LANGUAGE', + inputType: 'search_select', + filterOperators: OPERATOR_TYPES_1, + }, + { + key: 'email', + name: 'EMAIL', + inputType: 'plain_text', + filterOperators: OPERATOR_TYPES_2, + }, + { + key: 'mail_subject', + name: 'MAIL_SUBJECT', + inputType: 'plain_text', + filterOperators: OPERATOR_TYPES_2, + }, + { + key: 'country_code', + name: 'COUNTRY_NAME', + inputType: 'search_select', + filterOperators: OPERATOR_TYPES_1, + }, + { + key: 'referer', + name: 'REFERER_LINK', + inputType: 'plain_text', + filterOperators: OPERATOR_TYPES_2, + }, + { + key: 'assignee_id', + name: 'ASSIGNEE_NAME', + inputType: 'search_select', + filterOperators: OPERATOR_TYPES_3, + }, + { + key: 'phone_number', + name: 'PHONE_NUMBER', + inputType: 'plain_text', + filterOperators: OPERATOR_TYPES_6, + }, + { + key: 'team_id', + name: 'TEAM_NAME', + inputType: 'search_select', + filterOperators: OPERATOR_TYPES_3, + }, + { + key: 'inbox_id', + name: 'INBOX', + inputType: 'multi_select', + filterOperators: OPERATOR_TYPES_1, + }, + { + key: 'conversation_language', + name: 'CONVERSATION_LANGUAGE', + inputType: 'multi_select', + filterOperators: OPERATOR_TYPES_1, + }, + { + key: 'priority', + name: 'PRIORITY', + inputType: 'multi_select', + filterOperators: OPERATOR_TYPES_1, + }, + ], + actions: [ + { + key: 'assign_agent', + name: 'ASSIGN_AGENT', + }, + { + key: 'assign_team', + name: 'ASSIGN_TEAM', + }, + { + key: 'send_email_to_team', + name: 'SEND_EMAIL_TO_TEAM', + }, + { + key: 'send_message', + name: 'SEND_MESSAGE', + }, + { + key: 'send_email_transcript', + name: 'SEND_EMAIL_TRANSCRIPT', + }, + { + key: 'send_webhook_event', + name: 'SEND_WEBHOOK_EVENT', + }, + { + key: 'send_attachment', + name: 'SEND_ATTACHMENT', + }, + ], + }, }; export const AUTOMATION_RULE_EVENTS = [ @@ -479,6 +579,10 @@ export const AUTOMATION_RULE_EVENTS = [ key: 'conversation_updated', value: 'CONVERSATION_UPDATED', }, + { + key: 'conversation_resolved', + value: 'CONVERSATION_RESOLVED', + }, { key: 'message_created', value: 'MESSAGE_CREATED', diff --git a/app/listeners/automation_rule_listener.rb b/app/listeners/automation_rule_listener.rb index 6974e227a..0515d6952 100644 --- a/app/listeners/automation_rule_listener.rb +++ b/app/listeners/automation_rule_listener.rb @@ -1,53 +1,18 @@ class AutomationRuleListener < BaseListener def conversation_updated(event) - return if performed_by_automation?(event) - - conversation = event.data[:conversation] - account = conversation.account - changed_attributes = event.data[:changed_attributes] - - return unless rule_present?('conversation_updated', account) - - rules = current_account_rules('conversation_updated', account) - - rules.each do |rule| - conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform - AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present? - end + process_conversation_event(event, 'conversation_updated') end def conversation_created(event) - return if performed_by_automation?(event) || ignore_auto_reply_event?(event) - - conversation = event.data[:conversation] - account = conversation.account - changed_attributes = event.data[:changed_attributes] - - return unless rule_present?('conversation_created', account) - - rules = current_account_rules('conversation_created', account) - - rules.each do |rule| - conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform - ::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present? - end + process_conversation_event(event, 'conversation_created') end def conversation_opened(event) - return if performed_by_automation?(event) || ignore_auto_reply_event?(event) + process_conversation_event(event, 'conversation_opened') + end - conversation = event.data[:conversation] - account = conversation.account - changed_attributes = event.data[:changed_attributes] - - return unless rule_present?('conversation_opened', account) - - rules = current_account_rules('conversation_opened', account) - - rules.each do |rule| - conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform - AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present? - end + def conversation_resolved(event) + process_conversation_event(event, 'conversation_resolved') end def message_created(event) @@ -69,6 +34,28 @@ class AutomationRuleListener < BaseListener end end + private + + def process_conversation_event(event, event_name) + return if performed_by_automation?(event) + + auto_reply_skip_events = %w[conversation_created conversation_opened] + return if auto_reply_skip_events.include?(event_name) && ignore_auto_reply_event?(event) + + conversation = event.data[:conversation] + account = conversation.account + changed_attributes = event.data[:changed_attributes] + + return unless rule_present?(event_name, account) + + rules = current_account_rules(event_name, account) + + rules.each do |rule| + conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform + AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present? + end + end + def rule_present?(event_name, account) return if account.blank? diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb index e1c365f94..57a096a10 100644 --- a/spec/listeners/automation_rule_listener_spec.rb +++ b/spec/listeners/automation_rule_listener_spec.rb @@ -130,6 +130,42 @@ describe AutomationRuleListener do end end + describe 'conversation_resolved' do + let!(:automation_rule) { create(:automation_rule, event_name: 'conversation_resolved', account: account) } + let(:event) do + Events::Base.new('conversation_resolved', Time.zone.now, { conversation: conversation, + changed_attributes: { status: %w[Snoozed Open] } }) + end + + context 'when matching rules are present' do + it 'calls AutomationRules::ActionService if conditions match' do + allow(condition_match).to receive(:present?).and_return(true) + listener.conversation_resolved(event) + expect(AutomationRules::ActionService).to have_received(:new).with(automation_rule, account, conversation) + end + + it 'does not call AutomationRules::ActionService if conditions do not match' do + allow(condition_match).to receive(:present?).and_return(false) + listener.conversation_resolved(event) + expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation) + end + + it 'calls AutomationRules::ActionService for each rule when multiple rules are present' do + create(:automation_rule, event_name: 'conversation_resolved', account: account) + allow(condition_match).to receive(:present?).and_return(true) + listener.conversation_resolved(event) + expect(AutomationRules::ActionService).to have_received(:new).twice + end + + it 'does not call AutomationRules::ActionService if performed by automation' do + event.data[:performed_by] = automation_rule + allow(condition_match).to receive(:present?).and_return(true) + listener.conversation_resolved(event) + expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation) + end + end + end + describe 'message_created' do let!(:automation_rule) { create(:automation_rule, event_name: 'message_created', account: account) } let!(:message) { create(:message, account: account, conversation: conversation) } diff --git a/swagger/definitions/request/automation_rule/create_update_payload.yml b/swagger/definitions/request/automation_rule/create_update_payload.yml index 091fa2aaa..75aedd41b 100644 --- a/swagger/definitions/request/automation_rule/create_update_payload.yml +++ b/swagger/definitions/request/automation_rule/create_update_payload.yml @@ -13,6 +13,7 @@ properties: enum: - conversation_created - conversation_updated + - conversation_resolved - message_created example: message_created description: The event when you want to execute the automation actions diff --git a/swagger/definitions/resource/automation_rule.yml b/swagger/definitions/resource/automation_rule.yml index b561441ff..ad96c58ff 100644 --- a/swagger/definitions/resource/automation_rule.yml +++ b/swagger/definitions/resource/automation_rule.yml @@ -10,4 +10,4 @@ properties: - type: object description: Single automation rule (for show/create/update endpoints) allOf: - - $ref: '#/components/schemas/automation_rule_item' \ No newline at end of file + - $ref: '#/components/schemas/automation_rule_item' diff --git a/swagger/swagger.json b/swagger/swagger.json index 8da8e2c5b..aa7455e7f 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -10610,6 +10610,7 @@ "enum": [ "conversation_created", "conversation_updated", + "conversation_resolved", "message_created" ], "example": "message_created", diff --git a/swagger/tag_groups/application_swagger.json b/swagger/tag_groups/application_swagger.json index 23a9ab0b2..a36443f81 100644 --- a/swagger/tag_groups/application_swagger.json +++ b/swagger/tag_groups/application_swagger.json @@ -8971,6 +8971,7 @@ "enum": [ "conversation_created", "conversation_updated", + "conversation_resolved", "message_created" ], "example": "message_created", diff --git a/swagger/tag_groups/client_swagger.json b/swagger/tag_groups/client_swagger.json index 6bbae4fb2..16c1d5bc7 100644 --- a/swagger/tag_groups/client_swagger.json +++ b/swagger/tag_groups/client_swagger.json @@ -3594,6 +3594,7 @@ "enum": [ "conversation_created", "conversation_updated", + "conversation_resolved", "message_created" ], "example": "message_created", diff --git a/swagger/tag_groups/other_swagger.json b/swagger/tag_groups/other_swagger.json index 414a44e63..c8a5294d3 100644 --- a/swagger/tag_groups/other_swagger.json +++ b/swagger/tag_groups/other_swagger.json @@ -3009,6 +3009,7 @@ "enum": [ "conversation_created", "conversation_updated", + "conversation_resolved", "message_created" ], "example": "message_created", diff --git a/swagger/tag_groups/platform_swagger.json b/swagger/tag_groups/platform_swagger.json index 215fcd38b..f816b8c94 100644 --- a/swagger/tag_groups/platform_swagger.json +++ b/swagger/tag_groups/platform_swagger.json @@ -3770,6 +3770,7 @@ "enum": [ "conversation_created", "conversation_updated", + "conversation_resolved", "message_created" ], "example": "message_created",