From 1008f0af7def477a86bc13cb59f8ffd1857ad45f Mon Sep 17 00:00:00 2001 From: Anna Buianova Date: Thu, 21 Mar 2024 20:31:43 +0300 Subject: [PATCH] Automatically resolve spam reports when the offending user is marked as spam (#20781) * Start resolving spam reports automatically after assigning spam role * Specs for resolving spam reports + async resolving --- .../moderator/manage_activity_and_roles.rb | 7 +++ app/services/users/resolve_spam_reports.rb | 35 ++++++++++++ .../users/resolve_spam_reports_worker.rb | 14 +++++ .../manage_activity_and_roles_spec.rb | 23 ++++++++ .../users/resolve_spam_reports_spec.rb | 55 +++++++++++++++++++ .../users/resolve_spam_reports_worker_spec.rb | 19 +++++++ 6 files changed, 153 insertions(+) create mode 100644 app/services/users/resolve_spam_reports.rb create mode 100644 app/workers/users/resolve_spam_reports_worker.rb create mode 100644 spec/services/users/resolve_spam_reports_spec.rb create mode 100644 spec/workers/users/resolve_spam_reports_worker_spec.rb diff --git a/app/services/moderator/manage_activity_and_roles.rb b/app/services/moderator/manage_activity_and_roles.rb index f94142e15..800d4b721 100644 --- a/app/services/moderator/manage_activity_and_roles.rb +++ b/app/services/moderator/manage_activity_and_roles.rb @@ -78,6 +78,7 @@ module Moderator user.add_role(:spam) remove_privileges remove_notifications + resolve_spam_reports when "Super Moderator" assign_elevated_role_to_user(user, :super_moderator) TagModerators::AddTrustedRole.call(user) @@ -158,5 +159,11 @@ module Moderator def update_roles handle_user_status(user_params[:user_status], user_params[:note_for_current_role]) end + + private + + def resolve_spam_reports + Users::ResolveSpamReportsWorker.perform_async(user.id) + end end end diff --git a/app/services/users/resolve_spam_reports.rb b/app/services/users/resolve_spam_reports.rb new file mode 100644 index 000000000..9a522eb9f --- /dev/null +++ b/app/services/users/resolve_spam_reports.rb @@ -0,0 +1,35 @@ +module Users + class ResolveSpamReports + def self.call(...) + new(...).call + end + + def initialize(user) + @user = user + end + + def call + relation = FeedbackMessage.where(status: "Open", category: "spam") + + # profile reports by url and by path + profile_reports = relation.where(reported_url: [URL.url(user.path), user.path]) + profile_reports.update_all(status: "Resolved") + + # articles can be reported by url or by path + article_paths = user.articles.map(&:path) + article_paths += article_paths.map { |p| URL.url(p) } + article_reports = relation.where(reported_url: article_paths) + article_reports.update_all(status: "Resolved") + + # comments can be reported by url or by path + comment_paths = user.comments.map(&:path) + comment_paths += comment_paths.map { |p| URL.url(p) } + comment_reports = relation.where(reported_url: comment_paths) + comment_reports.update_all(status: "Resolved") + end + + private + + attr_reader :user + end +end diff --git a/app/workers/users/resolve_spam_reports_worker.rb b/app/workers/users/resolve_spam_reports_worker.rb new file mode 100644 index 000000000..1711a7339 --- /dev/null +++ b/app/workers/users/resolve_spam_reports_worker.rb @@ -0,0 +1,14 @@ +module Users + class ResolveSpamReportsWorker + include Sidekiq::Job + + sidekiq_options queue: :medium_priority, retry: 10, lock: :until_executing + + def perform(user_id) + user = User.find_by(id: user_id) + return unless user + + Users::ResolveSpamReports.call(user) + end + end +end diff --git a/spec/services/moderator/manage_activity_and_roles_spec.rb b/spec/services/moderator/manage_activity_and_roles_spec.rb index f5ed5c18a..fbb363b03 100644 --- a/spec/services/moderator/manage_activity_and_roles_spec.rb +++ b/spec/services/moderator/manage_activity_and_roles_spec.rb @@ -250,6 +250,29 @@ RSpec.describe Moderator::ManageActivityAndRoles, type: :service do end end + describe "removes reports when adding the spam role" do + let(:spam_user) { create(:user) } + let(:spam_article) { create(:article, user: spam_user) } + let!(:report) do + create(:feedback_message, category: "spam", status: "Open", reported_url: URL.url(spam_article.path)) + end + + it "calls ResolveSpamReports" do + allow(Users::ResolveSpamReports).to receive(:call) + sidekiq_perform_enqueued_jobs do + manage_roles_for(spam_user, user_status: "Spam") + end + expect(Users::ResolveSpamReports).to have_received(:call).with(spam_user) + end + + it "actually removes the report" do + sidekiq_perform_enqueued_jobs do + manage_roles_for(spam_user, user_status: "Spam") + end + expect(report.reload.status).to eq("Resolved") + end + end + describe "removes notifications when adding the spam role" do let(:nice_article) { create(:article, user: user) } let(:spam_user) { create(:user) } diff --git a/spec/services/users/resolve_spam_reports_spec.rb b/spec/services/users/resolve_spam_reports_spec.rb new file mode 100644 index 000000000..3e95148a1 --- /dev/null +++ b/spec/services/users/resolve_spam_reports_spec.rb @@ -0,0 +1,55 @@ +require "rails_helper" + +RSpec.describe Users::ResolveSpamReports, type: :service do + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:article) { create(:article, user: user) } + let(:article2) { create(:article, user: user) } + let(:article3) { create(:article, user: user2) } + let(:comment) { create(:comment, user: user, commentable: article2) } + let(:comment2) { create(:comment, user: user, commentable: article) } + + it "doesn't fail when user has no reports" do + described_class.call(user) + end + + def spam_report(url) + create(:feedback_message, category: "spam", status: "Open", reported_url: url) + end + + context "with reports" do + let!(:rep) { spam_report(user.path) } + let!(:rep2) { spam_report(URL.url(user.path)) } + let!(:a_rep) { spam_report(article.path) } + let!(:a_rep2) { spam_report(URL.url(article2.path)) } + let!(:c_rep) { spam_report(comment.path) } + let!(:c_rep2) { spam_report(URL.url(comment2.path)) } + + it "updates statused of the user's profile, article and comment reports", :aggregate_failures do + described_class.call(user) + expect(rep.reload.status).to eq("Resolved") + expect(rep2.reload.status).to eq("Resolved") + expect(a_rep.reload.status).to eq("Resolved") + expect(a_rep2.reload.status).to eq("Resolved") + expect(c_rep.reload.status).to eq("Resolved") + expect(c_rep2.reload.status).to eq("Resolved") + end + + it "doesn't update non-spam reports" do + other_rep = create(:feedback_message, category: "other", status: "Open", reported_url: user.path) + harassment_rep = create(:feedback_message, category: "harassment", status: "Open", + reported_url: URL.url(article.path)) + described_class.call(user) + expect(other_rep.reload.status).to eq("Open") + expect(harassment_rep.reload.status).to eq("Open") + end + + it "doesn't update other users reports" do + u2_rep = spam_report(URL.url(user2.path)) + a3_rep = spam_report(article3.path) + described_class.call(user) + expect(u2_rep.reload.status).to eq("Open") + expect(a3_rep.reload.status).to eq("Open") + end + end +end diff --git a/spec/workers/users/resolve_spam_reports_worker_spec.rb b/spec/workers/users/resolve_spam_reports_worker_spec.rb new file mode 100644 index 000000000..e3f89cb45 --- /dev/null +++ b/spec/workers/users/resolve_spam_reports_worker_spec.rb @@ -0,0 +1,19 @@ +require "rails_helper" + +RSpec.describe Users::ResolveSpamReportsWorker, type: :worker do + describe "#perform" do + let(:user) { create(:user) } + let(:worker) { subject } + + it "calls spam reports resolver" do + allow(Users::ResolveSpamReports).to receive(:call).with(user) + + worker.perform(user.id) + expect(Users::ResolveSpamReports).to have_received(:call).with(user) + end + + it "doesn't fail with invalid url" do + worker.perform(-1) + end + end +end