You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
5.7 KiB
193 lines
5.7 KiB
# frozen_string_literal: true |
|
|
|
class ActivityPub::Activity |
|
include JsonLdHelper |
|
include Redisable |
|
include Lockable |
|
|
|
SUPPORTED_TYPES = %w(Note Question Article).freeze |
|
CONVERTED_TYPES = %w(Image Audio Video Page Event).freeze |
|
|
|
def initialize(json, account, **options) |
|
@json = json |
|
@account = account |
|
@object = @json['object'] |
|
@options = options |
|
end |
|
|
|
def perform |
|
raise NotImplementedError |
|
end |
|
|
|
class << self |
|
def factory(json, account, **) |
|
klass_for(json)&.new(json, account, **) |
|
end |
|
|
|
private |
|
|
|
def klass_for(json) |
|
case json['type'] |
|
when 'Create' |
|
ActivityPub::Activity::Create |
|
when 'Announce' |
|
ActivityPub::Activity::Announce |
|
when 'Delete' |
|
ActivityPub::Activity::Delete |
|
when 'Follow' |
|
ActivityPub::Activity::Follow |
|
when 'Like' |
|
ActivityPub::Activity::Like |
|
when 'Block' |
|
ActivityPub::Activity::Block |
|
when 'Update' |
|
ActivityPub::Activity::Update |
|
when 'Undo' |
|
ActivityPub::Activity::Undo |
|
when 'Accept' |
|
ActivityPub::Activity::Accept |
|
when 'Reject' |
|
ActivityPub::Activity::Reject |
|
when 'Flag' |
|
ActivityPub::Activity::Flag |
|
when 'Add' |
|
ActivityPub::Activity::Add |
|
when 'Remove' |
|
ActivityPub::Activity::Remove |
|
when 'Move' |
|
ActivityPub::Activity::Move |
|
when 'QuoteRequest' |
|
ActivityPub::Activity::QuoteRequest |
|
end |
|
end |
|
end |
|
|
|
protected |
|
|
|
def status_from_uri(uri) |
|
ActivityPub::TagManager.instance.uri_to_resource(uri, Status) |
|
end |
|
|
|
def account_from_uri(uri) |
|
ActivityPub::TagManager.instance.uri_to_resource(uri, Account) |
|
end |
|
|
|
def object_uri |
|
@object_uri ||= uri_from_bearcap(value_or_id(@object)) |
|
end |
|
|
|
def unsupported_object_type? |
|
@object.is_a?(String) || !(supported_object_type? || converted_object_type?) |
|
end |
|
|
|
def supported_object_type? |
|
equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) |
|
end |
|
|
|
def converted_object_type? |
|
equals_or_includes_any?(@object['type'], CONVERTED_TYPES) |
|
end |
|
|
|
def delete_arrived_first?(uri) |
|
redis.exists?("delete_upon_arrival:#{@account.id}:#{uri}") |
|
end |
|
|
|
def delete_later!(uri) |
|
redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, true) |
|
end |
|
|
|
def status_from_object |
|
# If the status is already known, return it |
|
status = status_from_uri(object_uri) |
|
|
|
return status unless status.nil? |
|
|
|
# If the boosted toot is embedded and it is a self-boost, handle it like a Create |
|
unless unsupported_object_type? |
|
actor_id = value_or_id(first_of_value(@object['attributedTo'])) |
|
|
|
if actor_id == @account.uri |
|
virtual_object = { 'type' => 'Create', 'actor' => actor_id, 'object' => @object } |
|
return ActivityPub::Activity.factory(virtual_object, @account, request_id: @options[:request_id]).perform |
|
end |
|
end |
|
|
|
fetch_remote_original_status |
|
end |
|
|
|
def quote_from_request_json(json) |
|
quoted_status_uri = value_or_id(json['object']) |
|
quoting_status_uri = value_or_id(json['instrument']) |
|
return if quoting_status_uri.nil? || quoted_status_uri.nil? |
|
|
|
quoting_status = status_from_uri(quoting_status_uri) |
|
return unless quoting_status.present? && quoting_status.quote.present? |
|
|
|
quoted_status = status_from_uri(quoted_status_uri) |
|
return unless quoted_status.present? && quoted_status.account == @account && quoting_status.quote.quoted_status == quoted_status |
|
|
|
quoting_status.quote |
|
end |
|
|
|
def dereference_object! |
|
return unless @object.is_a?(String) |
|
|
|
dereferencer = ActivityPub::Dereferencer.new(@object, permitted_origin: @account.uri, signature_actor: signed_fetch_actor) |
|
|
|
@object = dereferencer.object unless dereferencer.object.nil? |
|
end |
|
|
|
def signed_fetch_actor |
|
return Account.find(@options[:delivered_to_account_id]) if @options[:delivered_to_account_id].present? |
|
|
|
first_mentioned_local_account || first_local_follower |
|
end |
|
|
|
def first_mentioned_local_account |
|
audience = (as_array(@json['to']) + as_array(@json['cc'])).map { |x| value_or_id(x) }.uniq |
|
ActivityPub::TagManager.instance.uris_to_local_accounts(audience).first |
|
end |
|
|
|
def first_local_follower |
|
@account.followers.local.first |
|
end |
|
|
|
def follow_request_from_object |
|
@follow_request_from_object ||= FollowRequest.find_by(target_account: @account, uri: object_uri) unless object_uri.nil? |
|
end |
|
|
|
def quote_request_from_object |
|
@quote_request_from_object ||= Quote.find_by(quoted_account: @account, activity_uri: object_uri) unless object_uri.nil? |
|
end |
|
|
|
def follow_from_object |
|
@follow_from_object ||= ::Follow.find_by(target_account: @account, uri: object_uri) unless object_uri.nil? |
|
end |
|
|
|
def fetch_remote_original_status |
|
if object_uri.start_with?('http') |
|
return if ActivityPub::TagManager.instance.local_uri?(object_uri) |
|
|
|
ActivityPub::FetchRemoteStatusService.new.call(object_uri, on_behalf_of: @account.followers.local.first, request_id: @options[:request_id]) |
|
elsif @object['url'].present? |
|
::FetchRemoteStatusService.new.call(@object['url'], request_id: @options[:request_id]) |
|
end |
|
end |
|
|
|
def fetch? |
|
!@options[:delivery] |
|
end |
|
|
|
def followed_by_local_accounts? |
|
@account.passive_relationships.exists? || (@options[:relayed_through_actor].is_a?(Account) && @options[:relayed_through_actor].passive_relationships&.exists?) |
|
end |
|
|
|
def requested_through_relay? |
|
@options[:relayed_through_actor] && Relay.find_by(inbox_url: @options[:relayed_through_actor].inbox_url)&.enabled? |
|
end |
|
|
|
def reject_payload! |
|
Rails.logger.info("Rejected #{@json['type']} activity #{@json['id']} from #{@account.uri}#{@options[:relayed_through_actor] && "via #{@options[:relayed_through_actor].uri}"}") |
|
nil |
|
end |
|
end
|
|
|