include JsonLdHelper
include Redisable
+ SUPPORTED_TYPES = %w(Note).freeze
+ CONVERTED_TYPES = %w(Image Video Article Page).freeze
+
def initialize(json, account, **options)
@json = json
@account = account
@object_uri ||= 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 distribute(status)
crawl_links(status)
redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, uri)
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'])) || @account.uri
+ if actor_id == @account.uri
+ return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
+ end
+ end
+
+ # If the status is not from the actor, try to fetch it
+ return fetch_remote_original_status if value_or_id(first_of_value(@json['attributedTo'])) == @account.uri
+ end
+
def fetch_remote_original_status
if object_uri.start_with?('http')
return if ActivityPub::TagManager.instance.local_uri?(object_uri)
class ActivityPub::Activity::Announce < ActivityPub::Activity
def perform
- original_status = status_from_uri(object_uri)
- original_status ||= fetch_remote_original_status
-
+ original_status = status_from_object
return if original_status.nil? || delete_arrived_first?(@json['id']) || !announceable?(original_status)
status = Status.find_by(account: @account, reblog: original_status)
# frozen_string_literal: true
class ActivityPub::Activity::Create < ActivityPub::Activity
- SUPPORTED_TYPES = %w(Note).freeze
- CONVERTED_TYPES = %w(Image Video Article Page).freeze
-
def perform
return if unsupported_object_type? || invalid_origin?(@object['id'])
return if Tombstone.exists?(uri: @object['id'])
@object['nameMap'].is_a?(Hash) && !@object['nameMap'].empty?
end
- def unsupported_object_type?
- @object.is_a?(String) || !(supported_object_type? || converted_object_type?)
- end
-
def unsupported_media_type?(mime_type)
mime_type.present? && !(MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES).include?(mime_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 skip_download?
return @skip_download if defined?(@skip_download)
@skip_download ||= DomainBlock.find_by(domain: @account.domain)&.reject_media?
class ActivityPub::ActivitySerializer < ActiveModel::Serializer
attributes :id, :type, :actor, :published, :to, :cc
- has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, unless: :announce?
- attribute :proper_uri, key: :object, if: :announce?
+ has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, unless: :owned_announce?
+ attribute :proper_uri, key: :object, if: :owned_announce?
attribute :atom_uri, if: :announce?
def id
def announce?
object.reblog?
end
+
+ def owned_announce?
+ announce? && object.account == object.proper.account && object.proper.private_visibility?
+ end
end
require 'rails_helper'
RSpec.describe ActivityPub::Activity::Announce do
- let(:sender) { Fabricate(:account) }
+ let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers') }
let(:recipient) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: recipient) }
id: 'foo',
type: 'Announce',
actor: ActivityPub::TagManager.instance.uri_for(sender),
- object: ActivityPub::TagManager.instance.uri_for(status),
+ object: object_json,
}.with_indifferent_access
end
- describe '#perform' do
- subject { described_class.new(json, sender) }
+ subject { described_class.new(json, sender) }
+
+ before do
+ sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
+ end
+ describe '#perform' do
before do
subject.perform
end
- it 'creates a reblog by sender of status' do
- expect(sender.reblogged?(status)).to be true
+ context 'a known status' do
+ let(:object_json) do
+ ActivityPub::TagManager.instance.uri_for(status)
+ end
+
+ it 'creates a reblog by sender of status' do
+ expect(sender.reblogged?(status)).to be true
+ end
+ end
+
+ context 'self-boost of a previously unknown status with missing attributedTo' do
+ let(:object_json) do
+ {
+ id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
+ type: 'Note',
+ content: 'Lorem ipsum',
+ to: 'http://example.com/followers',
+ }
+ end
+
+ it 'creates a reblog by sender of status' do
+ expect(sender.reblogged?(sender.statuses.first)).to be true
+ end
+ end
+
+ context 'self-boost of a previously unknown status with correct attributedTo' do
+ let(:object_json) do
+ {
+ id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
+ type: 'Note',
+ content: 'Lorem ipsum',
+ attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+ to: 'http://example.com/followers',
+ }
+ end
+
+ it 'creates a reblog by sender of status' do
+ expect(sender.reblogged?(sender.statuses.first)).to be true
+ end
end
end
end