Change: #647 NGワードの入力フォーム (#663)

* Change: #647 NGワードの入力フォーム

* Wip: 画面改造

* テストコード、画面

* Fix: 複数の問題
This commit is contained in:
KMY(雪あすか) 2024-03-26 08:44:16 +09:00 committed by GitHub
parent 0d2b415e26
commit 95ab1f729c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 526 additions and 172 deletions

View file

@ -31,4 +31,5 @@ linters:
exclude: exclude:
- 'app/views/application/_sidebar.html.haml' - 'app/views/application/_sidebar.html.haml'
- 'app/views/admin/ng_rules/_ng_rule_fields.html.haml' - 'app/views/admin/ng_rules/_ng_rule_fields.html.haml'
- 'app/views/admin/ng_words/keywords/_ng_word.html.haml'
- 'app/views/admin/sensitive_words/_sensitive_word.html.haml' - 'app/views/admin/sensitive_words/_sensitive_word.html.haml'

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Admin
class NgWords::KeywordsController < NgWordsController
def show
super
@ng_words = ::NgWord.caches.presence || [::NgWord.new]
end
protected
def validate
begin
::NgWord.save_from_raws(settings_params_test)
return true
rescue
flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path
end
false
end
private
def after_update_redirect_path
admin_ng_words_keywords_path
end
end
end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Admin
class NgWords::SettingsController < NgWordsController
protected
def after_update_redirect_path
admin_ng_words_settings_path
end
end
end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Admin
class NgWords::WhiteListController < NgWordsController
protected
def after_update_redirect_path
admin_ng_words_white_list_path
end
end
end

View file

@ -11,13 +11,7 @@ module Admin
def create def create
authorize :ng_words, :create? authorize :ng_words, :create?
begin return unless validate
test_words
rescue
flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path
return
end
@admin_settings = Form::AdminSettings.new(settings_params) @admin_settings = Form::AdminSettings.new(settings_params)
@ -29,19 +23,24 @@ module Admin
end end
end end
private protected
def test_words def validate
ng_words = "#{settings_params['ng_words']}\n#{settings_params['ng_words_for_stranger_mention']}".split(/\r\n|\r|\n/).filter(&:present?) true
Admin::NgWord.reject_with_custom_words?('Sample text', ng_words)
end end
def after_update_redirect_path def after_update_redirect_path
admin_ng_words_path admin_ng_words_path
end end
private
def settings_params def settings_params
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS) params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
end end
def settings_params_test
params.require(:form_admin_settings)[:ng_words_test]
end
end end
end end

View file

@ -13,7 +13,7 @@ module Admin
authorize :sensitive_words, :create? authorize :sensitive_words, :create?
begin begin
test_words ::SensitiveWord.save_from_raws(settings_params_test)
rescue rescue
flash[:alert] = I18n.t('admin.ng_words.test_error') flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path redirect_to after_update_redirect_path
@ -22,7 +22,7 @@ module Admin
@admin_settings = Form::AdminSettings.new(settings_params) @admin_settings = Form::AdminSettings.new(settings_params)
if @admin_settings.save && ::SensitiveWord.save_from_raws(settings_params_test) if @admin_settings.save
flash[:notice] = I18n.t('generic.changes_saved_msg') flash[:notice] = I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path redirect_to after_update_redirect_path
else else
@ -32,11 +32,6 @@ module Admin
private private
def test_words
sensitive_words = settings_params_test['keywords'].compact.uniq
Admin::NgWord.reject_with_custom_words?('Sample text', sensitive_words)
end
def after_update_redirect_path def after_update_redirect_path
admin_sensitive_words_path admin_sensitive_words_path
end end

View file

@ -320,7 +320,8 @@ Rails.delegate(
document, document,
'#sensitive-words-table .add-row-button', '#sensitive-words-table .add-row-button',
'click', 'click',
() => { (ev) => {
ev.preventDefault();
addTableRow('sensitive-words-table'); addTableRow('sensitive-words-table');
}, },
); );
@ -329,8 +330,24 @@ Rails.delegate(
document, document,
'#sensitive-words-table .delete-row-button', '#sensitive-words-table .delete-row-button',
'click', 'click',
({ target }) => { (ev) => {
removeTableRow(target, 'sensitive-words-table'); ev.preventDefault();
removeTableRow(ev.target, 'sensitive-words-table');
},
);
Rails.delegate(document, '#ng-words-table .add-row-button', 'click', (ev) => {
ev.preventDefault();
addTableRow('ng-words-table');
});
Rails.delegate(
document,
'#ng-words-table .delete-row-button',
'click',
(ev) => {
ev.preventDefault();
removeTableRow(ev.target, 'ng-words-table');
}, },
); );

View file

@ -160,11 +160,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
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?
valid = !Admin::NgWord.reject?("#{@params[:spoiler_text]}\n#{@params[:text]}", uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?) if valid valid = !Admin::NgWord.reject?("#{@params[:spoiler_text]}\n#{@params[:text]}", uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, stranger: mention_to_local_stranger? || reference_to_local_stranger?) if valid
valid = !Admin::NgWord.hashtag_reject?(@tags.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid valid = !Admin::NgWord.hashtag_reject?(@tags.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid
valid = !Admin::NgWord.mention_reject?(@raw_mention_uris.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid valid = !Admin::NgWord.mention_reject?(@raw_mention_uris.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid
valid = !Admin::NgWord.stranger_mention_reject_with_count?(@raw_mention_uris.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid && (mention_to_local_stranger? || reference_to_local_stranger?) valid = !Admin::NgWord.stranger_mention_reject_with_count?(@raw_mention_uris.size, uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?, text: "#{@params[:spoiler_text]}\n#{@params[:text]}") if valid && (mention_to_local_stranger? || reference_to_local_stranger?)
valid = !Admin::NgWord.stranger_mention_reject?("#{@params[:spoiler_text]}\n#{@params[:text]}", uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?) if valid && (mention_to_local_stranger? || reference_to_local_stranger?)
valid = false if valid && Setting.block_unfollow_account_mention && (mention_to_local_stranger? || reference_to_local_stranger?) && !local_following_sender? valid = false if valid && Setting.block_unfollow_account_mention && (mention_to_local_stranger? || reference_to_local_stranger?) && !local_following_sender?
valid valid

View file

@ -4,20 +4,25 @@ class Admin::NgWord
class << self class << self
def reject?(text, **options) def reject?(text, **options)
text = PlainTextFormatter.new(text, false).to_s if options[:uri].present? text = PlainTextFormatter.new(text, false).to_s if options[:uri].present?
hit_word = ng_words.detect { |word| include?(text, word) ? word : nil }
if options.delete(:stranger)
::NgWord.caches.detect { |word| include?(text, word) ? word : nil }&.keyword.tap do |hit_word|
record!(:ng_words_for_stranger_mention, text, hit_word, options) if hit_word.present?
end.present?
else
::NgWord.caches.filter { |w| !w.stranger }.detect { |word| include?(text, word) ? word : nil }&.keyword.tap do |hit_word|
record!(:ng_words, text, hit_word, options) if hit_word.present? record!(:ng_words, text, hit_word, options) if hit_word.present?
hit_word.present? end.present?
end
end end
def stranger_mention_reject?(text, **options) def stranger_mention_reject?(text, **options)
text = PlainTextFormatter.new(text, false).to_s if options[:uri].present? opts = options.merge({ stranger: true })
hit_word = ng_words_for_stranger_mention.detect { |word| include?(text, word) ? word : nil } reject?(text, **opts)
record!(:ng_words_for_stranger_mention, text, hit_word, options) if hit_word.present?
hit_word.present?
end end
def reject_with_custom_words?(text, custom_ng_words) def reject_with_custom_word?(text, word)
custom_ng_words.any? { |word| include?(text, word) } include_with_regexp?(text, word)
end end
def hashtag_reject?(hashtag_count, **options) def hashtag_reject?(hashtag_count, **options)
@ -53,19 +58,15 @@ class Admin::NgWord
private private
def include?(text, word) def include?(text, word)
if word.start_with?('?') && word.size >= 2 if word.regexp
text =~ /#{word[1..]}/i text =~ /#{word.keyword}/
else else
text.include?(word) text.include?(word.keyword)
end end
end end
def ng_words def include_with_regexp?(text, word)
Setting.ng_words || [] text =~ /#{word}/i
end
def ng_words_for_stranger_mention
Setting.ng_words_for_stranger_mention || []
end end
def post_hash_tags_max def post_hash_tags_max

View file

@ -44,8 +44,6 @@ class Form::AdminSettings
delete_content_cache_without_reaction delete_content_cache_without_reaction
status_page_url status_page_url
captcha_enabled captcha_enabled
ng_words
ng_words_for_stranger_mention
stranger_mention_from_local_ng stranger_mention_from_local_ng
hide_local_users_for_anonymous hide_local_users_for_anonymous
post_hash_tags_max post_hash_tags_max
@ -122,8 +120,6 @@ class Form::AdminSettings
}.freeze }.freeze
STRING_ARRAY_KEYS = %i( STRING_ARRAY_KEYS = %i(
ng_words
ng_words_for_stranger_mention
emoji_reaction_disallow_domains emoji_reaction_disallow_domains
permit_new_account_domains permit_new_account_domains
).freeze ).freeze

87
app/models/ng_word.rb Normal file
View file

@ -0,0 +1,87 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: ng_words
#
# id :bigint(8) not null, primary key
# keyword :string not null
# regexp :boolean default(FALSE), not null
# stranger :boolean default(TRUE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class NgWord < ApplicationRecord
attr_accessor :keywords, :regexps, :strangers
validate :check_regexp
class << self
def caches
Rails.cache.fetch('ng_words') { NgWord.where.not(id: 0).order(:keyword).to_a }
end
def save_from_hashes(rows)
unmatched = caches
matched = []
NgWord.transaction do
rows.filter { |item| item[:keyword].present? }.each do |item|
exists = unmatched.find { |i| i.keyword == item[:keyword] }
if exists.present?
unmatched.delete(exists)
matched << exists
next if exists.regexp == item[:regexp] && exists.stranger == item[:stranger]
exists.update!(regexp: item[:regexp], stranger: item[:stranger])
elsif matched.none? { |i| i.keyword == item[:keyword] }
NgWord.create!(
keyword: item[:keyword],
regexp: item[:regexp],
stranger: item[:stranger]
)
end
end
NgWord.destroy(unmatched.map(&:id))
end
true
end
def save_from_raws(rows)
regexps = rows['regexps'] || []
strangers = rows['strangers'] || []
hashes = (rows['keywords'] || []).zip(rows['temporary_ids'] || []).map do |item|
temp_id = item[1]
{
keyword: item[0],
regexp: regexps.include?(temp_id),
stranger: strangers.include?(temp_id),
}
end
save_from_hashes(hashes)
end
end
private
def invalidate_cache!
Rails.cache.delete('ng_words')
end
def check_regexp
return if keyword.blank? || !regexp
begin
Admin::NgWord.reject_with_custom_word?('Sample text', keyword)
rescue
raise Mastodon::ValidationError, I18n.t('admin.ng_words.test_error')
end
end
end

View file

@ -16,6 +16,8 @@
class SensitiveWord < ApplicationRecord class SensitiveWord < ApplicationRecord
attr_accessor :keywords, :regexps, :remotes, :spoilers attr_accessor :keywords, :regexps, :remotes, :spoilers
validate :check_regexp
class << self class << self
def caches def caches
Rails.cache.fetch('sensitive_words') { SensitiveWord.where.not(id: 0).order(:keyword).to_a } Rails.cache.fetch('sensitive_words') { SensitiveWord.where.not(id: 0).order(:keyword).to_a }
@ -50,8 +52,6 @@ class SensitiveWord < ApplicationRecord
end end
true true
# rescue
# false
end end
def save_from_raws(rows) def save_from_raws(rows)
@ -78,4 +78,14 @@ class SensitiveWord < ApplicationRecord
def invalidate_cache! def invalidate_cache!
Rails.cache.delete('sensitive_words') Rails.cache.delete('sensitive_words')
end end
def check_regexp
return if keyword.blank? || !regexp
begin
Admin::NgWord.reject_with_custom_word?('Sample text', keyword)
rescue
raise Mastodon::ValidationError, I18n.t('admin.ng_words.test_error')
end
end
end end

View file

@ -162,10 +162,9 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
end end
def valid_status? def valid_status?
valid = !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status) valid = !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status, stranger: mention_to_local_stranger? || reference_to_local_stranger?)
valid = !Admin::NgWord.hashtag_reject?(@raw_tags.size) if valid valid = !Admin::NgWord.hashtag_reject?(@raw_tags.size) if valid
valid = false if valid && Admin::NgWord.mention_reject?(@raw_mentions.size, uri: @status.uri, target_type: :status, text: "#{@status_parser.spoiler_text}\n#{@status_parser.text}") valid = false if valid && Admin::NgWord.mention_reject?(@raw_mentions.size, uri: @status.uri, target_type: :status, text: "#{@status_parser.spoiler_text}\n#{@status_parser.text}")
valid = false if valid && (mention_to_local_stranger? || reference_to_local_stranger?) && Admin::NgWord.stranger_mention_reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status)
valid = false if valid && (mention_to_local_stranger? || reference_to_local_stranger?) && Admin::NgWord.stranger_mention_reject_with_count?(@raw_mentions.size, uri: @status.uri, target_type: :status, text: "#{@status_parser.spoiler_text}\n#{@status_parser.text}") valid = false if valid && (mention_to_local_stranger? || reference_to_local_stranger?) && Admin::NgWord.stranger_mention_reject_with_count?(@raw_mentions.size, uri: @status.uri, target_type: :status, text: "#{@status_parser.spoiler_text}\n#{@status_parser.text}")
valid = false if valid && (mention_to_local_stranger? || reference_to_local_stranger?) && reject_reply_exclude_followers? valid = false if valid && (mention_to_local_stranger? || reference_to_local_stranger?) && reject_reply_exclude_followers?

View file

@ -0,0 +1,10 @@
- temporary_id = defined?(@temp_id) ? @temp_id += 1 : @temp_id = 1
%tr{ class: template ? 'template-row' : nil }
%td= f.input :keywords, as: :string, input_html: { multiple: true, value: ng_word.keyword }
%td
.label_input__wrapper= f.check_box :regexps, { multiple: true, checked: ng_word.regexp }, temporary_id, nil
%td
.label_input__wrapper= f.check_box :strangers, { multiple: true, checked: ng_word.stranger }, temporary_id, nil
%td
= hidden_field_tag :'form_admin_settings[ng_words_test][temporary_ids][]', temporary_id, class: 'temporary_id'
= link_to safe_join([fa_icon('times'), t('filters.index.delete')]), '#', class: 'table-action-link delete-row-button'

View file

@ -0,0 +1,43 @@
- content_for :page_title do
= t('admin.ng_words.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :heading do
%h2= t('admin.ng_words.title')
= render partial: 'admin/ng_words/shared/links'
= simple_form_for @admin_settings, url: admin_ng_words_keywords_path, html: { method: :post } do |f|
= render 'shared/error_messages', object: @admin_settings
%p.lead
= t('admin.ng_words.preamble')
= link_to t('admin.ngword_histories.title'), admin_ngword_histories_path
%p= t 'admin.ng_words.phrases.regexp_html'
%p= t 'admin.ng_words.phrases.stranger_html'
%hr/
.table-wrapper
%table.table.keywords-table#ng-words-table
%thead
%tr
%th= t('simple_form.labels.defaults.phrase')
%th= t('admin.ng_words.phrases.regexp_short')
%th= t('admin.ng_words.phrases.stranger_short')
%th
%tbody
= f.simple_fields_for :ng_words_test, @ng_words do |keyword|
= render partial: 'ng_word', collection: @ng_words, locals: { f: keyword, template: false }
= f.simple_fields_for :ng_words_test, @ng_words do |keyword|
= render partial: 'ng_word', collection: [NgWord.new], locals: { f: keyword, template: true }
%tfoot
%tr
%td{ colspan: 4 }
= link_to safe_join([fa_icon('plus'), t('filters.edit.add_keyword')]), '#', class: 'table-action-link add-row-button'
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -4,21 +4,13 @@
- content_for :header_tags do - content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
= simple_form_for @admin_settings, url: admin_ng_words_path, html: { method: :post } do |f| - content_for :heading do
%h2= t('admin.ng_words.title')
= render partial: 'admin/ng_words/shared/links'
= simple_form_for @admin_settings, url: admin_ng_words_settings_path, html: { method: :post } do |f|
= render 'shared/error_messages', object: @admin_settings = render 'shared/error_messages', object: @admin_settings
%p.lead= t('admin.ng_words.preamble')
%p.hint
= t 'admin.ng_words.history_hint'
= link_to t('admin.ngword_histories.title'), admin_ngword_histories_path
.fields-group
= f.input :ng_words_for_stranger_mention, wrapper: :with_label, as: :text, input_html: { rows: 10 }, label: t('admin.ng_words.keywords_for_stranger_mention'), hint: t('admin.ng_words.keywords_for_stranger_mention_hint')
.fields-group
= f.input :ng_words, wrapper: :with_label, as: :text, input_html: { rows: 10 }, label: t('admin.ng_words.keywords'), hint: t('admin.ng_words.keywords_hint')
.fields-group .fields-group
= f.input :post_hash_tags_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_hash_tags_max') = f.input :post_hash_tags_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_hash_tags_max')
@ -28,17 +20,6 @@
.fields-group .fields-group
= f.input :post_mentions_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_mentions_max') = f.input :post_mentions_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_mentions_max')
%h4= t('admin.ng_words.white_list')
%p.lead
= t('admin.ng_words.white_list_hint')
= link_to t('admin.ng_words.remote_approval_list'), admin_accounts_path(status: 'remote_pending', origin: 'remote')
.fields-group
= f.input :hold_remote_new_accounts, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hold_remote_new_accounts'), hint: t('admin.ng_words.remote_approval_hint')
.fields-group
= f.input :permit_new_account_domains, wrapper: :with_label, as: :text, kmyblue: true, input_html: { rows: 6 }, label: t('admin.ng_words.permit_new_account_domains')
%h4= t('admin.ng_words.deprecated') %h4= t('admin.ng_words.deprecated')
%p.hint= t('admin.ng_words.deprecated_hint') %p.hint= t('admin.ng_words.deprecated_hint')

View file

@ -0,0 +1,6 @@
.content__heading__tabs
= render_navigation renderer: :links do |primary|
:ruby
primary.item :keywords, safe_join([fa_icon('pencil fw'), t('admin.ng_words.keywords')]), admin_ng_words_keywords_path
primary.item :white_list, safe_join([fa_icon('list fw'), t('admin.ng_words.white_list')]), admin_ng_words_white_list_path
primary.item :settings, safe_join([fa_icon('cog fw'), t('admin.ng_words.settings')]), admin_ng_words_settings_path

View file

@ -0,0 +1,25 @@
- content_for :page_title do
= t('admin.ng_words.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :heading do
%h2= t('admin.ng_words.title')
= render partial: 'admin/ng_words/shared/links'
= simple_form_for @admin_settings, url: admin_ng_words_white_list_path, html: { method: :post } do |f|
= render 'shared/error_messages', object: @admin_settings
%p.lead
= t('admin.ng_words.white_list_hint')
= link_to t('admin.ng_words.remote_approval_list'), admin_accounts_path(status: 'remote_pending', origin: 'remote')
.fields-group
= f.input :hold_remote_new_accounts, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hold_remote_new_accounts'), hint: t('admin.ng_words.remote_approval_hint')
.fields-group
= f.input :permit_new_account_domains, wrapper: :with_label, as: :text, kmyblue: true, input_html: { rows: 6 }, label: t('admin.ng_words.permit_new_account_domains')
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -3,7 +3,7 @@
.filters .filters
.back-link .back-link
= link_to admin_ng_words_path do = link_to admin_ng_words_keywords_path do
= fa_icon 'chevron-left fw' = fa_icon 'chevron-left fw'
= t('admin.ngword_histories.back_to_ng_words') = t('admin.ngword_histories.back_to_ng_words')

View file

@ -751,19 +751,21 @@ en:
deprecated_hint: These settings will be removed in the next LTS or kmyblue version 14.0, whichever comes first. Please refer to the description of each setting and replace them with the new settings if possible. deprecated_hint: These settings will be removed in the next LTS or kmyblue version 14.0, whichever comes first. Please refer to the description of each setting and replace them with the new settings if possible.
hide_local_users_for_anonymous: Hide timeline local user posts from anonymous hide_local_users_for_anonymous: Hide timeline local user posts from anonymous
hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。 hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。
history_hint: We recommend that you regularly check your NG words to make sure that you have not specified the NG words incorrectly.
hold_remote_new_accounts: Hold new remote accounts hold_remote_new_accounts: Hold new remote accounts
keywords: Reject keywords keywords: Reject keywords
keywords_for_stranger_mention: Reject keywords when mention/reply/reference/quote from strangers
keywords_for_stranger_mention_hint: This words are checked posts from other servers only.
keywords_hint: The first character of the line is "?". to use regular expressions
permit_new_account_domains: Domain list to automatically approve new users permit_new_account_domains: Domain list to automatically approve new users
preamble: This setting is useful for solving problems related to spam that are difficult to address with domain blocking. You can reject posts that contain specific keywords or too many mentions. Please consider your settings carefully to avoid deleting problematic posts. More detailed settings can be made by using "NG Rules". preamble: This setting is useful for solving problems related to spam that are difficult to address with domain blocking. You can reject posts that meet certain criteria, such as the inclusion of specific keywords. Please consider your settings carefully and check your history regularly to ensure that problem-free submissions are not deleted.
post_hash_tags_max: Hash tags max for posts post_hash_tags_max: Hash tags max for posts
post_mentions_max: Mentions max for posts post_mentions_max: Mentions max for posts
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (If the mentions include at least one person who is not a follower of yours) post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (If the mentions include at least one person who is not a follower of yours)
phrases:
regexp_html: <strong>正規</strong> 表現 にチェックの入っている項目は、正規表現を用いての比較となります。
regexp_short: 正規
stranger_html: <strong>無関</strong> 係のフォロワーからのメンション にチェックの入っている項目は、フォロー関係にないアカウントからのメンション、返信、参照などのみに適用されます。
stranger_short: 無関
remote_approval_list: List of remote accounts awaiting approval remote_approval_list: List of remote accounts awaiting approval
remote_approval_hint: Newly recognized accounts with unspecified domains will be placed in Suspended status. You can review that list and approve them if necessary. If this setting is not enabled, all remote accounts will be approved immediately. remote_approval_hint: Newly recognized accounts with unspecified domains will be placed in Suspended status. You can review that list and approve them if necessary. If this setting is not enabled, all remote accounts will be approved immediately.
settings: Settings
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: この設定は削除予定です。設定削除後は、常にチェックをつけている場合と同じ挙動になります。この動作を希望しない場合は、NGルールで代替してください。 stranger_mention_from_local_ng_hint: この設定は削除予定です。設定削除後は、常にチェックをつけている場合と同じ挙動になります。この動作を希望しない場合は、NGルールで代替してください。
test_error: Testing is returned any errors test_error: Testing is returned any errors

View file

@ -684,7 +684,7 @@ ja:
empty: NGルールが空です empty: NGルールが空です
empty_title: 空のタイトル empty_title: 空のタイトル
hit_count: ここ一週間で %{count} 件の検出 hit_count: ここ一週間で %{count} 件の検出
preamble: スパムの中にはどうしても緩すぎる条件を指定しなければならず、他の正常な投稿が規制に巻き込まれやすくなる場合があります。NGルールではアカウントや投稿の特徴などを詳細に指定して、巻き込まれる投稿を少しでも減らすことができます。設定は慎重に検討し、正常な投稿の巻き込みが最小限になるよう定期的に履歴を確認してください。 preamble: 「NGワードとスパム」機能においてどうしても緩すぎる条件を指定しなければならず、他の正常な投稿が規制に巻き込まれやすくなる場合があります。NGルールではアカウントや投稿の特徴などを詳細に指定して、巻き込まれる投稿を少しでも減らすことができます。設定は慎重に検討し、正常な投稿の巻き込みが最小限になるよう定期的に履歴を確認してください。
title: NGルール title: NGルール
new: new:
save: 新規NGルールを保存 save: 新規NGルールを保存
@ -750,19 +750,21 @@ ja:
deprecated_hint: これらの設定は、次回のLTS、またはkmyblueバージョン14.0のどちらか早い方で削除する予定です。それぞれの設定の説明を参照して、可能であれば新しい設定に置き換えてください。 deprecated_hint: これらの設定は、次回のLTS、またはkmyblueバージョン14.0のどちらか早い方で削除する予定です。それぞれの設定の説明を参照して、可能であれば新しい設定に置き換えてください。
hide_local_users_for_anonymous: ログインしていない状態でローカルユーザーの投稿をタイムラインから取得できないようにする hide_local_users_for_anonymous: ログインしていない状態でローカルユーザーの投稿をタイムラインから取得できないようにする
hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。 hide_local_users_for_anonymous_hint: この設定は削除予定です。設定削除後は、常にチェックをつけていない場合と同じ挙動になります。サーバー設定の「見つける」にある「公開タイムラインへの未認証のアクセスを許可する」で、完全ではありませんが代替可能です。
history_hint: 設定されたNGワードによって実際に拒否された投稿などは、履歴より確認できます。NGワードの指定に誤りがないか定期的に確認することをおすすめします。
hold_remote_new_accounts: リモートの新規アカウントを保留する hold_remote_new_accounts: リモートの新規アカウントを保留する
keywords: 投稿できないキーワード keywords: 拒否するキーワード
keywords_for_stranger_mention: フォローしていないアカウントへのメンションや参照で利用できないキーワード
keywords_for_stranger_mention_hint: フォローしていないアカウントへのメンション、参照、引用にのみ適用されます
keywords_hint: 行を「?」で始めると、正規表現が使えます
permit_new_account_domains: 新規ユーザーを自動承認するドメイン permit_new_account_domains: 新規ユーザーを自動承認するドメイン
preamble: ドメインブロックでは対処の難しいスパムに関する問題の解決に、この設定が役に立ちます。特定キーワードが含まれていたり、メンション数が多すぎたりする投稿を拒否することができます。問題のない投稿が削除されないよう、設定は慎重に検討してください。「NGルール」を用いることで、さらに細かい設定が可能です。 phrases:
regexp_html: <strong>正規</strong> 表現 にチェックの入っている項目は、正規表現を用いての比較となります。
regexp_short: 正規
stranger_html: <strong>無関</strong> 係のフォロワーからのメンション にチェックの入っている項目は、フォロー関係にないアカウントからのメンション、返信、参照などのみに適用されます。
stranger_short: 無関
preamble: ドメインブロックでは対処の難しいスパムに関する問題の解決に、この設定が役に立ちます。特定キーワードが含まれているなどの条件を満たした投稿を拒否することができます。問題のない投稿が削除されないよう設定は慎重に検討し、定期的に履歴を確認してください。
post_hash_tags_max: 投稿に設定可能なハッシュタグの最大数 post_hash_tags_max: 投稿に設定可能なハッシュタグの最大数
post_mentions_max: 投稿に設定可能なメンションの最大数 post_mentions_max: 投稿に設定可能なメンションの最大数
post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (メンション先にフォロワー以外を1人でも含む場合) post_stranger_mentions_max: 投稿に設定可能なメンションの最大数 (メンション先にフォロワー以外を1人でも含む場合)
remote_approval_list: 承認待ちのリモートアカウント一覧 remote_approval_list: 承認待ちのリモートアカウント一覧
remote_approval_hint: 指定されていないドメインで新しく認識されたアカウントはサスペンド状態になります。その一覧を確認し、必要であれば承認を行うことができます。この設定が有効でない場合、全てのリモートアカウントが即座に承認されます。 remote_approval_hint: 指定されていないドメインで新しく認識されたアカウントはサスペンド状態になります。その一覧を確認し、必要であれば承認を行うことができます。この設定が有効でない場合、全てのリモートアカウントが即座に承認されます。
settings: 詳細設定
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: この設定は削除予定です。設定削除後は、常にチェックをつけている場合と同じ挙動になります。この動作を希望しない場合は、NGルールで代替してください。 stranger_mention_from_local_ng_hint: この設定は削除予定です。設定削除後は、常にチェックをつけている場合と同じ挙動になります。この動作を希望しない場合は、NGルールで代替してください。
test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません

View file

@ -49,7 +49,7 @@ SimpleNavigation::Configuration.run do |navigation|
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks, :manage_ng_words, :manage_sensitive_words) && !self_destruct } do |s| n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks, :manage_ng_words, :manage_sensitive_words) && !self_destruct } do |s|
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports}, if: -> { current_user.can?(:manage_reports) } s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports}, if: -> { current_user.can?(:manage_reports) }
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) } s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) }
s.item :ng_words, safe_join([fa_icon('list fw'), t('admin.ng_words.title')]), admin_ng_words_path, highlights_on: %r{/admin/(ng_words|ngword_histories)}, if: -> { current_user.can?(:manage_ng_words) } s.item :ng_words, safe_join([fa_icon('list fw'), t('admin.ng_words.title')]), admin_ng_words_keywords_path, highlights_on: %r{/admin/(ng_words|ngword_histories)}, if: -> { current_user.can?(:manage_ng_words) }
s.item :ng_rules, safe_join([fa_icon('rub fw'), t('admin.ng_rules.title')]), admin_ng_rules_path, highlights_on: %r{/admin/(ng_rules|ng_rule_histories)}, if: -> { current_user.can?(:manage_ng_words) } s.item :ng_rules, safe_join([fa_icon('rub fw'), t('admin.ng_rules.title')]), admin_ng_rules_path, highlights_on: %r{/admin/(ng_rules|ng_rule_histories)}, if: -> { current_user.can?(:manage_ng_words) }
s.item :sensitive_words, safe_join([fa_icon('list fw'), t('admin.sensitive_words.title')]), admin_sensitive_words_path, highlights_on: %r{/admin/sensitive_words}, if: -> { current_user.can?(:manage_sensitive_words) } s.item :sensitive_words, safe_join([fa_icon('list fw'), t('admin.sensitive_words.title')]), admin_sensitive_words_path, highlights_on: %r{/admin/sensitive_words}, if: -> { current_user.can?(:manage_sensitive_words) }
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path, if: -> { current_user.can?(:manage_invites) } s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path, if: -> { current_user.can?(:manage_invites) }

