Add: Webでの引用表示 (#50)

* Add compacted component

* 引用表示の間にコンテナをはさみ、不要なコードを削除

* 引用APIを作成、ついでにブロック状況を引用APIに反映

* テスト修正など

* 引用をキャッシュに登録

* `quote_id`が`quote_of_id`になったのをSerializerに反映

* Fix test

* 引用をフィルターの対象に含める設定+エラー修正

* ストリーミングの存在しないプロパティ削除によるエラーを修正

* Fix lint

* 他のサーバーから来た引用付き投稿を処理

* Fix test

* フィルター設定時エラーの調整

* 画像つき投稿のスタイルを調整

* 画像つき投稿の最大高さを調整

* 引用禁止・非表示の設定を追加

* ブロック対応

* マイグレーションコード調整

* 引用設定の翻訳を作成

* Lint修正

* 参照1つの場合は引用に変換する設定を削除

* 不要になったテストを削除

* ブロック設定追加、バグ修正

* 他サーバーへ引用送信・受け入れ
This commit is contained in:
KMY(雪あすか) 2023-10-02 11:12:51 +09:00 committed by GitHub
parent 3c649aa74d
commit 44b739a39a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 1362 additions and 120 deletions

View file

@ -254,9 +254,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
def update_references!
references = @json['references'].nil? ? [] : ActivityPub::FetchReferencesService.new.call(@status, @json['references'])
quote = @json['quote'] || @json['quoteUrl'] || @json['quoteURL'] || @json['_misskey_quote']
references << quote if quote
ProcessReferencesService.perform_worker_async(@status, [], references)
ProcessReferencesService.perform_worker_async(@status, [], references, [quote].compact)
end
def expected_type?

View file

@ -10,14 +10,17 @@ class ProcessReferencesService < BaseService
REFURL_EXP = /(RT|QT|BT|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)
def call(status, reference_parameters, urls: nil, fetch_remote: true, no_fetch_urls: nil, quote_urls: nil)
@status = status
@reference_parameters = reference_parameters || []
@urls = urls || []
@quote_urls = quote_urls || []
@urls = (urls - @quote_urls) || []
@no_fetch_urls = no_fetch_urls || []
@fetch_remote = fetch_remote
@again = false
@attributes = {}
with_redis_lock("process_status_refs:#{@status.id}") do
@references_count = old_references.size
@ -38,27 +41,27 @@ class ProcessReferencesService < BaseService
launch_worker if @again
end
def self.need_process?(status, reference_parameters, urls)
reference_parameters.any? || (urls || []).any? || FormattingHelper.extract_status_plain_text(status).scan(REFURL_EXP).pluck(3).uniq.any?
def self.need_process?(status, reference_parameters, urls, quote_urls)
reference_parameters.any? || (urls || []).any? || (quote_urls || []).any? || FormattingHelper.extract_status_plain_text(status).scan(REFURL_EXP).pluck(3).uniq.any?
end
def self.perform_worker_async(status, reference_parameters, urls)
return unless need_process?(status, reference_parameters, urls)
def self.perform_worker_async(status, reference_parameters, urls, quote_urls)
return unless need_process?(status, reference_parameters, urls, quote_urls)
Rails.cache.write("status_reference:#{status.id}", true, expires_in: 10.minutes)
ProcessReferencesWorker.perform_async(status.id, reference_parameters, urls, [])
ProcessReferencesWorker.perform_async(status.id, reference_parameters, urls, [], quote_urls || [])
end
def self.call_service(status, reference_parameters, urls)
return unless need_process?(status, reference_parameters, urls)
def self.call_service(status, reference_parameters, urls, quote_urls = [])
return unless need_process?(status, reference_parameters, urls, quote_urls)
ProcessReferencesService.new.call(status, reference_parameters || [], urls: urls || [], fetch_remote: false)
ProcessReferencesService.new.call(status, reference_parameters || [], urls: urls || [], fetch_remote: false, quote_urls: quote_urls)
end
private
def references
@references ||= @reference_parameters + scan_text!
@references ||= @reference_parameters + scan_text! + quote_status_ids
end
def old_references
@ -88,12 +91,24 @@ class ProcessReferencesService < BaseService
target_urls = urls + @urls
target_urls.map do |url|
status = ResolveURLService.new.call(url, on_behalf_of: @status.account, fetch_remote: @fetch_remote && @no_fetch_urls.exclude?(url))
@no_fetch_urls << url if !@fetch_remote && status.present? && status.local?
status = url_to_status(url)
@no_fetch_urls << url if !@fetch_remote && status.present?
status
end
end
def url_to_status(url)
ResolveURLService.new.call(url, on_behalf_of: @status.account, fetch_remote: @fetch_remote && @no_fetch_urls.exclude?(url))
end
def quote_status_ids
@quote_status_ids ||= @quote_urls.filter_map { |url| url_to_status(url) }.map(&:id)
end
def quotable?(target_status)
@status.account.allow_quote? && StatusPolicy.new(@status.account, target_status).quote?
end
def add_references
return if added_references.empty?
@ -101,7 +116,12 @@ class ProcessReferencesService < BaseService
statuses = Status.where(id: added_references)
statuses.each do |status|
@added_objects << @status.reference_objects.new(target_status: status, attribute_type: @attributes[status.id])
attribute_type = quote_status_ids.include?(status.id) ? 'QT' : @attributes[status.id]
attribute_type = 'BT' unless quotable?(status)
quote_type = attribute_type.present? ? attribute_type.casecmp('QT').zero? : false
@status.quote_of_id = status.id if quote_type && (@status.quote_of_id.nil? || references.exclude?(@status.quote_of_id))
@added_objects << @status.reference_objects.new(target_status: status, attribute_type: attribute_type, quote: quote_type)
status.increment_count!(:status_referred_by_count)
@references_count += 1
@ -133,6 +153,6 @@ class ProcessReferencesService < BaseService
end
def launch_worker
ProcessReferencesWorker.perform_async(@status.id, @reference_parameters, @urls, @no_fetch_urls)
ProcessReferencesWorker.perform_async(@status.id, @reference_parameters, @urls, @no_fetch_urls, @quote_urls)
end
end

View file

@ -6,16 +6,16 @@ class ResolveURLService < BaseService
USERNAME_STATUS_RE = %r{/@(?<username>#{Account::USERNAME_RE})/(?<status_id>[0-9]+)\Z}
def call(url, on_behalf_of: nil, fetch_remote: true)
def call(url, on_behalf_of: nil, fetch_remote: true, local_only: false)
@url = url
@on_behalf_of = on_behalf_of
@fetch_remote = fetch_remote
if local_url?
process_local_url
elsif fetch_remote && !fetched_resource.nil?
elsif !local_only && fetch_remote && !fetched_resource.nil?
process_url
else
elsif !local_only
process_url_from_db
end
end