[deploy] Add ability for admin to add anti-spam terms (#10615)
* Add ability for admin to add anti-spam terms * Fix specs * Fix tests * Fix typo * Fix test
This commit is contained in:
parent
c0272357c0
commit
eefbe25bd3
13 changed files with 110 additions and 4 deletions
|
|
@ -56,6 +56,7 @@ module Admin
|
|||
facebook_secret
|
||||
allow_email_password_registration
|
||||
primary_brand_color_hex
|
||||
spam_trigger_terms
|
||||
]
|
||||
|
||||
allowed_params = allowed_params |
|
||||
|
|
|
|||
|
|
@ -222,6 +222,10 @@ module Constants
|
|||
description: "Used as the secondary logo",
|
||||
placeholder: "https://image.url"
|
||||
},
|
||||
spam_trigger_terms: {
|
||||
description: "Individual (case insensitive) phrases that trigger spam alerts, comma separated.",
|
||||
placeholder: "used cares near you, pokemon go hack"
|
||||
},
|
||||
shop_url: {
|
||||
description: "Used as the shop url of the community",
|
||||
placeholder: "https://shop.url"
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ class Article < ApplicationRecord
|
|||
before_save :calculate_base_scores
|
||||
before_save :fetch_video_duration
|
||||
before_save :set_caches
|
||||
before_save :create_conditional_autovomits
|
||||
before_create :create_password
|
||||
before_destroy :before_destroy_actions, prepend: true
|
||||
|
||||
|
|
@ -677,6 +678,17 @@ class Article < ApplicationRecord
|
|||
self.spaminess_rating = 0 if new_record?
|
||||
end
|
||||
|
||||
def create_conditional_autovomits
|
||||
return unless SiteConfig.spam_trigger_terms.any? { |term| title.downcase.include?(term.downcase) }
|
||||
|
||||
Reaction.create(
|
||||
user_id: SiteConfig.mascot_user_id,
|
||||
reactable_id: id,
|
||||
reactable_type: "Article",
|
||||
category: "vomit",
|
||||
)
|
||||
end
|
||||
|
||||
def async_bust
|
||||
Articles::BustCacheWorker.perform_async(id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Comment < ApplicationRecord
|
|||
|
||||
before_validation :evaluate_markdown, if: -> { body_markdown }
|
||||
before_save :set_markdown_character_count, if: :body_markdown
|
||||
before_save :create_conditional_autovomits
|
||||
before_create :adjust_comment_parent_based_on_depth
|
||||
after_create :after_create_checks
|
||||
after_create :notify_slack_channel_about_warned_users
|
||||
|
|
@ -244,6 +245,20 @@ class Comment < ApplicationRecord
|
|||
Comments::SendEmailNotificationWorker.perform_async(id)
|
||||
end
|
||||
|
||||
def create_conditional_autovomits
|
||||
return unless
|
||||
SiteConfig.spam_trigger_terms.any? { |term| body_markdown.downcase.include?(term.downcase) } &&
|
||||
user.registered_at > 5.days.ago
|
||||
|
||||
self.score = -1
|
||||
Reaction.create(
|
||||
user_id: SiteConfig.mascot_user_id,
|
||||
reactable_id: id,
|
||||
reactable_type: "Comment",
|
||||
category: "vomit",
|
||||
)
|
||||
end
|
||||
|
||||
def should_send_email_notification?
|
||||
parent_exists? &&
|
||||
parent_user.class.name != "Podcast" &&
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class Reaction < ApplicationRecord
|
|||
end
|
||||
|
||||
def negative_reaction_from_untrusted_user?
|
||||
return if user&.any_admin?
|
||||
return if user&.any_admin? || user&.id == SiteConfig.mascot_user_id
|
||||
|
||||
negative? && !user.trusted
|
||||
end
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class SiteConfig < RailsSettings::Base
|
|||
field :suggested_tags, type: :array, default: %w[]
|
||||
field :suggested_users, type: :array, default: %w[]
|
||||
|
||||
# Rate limits
|
||||
# Rate limits and spam prevention
|
||||
field :rate_limit_follow_count_daily, type: :integer, default: 500
|
||||
field :rate_limit_comment_creation, type: :integer, default: 9
|
||||
field :rate_limit_listing_creation, type: :integer, default: 1
|
||||
|
|
@ -132,6 +132,8 @@ class SiteConfig < RailsSettings::Base
|
|||
field :rate_limit_user_update, type: :integer, default: 5
|
||||
field :rate_limit_user_subscription_creation, type: :integer, default: 3
|
||||
|
||||
field :spam_trigger_terms, type: :array, default: []
|
||||
|
||||
# Social Media
|
||||
field :social_media_handles, type: :hash, default: {
|
||||
twitter: nil,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ module Notifications
|
|||
end
|
||||
|
||||
def call
|
||||
return if comment.score.negative?
|
||||
|
||||
user_ids = Set.new(comment_user_ids + subscribed_user_ids + top_level_user_ids + author_subscriber_user_ids)
|
||||
|
||||
json_data = {
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@
|
|||
<div class="card mt-3">
|
||||
<%= render partial: "card_header",
|
||||
locals: {
|
||||
header: "Rate limits",
|
||||
header: "Rate limits and anti-spam",
|
||||
state: "collapse",
|
||||
target: "rateLimitsBodyContainer",
|
||||
expanded: "false"
|
||||
|
|
@ -851,6 +851,14 @@
|
|||
<div class="alert alert-info"><%= field_hash[:description] %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="form-group">
|
||||
<%= admin_config_label :spam_trigger_terms %>
|
||||
<%= f.text_field :spam_trigger_terms,
|
||||
class: "form-control",
|
||||
value: SiteConfig.spam_trigger_terms.to_s,
|
||||
placeholder: Constants::SiteConfig::DETAILS[:spam_trigger_terms][:placeholder] %>
|
||||
<div class="alert alert-info"><%= Constants::SiteConfig::DETAILS[:spam_trigger_terms][:description] %></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
<div class="d-flex justify-content-between" data-controller="reaction" data-reaction-id="<%= reaction.id %>">
|
||||
<span>
|
||||
🤢 <a href="<%= reaction.user.path %>">@<%= reaction.user.username %></a>
|
||||
<% if reaction.user_id == SiteConfig.mascot_user_id %>
|
||||
<strong>(auto-generated)</strong>
|
||||
<% end %>
|
||||
</span>
|
||||
<span>
|
||||
<strong><%= reaction.reactable_type %>:</strong>
|
||||
|
|
|
|||
|
|
@ -776,6 +776,25 @@ RSpec.describe Article, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "spam" do
|
||||
before do
|
||||
allow(SiteConfig).to receive(:mascot_user_id).and_return(user.id)
|
||||
allow(SiteConfig).to receive(:spam_trigger_terms).and_return(["yahoomagoo gogo", "testtestetest"])
|
||||
end
|
||||
|
||||
it "creates vomit reaction if possible spam" do
|
||||
article.body_markdown = article.body_markdown.gsub(article.title, "This post is about Yahoomagoo gogo")
|
||||
article.save
|
||||
expect(Reaction.last.category).to eq("vomit")
|
||||
expect(Reaction.last.user_id).to eq(user.id)
|
||||
end
|
||||
|
||||
it "does not create vomit reaction if does not have matching title" do
|
||||
article.save
|
||||
expect(Reaction.last).to be nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "async score calc" do
|
||||
it "enqueues Articles::ScoreCalcWorker if published" do
|
||||
sidekiq_assert_enqueued_with(job: Articles::ScoreCalcWorker, args: [article.id]) do
|
||||
|
|
|
|||
|
|
@ -397,6 +397,32 @@ RSpec.describe Comment, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "spam" do
|
||||
before do
|
||||
allow(SiteConfig).to receive(:mascot_user_id).and_return(user.id)
|
||||
allow(SiteConfig).to receive(:spam_trigger_terms).and_return(["yahoomagoo gogo", "anothertestterm"])
|
||||
end
|
||||
|
||||
it "creates vomit reaction if possible spam" do
|
||||
comment.body_markdown = "This post is about Yahoomagoo gogo"
|
||||
comment.save
|
||||
expect(Reaction.last.category).to eq("vomit")
|
||||
expect(Reaction.last.user_id).to eq(user.id)
|
||||
end
|
||||
|
||||
it "does not create vomit reaction if user is established in this context" do
|
||||
user.update_column(:registered_at, 10.days.ago)
|
||||
comment.body_markdown = "This post is about Yahoomagoo gogo"
|
||||
comment.save
|
||||
expect(Reaction.last).to be nil
|
||||
end
|
||||
|
||||
it "does not create vomit reaction if does not have matching title" do
|
||||
comment.save
|
||||
expect(Reaction.last).to be nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when callbacks are triggered before save" do
|
||||
it "generates character count before saving" do
|
||||
comment.save
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ RSpec.describe "/admin/config", type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe "Rate Limits" do
|
||||
describe "Rate Limits and spam" do
|
||||
it "updates rate_limit_follow_count_daily" do
|
||||
expect do
|
||||
post "/admin/config", params: { site_config: { rate_limit_follow_count_daily: 3 },
|
||||
|
|
@ -544,6 +544,13 @@ RSpec.describe "/admin/config", type: :request do
|
|||
confirmation: confirmation_message }
|
||||
end.to change(SiteConfig, :rate_limit_send_email_confirmation).from(2).to(3)
|
||||
end
|
||||
|
||||
it "updates spam_trigger_terms" do
|
||||
spam_trigger_terms = "hey, pokemon go hack"
|
||||
post "/admin/config", params: { site_config: { spam_trigger_terms: spam_trigger_terms },
|
||||
confirmation: confirmation_message }
|
||||
expect(SiteConfig.spam_trigger_terms).to eq(["hey", "pokemon go hack"])
|
||||
end
|
||||
end
|
||||
|
||||
describe "Social Media" do
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@ RSpec.describe Notifications::NewComment::Send, type: :service do
|
|||
expect(notification.json_data["user"]["username"]).to eq(child_comment.user.username)
|
||||
end
|
||||
|
||||
it "does not send if comment has negative score already" do
|
||||
prior_notification_size = Notification.all.size
|
||||
child_comment.update_column(:score, -1)
|
||||
described_class.call(child_comment)
|
||||
expect(Notification.all.size).to eq prior_notification_size
|
||||
end
|
||||
|
||||
it "creates the correct comment data for the notification" do
|
||||
described_class.call(child_comment)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue