diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 428077b11fb..727a6a63c4b 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -79,9 +79,11 @@ class FanOutOnWriteService < BaseService end def notify_mentioned_accounts! - @status.active_mentions.where.not(id: @options[:silenced_account_ids] || []).joins(:account).merge(Account.local).select(:id, :account_id).reorder(nil).find_in_batches do |mentions| + @status.active_mentions.joins(:account).merge(Account.local).select(:id, :account_id).reorder(nil).find_in_batches do |mentions| LocalNotificationWorker.push_bulk(mentions) do |mention| - [mention.account_id, mention.id, 'Mention', 'mention'] + options = { 'silenced' => true } if @options[:silenced_account_ids]&.include?(mention.account_id) + + [mention.account_id, mention.id, 'Mention', 'mention', options].compact end next unless update? diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 8d0986adcb9..0c40e7b3a8b 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -30,12 +30,13 @@ class NotifyService < BaseService annual_report ).freeze - def initialize(notification) + def initialize(notification, **options) @recipient = notification.account @sender = notification.from_account @notification = notification @policy = NotificationPolicy.find_or_initialize_by(account: @recipient) @from_staff = @sender.local? && @sender.user.present? && @sender.user_role&.bypass_block?(@recipient.user_role) + @options = options end private @@ -164,7 +165,7 @@ class NotifyService < BaseService end def blocked_by_limited_accounts_policy? - @policy.drop_limited_accounts? && @sender.silenced? && not_following? + @policy.drop_limited_accounts? && (@options[:silenced] || @sender.silenced?) && not_following? end end @@ -200,13 +201,14 @@ class NotifyService < BaseService end def filtered_by_limited_accounts_policy? - @policy.filter_limited_accounts? && @sender.silenced? && not_following? + @policy.filter_limited_accounts? && (@options[:silenced] || @sender.silenced?) && not_following? end end - def call(recipient, type, activity) + def call(recipient, type, activity, **options) return if recipient.user.nil? + @options = options @recipient = recipient @activity = activity @notification = Notification.new(account: @recipient, type: type, activity: @activity) @@ -236,11 +238,11 @@ class NotifyService < BaseService private def drop? - DropCondition.new(@notification).drop? + DropCondition.new(@notification, silenced: @options[:silenced]).drop? end def filter? - FilterCondition.new(@notification).filter? + FilterCondition.new(@notification, silenced: @options[:silenced]).filter? end def update_notification_request! diff --git a/app/workers/local_notification_worker.rb b/app/workers/local_notification_worker.rb index e76572984df..6092aeb962c 100644 --- a/app/workers/local_notification_worker.rb +++ b/app/workers/local_notification_worker.rb @@ -3,7 +3,7 @@ class LocalNotificationWorker include Sidekiq::Worker - def perform(receiver_account_id, activity_id = nil, activity_class_name = nil, type = nil) + def perform(receiver_account_id, activity_id = nil, activity_class_name = nil, type = nil, options = {}) if activity_id.nil? && activity_class_name.nil? activity = Mention.find(receiver_account_id) receiver = activity.account @@ -23,7 +23,7 @@ class LocalNotificationWorker return end - NotifyService.new.call(receiver, type || activity_class_name.underscore, activity) + NotifyService.new.call(receiver, type || activity_class_name.underscore, activity, **options.symbolize_keys) rescue ActiveRecord::RecordNotFound true end diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index 9f488995606..79ecd06c8d9 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -45,7 +45,9 @@ RSpec.describe FanOutOnWriteService do let(:visibility) { 'public' } it 'adds status to home feed of author and followers and broadcasts', :inline_jobs do - subject.call(status) + expect { subject.call(status) } + .to change(bob.notifications, :count).by(1) + .and change(eve.notifications, :count).by(1) expect(status.id) .to be_in(home_feed_of(alice)) @@ -58,6 +60,14 @@ RSpec.describe FanOutOnWriteService do expect(redis).to have_received(:publish).with('timeline:public:local', anything) expect(redis).to have_received(:publish).with('timeline:public:media', anything) end + + context 'with silenced_account_ids' do + it 'calls LocalNotificationWorker with the expected arguments' do + expect { subject.call(status, silenced_account_ids: [eve.id]) } + .to enqueue_sidekiq_job(LocalNotificationWorker).with(bob.id, anything, 'Mention', 'mention') + .and enqueue_sidekiq_job(LocalNotificationWorker).with(eve.id, anything, 'Mention', 'mention', { 'silenced' => true }) + end + end end context 'when status is limited' do diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index 9d9d4eed3d4..9927fa9f049 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -224,13 +224,25 @@ RSpec.describe NotifyService do end end + context 'when sender is considered silenced through `silenced` option and recipient has a policy to ignore silenced accounts' do + subject { described_class.new(notification, silenced: true) } + + before do + notification.account.create_notification_policy!(for_limited_accounts: :drop) + end + + it 'returns true' do + expect(subject.drop?).to be true + end + end + context 'when sender is new and recipient has a default policy' do it 'returns false' do expect(subject.drop?).to be false end end - context 'when sender is new and recipient has a policy to ignore silenced accounts' do + context 'when sender is new and recipient has a policy to ignore new accounts' do before do notification.account.create_notification_policy!(for_new_accounts: :drop) end @@ -240,7 +252,7 @@ RSpec.describe NotifyService do end end - context 'when sender is new and followed and recipient has a policy to ignore silenced accounts' do + context 'when sender is new and followed and recipient has a policy to ignore new accounts' do before do notification.account.create_notification_policy!(for_new_accounts: :drop) notification.account.follow!(notification.from_account) @@ -300,6 +312,34 @@ RSpec.describe NotifyService do end end + context 'when sender is considered silenced through the `silenced` option' do + subject { described_class.new(notification, silenced: true) } + + it 'returns true' do + expect(subject.filter?).to be true + end + + context 'when recipient follows sender' do + before do + notification.account.follow!(notification.from_account) + end + + it 'returns false' do + expect(subject.filter?).to be false + end + end + + context 'when recipient is allowing limited accounts' do + before do + notification.account.create_notification_policy!(for_limited_accounts: :accept) + end + + it 'returns false' do + expect(subject.filter?).to be false + end + end + end + context 'when recipient is filtering not-followed senders' do before do Fabricate(:notification_policy, account: notification.account, filter_not_following: true)