Add: #605 リモート投稿に適用するセンシティブワード設定 (#612)

This commit is contained in:
KMY(雪あすか) 2024-02-27 10:14:42 +09:00 committed by GitHub
parent 7d96d5828e
commit 9dd11117db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 149 additions and 11 deletions

View file

@ -34,7 +34,9 @@ module Admin
def test_words def test_words
sensitive_words = settings_params['sensitive_words'].split(/\r\n|\r|\n/) sensitive_words = settings_params['sensitive_words'].split(/\r\n|\r|\n/)
sensitive_words_for_full = settings_params['sensitive_words_for_full'].split(/\r\n|\r|\n/) sensitive_words_for_full = settings_params['sensitive_words_for_full'].split(/\r\n|\r|\n/)
Admin::NgWord.reject_with_custom_words?('Sample text', sensitive_words + sensitive_words_for_full) sensitive_words_all = settings_params['sensitive_words_all'].split(/\r\n|\r|\n/)
sensitive_words_all_for_full = settings_params['sensitive_words_all_for_full'].split(/\r\n|\r|\n/)
Admin::NgWord.reject_with_custom_words?('Sample text', sensitive_words + sensitive_words_for_full + sensitive_words_all + sensitive_words_all_for_full)
end end
def after_update_redirect_path def after_update_redirect_path

View file

@ -81,6 +81,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
@raw_mention_uris = [] @raw_mention_uris = []
process_status_params process_status_params
process_sensitive_words
process_tags process_tags
process_audience process_audience
@ -144,6 +145,14 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
} }
end end
def process_sensitive_words
return unless %i(public public_unlisted login).include?(@params[:visibility].to_sym) && Admin::SensitiveWord.sensitive?(@params[:text], @params[:spoiler_text], local: false)
@params[:text] = Admin::SensitiveWord.modified_text(@params[:text], @params[:spoiler_text])
@params[:spoiler_text] = Admin::SensitiveWord.alternative_text
@params[:sensitive] = true
end
def valid_status? def valid_status?
valid = true valid = true
valid = false if valid && !valid_status_for_ng_rule? valid = false if valid && !valid_status_for_ng_rule?

View file

@ -2,8 +2,13 @@
class Admin::SensitiveWord class Admin::SensitiveWord
class << self class << self
def sensitive?(text, spoiler_text) def sensitive?(text, spoiler_text, local: true)
exposure_text = spoiler_text.presence || text exposure_text = spoiler_text.presence || text
sensitive = (spoiler_text.blank? && sensitive_words_all.any? { |word| include?(text, word) }) ||
sensitive_words_all_for_full.any? { |word| include?(exposure_text, word) }
return sensitive if sensitive || !local
(spoiler_text.blank? && sensitive_words.any? { |word| include?(text, word) }) || (spoiler_text.blank? && sensitive_words.any? { |word| include?(text, word) }) ||
sensitive_words_for_full.any? { |word| include?(exposure_text, word) } sensitive_words_for_full.any? { |word| include?(exposure_text, word) }
end end
@ -12,6 +17,10 @@ class Admin::SensitiveWord
spoiler_text.present? ? "#{spoiler_text}\n\n#{text}" : text spoiler_text.present? ? "#{spoiler_text}\n\n#{text}" : text
end end
def alternative_text
Setting.auto_warning_text.presence || I18n.t('admin.sensitive_words.alert') || 'CW'
end
private private
def include?(text, word) def include?(text, word)
@ -29,5 +38,13 @@ class Admin::SensitiveWord
def sensitive_words_for_full def sensitive_words_for_full
Setting.sensitive_words_for_full || [] Setting.sensitive_words_for_full || []
end end
def sensitive_words_all
Setting.sensitive_words_all || []
end
def sensitive_words_all_for_full
Setting.sensitive_words_all_for_full || []
end
end end
end end

View file

@ -53,6 +53,9 @@ class Form::AdminSettings
post_stranger_mentions_max post_stranger_mentions_max
sensitive_words sensitive_words
sensitive_words_for_full sensitive_words_for_full
sensitive_words_all
sensitive_words_all_for_full
auto_warning_text
authorized_fetch authorized_fetch
receive_other_servers_emoji_reaction receive_other_servers_emoji_reaction
streaming_other_servers_emoji_reaction streaming_other_servers_emoji_reaction
@ -127,6 +130,8 @@ class Form::AdminSettings
ng_words_for_stranger_mention ng_words_for_stranger_mention
sensitive_words sensitive_words
sensitive_words_for_full sensitive_words_for_full
sensitive_words_all
sensitive_words_all_for_full
emoji_reaction_disallow_domains emoji_reaction_disallow_domains
permit_new_account_domains permit_new_account_domains
).freeze ).freeze

