moderate 1.0.0.beta1 → 1.0.0
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/.rubocop.yml +96 -1
- data/.simplecov +2 -10
- data/AGENTS.md +1 -1
- data/Appraisals +10 -8
- data/CHANGELOG.md +660 -20
- data/CLAUDE.md +1 -1
- data/README.md +149 -7
- data/Rakefile +2 -0
- data/app/controllers/concerns/moderate/moderation.rb +23 -11
- data/app/controllers/moderate/appeals_controller.rb +9 -5
- data/app/controllers/moderate/application_controller.rb +3 -3
- data/app/controllers/moderate/notices_controller.rb +3 -6
- data/app/helpers/moderate/engine_helper.rb +32 -11
- data/app/views/moderate/appeals/new.html.erb +6 -0
- data/app/views/moderate/transparency_reports/_summary_card.html.erb +2 -2
- data/app/views/moderate/transparency_reports/show.html.erb +5 -5
- data/config/locales/en.yml +200 -0
- data/config/locales/es.yml +177 -0
- data/context7.json +4 -0
- data/db/migrate/20261018200000_add_pending_key_to_moderate_flags.rb +139 -0
- data/db/migrate/20261018210000_add_retention_and_classifications_to_moderate_tables.rb +103 -0
- data/docs/chats.md +140 -0
- data/docs/compliance.md +92 -3
- data/docs/configuration.md +30 -2
- data/docs/dsa-notice-form.md +1 -1
- data/docs/images/moderate-user-report-block-actions.webp +0 -0
- data/docs/madmin.md +44 -14
- data/docs/notifications.md +17 -4
- data/examples/aws_rekognition_adapter.rb +6 -6
- data/examples/openai_moderation_adapter.rb +1 -1
- data/examples/regexp_pattern_adapter.rb +68 -0
- data/gemfiles/{rails_7.1.gemfile → rails_8.0.gemfile} +8 -6
- data/gemfiles/rails_8.1.gemfile +6 -5
- data/lib/generators/moderate/install_generator.rb +25 -0
- data/lib/generators/moderate/templates/create_moderate_tables.rb.erb +17 -2
- data/lib/generators/moderate/templates/initializer.rb +9 -15
- data/lib/generators/moderate/views_generator.rb +3 -3
- data/lib/moderate/attachments.rb +137 -0
- data/lib/moderate/configuration.rb +101 -12
- data/lib/moderate/engine.rb +86 -25
- data/lib/moderate/event.rb +21 -0
- data/lib/moderate/filters/base.rb +1 -0
- data/lib/moderate/filters/wordlist.rb +5 -2
- data/lib/moderate/jobs/classify_job.rb +100 -16
- data/lib/moderate/label.rb +2 -2
- data/lib/moderate/macros.rb +8 -3
- data/lib/moderate/models/appeal.rb +10 -1
- data/lib/moderate/models/block.rb +23 -19
- data/lib/moderate/models/classification.rb +24 -0
- data/lib/moderate/models/concerns/actor.rb +17 -2
- data/lib/moderate/models/concerns/content_filterable.rb +241 -56
- data/lib/moderate/models/concerns/deferred_notices.rb +90 -0
- data/lib/moderate/models/concerns/reportable.rb +84 -4
- data/lib/moderate/models/flag.rb +199 -3
- data/lib/moderate/models/report.rb +180 -16
- data/lib/moderate/result.rb +3 -10
- data/lib/moderate/retention.rb +241 -0
- data/lib/moderate/services/intake_notice.rb +10 -10
- data/lib/moderate/services/intake_report.rb +42 -13
- data/lib/moderate/services/resolve_appeal.rb +8 -2
- data/lib/moderate/services/resolve_flag.rb +183 -35
- data/lib/moderate/services/resolve_report.rb +288 -35
- data/lib/moderate/text_validator.rb +15 -14
- data/lib/moderate/version.rb +1 -1
- data/lib/moderate.rb +337 -17
- data/lib/tasks/moderate.rake +23 -0
- metadata +24 -14
- data/gemfiles/rails_7.2.gemfile +0 -36
- data/lib/moderate/text.rb +0 -38
- data/lib/moderate/word_list.rb +0 -90
data/lib/moderate/text.rb
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Moderate
|
|
4
|
-
class Text
|
|
5
|
-
class << self
|
|
6
|
-
def bad_words?(text)
|
|
7
|
-
return false if text.blank?
|
|
8
|
-
|
|
9
|
-
@words_set ||= Set.new(compute_word_list)
|
|
10
|
-
text.downcase.split(/\W+/).any? { |word| @words_set.include?(word) }
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
def compute_word_list
|
|
16
|
-
@default_words ||= begin
|
|
17
|
-
words = WordList.load
|
|
18
|
-
logger.info("[moderate gem] Loaded #{words.size} words from word list")
|
|
19
|
-
words
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
result = (@default_words + Moderate.configuration.additional_words -
|
|
23
|
-
Moderate.configuration.excluded_words).to_set
|
|
24
|
-
logger.debug("[moderate gem] Final word list size: #{result.size}")
|
|
25
|
-
result
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def reset_word_list!
|
|
29
|
-
@words_set = nil
|
|
30
|
-
@default_words = nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def logger
|
|
34
|
-
@logger ||= defined?(Rails) ? Rails.logger : Logger.new($stdout)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
data/lib/moderate/word_list.rb
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'net/http'
|
|
4
|
-
require 'uri'
|
|
5
|
-
require 'tmpdir'
|
|
6
|
-
require 'logger'
|
|
7
|
-
|
|
8
|
-
module Moderate
|
|
9
|
-
class WordList
|
|
10
|
-
WORD_LIST_URL = 'https://raw.githubusercontent.com/coffee-and-fun/google-profanity-words/main/data/en.txt'
|
|
11
|
-
CACHE_TTL = 30 * 24 * 60 * 60 # 30 days in seconds
|
|
12
|
-
|
|
13
|
-
class << self
|
|
14
|
-
def load
|
|
15
|
-
cache_path = cache_file_path
|
|
16
|
-
|
|
17
|
-
begin
|
|
18
|
-
if File.exist?(cache_path)
|
|
19
|
-
if cache_valid?(cache_path)
|
|
20
|
-
words = File.read(cache_path, encoding: 'UTF-8').split("\n").to_set
|
|
21
|
-
return words unless words.empty?
|
|
22
|
-
else
|
|
23
|
-
logger.info("[moderate gem] Cache expired (older than #{CACHE_TTL / 86400} days). Refreshing word list...")
|
|
24
|
-
return download_and_cache(cache_path)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
logger.info("[moderate gem] No cache found. Downloading word list for the first time...")
|
|
29
|
-
download_and_cache(cache_path)
|
|
30
|
-
rescue StandardError => e
|
|
31
|
-
logger.error("[moderate gem] Error loading word list: #{e.message}")
|
|
32
|
-
logger.debug("[moderate gem] #{e.backtrace.join("\n")}")
|
|
33
|
-
raise Moderate::Error, "Failed to load bad words list: #{e.message}"
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def refresh_word_list!
|
|
38
|
-
logger.info("[moderate gem] Manually refreshing word list")
|
|
39
|
-
cache_path = cache_file_path
|
|
40
|
-
File.delete(cache_path) if File.exist?(cache_path)
|
|
41
|
-
download_and_cache(cache_path)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def cache_valid?(cache_path)
|
|
47
|
-
return false unless File.exist?(cache_path)
|
|
48
|
-
|
|
49
|
-
cache_age = Time.now - File.mtime(cache_path)
|
|
50
|
-
cache_age <= CACHE_TTL
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def cache_file_path
|
|
54
|
-
if defined?(Rails)
|
|
55
|
-
Rails.root.join('tmp', 'moderate_bad_words.txt')
|
|
56
|
-
else
|
|
57
|
-
File.join(Dir.tmpdir, 'moderate_bad_words.txt')
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def download_and_cache(cache_path)
|
|
62
|
-
uri = URI(WORD_LIST_URL)
|
|
63
|
-
response = Net::HTTP.get_response(uri)
|
|
64
|
-
|
|
65
|
-
unless response.is_a?(Net::HTTPSuccess)
|
|
66
|
-
raise Moderate::Error, "Failed to download word list. HTTP Status: #{response.code}"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
content = response.body.force_encoding('UTF-8')
|
|
70
|
-
words = content.split("\n").map(&:strip).reject(&:empty?).to_set
|
|
71
|
-
|
|
72
|
-
if words.empty?
|
|
73
|
-
raise Moderate::Error, "Downloaded word list is empty"
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
logger.info("[moderate gem] Successfully downloaded and cached #{words.size} words to #{cache_path}")
|
|
77
|
-
logger.info("[moderate gem] Next cache refresh will occur after: #{Time.now + CACHE_TTL}")
|
|
78
|
-
|
|
79
|
-
File.write(cache_path, content, encoding: 'UTF-8')
|
|
80
|
-
logger.debug("[moderate gem] Cached word list to: #{cache_path}")
|
|
81
|
-
|
|
82
|
-
words
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def logger
|
|
86
|
-
@logger ||= defined?(Rails) ? Rails.logger : Logger.new($stdout)
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|