* Wip * Wip * Wip: History * Wip: テストコード作成 * Fix test * Wip * Wip * Wip * Fix test * Wip * Wip * Wip * Wip * なんとか完成、これから動作確認 * spell miss * Change ng rule timings * Fix test * Wip * Fix test * Wip * Fix form * 表示まわりの改善
This commit is contained in:
parent
0779c748a6
commit
7d96d5828e
56 changed files with 2062 additions and 42 deletions
|
@ -4,6 +4,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
|||
include JsonLdHelper
|
||||
include Redisable
|
||||
include Lockable
|
||||
include NgRuleHelper
|
||||
|
||||
class AbortError < ::StandardError; end
|
||||
|
||||
|
@ -168,17 +169,38 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
|||
end
|
||||
|
||||
def validate_status_mentions!
|
||||
raise AbortError if (mention_to_stranger? || reference_to_stranger?) && Admin::NgWord.stranger_mention_reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}", uri: @status.uri, target_type: :status)
|
||||
raise AbortError unless valid_status_for_ng_rule?
|
||||
raise AbortError if (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)
|
||||
raise AbortError if Admin::NgWord.mention_reject?(@raw_mentions.size, uri: @status.uri, target_type: :status, text: "#{@status_parser.spoiler_text}\n#{@status_parser.text}")
|
||||
raise AbortError if (mention_to_stranger? || reference_to_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}")
|
||||
raise AbortError if (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}")
|
||||
end
|
||||
|
||||
def mention_to_stranger?
|
||||
def valid_status_for_ng_rule?
|
||||
check_invalid_status_for_ng_rule! @account,
|
||||
reaction_type: 'edit',
|
||||
uri: @status.uri,
|
||||
url: @status_parser.url || @status.url,
|
||||
spoiler_text: @status.spoiler_text,
|
||||
text: @status.text,
|
||||
tag_names: @raw_tags,
|
||||
visibility: @status.visibility,
|
||||
searchability: @status.searchability,
|
||||
sensitive: @status.sensitive,
|
||||
media_count: @next_media_attachments.size,
|
||||
poll_count: @status.poll&.options&.size || 0,
|
||||
quote: quote,
|
||||
reply: @status.reply?,
|
||||
mention_count: @status.mentions.count,
|
||||
reference_count: reference_uris.size,
|
||||
mention_to_following: !(mention_to_local_stranger? || reference_to_local_stranger?)
|
||||
end
|
||||
|
||||
def mention_to_local_stranger?
|
||||
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @status.account.id && mentioned_account.local? && !mentioned_account.following?(@status.account) } ||
|
||||
(@status.thread.present? && @status.thread.account.id != @status.account.id && @status.thread.account.local? && !@status.thread.account.following?(@status.account))
|
||||
end
|
||||
|
||||
def reference_to_stranger?
|
||||
def reference_to_local_stranger?
|
||||
local_referred_accounts.any? { |account| !account.following?(@account) }
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class EmojiReactService < BaseService
|
|||
include Payloadable
|
||||
include Redisable
|
||||
include Lockable
|
||||
include NgRuleHelper
|
||||
|
||||
# React a status with emoji and notify remote user
|
||||
# @param [Account] account
|
||||
|
@ -18,9 +19,12 @@ class EmojiReactService < BaseService
|
|||
|
||||
raise Mastodon::ValidationError, I18n.t('reactions.errors.banned') if account.silenced? && !status.account.following?(account)
|
||||
|
||||
shortcode, domain = name.split('@')
|
||||
domain = nil if TagManager.instance.local_domain?(domain)
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless check_invalid_reaction_for_ng_rule! account, reaction_type: 'emoji_reaction', emoji_reaction_name: shortcode, emoji_reaction_origin_domain: domain, recipient: status.account, target_status: status
|
||||
|
||||
with_redis_lock("emoji_reaction:#{status.id}") do
|
||||
shortcode, domain = name.split('@')
|
||||
domain = nil if TagManager.instance.local_domain?(domain)
|
||||
custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain)
|
||||
return if domain.present? && !EmojiReaction.exists?(status: status, custom_emoji: custom_emoji)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class FavouriteService < BaseService
|
||||
include Authorization
|
||||
include Payloadable
|
||||
include NgRuleHelper
|
||||
|
||||
# Favourite a status and notify remote user
|
||||
# @param [Account] account
|
||||
|
@ -11,6 +12,8 @@ class FavouriteService < BaseService
|
|||
def call(account, status)
|
||||
authorize_with account, status, :favourite?
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless check_invalid_reaction_for_ng_rule! account, reaction_type: 'favourite', recipient: status.account, target_status: status
|
||||
|
||||
favourite = Favourite.find_by(account: account, status: status)
|
||||
|
||||
return favourite unless favourite.nil?
|
||||
|
|
|
@ -4,6 +4,7 @@ class FollowService < BaseService
|
|||
include Redisable
|
||||
include Payloadable
|
||||
include DomainControlHelper
|
||||
include NgRuleHelper
|
||||
|
||||
# Follow a remote user, notify remote user about the follow
|
||||
# @param [Account] source_account From which to follow
|
||||
|
@ -23,6 +24,8 @@ class FollowService < BaseService
|
|||
raise ActiveRecord::RecordNotFound if following_not_possible?
|
||||
raise Mastodon::NotPermittedError if following_not_allowed?
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless check_invalid_reaction_for_ng_rule! @source_account, reaction_type: 'follow', recipient: @target_account
|
||||
|
||||
if @source_account.following?(@target_account)
|
||||
return change_follow_options!
|
||||
elsif @source_account.requested?(@target_account)
|
||||
|
|
|
@ -4,6 +4,7 @@ class PostStatusService < BaseService
|
|||
include Redisable
|
||||
include LanguagesHelper
|
||||
include DtlHelper
|
||||
include NgRuleHelper
|
||||
|
||||
MIN_SCHEDULE_OFFSET = 5.minutes.freeze
|
||||
|
||||
|
@ -74,7 +75,7 @@ class PostStatusService < BaseService
|
|||
@visibility = :limited if %w(mutual circle reply).include?(@options[:visibility])
|
||||
@visibility = :unlisted if (@visibility == :public || @visibility == :public_unlisted || @visibility == :login) && @account.silenced?
|
||||
@visibility = :public_unlisted if @visibility == :public && !@options[:force_visibility] && !@options[:application]&.superapp && @account.user&.setting_public_post_to_unlisted && Setting.enable_public_unlisted_visibility
|
||||
@visibility = Setting.enable_public_unlisted_visibility ? :public_unlisted : :unlisted unless Setting.enable_public_visibility
|
||||
@visibility = Setting.enable_public_unlisted_visibility ? :public_unlisted : :unlisted if !Setting.enable_public_visibility && @visibility == :public
|
||||
@limited_scope = @options[:visibility]&.to_sym if @visibility == :limited && @options[:visibility] != 'limited'
|
||||
@searchability = searchability
|
||||
@searchability = :private if @account.silenced? && %i(public public_unlisted).include?(@searchability&.to_sym)
|
||||
|
@ -148,6 +149,7 @@ class PostStatusService < BaseService
|
|||
@status = @account.statuses.new(status_attributes)
|
||||
process_mentions_service.call(@status, limited_type: @status.limited_visibility? ? @limited_scope : '', circle: @circle, save_records: false)
|
||||
safeguard_mentions!(@status)
|
||||
validate_status_ng_rules!
|
||||
validate_status_mentions!
|
||||
|
||||
@status.limited_scope = :personal if @status.limited_visibility? && !@status.reply_limited? && !process_mentions_service.mentions?
|
||||
|
@ -218,9 +220,35 @@ class PostStatusService < BaseService
|
|||
raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if (mention_to_stranger? || reference_to_stranger?) && Setting.stranger_mention_from_local_ng && Admin::NgWord.stranger_mention_reject?("#{@options[:spoiler_text]}\n#{@options[:text]}")
|
||||
end
|
||||
|
||||
def validate_status_ng_rules!
|
||||
result = check_invalid_status_for_ng_rule! @account,
|
||||
reaction_type: 'create',
|
||||
spoiler_text: @options[:spoiler_text] || '',
|
||||
text: @text,
|
||||
tag_names: Extractor.extract_hashtags(@text) || [],
|
||||
visibility: @visibility.to_s,
|
||||
searchability: @searchability.to_s,
|
||||
sensitive: @sensitive,
|
||||
media_count: (@media || []).size,
|
||||
poll_count: @options.dig(:poll, 'options')&.size || 0,
|
||||
quote: quote_url,
|
||||
reply: @in_reply_to.present?,
|
||||
mention_count: mention_count,
|
||||
reference_count: reference_urls.size,
|
||||
mention_to_following: !(mention_to_stranger? || reference_to_stranger?)
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless result
|
||||
end
|
||||
|
||||
def mention_count
|
||||
@text.gsub(Account::MENTION_RE)&.count || 0
|
||||
end
|
||||
|
||||
def mention_to_stranger?
|
||||
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @account.id && !mentioned_account.following?(@account) } ||
|
||||
(@in_reply_to && @in_reply_to.account.id != @account.id && !@in_reply_to.account.following?(@account))
|
||||
return @mention_to_stranger if defined?(@mention_to_stranger)
|
||||
|
||||
@mention_to_stranger = @status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @account.id && !mentioned_account.following?(@account) } ||
|
||||
(@in_reply_to && @in_reply_to.account.id != @account.id && !@in_reply_to.account.following?(@account))
|
||||
end
|
||||
|
||||
def reference_to_stranger?
|
||||
|
@ -228,12 +256,20 @@ class PostStatusService < BaseService
|
|||
end
|
||||
|
||||
def referred_statuses
|
||||
statuses = ProcessReferencesService.extract_uris(@text).filter_map { |uri| ActivityPub::TagManager.instance.local_uri?(uri) && ActivityPub::TagManager.instance.uri_to_resource(uri, Status, url: true) }
|
||||
statuses = reference_urls.filter_map { |uri| ActivityPub::TagManager.instance.local_uri?(uri) && ActivityPub::TagManager.instance.uri_to_resource(uri, Status, url: true) }
|
||||
statuses += Status.where(id: @reference_ids) if @reference_ids.present?
|
||||
|
||||
statuses
|
||||
end
|
||||
|
||||
def quote_url
|
||||
ProcessReferencesService.extract_quote(@text)
|
||||
end
|
||||
|
||||
def reference_urls
|
||||
@reference_urls ||= ProcessReferencesService.extract_uris(@text) || []
|
||||
end
|
||||
|
||||
def validate_media!
|
||||
if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable)
|
||||
@media = []
|
||||
|
|
|
@ -8,6 +8,7 @@ class ProcessReferencesService < BaseService
|
|||
|
||||
DOMAIN = ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
|
||||
REFURL_EXP = /(RT|QT|BT|RN|RE)((:|;)?\s+|:|;)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
|
||||
QUOTEURL_EXP = /(QT|RN|RE)((:|;)?\s+|:|;)(#{URI::DEFAULT_PARSER.make_regexp(%w(http https))})/
|
||||
MAX_REFERENCES = 5
|
||||
|
||||
def call(status, reference_parameters, urls: nil, fetch_remote: true, no_fetch_urls: nil, quote_urls: nil)
|
||||
|
@ -45,8 +46,14 @@ class ProcessReferencesService < BaseService
|
|||
reference_parameters.any? || (urls || []).any? || (quote_urls || []).any? || FormattingHelper.extract_status_plain_text(status).scan(REFURL_EXP).pluck(3).uniq.any?
|
||||
end
|
||||
|
||||
def self.extract_uris(text)
|
||||
text.scan(REFURL_EXP).pluck(3)
|
||||
def self.extract_uris(text, remote: false)
|
||||
return text.scan(REFURL_EXP).pluck(3) unless remote
|
||||
|
||||
PlainTextFormatter.new(text, false).to_s.scan(REFURL_EXP).pluck(3)
|
||||
end
|
||||
|
||||
def self.extract_quote(text)
|
||||
text.scan(QUOTEURL_EXP).pick(3)
|
||||
end
|
||||
|
||||
def self.perform_worker_async(status, reference_parameters, urls, quote_urls)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class ReblogService < BaseService
|
||||
include Authorization
|
||||
include Payloadable
|
||||
include NgRuleHelper
|
||||
|
||||
# Reblog a status and notify its remote author
|
||||
# @param [Account] account Account to reblog from
|
||||
|
@ -16,6 +17,8 @@ class ReblogService < BaseService
|
|||
|
||||
authorize_with account, reblogged_status, :reblog?
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless check_invalid_reaction_for_ng_rule! account, reaction_type: 'reblog', recipient: reblogged_status.account, target_status: reblogged_status
|
||||
|
||||
reblog = account.statuses.find_by(reblog: reblogged_status)
|
||||
|
||||
return reblog unless reblog.nil?
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class UpdateStatusService < BaseService
|
||||
include Redisable
|
||||
include LanguagesHelper
|
||||
include NgRuleHelper
|
||||
|
||||
class NoChangesSubmittedError < StandardError; end
|
||||
|
||||
|
@ -31,6 +32,8 @@ class UpdateStatusService < BaseService
|
|||
validate_status!
|
||||
|
||||
Status.transaction do
|
||||
validate_status_ng_rules!
|
||||
|
||||
create_previous_edit! unless @options[:no_history]
|
||||
update_media_attachments! if @options.key?(:media_ids)
|
||||
update_poll! if @options.key?(:poll)
|
||||
|
@ -91,6 +94,30 @@ class UpdateStatusService < BaseService
|
|||
raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if (mention_to_stranger? || reference_to_stranger?) && Setting.stranger_mention_from_local_ng && Admin::NgWord.stranger_mention_reject?("#{@options[:spoiler_text]}\n#{@options[:text]}")
|
||||
end
|
||||
|
||||
def validate_status_ng_rules!
|
||||
result = check_invalid_status_for_ng_rule! @status.account,
|
||||
reaction_type: 'edit',
|
||||
spoiler_text: @options.key?(:spoiler_text) ? (@options[:spoiler_text] || '') : @status.spoiler_text,
|
||||
text: text,
|
||||
tag_names: Extractor.extract_hashtags(text) || [],
|
||||
visibility: @status.visibility,
|
||||
searchability: @status.searchability,
|
||||
sensitive: @options.key?(:sensitive) ? @options[:sensitive] : @status.sensitive,
|
||||
media_count: @options[:media_ids].present? ? @options[:media_ids].size : @status.media_attachments.count,
|
||||
poll_count: @options.dig(:poll, 'options')&.size || 0,
|
||||
quote: quote_url,
|
||||
reply: @status.reply?,
|
||||
mention_count: mention_count,
|
||||
reference_count: reference_urls.size,
|
||||
mention_to_following: !(mention_to_stranger? || reference_to_stranger?)
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless result
|
||||
end
|
||||
|
||||
def mention_count
|
||||
text.gsub(Account::MENTION_RE)&.count || 0
|
||||
end
|
||||
|
||||
def mention_to_stranger?
|
||||
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @status.account.id && !mentioned_account.following?(@status.account) } ||
|
||||
(@status.thread.present? && @status.thread.account.id != @status.account.id && !@status.thread.account.following?(@status.account))
|
||||
|
@ -101,9 +128,21 @@ class UpdateStatusService < BaseService
|
|||
end
|
||||
|
||||
def referred_statuses
|
||||
return [] unless @options[:text]
|
||||
return [] unless text
|
||||
|
||||
ProcessReferencesService.extract_uris(@options[:text]).filter_map { |uri| ActivityPub::TagManager.instance.local_uri?(uri) && ActivityPub::TagManager.instance.uri_to_resource(uri, Status, url: true) }
|
||||
reference_urls.filter_map { |uri| ActivityPub::TagManager.instance.local_uri?(uri) && ActivityPub::TagManager.instance.uri_to_resource(uri, Status, url: true) }
|
||||
end
|
||||
|
||||
def quote_url
|
||||
ProcessReferencesService.extract_quote(text)
|
||||
end
|
||||
|
||||
def reference_urls
|
||||
@reference_urls ||= ProcessReferencesService.extract_uris(text) || []
|
||||
end
|
||||
|
||||
def text
|
||||
@options.key?(:text) ? (@options[:text] || '') : @status.text
|
||||
end
|
||||
|
||||
def validate_media!
|
||||
|
|
|
@ -5,6 +5,7 @@ class VoteService < BaseService
|
|||
include Payloadable
|
||||
include Redisable
|
||||
include Lockable
|
||||
include NgRuleHelper
|
||||
|
||||
def call(account, poll, choices)
|
||||
return if choices.empty?
|
||||
|
@ -16,6 +17,8 @@ class VoteService < BaseService
|
|||
@choices = choices
|
||||
@votes = []
|
||||
|
||||
raise Mastodon::ValidationError, I18n.t('statuses.violate_rules') unless check_invalid_reaction_for_ng_rule! @account, reaction_type: 'vote', recipient: @poll.status.account, target_status: @poll.status
|
||||
|
||||
already_voted = true
|
||||
|
||||
with_redis_lock("vote:#{@poll.id}:#{@account.id}") do
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue