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:
- 'app/views/application/_sidebar.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'

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

View file

@ -13,7 +13,7 @@ module Admin
authorize :sensitive_words, :create?
begin
test_words
::SensitiveWord.save_from_raws(settings_params_test)
rescue
flash[:alert] = I18n.t('admin.ng_words.test_error')
redirect_to after_update_redirect_path
@ -22,7 +22,7 @@ module Admin
@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')
redirect_to after_update_redirect_path
else
@ -32,11 +32,6 @@ module Admin
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
admin_sensitive_words_path
end

View file

@ -320,7 +320,8 @@ Rails.delegate(
document,
'#sensitive-words-table .add-row-button',
'click',
() => {
(ev) => {
ev.preventDefault();
addTableRow('sensitive-words-table');
},
);
@ -329,8 +330,24 @@ Rails.delegate(
document,
'#sensitive-words-table .delete-row-button',
'click',
({ target }) => {
removeTableRow(target, 'sensitive-words-table');
(ev) => {
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?
valid = true
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.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?("#{@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

View file

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

View file

@ -44,8 +44,6 @@ class Form::AdminSettings
delete_content_cache_without_reaction
status_page_url
captcha_enabled
ng_words
ng_words_for_stranger_mention
stranger_mention_from_local_ng
hide_local_users_for_anonymous
post_hash_tags_max
@ -122,8 +120,6 @@ class Form::AdminSettings
}.freeze
STRING_ARRAY_KEYS = %i(
ng_words
ng_words_for_stranger_mention
emoji_reaction_disallow_domains
permit_new_account_domains
).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
attr_accessor :keywords, :regexps, :remotes, :spoilers
validate :check_regexp
class << self
def caches
Rails.cache.fetch('sensitive_words') { SensitiveWord.where.not(id: 0).order(:keyword).to_a }
@ -50,8 +52,6 @@ class SensitiveWord < ApplicationRecord
end
true
# rescue
# false
end
def save_from_raws(rows)
@ -78,4 +78,14 @@ class SensitiveWord < ApplicationRecord
def invalidate_cache!
Rails.cache.delete('sensitive_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

@ -162,10 +162,9 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
end
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 = 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?) && 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
= 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
%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
= 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
= 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')
%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
.back-link
= link_to admin_ng_words_path do
= link_to admin_ng_words_keywords_path do
= fa_icon 'chevron-left fw'
= 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.
hide_local_users_for_anonymous: Hide timeline local user posts from anonymous
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
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
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_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)
phrases:
regexp_html: <strong>正規</strong> 表現 にチェックの入っている項目は、正規表現を用いての比較となります。
regexp_short: 正規
stranger_html: <strong>無関</strong> 係のフォロワーからのメンション にチェックの入っている項目は、フォロー関係にないアカウントからのメンション、返信、参照などのみに適用されます。
stranger_short: 無関
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.
settings: Settings
stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する
stranger_mention_from_local_ng_hint: この設定は削除予定です。設定削除後は、常にチェックをつけている場合と同じ挙動になります。この動作を希望しない場合は、NGルールで代替してください。
test_error: Testing is returned any errors

View file

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

View file

@ -32,7 +32,11 @@ namespace :admin do
resources :action_logs, only: [:index]
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 :ng_rules, except: [:show] 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.
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
enable_extension "plpgsql"
@ -926,6 +926,14 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_12_230204) do
t.datetime "updated_at", null: false
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|
t.string "uri", null: false
t.integer "target_type", null: false

View file

@ -94,6 +94,7 @@ namespace :dangerous do
20240227225017
20240229233617
20240312230204
20240320231633
)
# Removed: account_groups
target_tables = %w(
@ -113,6 +114,7 @@ namespace :dangerous do
ng_rules
ng_rule_histories
ngword_histories
ng_words
pending_follow_requests
pending_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_sub) { false }
let(:content) { 'Lorem ipsum' }
let(:ng_words) { 'hello' }
let(:ng_words_for_stranger_mention) { 'ohagi' }
let(:ng_word) { 'hello' }
let(:ng_word_for_stranger_mention) { 'ohagi' }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@ -1767,7 +1767,8 @@ RSpec.describe ActivityPub::Activity::Create do
end
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
end
@ -1777,11 +1778,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to_not be_nil
end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end
context 'when hit ng words' do
@ -1796,7 +1792,7 @@ RSpec.describe ActivityPub::Activity::Create do
expect(history).to_not be_nil
expect(history.status_blocked?).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
@ -1813,11 +1809,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to be_nil
end
it 'records history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end
context 'when mention from tags' do
@ -1844,11 +1835,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to_not be_nil
end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end
context 'with using ng words for stranger' do
@ -1857,14 +1843,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to be_nil
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
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
expect(sender.statuses.first).to_not be_nil
end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
end
end
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
expect(sender.statuses.first).to be_nil
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
@ -1946,14 +1911,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to be_nil
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
context 'with following' do
@ -1967,11 +1924,6 @@ RSpec.describe ActivityPub::Activity::Create do
it 'creates status' do
expect(sender.statuses.first).to_not be_nil
end
it 'does not record history' do
history = NgwordHistory.find_by(uri: object_json[:id])
expect(history).to be_nil
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
it 'creates account when ng word is not set' do
Setting.ng_words = ['Amazon']
Fabricate(:ng_word, keyword: 'Amazon', stranger: false)
subject
expect(account.reload.display_name).to eq 'Ohagi'
@ -434,7 +434,7 @@ RSpec.describe ActivityPub::ProcessAccountService do
end
it 'does not create account when ng word is set' do
Setting.ng_words = ['Ohagi']
Fabricate(:ng_word, keyword: 'Ohagi', stranger: false)
subject
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' }
it 'update status' do
Form::AdminSettings.new(ng_words: 'test').save
Fabricate(:ng_word, keyword: 'test', stranger: false)
subject.call(status, json, json)
expect(status.reload.text).to_not eq content
@ -526,7 +526,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word aiueo' }
it 'update status' do
Form::AdminSettings.new(ng_words: 'test').save
Fabricate(:ng_word, keyword: 'test', stranger: false)
subject.call(status, json, json)
expect(status.reload.text).to eq content
@ -542,7 +542,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' }
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)
expect(status.reload.text).to_not eq content
@ -550,7 +551,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
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)
subject.call(status, json, json)
@ -568,7 +570,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' }
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)
expect(status.reload.text).to_not eq content
@ -589,7 +592,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json)
expect(status.reload.text).to eq content
@ -607,7 +610,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:thread) { Fabricate(:status, account: alice) }
it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json)
expect(status.reload.text).to_not eq content
@ -629,7 +632,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json)
expect(status.reload.text).to eq content
@ -658,7 +661,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
let(:content) { 'ng word test' }
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)
expect(status.reload.text).to_not eq content
@ -671,7 +675,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
it 'update status' do
Form::AdminSettings.new(ng_words_for_stranger_mention: 'test').save
Fabricate(:ng_word, keyword: 'test')
subject.call(status, json, json)
expect(status.reload.text).to eq content