View file

@ -210,6 +210,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
@status.sensitive = @account.sensitized? || @status_parser.sensitive || false @status.sensitive = @account.sensitized? || @status_parser.sensitive || false
@status.language = @status_parser.language @status.language = @status_parser.language
process_sensitive_words
@significant_changes = text_significantly_changed? || @status.spoiler_text_changed? || @media_attachments_changed || @poll_changed @significant_changes = text_significantly_changed? || @status.spoiler_text_changed? || @media_attachments_changed || @poll_changed
@status.edited_at = @status_parser.edited_at if significant_changes? @status.edited_at = @status_parser.edited_at if significant_changes?
@ -217,6 +219,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
@status.save! @status.save!
end end
def process_sensitive_words
return unless %i(public public_unlisted login).include?(@status.visibility.to_sym) && Admin::SensitiveWord.sensitive?(@status.text, @status.spoiler_text, local: false)
@status.text = Admin::SensitiveWord.modified_text(@status.text, @status.spoiler_text)
@status.spoiler_text = Admin::SensitiveWord.alternative_text
@status.sensitive = true
end
def read_metadata def read_metadata
@raw_tags = [] @raw_tags = []
@raw_mentions = [] @raw_mentions = []

View file

@ -122,7 +122,8 @@ class PostStatusService < BaseService
def process_sensitive_words def process_sensitive_words
if [:public, :public_unlisted, :login].include?(@visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@text, @options[:spoiler_text] || '') if [:public, :public_unlisted, :login].include?(@visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@text, @options[:spoiler_text] || '')
@text = Admin::SensitiveWord.modified_text(@text, @options[:spoiler_text]) @text = Admin::SensitiveWord.modified_text(@text, @options[:spoiler_text])
@options[:spoiler_text] = I18n.t('admin.sensitive_words.alert') @options[:spoiler_text] = Admin::SensitiveWord.alternative_text
@sensitive = true
end end
end end

View file

@ -209,7 +209,8 @@ class UpdateStatusService < BaseService
return unless [:public, :public_unlisted, :login].include?(@status.visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@status.text, @status.spoiler_text || '') return unless [:public, :public_unlisted, :login].include?(@status.visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@status.text, @status.spoiler_text || '')
@status.text = Admin::SensitiveWord.modified_text(@status.text, @status.spoiler_text) @status.text = Admin::SensitiveWord.modified_text(@status.text, @status.spoiler_text)
@status.spoiler_text = I18n.t('admin.sensitive_words.alert') @status.spoiler_text = Admin::SensitiveWord.alternative_text
@status.sensitive = true
end end
def update_expiration! def update_expiration!

View file

@ -15,5 +15,14 @@
.fields-group .fields-group
= f.input :sensitive_words, wrapper: :with_label, as: :text, input_html: { rows: 8 }, label: t('admin.sensitive_words.keywords'), hint: t('admin.sensitive_words.keywords_hint') = f.input :sensitive_words, wrapper: :with_label, as: :text, input_html: { rows: 8 }, label: t('admin.sensitive_words.keywords'), hint: t('admin.sensitive_words.keywords_hint')
.fields-group
= f.input :sensitive_words_all_for_full, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.sensitive_words.keywords_all_for_all'), hint: t('admin.sensitive_words.keywords_for_all_hint')
.fields-group
= f.input :sensitive_words_all, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.sensitive_words.keywords_all'), hint: t('admin.sensitive_words.keywords_hint')
.fields-group
= f.input :auto_warning_text, wrapper: :with_label, input_html: { placeholder: t('admin.sensitive_words.alert') }, label: t('admin.sensitive_words.auto_warning_text'), hint: t('admin.sensitive_words.auto_warning_text_hint')
.actions .actions
= f.button :button, t('generic.save_changes'), type: :submit = f.button :button, t('generic.save_changes'), type: :submit

View file

