From 50916ec4fed03b4e8783278ab2962d218d7307da Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 16 Feb 2024 18:42:24 +0900 Subject: [PATCH] =?UTF-8?q?Add:=20=E3=83=A1=E3=83=B3=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E6=95=B0=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/activitypub/activity/create.rb | 5 ++- app/models/admin/ng_word.rb | 13 ++++++ app/models/form/admin_settings.rb | 2 + .../process_status_update_service.rb | 2 +- app/services/post_status_service.rb | 1 + app/views/admin/ng_words/show.html.haml | 3 ++ config/locales/en.yml | 2 + config/locales/ja.yml | 2 + spec/lib/activitypub/activity/create_spec.rb | 41 +++++++++++++++++++ spec/services/post_status_service_spec.rb | 23 +++++++++++ 10 files changed, 91 insertions(+), 3 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d137a642000573..b73c2cddd1112d 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -143,8 +143,9 @@ def process_status_params end def valid_status? - valid = !Admin::NgWord.reject?("#{@params[:spoiler_text]}\n#{@params[:text]}", uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?) && !Admin::NgWord.hashtag_reject?(@tags.size) - + valid = !Admin::NgWord.reject?("#{@params[:spoiler_text]}\n#{@params[:text]}", uri: @params[:uri], target_type: :status, public: @status_parser.distributable_visibility?) + valid = !Admin::NgWord.hashtag_reject?(@tags.size) if valid + valid = !Admin::NgWord.mention_reject?(@mentions.count { |m| !m.silent }) if valid 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 diff --git a/app/models/admin/ng_word.rb b/app/models/admin/ng_word.rb index 5be03409d6d9a8..fb354584ec6be9 100644 --- a/app/models/admin/ng_word.rb +++ b/app/models/admin/ng_word.rb @@ -26,6 +26,14 @@ def hashtag_reject_with_extractor?(text) hashtag_reject?(Extractor.extract_hashtags(text)&.size || 0) end + def mention_reject?(mention_count) + post_mentions_max.positive? && post_mentions_max < mention_count + end + + def mention_reject_with_extractor?(text) + mention_reject?(text.gsub(Account::MENTION_RE)&.count || 0) + end + private def include?(text, word) @@ -49,6 +57,11 @@ def post_hash_tags_max value.is_a?(Integer) && value.positive? ? value : 0 end + def post_mentions_max + value = Setting.post_mentions_max + value.is_a?(Integer) && value.positive? ? value : 0 + end + def record!(type, text, keyword, options) return unless options[:uri] && options[:target_type] return if options.key?(:public) && !options.delete(:public) diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index fd694a887e4b7f..eb719d91d414d2 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -49,6 +49,7 @@ class Form::AdminSettings stranger_mention_from_local_ng hide_local_users_for_anonymous post_hash_tags_max + post_mentions_max sensitive_words sensitive_words_for_full authorized_fetch @@ -68,6 +69,7 @@ class Form::AdminSettings content_cache_retention_period backups_retention_period post_hash_tags_max + post_mentions_max registrations_limit registrations_limit_per_day registrations_start_hour diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 5dc3aa4fc131d2..2549c44ed6596e 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -161,7 +161,7 @@ def update_poll!(allow_significant_changes: true) end def valid_status? - !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status) && !Admin::NgWord.hashtag_reject?(@raw_tags.size) + !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status) && !Admin::NgWord.hashtag_reject?(@raw_tags.size) && !Admin::NgWord.mention_reject?(@raw_mentions.size) end def validate_status_mentions! diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 6776d08db6744f..4f416b39cdf033 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -209,6 +209,7 @@ def postprocess_status! def validate_status! raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if Admin::NgWord.reject?("#{@options[:spoiler_text]}\n#{@options[:text]}") raise Mastodon::ValidationError, I18n.t('statuses.too_many_hashtags') if Admin::NgWord.hashtag_reject_with_extractor?(@options[:text]) + raise Mastodon::ValidationError, I18n.t('statuses.too_many_mentions') if Admin::NgWord.mention_reject_with_extractor?(@options[:text]) end def validate_status_mentions! diff --git a/app/views/admin/ng_words/show.html.haml b/app/views/admin/ng_words/show.html.haml index 8b7c943f64dd94..5af34cb80a0bda 100644 --- a/app/views/admin/ng_words/show.html.haml +++ b/app/views/admin/ng_words/show.html.haml @@ -23,6 +23,9 @@ .fields-group = f.input :post_hash_tags_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_hash_tags_max') + .fields-group + = f.input :post_mentions_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_mentions_max') + .fields-group = f.input :hide_local_users_for_anonymous, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hide_local_users_for_anonymous') diff --git a/config/locales/en.yml b/config/locales/en.yml index 2955685c8f2570..a25c6df5cf3451 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -648,6 +648,7 @@ en: 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 post_hash_tags_max: Hash tags max for posts + post_mentions_max: Mentions max for posts stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります test_error: Testing is returned any errors @@ -1940,6 +1941,7 @@ en: show_thread: Show thread title: '%{name}: "%{quote}"' too_many_hashtags: Too many hashtags + too_many_mentions: Too many mentions visibilities: direct: Direct login: Login only diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 983b327c454589..1584a067484dfd 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -641,6 +641,7 @@ ja: keywords_for_stranger_mention_hint: フォローしていないアカウントへのメンション、参照、引用にのみ適用されます keywords_hint: 行を「?」で始めると、正規表現が使えます post_hash_tags_max: 投稿に設定可能なハッシュタグの最大数 + post_mentions_max: 投稿に設定可能なメンションの最大数 stranger_mention_from_local_ng: フォローしていないアカウントへのメンションのNGワードを、ローカルユーザーによる投稿にも適用する stranger_mention_from_local_ng_hint: サーバーの登録が承認制でない場合、あなたのサーバーにもスパムが入り込む可能性があります test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません @@ -1913,6 +1914,7 @@ ja: show_thread: スレッドを表示 title: '%{name}: "%{quote}"' too_many_hashtags: ハッシュタグが多すぎます + too_many_mentions: メンションが多すぎます visibilities: direct: ダイレクト login: ログインユーザーのみ diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index e71c2630706439..efb82b7ba3d880 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -2066,6 +2066,47 @@ def activity_for_object(json) end end end + + context 'when mentions limit is set' do + let(:post_mentions_max) { 2 } + let(:custom_before) { true } + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + content: 'Lorem ipsum', + tag: [ + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(Fabricate(:account)), + }, + { + type: 'Mention', + href: ActivityPub::TagManager.instance.uri_for(Fabricate(:account)), + }, + ], + } + end + + before do + Form::AdminSettings.new(post_mentions_max: post_mentions_max).save + subject.perform + end + + context 'when limit is enough' do + it 'creates status' do + expect(sender.statuses.first).to_not be_nil + end + end + + context 'when limit is over' do + let(:post_mentions_max) { 1 } + + it 'creates status' do + expect(sender.statuses.first).to be_nil + end + end + end end context 'when object URI uses bearcaps' do diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 36e92edfdfae77..68bedda10fe16a 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -740,6 +740,29 @@ expect { subject.call(account, text: text) }.to raise_error Mastodon::ValidationError end + + it 'using mentions under limit' do + Fabricate(:account, username: 'a') + Fabricate(:account, username: 'b') + account = Fabricate(:account) + text = '@a @b' + Form::AdminSettings.new(post_mentions_max: 2).save + + status = subject.call(account, text: text) + + expect(status).to be_persisted + expect(status.text).to eq text + end + + it 'using mentions over limit' do + Fabricate(:account, username: 'a') + Fabricate(:account, username: 'b') + account = Fabricate(:account) + text = '@a @b' + Form::AdminSettings.new(post_mentions_max: 1).save + + expect { subject.call(account, text: text) }.to raise_error Mastodon::ValidationError + end end def create_status_with_options(**options)