View file

@ -613,7 +613,7 @@ RSpec.describe PostStatusService do
it 'hit ng words' do
account = Fabricate(:account)
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)
end
@ -621,7 +621,7 @@ RSpec.describe PostStatusService do
it 'not hit ng words' do
account = Fabricate(:account)
text = 'ng word aiueo'
Form::AdminSettings.new(ng_words: 'test').save
Fabricate(:ng_word, keyword: 'test', stranger: false)
status = subject.call(account, text: text)
@ -633,7 +633,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil)
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)
end
@ -642,7 +643,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil)
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)
@ -655,7 +657,8 @@ RSpec.describe PostStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account)
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)
@ -666,7 +669,8 @@ RSpec.describe PostStatusService do
it 'hit ng words for reply' do
account = Fabricate(:account)
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)
end
@ -676,7 +680,8 @@ RSpec.describe PostStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account)
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)
@ -702,7 +707,8 @@ RSpec.describe PostStatusService do
account = Fabricate(:account)
Fabricate(:account, username: 'ohagi', domain: nil)
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)
end
@ -713,7 +719,8 @@ RSpec.describe PostStatusService do
target_status.account.follow!(account)
Fabricate(:account, username: 'ohagi', domain: nil)
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)

View file

@ -272,14 +272,14 @@ RSpec.describe UpdateStatusService do
it 'hit ng words' do
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)
end
it 'not hit ng words' do
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)
@ -290,7 +290,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for mention' do
Fabricate(:account, username: 'ohagi', domain: nil)
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(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
Fabricate(:account, username: 'ohagi', domain: nil)
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)
@ -312,7 +314,8 @@ RSpec.describe UpdateStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account)
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)
@ -322,7 +325,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for reply' do
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))
@ -334,7 +338,8 @@ RSpec.describe UpdateStatusService do
mentioned = Fabricate(:account, username: 'ohagi', domain: nil)
mentioned.follow!(account)
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))
@ -360,7 +365,8 @@ RSpec.describe UpdateStatusService do
it 'hit ng words for reference' do
target_status = Fabricate(: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')
@ -371,7 +377,8 @@ RSpec.describe UpdateStatusService do
target_status = Fabricate(:status)
target_status.account.follow!(status.account)
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')