diff --git a/app/models/message.rb b/app/models/message.rb index cea70e835..1cb47fab5 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -2,23 +2,24 @@ # # Table name: messages # -# id :integer not null, primary key -# additional_attributes :jsonb -# content :text -# content_attributes :json -# content_type :integer default("text"), not null -# external_source_ids :jsonb -# message_type :integer not null -# private :boolean default(FALSE) -# sender_type :string -# status :integer default("sent") -# created_at :datetime not null -# updated_at :datetime not null -# account_id :integer not null -# conversation_id :integer not null -# inbox_id :integer not null -# sender_id :bigint -# source_id :string +# id :integer not null, primary key +# additional_attributes :jsonb +# content :text +# content_attributes :json +# content_type :integer default("text"), not null +# external_source_ids :jsonb +# message_type :integer not null +# private :boolean default(FALSE) +# processed_message_content :text +# sender_type :string +# status :integer default("sent") +# created_at :datetime not null +# updated_at :datetime not null +# account_id :integer not null +# conversation_id :integer not null +# inbox_id :integer not null +# sender_id :bigint +# source_id :string # # Indexes # @@ -57,6 +58,7 @@ class Message < ApplicationRecord }.to_json.freeze before_validation :ensure_content_type + before_save :ensure_processed_message_content validates :account_id, presence: true validates :inbox_id, presence: true @@ -214,6 +216,13 @@ class Message < ApplicationRecord private + def ensure_processed_message_content + text_content_quoted = content_attributes.dig(:email, :text_content, :quoted) + html_content_quoted = content_attributes.dig(:email, :html_content, :quoted) + + self.processed_message_content = text_content_quoted || html_content_quoted || content + end + def ensure_content_type self.content_type ||= Message.content_types[:text] end diff --git a/app/services/automation_rules/conditions_filter_service.rb b/app/services/automation_rules/conditions_filter_service.rb index 432e9cfc3..f2f344e16 100644 --- a/app/services/automation_rules/conditions_filter_service.rb +++ b/app/services/automation_rules/conditions_filter_service.rb @@ -27,6 +27,7 @@ class AutomationRules::ConditionsFilterService < FilterService end records = base_relation.where(@query_string, @filter_values.with_indifferent_access) + records = perform_attribute_changed_filter(records) if @attribute_changed_query_filter.any? records.any? @@ -93,6 +94,8 @@ class AutomationRules::ConditionsFilterService < FilterService attribute_key = query_hash['attribute_key'] query_operator = query_hash['query_operator'] + attribute_key = 'processed_message_content' if attribute_key == 'content' + filter_operator_value = filter_operation(query_hash, current_index) case current_filter['attribute_type'] diff --git a/db/migrate/20230608040738_add_processed_message_content_to_messages.rb b/db/migrate/20230608040738_add_processed_message_content_to_messages.rb new file mode 100644 index 000000000..24514d6b2 --- /dev/null +++ b/db/migrate/20230608040738_add_processed_message_content_to_messages.rb @@ -0,0 +1,5 @@ +class AddProcessedMessageContentToMessages < ActiveRecord::Migration[7.0] + def change + add_column :messages, :processed_message_content, :text, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 5875dd977..d3be0e597 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_05_23_104139) do +ActiveRecord::Schema[7.0].define(version: 2023_06_08_040738) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "pg_trgm" @@ -663,6 +663,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_23_104139) do t.bigint "sender_id" t.jsonb "external_source_ids", default: {} t.jsonb "additional_attributes", default: {} + t.text "processed_message_content" t.index "((additional_attributes -> 'campaign_id'::text))", name: "index_messages_on_additional_attributes_campaign_id", using: :gin t.index ["account_id", "inbox_id"], name: "index_messages_on_account_id_and_inbox_id" t.index ["account_id"], name: "index_messages_on_account_id" diff --git a/spec/listeners/automation_rule_listener_old_spec.rb b/spec/listeners/automation_rule_listener_old_spec.rb index 68507ea76..5c02ef90a 100644 --- a/spec/listeners/automation_rule_listener_old_spec.rb +++ b/spec/listeners/automation_rule_listener_old_spec.rb @@ -505,51 +505,6 @@ describe AutomationRuleListener do end end - describe '#message_created' do - before do - automation_rule.update!( - event_name: 'message_created', - name: 'Call actions message created', - description: 'Add labels, assign team after message created', - conditions: [{ 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' }] - ) - end - - let!(:message) { create(:message, account: account, conversation: conversation, message_type: 'incoming') } - let!(:event) do - Events::Base.new('message_created', Time.zone.now, { conversation: conversation, message: message }) - end - - context 'when rule matches' do - it 'triggers automation rule to assign team' do - expect(conversation.team_id).not_to eq(team.id) - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) - listener.message_created(event) - conversation.reload - - expect(conversation.team_id).to eq(team.id) - end - - it 'triggers automation rule to add label' do - expect(conversation.labels).to eq([]) - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) - listener.message_created(event) - conversation.reload - - expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer') - end - - it 'triggers automation rule to assign best agent' do - expect(conversation.assignee).to be_nil - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) - listener.message_created(event) - conversation.reload - - expect(conversation.assignee).to eq(user_1) - end - end - end - describe '#message_created with conversation and contacts based conditions' do before do automation_rule.update!( diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb index 6f87b8342..e0103b19a 100644 --- a/spec/listeners/automation_rule_listener_spec.rb +++ b/spec/listeners/automation_rule_listener_spec.rb @@ -164,6 +164,13 @@ describe AutomationRuleListener do listener.message_created(event) expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation) end + + it 'does not call AutomationRules::ActionService if conditions do not match based on content' do + message.update!(processed_message_content: 'hi', content: "hi\n\nhello") + allow(condition_match).to receive(:present?).and_return(false) + listener.message_created(event) + expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation) + end end end end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index f7dbc9cf8..109946655 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -201,6 +201,7 @@ RSpec.describe Message do message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account)) allow(EmailReplyWorker).to receive(:perform_in).and_return(true) message.message_type = 'outgoing' + message.content_attributes = { email: { text_content: { quoted: 'quoted text' } } } message.save! expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id) end @@ -223,6 +224,15 @@ RSpec.describe Message do end end + context 'when processed_message_content is blank' do + let(:message) { build(:message, content_type: :text, account: create(:account), content: 'Processed message content') } + + it 'sets content_type as text' do + message.save! + expect(message.processed_message_content).to eq message.content + end + end + context 'when attachments size maximum' do let(:message) { build(:message, content_type: nil, account: create(:account)) } diff --git a/spec/services/automation_rules/conditions_filter_service_spec.rb b/spec/services/automation_rules/conditions_filter_service_spec.rb index 163430462..ec9f52062 100644 --- a/spec/services/automation_rules/conditions_filter_service_spec.rb +++ b/spec/services/automation_rules/conditions_filter_service_spec.rb @@ -3,6 +3,11 @@ require 'rails_helper' RSpec.describe AutomationRules::ConditionsFilterService do let(:account) { create(:account) } let(:conversation) { create(:conversation, account: account) } + let(:email_channel) { create(:channel_email, account: account) } + let(:email_inbox) { create(:inbox, channel: email_channel, account: account) } + let(:message) do + create(:message, account: account, conversation: conversation, content: 'test text', inbox: conversation.inbox, message_type: :incoming) + end let(:rule) { create(:automation_rule, account: account) } before do @@ -57,5 +62,54 @@ RSpec.describe AutomationRules::ConditionsFilterService do end end end + + context 'when conditions based on messages attributes' do + context 'when filter_operator is equal_to' do + before do + rule.conditions = [ + { 'values': ['test text'], 'attribute_key': 'content', 'query_operator': 'AND', 'filter_operator': 'equal_to' }, + { 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' } + ] + rule.save + end + + it 'will return true when conditions matches' do + expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(true) + end + + it 'will return false when conditions in rule does not match' do + message.update!(message_type: :outgoing) + expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(false) + end + end + + context 'when filter_operator is on processed_message_content' do + before do + rule.conditions = [ + { 'values': ['help'], 'attribute_key': 'content', 'query_operator': 'AND', 'filter_operator': 'contains' }, + { 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' } + ] + rule.save + end + + let(:conversation) { create(:conversation, account: account, inbox: email_inbox) } + let(:message) do + create(:message, account: account, conversation: conversation, content: "We will help you\n\n\n test", + inbox: conversation.inbox, message_type: :incoming, + content_attributes: { email: { text_content: { quoted: 'We will help you' } } }) + end + + it 'will return true for processed_message_content matches' do + message + expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(true) + end + + it 'will return false when processed_message_content does no match' do + rule.update(conditions: [{ 'values': ['text'], 'attribute_key': 'content', 'query_operator': nil, 'filter_operator': 'contains' }]) + + expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(false) + end + end + end end end