diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 19b0f05c556..c52dade699f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1942,6 +1942,7 @@ en: min_first_post_typing_time: "Minimum amount of time in milliseconds a user must type during first post, if threshold is not met post will automatically enter the needs approval queue. Set to 0 to disable (not recommended)" auto_silence_fast_typers_on_first_post: "Automatically silence users that do not meet min_first_post_typing_time" auto_silence_fast_typers_max_trust_level: "Maximum trust level to auto silence fast typers" + auto_silence_first_post_regex: "Case insensitive regex that if passed will cause first post by user to be silenced and sent to approval queue. Example: raging|a[bc]a , will cause all posts containing raging or aba or aca to be silenced on first. Only applies to first post." reviewable_claiming: "Does reviewable content need to be claimed before it can be acted upon?" reviewable_default_topics: "Show reviewable content grouped by topic by default" reviewable_default_visibility: "Don't show reviewable items unless they meet this priority" @@ -4903,6 +4904,7 @@ en: trust_level: "Users at low trust levels must have replies approved by staff. See `approve_unless_trust_level`." new_topics_unless_trust_level: "Users at low trust levels must have topics approved by staff. See `approve_new_topics_unless_trust_level`." fast_typer: "New user typed their first post suspiciously fast, suspected bot or spammer behavior. See `min_first_post_typing_time`." + auto_silence_regexp: "New user whose first post matches the `auto_silence_first_post_regex` setting." watched_word: "This post included a Watched Word. See your list of watched words." staged: "New topics and posts for staged users must be approved by staff. See `approve_unless_staged`." category: "Posts in this category require manual approval by staff. See the category settings." diff --git a/config/site_settings.yml b/config/site_settings.yml index 46705b46d93..faba7b8a3f5 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1660,6 +1660,7 @@ spam: min_first_post_typing_time: 3000 auto_silence_fast_typers_on_first_post: true auto_silence_fast_typers_max_trust_level: 0 + auto_silence_first_post_regex: "" high_trust_flaggers_auto_hide_posts: true cooldown_hours_until_reflag: default: 24 diff --git a/db/migrate/20210204190020_move_auto_silence_first_post_regex_to_watched_words.rb b/db/migrate/20210204190020_move_auto_silence_first_post_regex_to_watched_words.rb deleted file mode 100644 index ead20c60058..00000000000 --- a/db/migrate/20210204190020_move_auto_silence_first_post_regex_to_watched_words.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class MoveAutoSilenceFirstPostRegexToWatchedWords < ActiveRecord::Migration[6.0] - def up - execute <<~SQL - INSERT INTO watched_words (word, action, created_at, updated_at) - SELECT value, 3, created_at, updated_at - FROM site_settings - WHERE name = 'auto_silence_first_post_regex' - ON CONFLICT DO NOTHING - SQL - - execute <<~SQL - INSERT INTO watched_words (word, action, created_at, updated_at) - SELECT unnest(string_to_array(value, '|')), 3, created_at, updated_at - FROM site_settings - WHERE name = 'auto_silence_first_post_regex' - ON CONFLICT DO NOTHING - SQL - end - - def down - end -end diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb index cf0bf3e2728..9b72719232b 100644 --- a/lib/new_post_manager.rb +++ b/lib/new_post_manager.rb @@ -54,6 +54,25 @@ class NewPostManager manager.user.trust_level <= SiteSetting.auto_silence_fast_typers_max_trust_level end + def self.matches_auto_silence_regex?(manager) + args = manager.args + + pattern = SiteSetting.auto_silence_first_post_regex + + return false unless pattern.present? + return false unless is_first_post?(manager) + + begin + regex = Regexp.new(pattern, Regexp::IGNORECASE) + rescue => e + Rails.logger.warn "Invalid regex in auto_silence_first_post_regex #{e}" + return false + end + + "#{args[:title]} #{args[:raw]}" =~ regex + + end + def self.exempt_user?(user) user.staff? end @@ -83,6 +102,8 @@ class NewPostManager return :fast_typer if is_fast_typer?(manager) + return :auto_silence_regex if matches_auto_silence_regex?(manager) + return :staged if SiteSetting.approve_unless_staged? && user.staged? return :category if post_needs_approval_in_its_category?(manager) @@ -147,6 +168,8 @@ class NewPostManager I18n.with_locale(SiteSetting.default_locale) do if is_fast_typer?(manager) UserSilencer.silence(manager.user, Discourse.system_user, keep_posts: true, reason: I18n.t("user.new_user_typed_too_fast")) + elsif matches_auto_silence_regex?(manager) + UserSilencer.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(manager.user, Discourse.system_user, keep_posts: true, reason: I18n.t("user.email_in_spam_header")) end diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb index 92d35280518..1d22435ac21 100644 --- a/spec/requests/posts_controller_spec.rb +++ b/spec/requests/posts_controller_spec.rb @@ -857,6 +857,26 @@ describe PostsController do end end + it 'silences correctly based on auto_silence_first_post_regex' do + SiteSetting.auto_silence_first_post_regex = "I love candy|i eat s[1-5]" + + post "/posts.json", params: { + raw: 'this is the test content', + title: 'when I eat s3 sometimes when not looking' + } + + expect(response.status).to eq(200) + parsed = response.parsed_body + + expect(parsed["action"]).to eq("enqueued") + reviewable = ReviewableQueuedPost.find_by(created_by: user) + score = reviewable.reviewable_scores.first + expect(score.reason).to eq('auto_silence_regex') + + user.reload + expect(user).to be_silenced + end + it "can send a message to a group" do group = Group.create(name: 'test_group', messageable_level: Group::ALIAS_LEVELS[:nobody]) user1 = user