@ -958,9 +958,13 @@ en:
title: Server rules title: Server rules
sensitive_words: sensitive_words:
alert: This post contains sensitive words, so alert added alert: This post contains sensitive words, so alert added
hint: The sensitive keywords setting is applied to the "Public", "Local Public", and "Logged-in Users Only" public ranges that flow through the local timeline. A mandatory warning text will be added to any post that meets the criteria. auto_warning_text: Custom warning text
keywords: Sensitive keywords for local posts auto_warning_text_hint: If not specified, the default warning text is used.
keywords_for_all: Sensitive keywords for local posts (Contains CW alert) hint: This keywords is applied to public posts only..
keywords: Sensitive keywords for local only
keywords_all: Sensitive keywords for local+remote
keywords_all_for_all: Sensitive keywords for local+remote (Contains CW alert)
keywords_for_all: Sensitive keywords for local only (Contains CW alert)
keywords_for_all_hint: The first character of the line is "?". to use regular expressions keywords_for_all_hint: The first character of the line is "?". to use regular expressions
title: Sensitive words and moderation options title: Sensitive words and moderation options
settings: settings:

View file

@ -954,9 +954,13 @@ ja:
title: サーバーのルール title: サーバーのルール
sensitive_words: sensitive_words:
alert: この投稿にはセンシティブなキーワードが含まれるため、警告文が追加されました alert: この投稿にはセンシティブなキーワードが含まれるため、警告文が追加されました
hint: センシティブなキーワードの設定は、ローカルタイムラインを流れる公開範囲「公開」「ローカル公開」「ログインユーザーのみ」に対して適用されます。条件に該当した投稿には強制的に警告文章が追加されます。 auto_warning_text: カスタム警告文
keywords: ローカル投稿のみに適用するセンシティブなキーワード(警告文は除外) auto_warning_text_hint: 指定しなかった場合は、各言語のデフォルト警告文が使用されます
keywords_for_all: ローカル投稿のみに適用するセンシティブなキーワード(警告文にも適用) hint: センシティブなキーワードの設定は、当サーバーのローカルユーザーによる公開範囲「公開」「ローカル公開」「ログインユーザーのみ」に対して適用されます。
keywords: ローカルの投稿に適用するセンシティブなキーワード(警告文は除外)
keywords_all: ローカル・リモートの投稿に適用するセンシティブなキーワード(警告文は除外)
keywords_all_for_all: ローカル・リモートの投稿に適用するセンシティブなキーワード(警告文にも適用)
keywords_for_all: ローカルの投稿に適用するセンシティブなキーワード(警告文にも適用)
keywords_for_all_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。警告文にも含まれていればCWになります。行が「?」で始まっていれば正規表現が使えます keywords_for_all_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。警告文にも含まれていればCWになります。行が「?」で始まっていれば正規表現が使えます
keywords_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。ただし警告文に使用していた場合は無視されます keywords_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。ただし警告文に使用していた場合は無視されます
title: センシティブ単語と設定 title: センシティブ単語と設定

View file

@ -2087,6 +2087,45 @@ RSpec.describe ActivityPub::Activity::Create do
end end
end end
context 'when sensitive word is set' do
let(:custom_before) { true }
let(:content) { 'Lorem ipsum' }
let(:sensitive_words_all) { 'hello' }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: content,
to: 'https://www.w3.org/ns/activitystreams#Public',
}
end
before do
Form::AdminSettings.new(sensitive_words_all: sensitive_words_all, sensitive_words: 'ipsum').save
subject.perform
end
context 'when not contains sensitive words' do
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.spoiler_text).to eq ''
end
end
context 'when contains sensitive words' do
let(:content) { 'hello world' }
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.spoiler_text).to_not eq ''
end
end
end
context 'when hashtags limit is set' do context 'when hashtags limit is set' do
let(:post_hash_tags_max) { 2 } let(:post_hash_tags_max) { 2 }
let(:custom_before) { true } let(:custom_before) { true }

View file

@ -682,7 +682,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
end end
end end
context 'when ng rule is existing' do describe 'ng rule is set' do
context 'when ng rule is match' do context 'when ng rule is match' do
before do before do
Fabricate(:ng_rule, account_domain: 'example.com', status_text: 'universe') Fabricate(:ng_rule, account_domain: 'example.com', status_text: 'universe')
@ -707,5 +707,42 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
end end
end end
end end
describe 'sensitive word is set' do
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Note',
content: content,
updated: '2021-09-08T22:39:25Z',
tag: json_tags,
}
end
context 'when hit sensitive words' do
let(:content) { 'ng word aiueo' }
it 'update status' do
Form::AdminSettings.new(sensitive_words_all: 'test').save
subject.call(status, json, json)
expect(status.reload.text).to eq content
expect(status.spoiler_text).to eq ''
end
end
context 'when not hit sensitive words' do
let(:content) { 'ng word test' }
it 'update status' do
Form::AdminSettings.new(sensitive_words_all: 'test').save
subject.call(status, json, json)
expect(status.reload.text).to eq content
expect(status.spoiler_text).to_not eq ''
end
end
end
end end
end end