Add support for Link objects in attachment

This commit is contained in:
Eugen Rochko 2025-09-13 03:42:47 +02:00
parent 06803422da
commit cf7d701214
6 changed files with 58 additions and 9 deletions

View File

@ -44,6 +44,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def process_status def process_status
@tags = [] @tags = []
@mentions = [] @mentions = []
@links = []
@unresolved_mentions = [] @unresolved_mentions = []
@silenced_account_ids = [] @silenced_account_ids = []
@params = {} @params = {}
@ -72,7 +73,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def distribute def distribute
# Spread out crawling randomly to avoid DDoSing the link # Spread out crawling randomly to avoid DDoSing the link
LinkCrawlWorker.perform_in(rand(1..59).seconds, @status.id) # If there are no links in the attachment property, scan text for links
LinkCrawlWorker.perform_in(rand(1..59).seconds, @status.id, @links.first)
# Distribute into home and list feeds and notify mentioned accounts # Distribute into home and list feeds and notify mentioned accounts
::DistributionWorker.perform_async(@status.id, { 'silenced_account_ids' => @silenced_account_ids }) if @options[:override_timestamps] || @status.within_realtime_window? ::DistributionWorker.perform_async(@status.id, { 'silenced_account_ids' => @silenced_account_ids }) if @options[:override_timestamps] || @status.within_realtime_window?
@ -273,6 +275,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
media_attachments = [] media_attachments = []
as_array(@object['attachment']).each do |attachment| as_array(@object['attachment']).each do |attachment|
if attachment['href'].present?
preview_card_parser = ActivityPub::Parser::PreviewCardParser.new(attachment)
@links << preview_card_parser.url if preview_card_parser.url.present?
next
end
media_attachment_parser = ActivityPub::Parser::MediaAttachmentParser.new(attachment) media_attachment_parser = ActivityPub::Parser::MediaAttachmentParser.new(attachment)
next if media_attachment_parser.remote_url.blank? || media_attachments.size >= Status::MEDIA_ATTACHMENTS_LIMIT next if media_attachment_parser.remote_url.blank? || media_attachments.size >= Status::MEDIA_ATTACHMENTS_LIMIT

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class ActivityPub::Parser::PreviewCardParser
include JsonLdHelper
def initialize(json)
@json = json
end
# @param [PreviewCard] previous_record
def significantly_changes?(previous_record)
url != previous_record.url
end
def url
url = Addressable::URI.parse(@json['href'])&.normalize&.to_s
url unless unsupported_uri_scheme?(url)
rescue Addressable::URI::InvalidURIError
nil
end
end

View File

@ -135,7 +135,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
end end
def virtual_attachments def virtual_attachments
object.ordered_media_attachments object.ordered_media_attachments + [object.preview_card]
end end
def virtual_tags def virtual_tags
@ -253,6 +253,18 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
} }
end end
class PreviewCardSerializer < ActivityPub::Serializer
attributes :type, :href
def type
'Link'
end
def href
object.original_url
end
end
class MediaAttachmentSerializer < ActivityPub::Serializer class MediaAttachmentSerializer < ActivityPub::Serializer
context_extensions :blurhash, :focal_point context_extensions :blurhash, :focal_point

View File

@ -19,6 +19,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
@quote_changed = false @quote_changed = false
@request_id = request_id @request_id = request_id
@quote = nil @quote = nil
@next_media_attachments = []
@next_links = []
# Only native types can be updated at the moment # Only native types can be updated at the moment
return @status if !expected_type? || already_updated_more_recently? return @status if !expected_type? || already_updated_more_recently?
@ -80,9 +82,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
def update_media_attachments! def update_media_attachments!
previous_media_attachments = @status.media_attachments.to_a previous_media_attachments = @status.media_attachments.to_a
previous_media_attachments_ids = @status.ordered_media_attachment_ids || previous_media_attachments.map(&:id) previous_media_attachments_ids = @status.ordered_media_attachment_ids || previous_media_attachments.map(&:id)
@next_media_attachments = []
as_array(@json['attachment']).each do |attachment| as_array(@json['attachment']).each do |attachment|
if attachment['href'].present?
preview_card_parser = ActivityPub::Parser::PreviewCardParser.new(attachment)
@next_links << preview_card_parser.url if preview_card_parser.url.present?
next
end
media_attachment_parser = ActivityPub::Parser::MediaAttachmentParser.new(attachment) media_attachment_parser = ActivityPub::Parser::MediaAttachmentParser.new(attachment)
next if media_attachment_parser.remote_url.blank? || @next_media_attachments.size > Status::MEDIA_ATTACHMENTS_LIMIT next if media_attachment_parser.remote_url.blank? || @next_media_attachments.size > Status::MEDIA_ATTACHMENTS_LIMIT
@ -334,7 +341,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
def update_counts! def update_counts!
likes = @status_parser.favourites_count likes = @status_parser.favourites_count
shares = @status_parser.reblogs_count shares = @status_parser.reblogs_count
return if likes.nil? && shares.nil? return if likes.nil? && shares.nil?
@status.status_stat.tap do |status_stat| @status.status_stat.tap do |status_stat|
@ -387,7 +395,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
def reset_preview_card! def reset_preview_card!
@status.reset_preview_card! @status.reset_preview_card!
LinkCrawlWorker.perform_in(rand(1..59).seconds, @status.id) LinkCrawlWorker.perform_in(rand(1..59).seconds, @status.id, @next_links.first)
end end
def broadcast_updates! def broadcast_updates!

View File

@ -15,9 +15,9 @@ class FetchLinkCardService < BaseService
) )
}iox }iox
def call(status) def call(status, original_url = nil)
@status = status @status = status
@original_url = parse_urls @original_url = original_url || parse_urls
return if @original_url.nil? || @status.with_preview_card? || @status.with_media? || @status.quote.present? return if @original_url.nil? || @status.with_preview_card? || @status.with_media? || @status.quote.present?

View File

@ -5,8 +5,8 @@ class LinkCrawlWorker
sidekiq_options queue: 'pull', retry: 0 sidekiq_options queue: 'pull', retry: 0
def perform(status_id) def perform(status_id, url = nil)
FetchLinkCardService.new.call(Status.find(status_id)) FetchLinkCardService.new.call(Status.find(status_id), url)
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordNotUnique rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordNotUnique
true true
end end