federails-moderation 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +80 -0
- data/Rakefile +8 -0
- data/app/models/federails/moderation/domain_block.rb +9 -0
- data/app/models/federails/moderation/report.rb +24 -0
- data/app/services/federails/moderation/application_service.rb +7 -0
- data/app/services/federails/moderation/report_creation_service.rb +40 -0
- data/app/validators/federails/moderation/domain_name_validator.rb +9 -0
- data/config/initializers/extensions.rb +9 -0
- data/config/initializers/handlers.rb +5 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20241127105043_create_federails_moderation_reports.rb +13 -0
- data/db/migrate/20241128115659_create_federails_moderation_domain_blocks.rb +8 -0
- data/lib/federails/moderation/engine.rb +9 -0
- data/lib/federails/moderation/filtered_inbox.rb +18 -0
- data/lib/federails/moderation/filtered_notifier.rb +16 -0
- data/lib/federails/moderation/railtie.rb +6 -0
- data/lib/federails/moderation/version.rb +5 -0
- data/lib/federails/moderation.rb +10 -0
- data/lib/tasks/federails/moderation_tasks.rake +4 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 77a1dbaadfa4f01d0bfa8be0f60c375f058681eae11f86c7653c16287d2807a4
|
4
|
+
data.tar.gz: 73a7dce31e8dda52937779d0c3028d5b52996d01c77203456997715db7999ca8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dfd71ccba76ac2565aae73c3f4c884ee30c4ae9ac5daecc7db5f8ac2b4d924da599e8c260d6a111cc91341c8897e3228fad491e4230e576986109c7afcdc1530
|
7
|
+
data.tar.gz: 1ab673cae009ca57ab252b4cbd777f2b7e451b63f017d6a73598bba8c3e5befaab962184eb9fe88bfb9b2431f319dc0db9831f7019c167a6a667673b5b683a58
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Federails::Moderation
|
2
|
+
|
3
|
+
A gem that provides moderation capabilities for [Federails](https://gitlab.com/experimentslabs/federails). It adds the following features:
|
4
|
+
|
5
|
+
* Handle incoming `Flag` activities, normally used for reporting content to moderators
|
6
|
+
* Block federation with particular servers
|
7
|
+
|
8
|
+
## Requirements
|
9
|
+
|
10
|
+
* [Federails](https://gitlab.com/experimentslabs/federails) >= 0.4
|
11
|
+
* Ruby >= 3.0
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
Add the gem to your Rails application:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
> bundle add "federails-moderation"
|
18
|
+
```
|
19
|
+
|
20
|
+
Then install the database migrations:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
> bin/rails federails_moderation:install:migrations
|
24
|
+
> bin/rails db:migrate
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Reports
|
30
|
+
|
31
|
+
When your application receives a `Flag` activitypub message, it will create a `Federails::Moderation::Report` record with the details.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# Find all unresolved reports
|
35
|
+
reports = Federails::Moderation::Report.where(resolution: nil)
|
36
|
+
r = reports.first
|
37
|
+
|
38
|
+
# Reports can have a comment that was sent with them:
|
39
|
+
puts r.content
|
40
|
+
# "This message is spam"
|
41
|
+
|
42
|
+
# It will be associated with the actor that sent it:
|
43
|
+
puts r.actor.at_address
|
44
|
+
# "@floppy@mastodon.me.uk"
|
45
|
+
|
46
|
+
# Deal with the report in your app, then either:
|
47
|
+
r.resolve!
|
48
|
+
# or, you can ignore it
|
49
|
+
r.ignore!
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
### Domain Blocks
|
54
|
+
|
55
|
+
You can block domains that are abusive like so:
|
56
|
+
|
57
|
+
```
|
58
|
+
Federails::Moderation::DomainBlock.create(domain: "spammer.com")
|
59
|
+
```
|
60
|
+
|
61
|
+
Once the block is created, incoming messages from that domain, or from any actor on that domain, will be ignored. Also, outgoing messages to that domain will be dropped before being delivered.
|
62
|
+
|
63
|
+
Actors from the blocked domain aren't currently auto-removed; this may be added in the future, but for now that's left up to your application.
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/manyfold3d/federails-moderation. This project is intended to be a safe, welcoming space for collaboration; everyone interacting in the project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/manyfold3d/federails-moderation/blob/master/CODE_OF_CONDUCT.md).
|
68
|
+
|
69
|
+
## Support & Community
|
70
|
+
|
71
|
+
For help, join the main [Federails matrix chat room](https://matrix.to/#/#federails:matrix.org).
|
72
|
+
|
73
|
+
## Acknowledgements
|
74
|
+
|
75
|
+
This gem builds on top of [Federails](https://gitlab.com/experimentslabs/federails), by [Manuel Tancoigne](https://gitlab.com/mtancoigne).
|
76
|
+
|
77
|
+
This gem was created as part of [Manyfold](https://manyfold.app), with funding from [NGI0 Entrust](https://nlnet.nl/entrust), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program.
|
78
|
+
|
79
|
+
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
|
80
|
+
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/entrust)
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
module Federails::Moderation
|
2
|
+
class DomainBlock < ApplicationRecord
|
3
|
+
validates :domain, presence: true, uniqueness: true, "federails/moderation/domain_name": true
|
4
|
+
|
5
|
+
def self.blocked?(query)
|
6
|
+
self.exists?(domain: [ query, PublicSuffix.parse(query).domain ])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Federails::Moderation::Report < ApplicationRecord
|
2
|
+
belongs_to :federails_actor, class_name: "Federails::Actor", optional: true
|
3
|
+
belongs_to :object, polymorphic: true, optional: true
|
4
|
+
|
5
|
+
def resolve!
|
6
|
+
update!(resolved_at: DateTime.now, resolution: "resolved")
|
7
|
+
end
|
8
|
+
|
9
|
+
def ignore!
|
10
|
+
update!(resolved_at: DateTime.now, resolution: "ignored")
|
11
|
+
end
|
12
|
+
|
13
|
+
def reporter_address
|
14
|
+
if federails_actor
|
15
|
+
federails_actor.at_address
|
16
|
+
else
|
17
|
+
URI.parse(federated_url).host
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def local?
|
22
|
+
federails_actor&.local?
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Federails::Moderation
|
2
|
+
class ReportCreationService < ApplicationService
|
3
|
+
def initialize(activity)
|
4
|
+
@activity = activity
|
5
|
+
Rails.logger.info "Report recieved: #{@activity.inspect}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
Report.create!(
|
10
|
+
federails_actor: find_reporter,
|
11
|
+
object: find_objects.first,
|
12
|
+
federated_url: @activity["id"],
|
13
|
+
content: @activity["content"]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def find_reporter
|
20
|
+
Federails::Actor.find_or_create_by_federation_url(@activity["actor"])
|
21
|
+
rescue ActiveRecord::RecordInvalid, URI::InvalidURIError
|
22
|
+
# Anonymous reports will try to create invalid actors, so we end up here
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_objects
|
27
|
+
objects = Array(@activity["object"]).map { |url|
|
28
|
+
begin
|
29
|
+
# Find reported actors
|
30
|
+
Federails::Actor.find_by_federation_url(url)
|
31
|
+
# Other objects could be here too, but they will come later
|
32
|
+
rescue ActiveRecord::RecordNotFound
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
}.compact
|
36
|
+
Rails.logger.warn "Federails::Moderation cannot currently handle multiple objects in a single report, only the first has been logged" if objects.length > 1
|
37
|
+
objects
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "public_suffix"
|
2
|
+
|
3
|
+
module Federails::Moderation
|
4
|
+
class DomainNameValidator < ActiveModel::EachValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
record.errors.add attribute, :invalid unless PublicSuffix.valid?(value, default_rule: nil)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "fediverse/inbox"
|
2
|
+
require "federails/moderation/filtered_inbox"
|
3
|
+
|
4
|
+
Fediverse::Inbox.send(:include, Federails::Moderation::FilteredInbox)
|
5
|
+
|
6
|
+
require "fediverse/notifier"
|
7
|
+
require "federails/moderation/filtered_notifier"
|
8
|
+
|
9
|
+
Fediverse::Notifier.send(:include, Federails::Moderation::FilteredNotifier)
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateFederailsModerationReports < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :federails_moderation_reports do |t|
|
4
|
+
t.string :federated_url
|
5
|
+
t.references :federails_actor, foreign_key: true
|
6
|
+
t.string :content
|
7
|
+
t.references :object, polymorphic: true
|
8
|
+
t.datetime :resolved_at
|
9
|
+
t.string :resolution
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Federails::Moderation
|
2
|
+
module FilteredInbox
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class <<self
|
7
|
+
def filter_request(payload)
|
8
|
+
filtered_dispatch_request(payload) unless
|
9
|
+
(payload["id"] && DomainBlock.blocked?(URI.parse(payload["id"]).host)) ||
|
10
|
+
(payload["actor"] && DomainBlock.blocked?(URI.parse(payload["actor"]).host))
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :filtered_dispatch_request, :dispatch_request
|
14
|
+
alias_method :dispatch_request, :filter_request
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Federails::Moderation
|
2
|
+
module FilteredNotifier
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class <<self
|
7
|
+
def filter_post_to_inbox(to:, message:, from: nil)
|
8
|
+
filtered_post_to_inbox(to: to, message: message, from: from) unless DomainBlock.blocked?(to.server)
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :filtered_post_to_inbox, :post_to_inbox
|
12
|
+
alias_method :post_to_inbox, :filter_post_to_inbox
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: federails-moderation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Smith
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.2.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.2.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: federails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: public_suffix
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '6.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '6.0'
|
55
|
+
description: Moderation additions for Federails; reporting, limit/suspend, server
|
56
|
+
blocking, etc
|
57
|
+
email:
|
58
|
+
- james@floppy.org.uk
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- app/models/federails/moderation/domain_block.rb
|
66
|
+
- app/models/federails/moderation/report.rb
|
67
|
+
- app/services/federails/moderation/application_service.rb
|
68
|
+
- app/services/federails/moderation/report_creation_service.rb
|
69
|
+
- app/validators/federails/moderation/domain_name_validator.rb
|
70
|
+
- config/initializers/extensions.rb
|
71
|
+
- config/initializers/handlers.rb
|
72
|
+
- config/routes.rb
|
73
|
+
- db/migrate/20241127105043_create_federails_moderation_reports.rb
|
74
|
+
- db/migrate/20241128115659_create_federails_moderation_domain_blocks.rb
|
75
|
+
- lib/federails/moderation.rb
|
76
|
+
- lib/federails/moderation/engine.rb
|
77
|
+
- lib/federails/moderation/filtered_inbox.rb
|
78
|
+
- lib/federails/moderation/filtered_notifier.rb
|
79
|
+
- lib/federails/moderation/railtie.rb
|
80
|
+
- lib/federails/moderation/version.rb
|
81
|
+
- lib/tasks/federails/moderation_tasks.rake
|
82
|
+
homepage: https://github.com/manyfold3d/federails-moderation
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata:
|
86
|
+
homepage_uri: https://github.com/manyfold3d/federails-moderation
|
87
|
+
source_code_uri: https://github.com/manyfold3d/federails-moderation
|
88
|
+
changelog_uri: https://github.com/manyfold3d/federails-moderation/releases
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.3'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubygems_version: 3.5.22
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Moderation additions for Federails.
|
108
|
+
test_files: []
|