federails-moderation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -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,7 @@
1
+ module Federails::Moderation
2
+ class ApplicationService
3
+ def self.call(*args, &block)
4
+ new(*args, &block).call
5
+ end
6
+ end
7
+ 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)
@@ -0,0 +1,5 @@
1
+ require "fediverse/inbox"
2
+
3
+ Rails.application.config.after_initialize do
4
+ Fediverse::Inbox.register_handler "Flag", "*", Federails::Moderation::ReportCreationService, :call
5
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -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,8 @@
1
+ class CreateFederailsModerationDomainBlocks < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :federails_moderation_domain_blocks do |t|
4
+ t.string "domain", null: false, index: { unique: true }
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ require "federails"
2
+
3
+ module Federails
4
+ module Moderation
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Federails::Moderation
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,6 @@
1
+ module Federails
2
+ module Moderation
3
+ class Railtie < ::Rails::Railtie
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Federails
2
+ module Moderation
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require "federails/moderation/version"
2
+ require "federails/moderation/engine"
3
+
4
+ module Federails
5
+ module Moderation
6
+ def self.table_name_prefix
7
+ "federails_moderation_"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :federails_moderation do
3
+ # # Task goes here
4
+ # 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: []