Octree |
Contact Us |
Our Blog (FR)
Decidim |
Decidim Docs |
Participatory Governance (META DECIDIM)
Decidim Community (Matrix+Element.io)
Anti-Spam for decidim
Flexible detection and reaction on spam for Decidim, sponsored by:
Available Detection
This anti-spam gem comes with three core detection for spam:
- Allowed TLDs: A list of all extensions (like
.com
) that are allowed. If an extension not present in the list is detected, aNot Allowed Tlds Found
is raised - Forbidden TLDs: A list of all extensions (like
.finance
) that are forbidden. If an extension not present in the list is detected, aForbidden Tlds Found
is raised - Words: A dictionnary of forbidden word. If a forbidden word is found, a
Word Found
is raised.
Rules
A rule is a link between the detection and what you do with the user (the agent). We work on two sets of rules: SPAM and SUSPICIOUS. You can this way define two kinds of actions and have a more fine-grained spam policy. For example, you could:
- When a
.xxx
domain is found, Lock the user - When a domain that is not
.com, .ch, .eu, .io
, Signalize the user to the admins.
Agent
An agent is activated by a rule with a detected content. We have for now two agent:
- Lock: Use the
Devise::Lockable
strategy to lock the user, and send unlock instructions by email - Sinalize: Do nothing, but sinalize the user to the admins
Installation
Add this line to your application's Gemfile:
gem "decidim-spam_signal", git: "https://github.com/octree-gva/decidim-module-spam_signal"
Then execute:
bundle
bundle exec rails decidim_spam_signal:install:migrations
bundle exec rails db:migrate
Environment Variable
export USER_BOT_EMAIL='bot@example.org' # user-bot used for signaling the spammers
Scripting
We don't have UI for this (and probably won't), so here some useful script:
Who is locked for more than a month and didn't unlock their account (CSV)?
require "csv"
headers=["id", "nickname", "email", "vérouillé le"]
CSV.open("locked-users.csv", "w") do |csv|
csv << headers
Decidim::User.where("locked_at < ?", 31.days.ago).pluck(:id, :nickname, :email, :locked_at).each {|usr| csv << usr }
end
Apply your spam strategy to existing data?
Decidim::User.all.each {|user| user.valid? }
Decidim::Comments::Comment.all.each {|comment| comment.valid? }
Contributing
You are welcome to fill issues in this repo, and help if you have time.
Add your own detection
An agent have two classes: A command class, and a form class (for settings).
For Command
, here some restrictions:
- You need to define a form to a
Decidim::Form
class, with absolute::<name>
namespace. You will have trouble with memory allocation if you don't - You need to suffix your command with
ScanCommand
name. That's a convention we use to avoid configurations. - call must broadcast :ok or one of the
output_symbols
defined
class CustomScanCommand < Decidim::SpamSignal::Scans::ScanHandler
def self.form
::CustomSettingsForm
end
def self.output_symbols
[:foo]
end
def call
return broadcast(:foo) if config["foo_enabled"]
broadcast(:ok)
end
end
For Settings:
- You need to include the module
Decidim::SpamSignal::SettingsForm
to make the whole thing work - We have special naming rules on how you name your attributes:
- if its start with
is_
or end with_enabled
, you will have a checkbox - if its ends with
_csv
you will have a textarea - the rest is like default decidim's form builder.
- if its start with
class CustomSettingsForm < Decidim::Form
include Decidim::SpamSignal::SettingsForm
attribute :foo_enabled, Boolean, default: false
# You can add validation here ;)
end
And now, you can register your command in an initializer:
Decidim::SpamSignal::Scans::ScansRepository.instance.register(:custom, ::CustomScanCommand)
And set the i18n fields:
decidim.spam_signal.scans.custom.name
decidim.spam_signal.scans.custom.description
decidim.spam_signal.forms.custom.custom_settings_form.foo_enabled
Add your own agent
We won't advise you create your own agent, as it seems the Lock agent as the strongest agent is already a good compromise for spam control. If you really want it, that's simple, it's almost like detection:
- Use
CopCommand
to suffix your command - Add a
self.form
to your settings (it can returnsnil
) - Use these different attributes:
-
suspicious_user
the user that have done something wrong -
admin_reporter
an admin user only used to report spam -
errors
an ActiveRecord error, to forbid saving (@see Working With Validation Errors in Rails Guides)
-
class CustomCopCommand < Decidim::SpamSignal::Cops::CopHandler
def self.form
::CustomSettingsForm
end
def call
suspicious_user.lock_access! # or do whatever with the admin
end
end
Settings are exactly the same logic, and i18n fields:
decidim.spam_signal.cops.custom.name
decidim.spam_signal.cops.custom.description
decidim.spam_signal.forms.custom.custom_settings_form.foo_enabled
To register it in an initializer:
Decidim::SpamSignal::Cops::CopsRepository.instance.register(:custom, ::CustomCopCommand)
N.B You will find in the code the term cop
to refer an agent.
This is provocative on purpose: participation must be as inclusive as possible, restricting participation is BAD. You are warned, be your own cop.
License
This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.