mirror of
https://github.com/mastodon/mastodon.git
synced 2024-12-22 17:35:40 +01:00
Add stop-gap antispam code (#32981)
This commit is contained in:
parent
4517e18b79
commit
dbddd40c1c
2 changed files with 53 additions and 0 deletions
45
app/lib/antispam.rb
Normal file
45
app/lib/antispam.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Antispam
|
||||||
|
include Redisable
|
||||||
|
|
||||||
|
ACCOUNT_AGE_EXEMPTION = 1.week.freeze
|
||||||
|
|
||||||
|
class SilentlyDrop < StandardError
|
||||||
|
attr_reader :status
|
||||||
|
|
||||||
|
def initialize(status)
|
||||||
|
super()
|
||||||
|
|
||||||
|
@status = status
|
||||||
|
|
||||||
|
status.created_at = Time.now.utc
|
||||||
|
status.id = Mastodon::Snowflake.id_at(status.created_at)
|
||||||
|
status.in_reply_to_account_id = status.thread&.account_id
|
||||||
|
|
||||||
|
status.delete # Make sure this is not persisted
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_preflight_check!(status)
|
||||||
|
return unless spammy_texts.any? { |spammy_text| status.text.include?(spammy_text) }
|
||||||
|
return unless status.thread.present? && !status.thread.account.following?(status.account)
|
||||||
|
return unless status.account.created_at >= ACCOUNT_AGE_EXEMPTION.ago
|
||||||
|
|
||||||
|
report_if_needed!(status.account)
|
||||||
|
|
||||||
|
raise SilentlyDrop, status
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def spammy_texts
|
||||||
|
redis.smembers('antispam:spammy_texts')
|
||||||
|
end
|
||||||
|
|
||||||
|
def report_if_needed!(account)
|
||||||
|
return if Report.unresolved.exists?(account: Account.representative, target_account: account)
|
||||||
|
|
||||||
|
Report.create!(account: Account.representative, target_account: account, category: :spam, comment: 'Account automatically reported for posting a banned URL')
|
||||||
|
end
|
||||||
|
end
|
|
@ -36,6 +36,8 @@ class PostStatusService < BaseService
|
||||||
@text = @options[:text] || ''
|
@text = @options[:text] || ''
|
||||||
@in_reply_to = @options[:thread]
|
@in_reply_to = @options[:thread]
|
||||||
|
|
||||||
|
@antispam = Antispam.new
|
||||||
|
|
||||||
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
||||||
|
|
||||||
validate_media!
|
validate_media!
|
||||||
|
@ -55,6 +57,8 @@ class PostStatusService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
@status
|
@status
|
||||||
|
rescue Antispam::SilentlyDrop => e
|
||||||
|
e.status
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -74,6 +78,7 @@ class PostStatusService < BaseService
|
||||||
@status = @account.statuses.new(status_attributes)
|
@status = @account.statuses.new(status_attributes)
|
||||||
process_mentions_service.call(@status, save_records: false)
|
process_mentions_service.call(@status, save_records: false)
|
||||||
safeguard_mentions!(@status)
|
safeguard_mentions!(@status)
|
||||||
|
@antispam.local_preflight_check!(@status)
|
||||||
|
|
||||||
# The following transaction block is needed to wrap the UPDATEs to
|
# The following transaction block is needed to wrap the UPDATEs to
|
||||||
# the media attachments when the status is created
|
# the media attachments when the status is created
|
||||||
|
@ -95,6 +100,7 @@ class PostStatusService < BaseService
|
||||||
|
|
||||||
def schedule_status!
|
def schedule_status!
|
||||||
status_for_validation = @account.statuses.build(status_attributes)
|
status_for_validation = @account.statuses.build(status_attributes)
|
||||||
|
@antispam.local_preflight_check!(status_for_validation)
|
||||||
|
|
||||||
if status_for_validation.valid?
|
if status_for_validation.valid?
|
||||||
# Marking the status as destroyed is necessary to prevent the status from being
|
# Marking the status as destroyed is necessary to prevent the status from being
|
||||||
|
@ -111,6 +117,8 @@ class PostStatusService < BaseService
|
||||||
else
|
else
|
||||||
raise ActiveRecord::RecordInvalid
|
raise ActiveRecord::RecordInvalid
|
||||||
end
|
end
|
||||||
|
rescue Antispam::SilentlyDrop
|
||||||
|
@status = @account.scheduled_status.new(scheduled_status_attributes).tap(&:delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
def postprocess_status!
|
def postprocess_status!
|
||||||
|
|
Loading…
Reference in a new issue