decidim-spam_detection 0.1.8 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/app/jobs/decidim/spam_detection/notify_admins.rb +19 -0
- data/app/mailers/decidim/spam_detection/spam_detection_mailer.rb +39 -0
- data/app/services/decidim/spam_detection/mark_users_service.rb +23 -3
- data/app/views/decidim/spam_detection/spam_detection_mailer/notify_detection.html.erb +16 -0
- data/config/i18n-tasks.yml +3 -1
- data/config/locales/ca.yml +25 -0
- data/config/locales/en.yml +18 -0
- data/config/locales/es.yml +24 -0
- data/config/locales/fr.yml +24 -0
- data/lib/decidim/spam_detection/abstract_spam_user_command.rb +12 -3
- data/lib/decidim/spam_detection/block_spam_user_command.rb +48 -16
- data/lib/decidim/spam_detection/report_spam_user_command.rb +32 -18
- data/lib/decidim/spam_detection/version.rb +1 -1
- metadata +13 -5
- data/config/assets.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3734b1cecb038a80ab8905ab0cd7d9680a452b50627f0e5c5d92ee5a5e08c831
|
4
|
+
data.tar.gz: b1a7b24b904478f66d15d5bb28430aa9f1baff2da793d9517478004e1f6cd3e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 441ffd50f429c3131f7f646eed4ba74552714908b570ff62fb9b0916e48c40e338087ff3c53ca1761b14a1483eb1217c4f4f122d020db655cbd5bbaf6413dfe4
|
7
|
+
data.tar.gz: 153cc30f6568d4566a8dcf610a4b39128748dc7bc4debafd2a3c7f62c9b021d2cabe3b9322f367fd3a1893f23d463feada7f8fc16b885054ec3f3f2d2bbad3e4
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Decidim::SpamDetection
|
2
2
|
[](https://codecov.io/gh/OpenSourcePolitics/decidim-spam_detection)
|
3
|
-

|
3
|
+

|
5
4
|
|
6
5
|
## Usage
|
7
6
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamDetection
|
5
|
+
class NotifyAdmins < ApplicationJob
|
6
|
+
queue_as :default
|
7
|
+
|
8
|
+
def perform(results_hash)
|
9
|
+
results_hash.each do |id, result|
|
10
|
+
next if result.keys == [:nothing]
|
11
|
+
|
12
|
+
Decidim::Organization.find(id).admins.each do |admin|
|
13
|
+
Decidim::SpamDetection::SpamDetectionMailer.notify_detection(admin, result).deliver_later
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module SpamDetection
|
5
|
+
class SpamDetectionMailer < Decidim::ApplicationMailer
|
6
|
+
helper_method :blocked_users_url, :reported_users_url
|
7
|
+
|
8
|
+
def notify_detection(user, results)
|
9
|
+
with_user(user) do
|
10
|
+
@reported_count = results[:reported_user]
|
11
|
+
@blocked_count = results[:blocked_user]
|
12
|
+
@organization = user.organization
|
13
|
+
@user = user
|
14
|
+
|
15
|
+
subject = I18n.t("notify_detection.subject", scope: "decidim.spam_detection_mailer")
|
16
|
+
mail(to: @user.email, subject: subject)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def blocked_users_url
|
23
|
+
decidim_admin.moderated_users_url(blocked: true, host: root_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
def reported_users_url
|
27
|
+
decidim_admin.moderated_users_url(blocked: false, host: root_url)
|
28
|
+
end
|
29
|
+
|
30
|
+
def decidim_admin
|
31
|
+
Decidim::Admin::Engine.routes.url_helpers
|
32
|
+
end
|
33
|
+
|
34
|
+
def root_url
|
35
|
+
decidim.root_url(host: @user.organization.host)[0..-2]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -27,7 +27,7 @@ module Decidim
|
|
27
27
|
.where(admin: false, blocked: false, deleted_at: nil)
|
28
28
|
.where("(extended_data #> '{spam_detection, unreported_at}') is null")
|
29
29
|
.where("(extended_data #> '{spam_detection, unblocked_at}') is null")
|
30
|
-
@results =
|
30
|
+
@results = {}
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.call
|
@@ -38,11 +38,15 @@ module Decidim
|
|
38
38
|
spam_probability_array = Decidim::SpamDetection::ApiProxy.request(cleaned_users)
|
39
39
|
|
40
40
|
mark_spam_users(merge_response_with_users(spam_probability_array))
|
41
|
+
notify_admins!
|
41
42
|
end
|
42
43
|
|
43
44
|
def mark_spam_users(probability_array)
|
44
45
|
probability_array.each do |probability_hash|
|
45
|
-
|
46
|
+
result = Decidim::SpamDetection::SpamUserCommandAdapter.call(probability_hash).result
|
47
|
+
organization_id = probability_hash["decidim_organization_id"]
|
48
|
+
|
49
|
+
add_to_results(organization_id.to_s, result)
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
@@ -56,7 +60,23 @@ module Decidim
|
|
56
60
|
end
|
57
61
|
|
58
62
|
def status
|
59
|
-
@results.
|
63
|
+
@results.each_with_object({}) do |result, hash|
|
64
|
+
hash[result[0]] = result[1].tally
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def notify_admins!
|
69
|
+
Decidim::SpamDetection::NotifyAdmins.perform_later(status)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def add_to_results(organization_id, result)
|
75
|
+
if @results[organization_id]
|
76
|
+
@results[organization_id] << result
|
77
|
+
else
|
78
|
+
@results[organization_id] = [result]
|
79
|
+
end
|
60
80
|
end
|
61
81
|
end
|
62
82
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<p class="email-greeting"><%= t(".hello", name: @user.name) %></p>
|
2
|
+
<p><%= t(".intro") %></p>
|
3
|
+
|
4
|
+
<% if @reported_count.present? %>
|
5
|
+
<p>
|
6
|
+
<%= t(".reported_count", count: @reported_count) %>
|
7
|
+
</p>
|
8
|
+
<p><%= link_to(t(".reported_link"), reported_users_url ) %></p>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% if @blocked_count.present? %>
|
12
|
+
<p>
|
13
|
+
<%= t(".blocked_count", count: @blocked_count) %>
|
14
|
+
</p>
|
15
|
+
<p><%= link_to(t(".blocked_link"), blocked_users_url ) %></p>
|
16
|
+
<% end %>
|
data/config/i18n-tasks.yml
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
ca:
|
3
|
+
blocked_user:
|
4
|
+
reason: L'usuari ha estat bloquejat a causa d'una alta probabilitat de spam per
|
5
|
+
la tasca de detecció automàtica de spam de Decidim amb una probabilitat de %{probability}%
|
6
|
+
decidim:
|
7
|
+
components:
|
8
|
+
spam_detection:
|
9
|
+
name: DeteccioSpam
|
10
|
+
spam_detection:
|
11
|
+
spam_detection_mailer:
|
12
|
+
notify_detection:
|
13
|
+
blocked_count: Comptes de spam bloquejats %{count}
|
14
|
+
blocked_link: Consulteu la llista de tots els comptes bloquejats
|
15
|
+
hello: Hola %{name}
|
16
|
+
intro: Aquí teniu l'informe de la tasca de detecció de spam
|
17
|
+
reported_count: Comptes blocs de %{count}
|
18
|
+
reported_link: Consulteu la llista de tots els comptes reportats al lloc
|
19
|
+
spam_detection_mailer:
|
20
|
+
notify_detection:
|
21
|
+
subject: Informe de la tasca automàtica detecció de spam
|
22
|
+
reported_user:
|
23
|
+
details: L'usuari s'ha marcat com spam a causa d'una alta probabilitat de spam
|
24
|
+
per la tasca de detecció automàtica de spam de Decidim amb una probabilitat
|
25
|
+
de %{probability}%
|
data/config/locales/en.yml
CHANGED
@@ -1,6 +1,24 @@
|
|
1
1
|
---
|
2
2
|
en:
|
3
|
+
blocked_user:
|
4
|
+
reason: The user was blocked because of a high spam probability by the automated
|
5
|
+
spam detection task with a probability of %{probability}%
|
3
6
|
decidim:
|
4
7
|
components:
|
5
8
|
spam_detection:
|
6
9
|
name: SpamDetection
|
10
|
+
spam_detection:
|
11
|
+
spam_detection_mailer:
|
12
|
+
notify_detection:
|
13
|
+
blocked_count: 'Spam accounts blocked: %{count}'
|
14
|
+
blocked_link: See the list of all blocked accounts
|
15
|
+
hello: Hello %{name}
|
16
|
+
intro: Here is the report of the automated spam detection task.
|
17
|
+
reported_count: 'Spam accounts reported: %{count}'
|
18
|
+
reported_link: See the list of all reported accounts
|
19
|
+
spam_detection_mailer:
|
20
|
+
notify_detection:
|
21
|
+
subject: Automated spam detection task digest
|
22
|
+
reported_user:
|
23
|
+
details: The user was reported because of a high spam probability by the automated
|
24
|
+
spam detection task with a probability of %{probability}%
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
es:
|
3
|
+
blocked_user:
|
4
|
+
reason: El usuario fue bloqueado debido a una alta probabilidad de spam por la
|
5
|
+
tarea de detección automática de spam con una probabilidad de %{probability}%
|
6
|
+
decidim:
|
7
|
+
components:
|
8
|
+
spam_detection:
|
9
|
+
name: DeteccionSpam
|
10
|
+
spam_detection:
|
11
|
+
spam_detection_mailer:
|
12
|
+
notify_detection:
|
13
|
+
blocked_count: 'Cuentas de spam bloqueadas : %{count}'
|
14
|
+
blocked_link: Ver la lista de todas las cuentas bloqueadas
|
15
|
+
hello: Hola %{name}
|
16
|
+
intro: Aquí está el informe de la tarea de detección automática de spam.
|
17
|
+
reported_count: Cuentas de spam señaladas %{count}
|
18
|
+
reported_link: Ver la lista de cuentas señaladas
|
19
|
+
spam_detection_mailer:
|
20
|
+
notify_detection:
|
21
|
+
subject: Informe de la tarea automática detección de spam
|
22
|
+
reported_user:
|
23
|
+
details: El usuario fue señalado debido a una alta probabilidad de spam por la
|
24
|
+
tarea de detección automática de spam con una probabilidad de %{probability}%
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
fr:
|
3
|
+
blocked_user:
|
4
|
+
reason: L'utilisateur a été bloqué en raison d'une forte probabilité de spam par
|
5
|
+
la tâche de détection automatique de spam avec une probabilité de %{probability}%
|
6
|
+
decidim:
|
7
|
+
components:
|
8
|
+
spam_detection:
|
9
|
+
name: DetectionSpam
|
10
|
+
spam_detection:
|
11
|
+
spam_detection_mailer:
|
12
|
+
notify_detection:
|
13
|
+
blocked_count: 'Comptes spam bloqués : %{count}'
|
14
|
+
blocked_link: Voir la liste de tous les comptes bloqués du site
|
15
|
+
hello: Bonjour %{name}
|
16
|
+
intro: Voici le rapport de la tâche de détection automatisée des spams.
|
17
|
+
reported_count: 'Comptes spam signalés : %{count}'
|
18
|
+
reported_link: Voir la liste de tous les comptes signalés
|
19
|
+
spam_detection_mailer:
|
20
|
+
notify_detection:
|
21
|
+
subject: Rapport de la tâche de détection automatique de spam
|
22
|
+
reported_user:
|
23
|
+
details: L'utilisateur a été signalé en raison d'une forte probabilité de spam
|
24
|
+
par la tâche de détection automatique de spam avec une probabilité de %{probability}%
|
@@ -24,6 +24,14 @@ module Decidim
|
|
24
24
|
raise NotImplementedError
|
25
25
|
end
|
26
26
|
|
27
|
+
def reason
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def details
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
27
35
|
def moderation_user
|
28
36
|
moderation_admin_params = {
|
29
37
|
name: SPAM_USER[:name],
|
@@ -56,9 +64,10 @@ module Decidim
|
|
56
64
|
end
|
57
65
|
|
58
66
|
def add_spam_detection_metadata!(metadata)
|
59
|
-
@user.
|
60
|
-
|
61
|
-
|
67
|
+
@user.extended_data = @user.extended_data
|
68
|
+
.dup
|
69
|
+
.deep_merge("spam_detection" => metadata)
|
70
|
+
@user.save(validate: false)
|
62
71
|
end
|
63
72
|
end
|
64
73
|
end
|
@@ -9,28 +9,60 @@ module Decidim
|
|
9
9
|
prepend Decidim::SpamDetection::Command
|
10
10
|
|
11
11
|
def call
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
ActiveRecord::Base.transaction do
|
13
|
+
create_user_moderation
|
14
|
+
block!
|
15
|
+
register_justification!
|
16
|
+
notify_user!
|
17
|
+
add_spam_detection_metadata!({ "blocked_at" => Time.current, "spam_probability" => @probability })
|
18
|
+
end
|
15
19
|
|
16
|
-
|
17
|
-
user = @user
|
20
|
+
Rails.logger.info("User with id #{@user["id"]} was blocked for spam with a probability of #{@probability}%")
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
form.define_singleton_method(:blocking_user) { moderator }
|
22
|
-
|
23
|
-
Decidim::Admin::BlockUser.call(form)
|
22
|
+
:ok
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
"blocked_at" => Time.current,
|
27
|
-
"spam_probability" => @probability
|
28
|
-
})
|
25
|
+
private
|
29
26
|
|
27
|
+
def create_user_moderation
|
30
28
|
@user.create_user_moderation
|
31
|
-
|
29
|
+
end
|
32
30
|
|
33
|
-
|
31
|
+
def register_justification!
|
32
|
+
UserBlock.create!(justification: reason, user: @user, blocking_user: @moderator)
|
33
|
+
end
|
34
|
+
|
35
|
+
def notify_user!
|
36
|
+
Decidim::BlockUserJob.perform_later(@user, reason)
|
37
|
+
end
|
38
|
+
|
39
|
+
def block!
|
40
|
+
Decidim.traceability.perform_action!(
|
41
|
+
"block",
|
42
|
+
@user,
|
43
|
+
@moderator,
|
44
|
+
extra: {
|
45
|
+
reportable_type: @user.class.name,
|
46
|
+
current_justification: reason
|
47
|
+
},
|
48
|
+
resource: {
|
49
|
+
# Make sure the action log entry gets the original user name instead
|
50
|
+
# of "Blocked user". Otherwise the log entries would show funny
|
51
|
+
# messages such as "Mr. Admin blocked user Blocked user"-
|
52
|
+
title: @user.name
|
53
|
+
}
|
54
|
+
) do
|
55
|
+
@user.blocked = true
|
56
|
+
@user.blocked_at = Time.current
|
57
|
+
@user.blocking = @current_blocking
|
58
|
+
@user.extended_data["user_name"] = @user.name
|
59
|
+
@user.name = "Blocked user"
|
60
|
+
@user.save!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def reason
|
65
|
+
I18n.t("blocked_user.reason", probability: @probability)
|
34
66
|
end
|
35
67
|
end
|
36
68
|
end
|
@@ -9,29 +9,43 @@ module Decidim
|
|
9
9
|
prepend Decidim::SpamDetection::Command
|
10
10
|
|
11
11
|
def call
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
ActiveRecord::Base.transaction do
|
13
|
+
find_or_create_moderation!
|
14
|
+
create_report!
|
15
|
+
update_report_count!
|
16
|
+
add_spam_detection_metadata!({ "reported_at" => Time.current, "spam_probability" => @probability })
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
moderator = @moderator
|
19
|
-
user = @user
|
19
|
+
Rails.logger.info("User with id #{@user.id} was reported for spam with a probability of #{@probability}%")
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
report.define_singleton_method(:current_user) { moderator }
|
24
|
-
report.define_singleton_method(:reportable) { user }
|
25
|
-
report.call
|
21
|
+
:ok
|
22
|
+
end
|
26
23
|
|
27
|
-
|
28
|
-
"reported_at" => Time.current,
|
29
|
-
"spam_probability" => @probability
|
30
|
-
})
|
24
|
+
private
|
31
25
|
|
32
|
-
|
26
|
+
def reason
|
27
|
+
"spam"
|
28
|
+
end
|
33
29
|
|
34
|
-
|
30
|
+
def details
|
31
|
+
I18n.t("reported_user.details", probability: @probability)
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_or_create_moderation!
|
35
|
+
@moderation = UserModeration.find_or_create_by!(user: @user)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_report!
|
39
|
+
@report = UserReport.create!(
|
40
|
+
moderation: @moderation,
|
41
|
+
user: @moderator,
|
42
|
+
reason: reason,
|
43
|
+
details: details
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_report_count!
|
48
|
+
@moderation.update!(report_count: @moderation.report_count + 1)
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decidim-spam_detection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Armand Fardeau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: decidim-core
|
@@ -24,7 +24,10 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.25'
|
27
|
-
description: ".
|
27
|
+
description: " SpamDetection is a detection bot made by OpenSourcePolitics. \n It
|
28
|
+
works with a spam detection service (https://github.com/OpenSourcePolitics/spam_detection)
|
29
|
+
\n which marks the user with a spam probability score, \n between 0.7 and 0.99
|
30
|
+
it is probable, and above 0.99 it is very sure.\n"
|
28
31
|
email:
|
29
32
|
- fardeauarmand@gmail.com
|
30
33
|
executables: []
|
@@ -37,10 +40,15 @@ files:
|
|
37
40
|
- app/commands/decidim/admin/unblock_user.rb
|
38
41
|
- app/commands/decidim/admin/unreport_user.rb
|
39
42
|
- app/jobs/decidim/spam_detection/mark_users_job.rb
|
43
|
+
- app/jobs/decidim/spam_detection/notify_admins.rb
|
44
|
+
- app/mailers/decidim/spam_detection/spam_detection_mailer.rb
|
40
45
|
- app/services/decidim/spam_detection/mark_users_service.rb
|
41
|
-
-
|
46
|
+
- app/views/decidim/spam_detection/spam_detection_mailer/notify_detection.html.erb
|
42
47
|
- config/i18n-tasks.yml
|
48
|
+
- config/locales/ca.yml
|
43
49
|
- config/locales/en.yml
|
50
|
+
- config/locales/es.yml
|
51
|
+
- config/locales/fr.yml
|
44
52
|
- lib/decidim/spam_detection.rb
|
45
53
|
- lib/decidim/spam_detection/abstract_spam_user_command.rb
|
46
54
|
- lib/decidim/spam_detection/admin.rb
|
@@ -55,7 +63,7 @@ files:
|
|
55
63
|
- lib/decidim/spam_detection/test/factories.rb
|
56
64
|
- lib/decidim/spam_detection/version.rb
|
57
65
|
- lib/tasks/decidim_spam_detection.rake
|
58
|
-
homepage: https://github.com/
|
66
|
+
homepage: https://github.com/OpenSourcePolitics/decidim-spam_detection
|
59
67
|
licenses:
|
60
68
|
- AGPL-3.0
|
61
69
|
metadata: {}
|
data/config/assets.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
base_path = File.expand_path("..", __dir__)
|
4
|
-
|
5
|
-
Decidim::Webpacker.register_path("#{base_path}/app/packs")
|
6
|
-
Decidim::Webpacker.register_entrypoints(
|
7
|
-
decidim_spam_detection: "#{base_path}/app/packs/entrypoints/decidim_spam_detection.js"
|
8
|
-
)
|
9
|
-
Decidim::Webpacker.register_stylesheet_import("stylesheets/decidim/spam_detection/spam_detection")
|