View file

@ -32,7 +32,11 @@ namespace :admin do
resources :action_logs, only: [:index] resources :action_logs, only: [:index]
resources :warning_presets, except: [:new, :show] resources :warning_presets, except: [:new, :show]
resource :ng_words, only: [:show, :create] namespace :ng_words do
resource :keywords, only: [:show, :create], controller: 'keywords'
resource :white_list, only: [:show, :create], controller: 'white_list'
resource :settings, only: [:show, :create], controller: 'settings'
end
resources :ngword_histories, only: [:index] resources :ngword_histories, only: [:index]
resources :ng_rules, except: [:show] do resources :ng_rules, except: [:show] do
member do member do

View file

@ -0,0 +1,63 @@
# frozen_string_literal: true
class CreateNgWords < ActiveRecord::Migration[7.1]
class Setting < ApplicationRecord
def value
YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess, Symbol]) if self[:value].present?
end
def value=(new_value)
self[:value] = new_value.to_yaml
end
end
class NgWord < ApplicationRecord; end
def normalized_keyword(keyword)
if regexp?(keyword)
keyword[1..]
else
keyword
end
end
def regexp?(keyword)
keyword.start_with?('?') && keyword.size >= 2
end
def up
create_table :ng_words do |t|
t.string :keyword, null: false
t.boolean :regexp, null: false, default: false
t.boolean :stranger, null: false, default: true
t.timestamps
end
settings = Setting.where(var: %i(ng_words ng_words_for_stranger_mention))
ng_words = settings.find { |s| s.var == 'ng_words' }&.value&.compact_blank&.uniq || []
ng_words_for_stranger_mention = settings.find { |s| s.var == 'ng_words_for_stranger_mention' }&.value&.compact_blank&.uniq || []
(ng_words + ng_words_for_stranger_mention).compact.uniq.each do |word|
NgWord.create!(
keyword: normalized_keyword(word),
regexp: regexp?(word),
stranger: ng_words_for_stranger_mention.include?(word)
)
end
settings.destroy_all
end
def down
ng_words = NgWord.where(stranger: false).map { |s| s.regexp ? "?#{s.keyword}" : s.keyword }
ng_words_for_stranger_mention = NgWord.where(stranger: true).map { |s| s.regexp ? "?#{s.keyword}" : s.keyword }
Setting.where(var: %i(ng_words ng_words_for_stranger_mention)).destroy_all
Setting.new(var: :ng_words).tap { |s| s.value = ng_words }.save!
Setting.new(var: :ng_words_for_stranger_mention).tap { |s| s.value = ng_words_for_stranger_mention }.save!
drop_table :ng_words
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_03_12_230204) do ActiveRecord::Schema[7.1].define(version: 2024_03_20_231633) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -926,6 +926,14 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_12_230204) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
create_table "ng_words", force: :cascade do |t|
t.string "keyword", null: false
t.boolean "regexp", default: false, null: false
t.boolean "stranger", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "ngword_histories", force: :cascade do |t| create_table "ngword_histories", force: :cascade do |t|
t.string "uri", null: false t.string "uri", null: false
t.integer "target_type", null: false t.integer "target_type", null: false

