Merge remote-tracking branch 'origin/kb_development' into kb_migration

This commit is contained in:
KMY 2023-04-06 16:57:01 +09:00
commit 94cd200097
50 changed files with 801 additions and 66 deletions

View file

@ -29,6 +29,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account ||= Account.find_remote(@username, @domain)
@old_public_key = @account&.public_key
@old_protocol = @account&.protocol
@old_searchability = @account&.searchability
@suspension_changed = false
if @account.nil?
@ -112,6 +113,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.fields = property_values || {}
@account.also_known_as = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) }
@account.discoverable = @json['discoverable'] || false
@account.searchability = searchability_from_audience
end
def set_fetchable_key!
@ -150,6 +152,10 @@ class ActivityPub::ProcessAccountService < BaseService
end
end
def after_searchability_change!
SearchabilityUpdateWorker.perform_async(@account.id) if @account.statuses.unset_searchability.exists?
end
def after_protocol_change!
ActivityPub::PostUpgradeWorker.perform_async(@account.domain)
end
@ -224,6 +230,24 @@ class ActivityPub::ProcessAccountService < BaseService
end
end
def audience_searchable_by
return nil if @json['searchableBy'].nil?
@audience_searchable_by_processaccountservice = as_array(@json['searchableBy']).map { |x| value_or_id(x) }
end
def searchability_from_audience
if audience_searchable_by.nil?
:private
elsif audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
:public
elsif audience_searchable_by.include?(@account.followers_url)
:unlisted # Followers only in kmyblue (generics: private)
else
:private # Reaction only in kmyblue (generics: direct)
end
end
def property_values
return unless @json['attachment'].is_a?(Array)
@ -310,6 +334,10 @@ class ActivityPub::ProcessAccountService < BaseService
!@old_protocol.nil? && @old_protocol != @account.protocol
end
def searchability_changed?
!@old_searchability.nil? && @old_searchability != @account.searchability
end
def process_tags
return if @json['tag'].blank?

View file

@ -22,6 +22,7 @@ class PostStatusService < BaseService
# @option [Status] :thread Optional status to reply to
# @option [Boolean] :sensitive
# @option [String] :visibility
# @option [String] :searchability
# @option [String] :spoiler_text
# @option [String] :language
# @option [String] :scheduled_at
@ -66,12 +67,28 @@ class PostStatusService < BaseService
@text = @options.delete(:spoiler_text) if @text.blank? && @options[:spoiler_text].present?
@visibility = @options[:visibility] || @account.user&.setting_default_privacy
@visibility = :unlisted if (@visibility&.to_sym == :public || @visibility&.to_sym == :public_unlisted) && @account.silenced?
@searchability= searchability
@scheduled_at = @options[:scheduled_at]&.to_datetime
@scheduled_at = nil if scheduled_in_the_past?
rescue ArgumentError
raise ActiveRecord::RecordInvalid
end
def searchability
case @options[:searchability]&.to_sym
when :public
case @visibility&.to_sym when :public, :public_unlisted then :public when :unlisted then :unlisted when :private then :private else :direct end
when :unlisted
case @visibility&.to_sym when :public, :public_unlisted, :unlisted then :unlisted when :private then :private else :direct end
when :private
case @visibility&.to_sym when :public, :public_unlisted, :unlisted, :private then :private else :direct end
when nil
@account.searchability
else
:direct
end
end
def process_status!
@status = @account.statuses.new(status_attributes)
process_mentions_service.call(@status, save_records: false)
@ -196,6 +213,7 @@ class PostStatusService < BaseService
sensitive: @sensitive,
spoiler_text: @options[:spoiler_text] || '',
visibility: @visibility,
searchability: @searchability,
language: valid_locale_cascade(@options[:language], @account.user&.preferred_posting_language, I18n.default_locale),
application: @options[:application],
rate_limit: @options[:with_rate_limit],

View file

@ -8,6 +8,7 @@ class SearchService < BaseService
@limit = limit.to_i
@offset = options[:type].blank? ? 0 : options[:offset].to_i
@resolve = options[:resolve] || false
@searchability = options[:searchability] || 'public'
default_results.tap do |results|
next if @query.blank? || @limit.zero?
@ -35,11 +36,26 @@ class SearchService < BaseService
end
def perform_statuses_search!
# definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id })).order(id: :desc)
definition = parsed_query.apply(StatusesIndex).order(id: :desc)
privacy_definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id }))
# 'private' searchability posts are NOT in here because it's already added at previous line.
case @searchability
when 'public'
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'public' }))
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'unlisted' }).filter(terms: { account_id: following_account_ids })) unless following_account_ids.empty?
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }))
when 'unlisted', 'private'
privacy_definition = privacy_definition.or(StatusesIndex.filter(terms: { searchability: %w(public unlisted) }).filter(terms: { account_id: following_account_ids })) unless following_account_ids.empty?
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }))
when 'direct'
privacy_definition = privacy_definition.or(StatusesIndex.filter(term: { searchability: 'direct' }).filter(term: { account_id: @account.id }))
end
definition = parsed_query.apply(StatusesIndex).order(id: :desc)
definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present?
definition = definition.and(privacy_definition)
if @options[:min_id].present? || @options[:max_id].present?
range = {}
range[:gt] = @options[:min_id].to_i if @options[:min_id].present?
@ -50,9 +66,9 @@ class SearchService < BaseService
results = definition.limit(@limit).offset(@offset).objects.compact
account_ids = results.map(&:account_id)
account_domains = results.map(&:account_domain)
preloaded_relations = @account.relations_map(account_ids, account_domains)
account_relations = @account.relations_map(account_ids, account_domains) # variable old name: preloaded_relations
results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).search_filtered? }
results.reject { |status| StatusFilter.new(status, @account, account_relations).filtered? }
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
[]
end
@ -115,4 +131,13 @@ class SearchService < BaseService
def parsed_query
SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query))
end
def following_account_ids
return @following_account_ids if defined?(@following_account_ids)
account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public unlisted private)).reorder(nil).select(1).to_sql
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public unlisted private)).reorder(nil).select(1).to_sql
following_accounts = Follow.where(account_id: @account.id).merge(Account.where("EXISTS (#{account_exists_sql})").or(Account.where("EXISTS (#{status_exists_sql})")))
@following_account_ids = following_accounts.pluck(:target_account_id)
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
class SearchabilityUpdateService < BaseService
def call(account)
statuses = account.statuses.unset_searchability
return unless statuses.exists?
ids = statuses.pluck(:id)
if account.public_searchability?
statuses.update_all('searchability = CASE visibility WHEN 0 THEN 0 WHEN 10 THEN 0 WHEN 1 THEN 2 WHEN 2 THEN 2 ELSE 3 END, updated_at = CURRENT_TIMESTAMP')
elsif account.unlisted_searchability?
statuses.update_all('searchability = CASE visibility WHEN 0 THEN 1 WHEN 10 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 2 ELSE 3 END, updated_at = CURRENT_TIMESTAMP')
elsif account.private_searchability?
statuses.update_all('searchability = CASE WHEN visibility IN (0, 1, 2, 10) THEN 2 ELSE 3 END, updated_at = CURRENT_TIMESTAMP')
else
statuses.update_all('searchability = 3, updated_at = CURRENT_TIMESTAMP')
end
return unless Chewy.enabled?
ids.each_slice(100) do |chunk_ids|
StatusesIndex.import chunk_ids, update_fields: [:searchability]
end
end
end