Created
April 4, 2019 02:15
-
-
Save muffinista/73fa9d4ca4fd2a9493ea4e3d68e3d8d0 to your computer and use it in GitHub Desktop.
anti-spam measures for mastodon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Gemfile b/Gemfile | |
index 8266488f8..ce47aa6e2 100644 | |
--- a/Gemfile | |
+++ b/Gemfile | |
@@ -147,3 +147,6 @@ group :production do | |
end | |
gem 'concurrent-ruby', require: false | |
+ | |
+gem 'ipcat' | |
+gem 'sentry-raven' | |
diff --git a/Gemfile.lock b/Gemfile.lock | |
index 7b8ffec9a..82d701486 100644 | |
--- a/Gemfile.lock | |
+++ b/Gemfile.lock | |
@@ -283,6 +283,7 @@ GEM | |
terminal-table (>= 1.5.1) | |
idn-ruby (0.1.0) | |
ipaddress (0.8.3) | |
+ ipcat (2.0.21) | |
iso-639 (0.2.8) | |
jaro_winkler (1.5.2) | |
jmespath (1.4.0) | |
@@ -551,6 +552,8 @@ GEM | |
scss_lint (0.57.1) | |
rake (>= 0.9, < 13) | |
sass (~> 3.5, >= 3.5.5) | |
+ sentry-raven (2.9.0) | |
+ faraday (>= 0.7.6, < 1.0) | |
sidekiq (5.2.5) | |
connection_pool (~> 2.2, >= 2.2.2) | |
rack (>= 1.5.0) | |
@@ -698,6 +701,7 @@ DEPENDENCIES | |
httplog (~> 1.2) | |
i18n-tasks (~> 0.9) | |
idn-ruby | |
+ ipcat | |
iso-639 | |
json-ld (~> 3.0) | |
json-ld-preloaded (~> 3.0) | |
@@ -749,6 +753,7 @@ DEPENDENCIES | |
rubocop (~> 0.63) | |
sanitize (~> 5.0) | |
scss_lint (~> 0.57) | |
+ sentry-raven | |
sidekiq (~> 5.2) | |
sidekiq-bulk (~> 0.2.0) | |
sidekiq-scheduler (~> 3.0) | |
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb | |
index ad7b1859f..3b4029132 100644 | |
--- a/app/controllers/auth/registrations_controller.rb | |
+++ b/app/controllers/auth/registrations_controller.rb | |
@@ -10,6 +10,9 @@ class Auth::RegistrationsController < Devise::RegistrationsController | |
before_action :set_instance_presenter, only: [:new, :create, :update] | |
before_action :set_body_classes, only: [:new, :create, :edit, :update] | |
+ include DatacenterConcern | |
+ | |
+ | |
def destroy | |
not_found | |
end | |
diff --git a/app/controllers/concerns/datacenter_concern.rb b/app/controllers/concerns/datacenter_concern.rb | |
new file mode 100644 | |
index 000000000..351f3147a | |
--- /dev/null | |
+++ b/app/controllers/concerns/datacenter_concern.rb | |
@@ -0,0 +1,33 @@ | |
+# frozen_string_literal: true | |
+ | |
+module DatacenterConcern | |
+ extend ActiveSupport::Concern | |
+ | |
+ included do | |
+ before_action :prevent_datacenters, only: [:new, :create] | |
+ end | |
+ | |
+ def prevent_datacenters | |
+ begin | |
+ require 'ipcat' | |
+ | |
+ if IPCat.datacenter?(request.remote_ip) != nil | |
+ redirect_to '/datacenters.html' | |
+ elsif File.exist?(Rails.root.join('blocks.txt')) | |
+ require 'ipaddr' | |
+ | |
+ has_block = File.read(Rails.root.join('blocks.txt')).split(/\n/).find { |ip| | |
+ net = IPAddr.new(ip) | |
+ net.include?(request.remote_ip) | |
+ } | |
+ | |
+ if has_block | |
+ redirect_to '/datacenters.html' | |
+ end | |
+ end | |
+ | |
+ rescue | |
+ | |
+ end | |
+ end | |
+end | |
diff --git a/app/models/status.rb b/app/models/status.rb | |
index 035423b40..43b45e542 100644 | |
--- a/app/models/status.rb | |
+++ b/app/models/status.rb | |
@@ -66,6 +66,7 @@ class Status < ApplicationRecord | |
validates :text, presence: true, unless: -> { with_media? || reblog? } | |
validates_with StatusLengthValidator | |
validates_with DisallowedHashtagsValidator | |
+ validates_with AllowedFollowingStatusValidator | |
validates :reblog, uniqueness: { scope: :account }, if: :reblog? | |
default_scope { recent } | |
diff --git a/app/validators/allowed_following_status_validator.rb b/app/validators/allowed_following_status_validator.rb | |
new file mode 100644 | |
index 000000000..b963590cd | |
--- /dev/null | |
+++ b/app/validators/allowed_following_status_validator.rb | |
@@ -0,0 +1,69 @@ | |
+# frozen_string_literal: true | |
+ | |
+class AllowedFollowingStatusValidator < ActiveModel::Validator | |
+ def validate(status) | |
+ @status = status | |
+ return unless !accounts.empty? | |
+ | |
+ # allow messages from local admin/moderators | |
+ return if status.account.user_moderator? || status.account.user_admin? | |
+ | |
+ # allow messages from accounts that seem to have legit traffic | |
+ return if status.account.account_stat.statuses_count > 25 | |
+ return if status.account.account_stat.followers_count > 5 | |
+ | |
+ not_following_accounts = [] | |
+ | |
+ begin | |
+ not_following_accounts = accounts.select { |a| | |
+ a && !a.following?(status.account) | |
+ }.reject { |a| | |
+ # allow messages to local admin/moderators | |
+ (a.respond_to?(:user_admin?) && a.user_admin?) || | |
+ (a.respond_to?(:user_moderator?) && a.user_moderator?) | |
+ } | |
+ rescue StandardError => ex | |
+ Raven.capture_exception(ex) if defined?(Raven) | |
+ end | |
+ | |
+ | |
+ if status.thread.present? | |
+ mentioned_accounts = [] | |
+ begin | |
+ #mentioned_accounts = status.thread.include(:account).map(&:account) | |
+ mentioned_accounts = status.mentions.includes(:account).map(&:account) | |
+ rescue StandardError => ex | |
+ Raven.capture_exception(ex) if defined?(Raven) | |
+ end | |
+ | |
+ # if the thread includes this account | |
+ if mentioned_accounts.include?(status.account) | |
+ # allow messages to accounts in the thread, if this account | |
+ # was mentioned in the thread | |
+ not_following_accounts = not_following_accounts.reject { |a| | |
+ mentioned_accounts.include?(a) | |
+ } | |
+ end | |
+ end | |
+ | |
+ | |
+ status.errors.add(:text, I18n.t('statuses.cannot_send')) unless not_following_accounts.empty? | |
+ end | |
+ | |
+ private | |
+ | |
+ def accounts | |
+ @status.text.scan(Account::MENTION_RE).collect do |match| | |
+ mentioned_account = nil | |
+ begin | |
+ username, domain = Regexp.last_match(1).split('@') | |
+ mentioned_account = Account.find_remote(username, domain) | |
+ mentioned_account = Account.find_local(username) if mentioned_account.nil? | |
+ rescue StandardError => ex | |
+ Raven.capture_exception(ex) if defined?(Raven) | |
+ end | |
+ | |
+ mentioned_account | |
+ end | |
+ end | |
+end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment