decidim-spam_signal 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.md +661 -0
- data/README.md +277 -0
- data/Rakefile +93 -0
- data/app/commands/decidim/comments/create_comment.rb +75 -0
- data/app/commands/decidim/spam_signal/admin/add_rule_command.rb +48 -0
- data/app/commands/decidim/spam_signal/admin/add_scanner_command.rb +46 -0
- data/app/commands/decidim/spam_signal/admin/remove_cop_command.rb +31 -0
- data/app/commands/decidim/spam_signal/admin/remove_rule_command.rb +31 -0
- data/app/commands/decidim/spam_signal/admin/remove_scanner_command.rb +31 -0
- data/app/commands/decidim/spam_signal/admin/update_cop_command.rb +41 -0
- data/app/commands/decidim/spam_signal/admin/update_rule_command.rb +47 -0
- data/app/commands/decidim/spam_signal/admin/update_scanner_command.rb +47 -0
- data/app/commands/decidim/spam_signal/application_handler.rb +27 -0
- data/app/commands/decidim/spam_signal/command.rb +14 -0
- data/app/commands/decidim/spam_signal/cops/cop_handler.rb +72 -0
- data/app/commands/decidim/spam_signal/cops/lock_cop_command.rb +58 -0
- data/app/commands/decidim/spam_signal/cops/sinalize_cop_command.rb +25 -0
- data/app/commands/decidim/spam_signal/scans/allowed_tlds_scan_command.rb +50 -0
- data/app/commands/decidim/spam_signal/scans/forbidden_tlds_scan_command.rb +49 -0
- data/app/commands/decidim/spam_signal/scans/scan_handler.rb +24 -0
- data/app/commands/decidim/spam_signal/scans/word_scan_command.rb +40 -0
- data/app/controllers/concerns/decidim/user_blocked_checker.rb +48 -0
- data/app/controllers/decidim/spam_signal/admin/application_controller.rb +18 -0
- data/app/controllers/decidim/spam_signal/admin/application_cops_controller.rb +122 -0
- data/app/controllers/decidim/spam_signal/admin/application_rules_controller.rb +136 -0
- data/app/controllers/decidim/spam_signal/admin/application_scans_controller.rb +110 -0
- data/app/controllers/decidim/spam_signal/admin/comment_cops_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/comment_rules_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/comment_scans_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/profile_cops_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/profile_rules_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/profile_scans_controller.rb +13 -0
- data/app/controllers/decidim/spam_signal/admin/spam_filter_reports_controller.rb +41 -0
- data/app/forms/decidim/spam_signal/cops/lock_settings_form.rb +14 -0
- data/app/forms/decidim/spam_signal/cops/no_settings_form.rb +11 -0
- data/app/forms/decidim/spam_signal/cops/sinalize_settings_form.rb +13 -0
- data/app/forms/decidim/spam_signal/no_settings_form.rb +9 -0
- data/app/forms/decidim/spam_signal/rule_form.rb +10 -0
- data/app/forms/decidim/spam_signal/scans/allowed_tlds_form.rb +13 -0
- data/app/forms/decidim/spam_signal/scans/forbidden_tlds_form.rb +13 -0
- data/app/forms/decidim/spam_signal/scans/word_settings_form.rb +13 -0
- data/app/forms/decidim/spam_signal/settings_form.rb +27 -0
- data/app/helpers/decidim/spam_signal/admin/spam_signal_helper.rb +22 -0
- data/app/helpers/decidim/spam_signal/application_helper.rb +10 -0
- data/app/models/decidim/spam_signal/config.rb +48 -0
- data/app/overrides/profiles_noindex.rb +17 -0
- data/app/packs/images/decidim/spam_signal/entrypoints/spam_signal.js +1 -0
- data/app/packs/images/decidim/spam_signal/icon.svg +1 -0
- data/app/repositories/decidim/spam_signal/spam_config_repo.rb +160 -0
- data/app/views/decidim/admin/moderated_users/_report.html.erb +15 -0
- data/app/views/decidim/admin/moderated_users/index.html.erb +82 -0
- data/app/views/decidim/comments/comments/error.js.erb +4 -0
- data/app/views/decidim/spam_signal/admin/comment_cops/edit.html.erb +9 -0
- data/app/views/decidim/spam_signal/admin/comment_rules/edit.html.erb +8 -0
- data/app/views/decidim/spam_signal/admin/comment_rules/new.html.erb +9 -0
- data/app/views/decidim/spam_signal/admin/comment_scans/edit.html.erb +8 -0
- data/app/views/decidim/spam_signal/admin/comment_scans/new.html.erb +8 -0
- data/app/views/decidim/spam_signal/admin/profile_cops/edit.html.erb +9 -0
- data/app/views/decidim/spam_signal/admin/profile_rules/edit.html.erb +8 -0
- data/app/views/decidim/spam_signal/admin/profile_rules/new.html.erb +9 -0
- data/app/views/decidim/spam_signal/admin/profile_scans/edit.html.erb +7 -0
- data/app/views/decidim/spam_signal/admin/profile_scans/new.html.erb +8 -0
- data/app/views/decidim/spam_signal/admin/shared/cops/_edit.html.erb +48 -0
- data/app/views/decidim/spam_signal/admin/shared/rules/_edit.html.erb +27 -0
- data/app/views/decidim/spam_signal/admin/shared/rules/_new.html.erb +28 -0
- data/app/views/decidim/spam_signal/admin/shared/scans/_edit.html.erb +25 -0
- data/app/views/decidim/spam_signal/admin/shared/scans/_new.html.erb +39 -0
- data/app/views/decidim/spam_signal/admin/spam_filter_reports/index.html.erb +282 -0
- data/config/assets.rb +8 -0
- data/config/i18n-tasks.yml +9 -0
- data/config/initializers/spam_signal.rb +7 -0
- data/config/locales/en.yml +114 -0
- data/config/locales/fr.yml +115 -0
- data/config/routes.rb +3 -0
- data/lib/decidim/spam_signal/admin.rb +10 -0
- data/lib/decidim/spam_signal/admin_engine.rb +42 -0
- data/lib/decidim/spam_signal/cop_bot.rb +41 -0
- data/lib/decidim/spam_signal/cops/cops_repository.rb +37 -0
- data/lib/decidim/spam_signal/engine.rb +23 -0
- data/lib/decidim/spam_signal/extractors/comment_extractor.rb +15 -0
- data/lib/decidim/spam_signal/extractors/extractor.rb +13 -0
- data/lib/decidim/spam_signal/extractors/profile_extractor.rb +15 -0
- data/lib/decidim/spam_signal/scans/scans_repository.rb +44 -0
- data/lib/decidim/spam_signal/spam_settings_form_builder.rb +22 -0
- data/lib/decidim/spam_signal/test/factories.rb +24 -0
- data/lib/decidim/spam_signal/test/scan_factories.rb +17 -0
- data/lib/decidim/spam_signal/validators/comment_spam_validator.rb +119 -0
- data/lib/decidim/spam_signal/validators/profile_spam_validator.rb +133 -0
- data/lib/decidim/spam_signal/version.rb +14 -0
- data/lib/decidim/spam_signal.rb +31 -0
- data/lib/tasks/antispam.rb +23 -0
- metadata +210 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
require "decidim/core"
|
5
|
+
require "deface"
|
6
|
+
|
7
|
+
module Decidim
|
8
|
+
module SpamSignal
|
9
|
+
# This is the engine that runs on the public interface of spam_signal.
|
10
|
+
class Engine < ::Rails::Engine
|
11
|
+
isolate_namespace Decidim::SpamSignal
|
12
|
+
|
13
|
+
config.to_prepare do
|
14
|
+
Decidim::User.include(ProfileSpamValidator)
|
15
|
+
Decidim::Comments::CommentForm.include(CommentSpamValidator)
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer "decidim_spam_signal.webpacker.assets_path" do
|
19
|
+
Decidim.register_assets_path File.expand_path("#{Decidim::SpamSignal::Engine.root}/app/packs")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamSignal
|
5
|
+
module Extractors
|
6
|
+
class CommentExtractor < Extractor
|
7
|
+
def self.extract(comment, config)
|
8
|
+
body = comment.attributes[:body]
|
9
|
+
return "" unless body.present?
|
10
|
+
body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamSignal
|
5
|
+
module Extractors
|
6
|
+
class ProfileExtractor < Extractor
|
7
|
+
def self.extract(user, _config)
|
8
|
+
"#{user.about}" + (user.personal_url ? "
|
9
|
+
===
|
10
|
+
#{user.personal_url}" : "")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module SpamSignal
|
7
|
+
module Scans
|
8
|
+
##
|
9
|
+
# Strategies repository
|
10
|
+
class ScansRepository
|
11
|
+
include Singleton
|
12
|
+
attr_reader :strategies
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@_strategies = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(strategy, command_klass)
|
19
|
+
@_strategies[strategy.to_sym] = command_klass
|
20
|
+
end
|
21
|
+
|
22
|
+
def unset_strategy(strategy)
|
23
|
+
key = strategy.to_sym
|
24
|
+
raise Error, "Cop's Strategy #{strategy} does not exists" unless @_strategies.key? key
|
25
|
+
@_strategies.except!(key)
|
26
|
+
end
|
27
|
+
|
28
|
+
def strategies
|
29
|
+
@_strategies.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def form_for(strategy)
|
33
|
+
strategy(strategy).form
|
34
|
+
end
|
35
|
+
|
36
|
+
def strategy(strategy)
|
37
|
+
key = "#{strategy}".to_sym
|
38
|
+
return @_strategies[key] if @_strategies.key? key
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamSignal
|
5
|
+
class SpamSettingsFormBuilder < Decidim::AuthorizationFormBuilder
|
6
|
+
def input_field(name, type, **options)
|
7
|
+
return hidden_field(name) if name.to_s == "handler_name"
|
8
|
+
case type
|
9
|
+
when :date, :datetime, :time, :"decidim/attributes/localized_date"
|
10
|
+
date_field name
|
11
|
+
when :integer, Integer
|
12
|
+
number_field name
|
13
|
+
else
|
14
|
+
return text_area name, rows: 5 if name.to_s.ends_with? "_csv"
|
15
|
+
return number_field name if name.to_s.starts_with? "num_"
|
16
|
+
return check_box name if name.to_s.starts_with?("is_") || name.to_s.ends_with?("enabled")
|
17
|
+
text_field name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "decidim/faker/localized"
|
4
|
+
require "decidim/faker/internet"
|
5
|
+
require "decidim/dev"
|
6
|
+
require "decidim/core/test/factories"
|
7
|
+
require_relative "scan_factories"
|
8
|
+
FactoryBot.define do
|
9
|
+
factory :spam_signal_config, class: "Decidim::SpamSignal::Config" do
|
10
|
+
organization { create(:organization) }
|
11
|
+
comment_settings { {
|
12
|
+
"scans" => {},
|
13
|
+
"rules" => {},
|
14
|
+
"spam_cop" => {},
|
15
|
+
"suspicious_cop" => {}
|
16
|
+
} }
|
17
|
+
profile_settings { {
|
18
|
+
"scans" => {},
|
19
|
+
"rules" => {},
|
20
|
+
"spam_cop" => {},
|
21
|
+
"suspicious_cop" => {}
|
22
|
+
}}
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SpamScan < Decidim::SpamSignal::Scans::ScanHandler
|
4
|
+
def call
|
5
|
+
broadcast(:spam)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
class SuspiciousScan < Decidim::SpamSignal::Scans::ScanHandler
|
9
|
+
def call
|
10
|
+
broadcast(:suspicious)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
class OkScan < Decidim::SpamSignal::Scans::ScanHandler
|
14
|
+
def call
|
15
|
+
broadcast(:ok)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamSignal
|
5
|
+
module CommentSpamValidator
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
validate :scan_spam
|
10
|
+
|
11
|
+
def scan_spam
|
12
|
+
return if body.empty?
|
13
|
+
tested_content = Extractors::CommentExtractor.extract(self, spam_config)
|
14
|
+
# Collect fired symbols
|
15
|
+
symboles = spam_scanners.map do |scan_command, scan_option|
|
16
|
+
scan_command.call(
|
17
|
+
tested_content,
|
18
|
+
scan_option
|
19
|
+
)
|
20
|
+
end.map(&:keys).flatten.filter { |s| s != :ok && s != :valid }
|
21
|
+
return if !symboles || symboles.empty?
|
22
|
+
|
23
|
+
# Check the rules
|
24
|
+
suspicious_rules = spam_ruleset("suspicious")
|
25
|
+
spam_rules = spam_ruleset("spam")
|
26
|
+
fire_suspicious_cop = suspicious_rules.map do |ruleset|
|
27
|
+
ruleset.map do |rule|
|
28
|
+
symboles.include? rule
|
29
|
+
end.all?
|
30
|
+
end.any?
|
31
|
+
fire_spam_cop = spam_rules.map do |ruleset|
|
32
|
+
ruleset.map do |rule|
|
33
|
+
symboles.include? rule
|
34
|
+
end.all?
|
35
|
+
end.any?
|
36
|
+
# If it's a cop fire it, else if it's a suspicous, fire it.
|
37
|
+
if fire_spam_cop && obvious_spam_cop
|
38
|
+
obvious_spam_cop.call(
|
39
|
+
errors: errors,
|
40
|
+
suspicious_user: author,
|
41
|
+
config: obvious_spam_cop_options,
|
42
|
+
reportable: author,
|
43
|
+
justification: "comment: " + tested_content
|
44
|
+
)
|
45
|
+
elsif fire_suspicious_cop && suspicious_spam_cop
|
46
|
+
suspicious_spam_cop.call(
|
47
|
+
errors: errors,
|
48
|
+
suspicious_user: author,
|
49
|
+
reportable: author,
|
50
|
+
config: suspicious_spam_cop_options,
|
51
|
+
justification: "comment: " + tested_content
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def obvious_spam_cop_options
|
57
|
+
cop = spam_config.comments.spam_cop
|
58
|
+
return nil unless cop
|
59
|
+
cop
|
60
|
+
end
|
61
|
+
|
62
|
+
def obvious_spam_cop
|
63
|
+
cop_key = obvious_spam_cop_options["handler_name"]
|
64
|
+
return nil unless cop_key
|
65
|
+
Cops::CopsRepository.instance.strategy(
|
66
|
+
cop_key
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def suspicious_spam_cop_options
|
71
|
+
cop = spam_config.comments.suspicious_cop
|
72
|
+
return nil unless cop
|
73
|
+
cop
|
74
|
+
end
|
75
|
+
|
76
|
+
def suspicious_spam_cop
|
77
|
+
cop_key = suspicious_spam_cop_options["handler_name"]
|
78
|
+
return nil unless cop_key
|
79
|
+
Cops::CopsRepository.instance.strategy(
|
80
|
+
cop_key
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def spam_config
|
85
|
+
@config ||= Config.get_config(context.current_organization)
|
86
|
+
end
|
87
|
+
|
88
|
+
def author
|
89
|
+
context.author || nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def scan_context
|
93
|
+
{
|
94
|
+
validator: commentable.commentable_type == "Decidim::Comments::Comment" ? "comment-reply" : "comment",
|
95
|
+
is_updating: id.present?,
|
96
|
+
date: id.present? ? updated_at : DateTime.now,
|
97
|
+
current_organization: context.current_organization,
|
98
|
+
author: context.author
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def spam_scanners
|
103
|
+
spam_config.comments.scans.map do |s, options|
|
104
|
+
options["context"] = scan_context
|
105
|
+
[Scans::ScansRepository.instance.strategy(s), options]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def spam_ruleset(handler_name = "spam")
|
110
|
+
spam_config.comments.rules.map do |rule_id, rule|
|
111
|
+
rule
|
112
|
+
end.filter { |r| r["handler_name"] == handler_name }.map do |r|
|
113
|
+
r["rules"].symbolize_keys.keys
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamSignal
|
5
|
+
module ProfileSpamValidator
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
validate :scan_spam
|
10
|
+
def scan_spam
|
11
|
+
return if !about || about.empty?
|
12
|
+
return if blocked_at_changed?(from: nil) # we are blocking the user, don't validate if it is spam.
|
13
|
+
current_user = self
|
14
|
+
tested_content = Extractors::ProfileExtractor.extract(self, spam_config)
|
15
|
+
|
16
|
+
# Collect fired symbols
|
17
|
+
symboles = spam_scanners.map do |scan_command, scan_option|
|
18
|
+
scan_command.call(
|
19
|
+
tested_content,
|
20
|
+
scan_option
|
21
|
+
)
|
22
|
+
end.map(&:keys).flatten.filter { |s| s != :ok && s != :valid }
|
23
|
+
return if !symboles || symboles.empty?
|
24
|
+
|
25
|
+
# Check the rules
|
26
|
+
suspicious_rules = spam_ruleset("suspicious")
|
27
|
+
spam_rules = spam_ruleset("spam")
|
28
|
+
fire_suspicious_cop = suspicious_rules.map do |ruleset|
|
29
|
+
ruleset.map do |rule|
|
30
|
+
symboles.include? rule
|
31
|
+
end.all?
|
32
|
+
end.any?
|
33
|
+
fire_spam_cop = spam_rules.map do |ruleset|
|
34
|
+
ruleset.map do |rule|
|
35
|
+
symboles.include? rule
|
36
|
+
end.all?
|
37
|
+
end.any?
|
38
|
+
|
39
|
+
# If it's a cop fire it, else if it's a suspicous, fire it.
|
40
|
+
if fire_spam_cop && obvious_spam_cop
|
41
|
+
obvious_spam_cop.call(
|
42
|
+
errors: errors,
|
43
|
+
error_key: :about,
|
44
|
+
suspicious_user: current_user,
|
45
|
+
config: obvious_spam_cop_options,
|
46
|
+
justification: "profile: " + tested_content
|
47
|
+
) do |on|
|
48
|
+
on(:save) {}
|
49
|
+
on(:restore_value) { restore_values(current_user) }
|
50
|
+
end
|
51
|
+
elsif fire_suspicious_cop && suspicious_spam_cop
|
52
|
+
suspicious_spam_cop.call(
|
53
|
+
errors: errors,
|
54
|
+
error_key: :about,
|
55
|
+
suspicious_user: current_user,
|
56
|
+
config: suspicious_spam_cop_options,
|
57
|
+
justification: "profile: " + tested_content
|
58
|
+
) do |on|
|
59
|
+
on(:save) {}
|
60
|
+
on(:restore_value) { restore_values(current_user) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Case the lock cop is there,
|
66
|
+
# it will save the user without validation,
|
67
|
+
# we should then update the attributes to before
|
68
|
+
# state
|
69
|
+
def restore_values(user)
|
70
|
+
user.about = user.about_was
|
71
|
+
user.personal_url = user.personal_url_was
|
72
|
+
end
|
73
|
+
|
74
|
+
def obvious_spam_cop_options
|
75
|
+
cop = spam_config.profiles.spam_cop
|
76
|
+
return nil unless cop
|
77
|
+
cop
|
78
|
+
end
|
79
|
+
|
80
|
+
def scan_context
|
81
|
+
{
|
82
|
+
validator: "profile",
|
83
|
+
is_updating: true,
|
84
|
+
date: updated_at,
|
85
|
+
current_organization: organization,
|
86
|
+
author: self
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def obvious_spam_cop
|
91
|
+
cop_key = obvious_spam_cop_options["handler_name"]
|
92
|
+
return nil unless cop_key
|
93
|
+
Cops::CopsRepository.instance.strategy(
|
94
|
+
cop_key
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def suspicious_spam_cop_options
|
99
|
+
cop = spam_config.profiles.suspicious_cop
|
100
|
+
return nil unless cop
|
101
|
+
cop
|
102
|
+
end
|
103
|
+
|
104
|
+
def suspicious_spam_cop
|
105
|
+
cop_key = suspicious_spam_cop_options["handler_name"]
|
106
|
+
return nil unless cop_key
|
107
|
+
Cops::CopsRepository.instance.strategy(
|
108
|
+
cop_key
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def spam_config
|
113
|
+
@config ||= Config.get_config(organization)
|
114
|
+
end
|
115
|
+
|
116
|
+
def spam_scanners
|
117
|
+
spam_config.profiles.scans.map do |s, options|
|
118
|
+
options["context"] = scan_context
|
119
|
+
[Scans::ScansRepository.instance.strategy(s), options]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def spam_ruleset(handler_name = "spam")
|
124
|
+
spam_config.profiles.rules.map do |rule_id, rule|
|
125
|
+
rule
|
126
|
+
end.filter { |r| r["handler_name"] == handler_name }.map do |r|
|
127
|
+
r["rules"].symbolize_keys.keys
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "spam_signal/admin"
|
4
|
+
require_relative "spam_signal/engine"
|
5
|
+
require_relative "spam_signal/admin_engine"
|
6
|
+
|
7
|
+
require_relative "spam_signal/cop_bot"
|
8
|
+
require_relative "spam_signal/spam_settings_form_builder"
|
9
|
+
|
10
|
+
require_relative "spam_signal/validators/profile_spam_validator"
|
11
|
+
require_relative "spam_signal/validators/comment_spam_validator"
|
12
|
+
|
13
|
+
require_relative "spam_signal/extractors/extractor"
|
14
|
+
require_relative "spam_signal/extractors/comment_extractor"
|
15
|
+
require_relative "spam_signal/extractors/profile_extractor"
|
16
|
+
|
17
|
+
require_relative "spam_signal/cops/cops_repository"
|
18
|
+
require_relative "spam_signal/scans/scans_repository"
|
19
|
+
|
20
|
+
module Decidim
|
21
|
+
# This namespace holds the logic of the `SpamSignal` component. This component
|
22
|
+
# allows users to create spam_signal in a participatory space.
|
23
|
+
module SpamSignal
|
24
|
+
class Error < StandardError; end
|
25
|
+
autoload :CopsRepository, "decidim/spam_signal/cops/cops_repository"
|
26
|
+
autoload :ScansRepository, "decidim/spam_signal/scans/scans_repository"
|
27
|
+
|
28
|
+
autoload :WordScanCommand, "decidim/spam_signal/scans/word_scan_command"
|
29
|
+
autoload :LockCopCommand, "decidim/spam_signal/cops/lock_cop_command"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
now_tag = "\n[#{DateTime.now.strftime("%d/%m/%Y %H:%M")}]"
|
3
|
+
Decidim::User.where.not(blocked_at: nil).each do |suspicious_user|
|
4
|
+
admin_reporter = CopBot.get(suspicious_user.organization)
|
5
|
+
suspicious_comments = Decidim::Comments::Comment.where(author: suspicious_user)
|
6
|
+
suspicious_comments.each do |spam|
|
7
|
+
moderation = Decidim::Moderation.find_or_create_by!(
|
8
|
+
reportable: spam,
|
9
|
+
participatory_space: spam.participatory_space
|
10
|
+
)
|
11
|
+
is_new = moderation.report_count == 0
|
12
|
+
moderation.update(reported_content: spam.body[admin_reporter.locale]) if !moderation.reported_content && spam.body[admin_reporter.locale]
|
13
|
+
report = Decidim::Report.find_or_create_by!(
|
14
|
+
moderation: moderation.reload,
|
15
|
+
user: admin_reporter) do |report|
|
16
|
+
report.locale = admin_reporter.locale
|
17
|
+
report.reason = "spam"
|
18
|
+
report.details = "#{now_tag}cascade: #{spam}"
|
19
|
+
end
|
20
|
+
report.update(details: "#{report.details}#{now_tag}cascade: #{spam}")unless is_new
|
21
|
+
moderation.update!(report_count: moderation.report_count + 1, hidden_at: Time.current)
|
22
|
+
|
23
|
+
end
|