From 69dfdf50843e4b68e80291f6fae8117617a38ac1 Mon Sep 17 00:00:00 2001 From: Ted Johansson Date: Wed, 15 Oct 2025 16:07:56 +0800 Subject: [PATCH] FIX: Moderator notifications when new post auto-silences a user (#35403) When a user is auto-silenced because they receive enough spam flag, we notify moderators (given the notify_mods_when_user_silenced is enabled). However, when a user is auto-silenced for other reasons, e.g. typing too fast, we don't send this notification. This commit: - Lifts the notification logic into a new UserSilencer#auto_silence method, which delegates 99% of its work to UserSilencer#silence. - Uses this new method in places where users are auto-silenced. --- app/services/spam_rule/auto_silence.rb | 25 ++++++------------------- app/services/user_silencer.rb | 26 ++++++++++++++++++++++++++ lib/new_post_manager.rb | 6 +++--- spec/lib/new_post_manager_spec.rb | 25 +++++++++++++++++-------- spec/services/auto_silence_spec.rb | 10 ++++++---- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/app/services/spam_rule/auto_silence.rb b/app/services/spam_rule/auto_silence.rb index 76f481b9634..3a4c7dc66bd 100644 --- a/app/services/spam_rule/auto_silence.rb +++ b/app/services/spam_rule/auto_silence.rb @@ -57,24 +57,11 @@ class SpamRule::AutoSilence end def silence_user - Post.transaction do - silencer = - UserSilencer.new( - @user, - Discourse.system_user, - message: :too_many_spam_flags, - post_id: @post&.id, - ) - - if silencer.silence && SiteSetting.notify_mods_when_user_silenced - @group_message = - GroupMessage.create( - Group[:moderators].name, - :user_automatically_silenced, - user: @user, - limit_once_per: false, - ) - end - end + UserSilencer.auto_silence( + @user, + Discourse.system_user, + message: :too_many_spam_flags, + post_id: @post&.id, + ) end end diff --git a/app/services/user_silencer.rb b/app/services/user_silencer.rb index 27adfa83d02..ba23c099c03 100644 --- a/app/services/user_silencer.rb +++ b/app/services/user_silencer.rb @@ -11,6 +11,10 @@ class UserSilencer UserSilencer.new(user, by_user, opts).silence end + def self.auto_silence(user, by_user = nil, opts = {}) + UserSilencer.new(user, by_user, opts).auto_silence + end + def self.unsilence(user, by_user = nil, opts = {}) UserSilencer.new(user, by_user, opts).unsilence end @@ -59,6 +63,15 @@ class UserSilencer end end + def auto_silence + if silence + notify_moderators + true + else + false + end + end + def hide_posts return unless @user.trust_level == TrustLevel[0] @@ -87,4 +100,17 @@ class UserSilencer StaffActionLogger.new(@by_user).log_unsilence_user(@user) if @by_user end end + + private + + def notify_moderators + return if !SiteSetting.notify_mods_when_user_silenced + + GroupMessage.create( + Group[:moderators].name, + :user_automatically_silenced, + user: @user, + limit_once_per: false, + ) + end end diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb index 9772f17d507..9b42fd3914f 100644 --- a/lib/new_post_manager.rb +++ b/lib/new_post_manager.rb @@ -178,21 +178,21 @@ class NewPostManager I18n.with_locale(SiteSetting.default_locale) do if is_fast_typer?(manager) - UserSilencer.silence( + UserSilencer.auto_silence( manager.user, Discourse.system_user, keep_posts: true, reason: I18n.t("user.new_user_typed_too_fast"), ) elsif auto_silence?(manager) || matches_auto_silence_regex?(manager) - UserSilencer.silence( + UserSilencer.auto_silence( manager.user, Discourse.system_user, keep_posts: true, reason: I18n.t("user.content_matches_auto_silence_regex"), ) elsif reason == :email_spam && is_first_post?(manager) - UserSilencer.silence( + UserSilencer.auto_silence( manager.user, Discourse.system_user, keep_posts: true, diff --git a/spec/lib/new_post_manager_spec.rb b/spec/lib/new_post_manager_spec.rb index beed52ee7cd..7604fb32e9b 100644 --- a/spec/lib/new_post_manager_spec.rb +++ b/spec/lib/new_post_manager_spec.rb @@ -258,17 +258,23 @@ RSpec.describe NewPostManager do end context "with a fast typer" do - before { user.update!(trust_level: 0) } + before do + user.update!(trust_level: 0) + SiteSetting.notify_mods_when_user_silenced = true + end it "adds the silence reason in the system locale" do manager = build_manager_with("this is new post content") I18n.with_locale(:fr) do # Simulate french user - result = NewPostManager.default_handler(manager) + expect { NewPostManager.default_handler(manager) }.to change { user.silenced? }.to( + true, + ).and change { user.silence_reason }.to( + I18n.t("user.new_user_typed_too_fast", locale: :en), + ).and change { + Topic.where("subtype = 'system_message' AND title LIKE '%silenced%'").count + }.by(1) end - - expect(user.silenced?).to eq(true) - expect(user.silence_reason).to eq(I18n.t("user.new_user_typed_too_fast", locale: :en)) end it "runs the watched words check before checking if the user is a fast typer" do @@ -692,6 +698,8 @@ RSpec.describe NewPostManager do let(:user) { Fabricate(:user, refresh_auto_groups: true) } let(:admin) { Fabricate(:admin) } + before { SiteSetting.notify_mods_when_user_silenced = true } + it "silences users if its their first post" do manager = NewPostManager.new( @@ -703,9 +711,10 @@ RSpec.describe NewPostManager do first_post_checks: true, ) - result = manager.perform - expect(result.action).to eq(:enqueued) - expect(user.silenced?).to be(true) + expect { @result = manager.perform }.to change { user.silenced? }.to(true).and change { + Topic.where("subtype = 'system_message' AND title LIKE '%silenced%'").count + }.by(1) + expect(@result.action).to eq(:enqueued) end it "doesn't silence or enqueue exempt users" do diff --git a/spec/services/auto_silence_spec.rb b/spec/services/auto_silence_spec.rb index bf029934784..55e4246e583 100644 --- a/spec/services/auto_silence_spec.rb +++ b/spec/services/auto_silence_spec.rb @@ -118,14 +118,16 @@ RSpec.describe SpamRule::AutoSilence do it "sends private message to moderators" do SiteSetting.notify_mods_when_user_silenced = true - autosilence.silence_user - expect(autosilence.group_message).to be_present + expect { autosilence.silence_user }.to change { + Topic.where("subtype = 'system_message' AND title LIKE '%silenced%'").count + }.by(1) end it "doesn't send a pm to moderators if notify_mods_when_user_silenced is false" do SiteSetting.notify_mods_when_user_silenced = false - autosilence.silence_user - expect(autosilence.group_message).to be_blank + expect { autosilence.silence_user }.not_to change { + Topic.where("subtype = 'system_message' AND title LIKE '%silenced%'").count + } end end end