View file

@ -94,6 +94,7 @@ namespace :dangerous do
20240227225017 20240227225017
20240229233617 20240229233617
20240312230204 20240312230204
20240320231633
) )
# Removed: account_groups # Removed: account_groups
target_tables = %w( target_tables = %w(
@ -113,6 +114,7 @@ namespace :dangerous do
ng_rules ng_rules
ng_rule_histories ng_rule_histories
ngword_histories ngword_histories
ng_words
pending_follow_requests pending_follow_requests
pending_statuses pending_statuses
scheduled_expiration_statuses scheduled_expiration_statuses

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
Fabricator(:ng_word) do
keyword { sequence(:keyword) { |i| "keyword_#{i}" } }
end

View file

@ -1755,8 +1755,8 @@ RSpec.describe ActivityPub::Activity::Create do
let(:custom_before) { true } let(:custom_before) { true }
let(:custom_before_sub) { false } let(:custom_before_sub) { false }
let(:content) { 'Lorem ipsum' } let(:content) { 'Lorem ipsum' }
let(:ng_words) { 'hello' } let(:ng_word) { 'hello' }
let(:ng_words_for_stranger_mention) { 'ohagi' } let(:ng_word_for_stranger_mention) { 'ohagi' }
let(:object_json) do let(:object_json) do
{ {
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@ -1767,7 +1767,8 @@ RSpec.describe ActivityPub::Activity::Create do
end end
before do before do
Form::AdminSettings.new(ng_words: ng_words, ng_words_for_stranger_mention: ng_words_for_stranger_mention).save Fabricate(:ng_word, keyword: ng_word, stranger: false)
Fabricate(:ng_word, keyword: ng_word_for_stranger_mention, stranger: true)
subject.perform unless custom_before_sub subject.perform unless custom_before_sub
end end
@ -1777,11 +1778,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to_not be_nil expect(sender.statuses.first).to_not be_nil
end end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end end
context 'when hit ng words' do context 'when hit ng words' do
@ -1796,7 +1792,7 @@ RSpec.describe ActivityPub::Activity::Create do
expect(history).to_not be_nil expect(history).to_not be_nil
expect(history.status_blocked?).to be true expect(history.status_blocked?).to be true
expect(history.within_ng_words?).to be true expect(history.within_ng_words?).to be true
expect(history.keyword).to eq ng_words expect(history.keyword).to eq ng_word
end end
end end
@ -1813,11 +1809,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to be_nil expect(sender.statuses.first).to be_nil
end end
it 'records history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end end
context 'when mention from tags' do context 'when mention from tags' do
@ -1844,11 +1835,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to_not be_nil expect(sender.statuses.first).to_not be_nil
end end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end end
context 'with using ng words for stranger' do context 'with using ng words for stranger' do
@ -1857,14 +1843,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to be_nil expect(sender.statuses.first).to be_nil
end end
it 'records history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to_not be_nil
expect(history.status_blocked?).to be true
expect(history.within_ng_words_for_stranger_mention?).to be true
expect(history.keyword).to eq ng_words_for_stranger_mention
end
end end
context 'with using ng words for stranger but receiver is following him' do context 'with using ng words for stranger but receiver is following him' do
@ -1879,11 +1857,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to_not be_nil expect(sender.statuses.first).to_not be_nil
end end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end end
context 'with using ng words for stranger but multiple receivers are partically following him' do context 'with using ng words for stranger but multiple receivers are partically following him' do
@ -1917,14 +1890,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to be_nil expect(sender.statuses.first).to be_nil
end end
it 'records history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to_not be_nil
expect(history.status_blocked?).to be true
expect(history.within_ng_words_for_stranger_mention?).to be true
expect(history.keyword).to eq ng_words_for_stranger_mention
end
end end
end end
@ -1946,14 +1911,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to be_nil expect(sender.statuses.first).to be_nil
end end
it 'records history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to_not be_nil
expect(history.status_blocked?).to be true
expect(history.within_ng_words_for_stranger_mention?).to be true
expect(history.keyword).to eq ng_words_for_stranger_mention
end
end end
context 'with following' do context 'with following' do
@ -1967,11 +1924,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do it 'creates status' do
expect(sender.statuses.first).to_not be_nil expect(sender.statuses.first).to_not be_nil
end end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end end
end end

View file

@ -0,0 +1,77 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::NgWord do
describe '#reject?' do
subject { described_class.reject?(text, stranger: stranger, uri: uri, target_type: :status) }
let(:text) { 'This is a ohagi.' }
let(:stranger) { false }
let(:uri) { nil }
context 'when general post' do
it 'ng word hits' do
Fabricate(:ng_word, keyword: 'ohagi', stranger: false)
expect(subject).to be true
end
it 'else ng word does not hit' do
Fabricate(:ng_word, keyword: 'angry', stranger: false)
expect(subject).to be false
end
it 'stranger word does not hit' do
Fabricate(:ng_word, keyword: 'ohagi', stranger: true)
expect(subject).to be false
end
end
context 'when mention to stranger' do
let(:stranger) { true }
it 'ng word hits' do
Fabricate(:ng_word, keyword: 'ohagi', stranger: true)
expect(subject).to be true
end
it 'else ng word does not hit' do
Fabricate(:ng_word, keyword: 'angry', stranger: true)
expect(subject).to be false
end
it 'general word hits' do
Fabricate(:ng_word, keyword: 'ohagi', stranger: false)
expect(subject).to be true
end
end
context 'when remote post' do
let(:uri) { 'https://example.com/note' }
it 'ng word hits' do
Fabricate(:ng_word, keyword: 'ohagi', stranger: false)
expect(subject).to be true
expect(NgwordHistory.find_by(uri: uri)).to_not be_nil
end
it 'else ng word does not hit' do
Fabricate(:ng_word, keyword: 'angry', stranger: false)
expect(subject).to be false
expect(NgwordHistory.find_by(uri: uri)).to be_nil
end
end
context 'when using regexp' do
it 'regexp hits with enable' do
Fabricate(:ng_word, keyword: 'oha[ghi]i', regexp: true, stranger: false)
expect(subject).to be true
end
it 'regexp does not hit without enable' do
Fabricate(:ng_word, keyword: 'oha[ghi]i', regexp: false, stranger: false)
expect(subject).to be false
end
end
end
end

View file

@ -425,7 +425,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
end end
it 'creates account when ng word is not set' do it 'creates account when ng word is not set' do
Setting.ng_words = ['Amazon'] Fabricate(:ng_word, keyword: 'Amazon', stranger: false)
subject subject
expect(account.reload.display_name).to eq 'Ohagi' expect(account.reload.display_name).to eq 'Ohagi'
@ -434,7 +434,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
end end
it 'does not create account when ng word is set' do it 'does not create account when ng word is set' do
Setting.ng_words = ['Ohagi'] Fabricate(:ng_word, keyword: 'Ohagi', stranger: false)
subject subject
expect(account.reload.display_name).to_not eq 'Ohagi' expect(account.reload.display_name).to_not eq 'Ohagi'

View file

@ -515,7 +515,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' } let(:content) { 'ng word test' }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to_not eq content expect(status.reload.text).to_not eq content
@ -526,7 +526,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word aiueo' } let(:content) { 'ng word aiueo' }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to eq content expect(status.reload.text).to eq content
@ -542,7 +542,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' } let(:content) { 'ng word test' }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to_not eq content expect(status.reload.text).to_not eq content
@ -550,7 +551,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end end
it 'update status when following' do it 'update status when following' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
alice.follow!(status.account) alice.follow!(status.account)
subject.call(status, json, json) subject.call(status, json, json)
@ -568,7 +570,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' } let(:content) { 'ng word test' }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '0').save Form::AdminSettings.new(stranger_mention_from_local_ng: '0').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to_not eq content expect(status.reload.text).to_not eq content
@ -589,7 +592,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end end
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to eq content expect(status.reload.text).to eq content
@ -607,7 +610,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:thread) { Fabricate(:status, account: alice) } let(:thread) { Fabricate(:status, account: alice) }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to_not eq content expect(status.reload.text).to_not eq content
@ -629,7 +632,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end end
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to eq content expect(status.reload.text).to eq content
@ -658,7 +661,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' } let(:content) { 'ng word test' }
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to_not eq content expect(status.reload.text).to_not eq content
@ -671,7 +675,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end end
it 'update status' do it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json) subject.call(status, json, json)
expect(status.reload.text).to eq content expect(status.reload.text).to eq content

View file

@ -613,7 +613,7 @@ RSpec.describe PostStatusService do
it 'hit ng words' do it 'hit ng words' do
account = Fabricate(:account) account = Fabricate(:account)
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError) expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError)
end end
@ -621,7 +621,7 @@ RSpec.describe PostStatusService do
it 'not hit ng words' do it 'not hit ng words' do
account = Fabricate(:account) account = Fabricate(:account)
text = 'ng word aiueo' text = 'ng word aiueo'
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
status = subject.call(account, text: text) status = subject.call(account, text: text)
@ -633,7 +633,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account) account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError) expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError)
end end
@ -642,7 +643,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account) account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '0').save Form::AdminSettings.new(stranger_mention_from_local_ng: '0').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
status = subject.call(account, text: text) status = subject.call(account, text: text)
@ -655,7 +657,8 @@ RSpec.describe PostStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil) mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account) mentioned.follow!(account)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
status = subject.call(account, text: text) status = subject.call(account, text: text)
@ -666,7 +669,8 @@ RSpec.describe PostStatusService do
it 'hit ng words for reply' do it 'hit ng words for reply' do
account = Fabricate(:account) account = Fabricate(:account)
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
expect { subject.call(account, text: text, thread: Fabricate(:status)) }.to raise_error(Mastodon::ValidationError) expect { subject.call(account, text: text, thread: Fabricate(:status)) }.to raise_error(Mastodon::ValidationError)
end end
@ -676,7 +680,8 @@ RSpec.describe PostStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil) mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account) mentioned.follow!(account)
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
status = subject.call(account, text: text) status = subject.call(account, text: text)
@ -702,7 +707,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account) account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}" text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}"
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError) expect { subject.call(account, text: text) }.to raise_error(Mastodon::ValidationError)
end end
@ -713,7 +719,8 @@ RSpec.describe PostStatusService do
target_status.account.follow!(account) target_status.account.follow!(account)
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}" text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}"
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test', stranger: true)
status = subject.call(account, text: text) status = subject.call(account, text: text)

View file

@ -272,14 +272,14 @@ RSpec.describe UpdateStatusService do
it 'hit ng words' do it 'hit ng words' do
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
expect { subject.call(status, status.account_id, text: text) }.to raise_error(Mastodon::ValidationError) expect { subject.call(status, status.account_id, text: text) }.to raise_error(Mastodon::ValidationError)
end end
it 'not hit ng words' do it 'not hit ng words' do
text = 'ng word aiueo' text = 'ng word aiueo'
Form::AdminSettings.new(ng_words: 'test').save Fabricate(:ng_word, keyword: 'test', stranger: false)
status2 = subject.call(status, status.account_id, text: text) status2 = subject.call(status, status.account_id, text: text)
@ -290,7 +290,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for mention' do it 'hit ng words for mention' do
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
expect { subject.call(status, status.account_id, text: text) }.to raise_error(Mastodon::ValidationError) expect { subject.call(status, status.account_id, text: text) }.to raise_error(Mastodon::ValidationError)
expect(status.reload.text).to_not eq text expect(status.reload.text).to_not eq text
@ -300,7 +301,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for mention but local posts are not checked' do it 'hit ng words for mention but local posts are not checked' do
Fabricate(:account, username: 'ohagi', domain: nil) Fabricate(:account, username: 'ohagi', domain: nil)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '0').save Form::AdminSettings.new(stranger_mention_from_local_ng: '0').save
Fabricate(:ng_word, keyword: 'test')
status2 = subject.call(status, status.account_id, text: text) status2 = subject.call(status, status.account_id, text: text)
@ -312,7 +314,8 @@ RSpec.describe UpdateStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil) mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account) mentioned.follow!(account)
text = 'ng word test @ohagi' text = 'ng word test @ohagi'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
status2 = subject.call(status, status.account_id, text: text) status2 = subject.call(status, status.account_id, text: text)
@ -322,7 +325,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for reply' do it 'hit ng words for reply' do
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
status = PostStatusService.new.call(account, text: 'hello', thread: Fabricate(:status)) status = PostStatusService.new.call(account, text: 'hello', thread: Fabricate(:status))
@ -334,7 +338,8 @@ RSpec.describe UpdateStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil) mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account) mentioned.follow!(account)
text = 'ng word test' text = 'ng word test'
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
status = PostStatusService.new.call(account, text: 'hello', thread: Fabricate(:status, account: mentioned)) status = PostStatusService.new.call(account, text: 'hello', thread: Fabricate(:status, account: mentioned))
@ -360,7 +365,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for reference' do it 'hit ng words for reference' do
target_status = Fabricate(:status) target_status = Fabricate(:status)
text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}" text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}"
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
status = PostStatusService.new.call(account, text: 'hello') status = PostStatusService.new.call(account, text: 'hello')
@ -371,7 +377,8 @@ RSpec.describe UpdateStatusService do
target_status = Fabricate(:status) target_status = Fabricate(:status)
target_status.account.follow!(status.account) target_status.account.follow!(status.account)
text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}" text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}"
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save Form::AdminSettings.new(stranger_mention_from_local_ng: '1').save
Fabricate(:ng_word, keyword: 'test')
status = PostStatusService.new.call(account, text: 'hello') status = PostStatusService.new.call(account, text: 'hello')