Federated reports (#6570)
* Fix #2176: Federated reports * UI for federated reports * Add spec for ActivityPub Flag handler * Add spec for ReportServicelocal
parent
4072b68686
commit
41a01bec23
13 changed files with 306 additions and 22 deletions
@ -0,0 +1,25 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class ActivityPub::Activity::Flag < ActivityPub::Activity |
||||
def perform |
||||
target_accounts = object_uris.map { |uri| account_from_uri(uri) }.compact.select(&:local?) |
||||
target_statuses_by_account = object_uris.map { |uri| status_from_uri(uri) }.compact.select(&:local?).group_by(&:account_id) |
||||
|
||||
target_accounts.each do |target_account| |
||||
next if Report.where(account: @account, target_account: target_account).exists? |
||||
|
||||
target_statuses = target_statuses_by_account[target_account.id] |
||||
|
||||
ReportService.new.call( |
||||
@account, |
||||
target_account, |
||||
status_ids: target_statuses.nil? ? [] : target_statuses.map(&:id), |
||||
comment: @json['content'] || '' |
||||
) |
||||
end |
||||
end |
||||
|
||||
def object_uris |
||||
@object_uris ||= Array(@object.is_a?(Array) ? @object.map { |item| value_or_id(item) } : value_or_id(@object)) |
||||
end |
||||
end |
@ -0,0 +1,27 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class ActivityPub::FlagSerializer < ActiveModel::Serializer |
||||
attributes :id, :type, :actor, :content |
||||
attribute :virtual_object, key: :object |
||||
|
||||
def id |
||||
# This is nil for now |
||||
ActivityPub::TagManager.instance.uri_for(object) |
||||
end |
||||
|
||||
def type |
||||
'Flag' |
||||
end |
||||
|
||||
def actor |
||||
ActivityPub::TagManager.instance.uri_for(instance_options[:account] || object.account) |
||||
end |
||||
|
||||
def virtual_object |
||||
[ActivityPub::TagManager.instance.uri_for(object.target_account)] + object.statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) } |
||||
end |
||||
|
||||
def content |
||||
object.comment |
||||
end |
||||
end |
@ -0,0 +1,54 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
class ReportService < BaseService |
||||
def call(source_account, target_account, options = {}) |
||||
@source_account = source_account |
||||
@target_account = target_account |
||||
@status_ids = options.delete(:status_ids) || [] |
||||
@comment = options.delete(:comment) || '' |
||||
@options = options |
||||
|
||||
create_report! |
||||
notify_staff! |
||||
forward_to_origin! if !@target_account.local? && ActiveModel::Type::Boolean.new.cast(@options[:forward]) |
||||
|
||||
@report |
||||
end |
||||
|
||||
private |
||||
|
||||
def create_report! |
||||
@report = @source_account.reports.create!( |
||||
target_account: @target_account, |
||||
status_ids: @status_ids, |
||||
comment: @comment |
||||
) |
||||
end |
||||
|
||||
def notify_staff! |
||||
User.staff.includes(:account).each do |u| |
||||
AdminMailer.new_report(u.account, @report).deliver_later |
||||
end |
||||
end |
||||
|
||||
def forward_to_origin! |
||||
ActivityPub::DeliveryWorker.perform_async( |
||||
payload, |
||||
some_local_account.id, |
||||
@target_account.inbox_url |
||||
) |
||||
end |
||||
|
||||
def payload |
||||
Oj.dump(ActiveModelSerializers::SerializableResource.new( |
||||
@report, |
||||
serializer: ActivityPub::FlagSerializer, |
||||
adapter: ActivityPub::Adapter, |
||||
account: some_local_account |
||||
).as_json) |
||||
end |
||||
|
||||
def some_local_account |
||||
@some_local_account ||= Account.local.where(suspended: false).first |
||||
end |
||||
end |
@ -0,0 +1,37 @@ |
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe ActivityPub::Activity::Flag do |
||||
let(:sender) { Fabricate(:account, domain: 'example.com') } |
||||
let(:flagged) { Fabricate(:account) } |
||||
let(:status) { Fabricate(:status, account: flagged, uri: 'foobar') } |
||||
|
||||
let(:json) do |
||||
{ |
||||
'@context': 'https://www.w3.org/ns/activitystreams', |
||||
id: nil, |
||||
type: 'Flag', |
||||
content: 'Boo!!', |
||||
actor: ActivityPub::TagManager.instance.uri_for(sender), |
||||
object: [ |
||||
ActivityPub::TagManager.instance.uri_for(flagged), |
||||
ActivityPub::TagManager.instance.uri_for(status), |
||||
], |
||||
}.with_indifferent_access |
||||
end |
||||
|
||||
describe '#perform' do |
||||
subject { described_class.new(json, sender) } |
||||
|
||||
before do |
||||
subject.perform |
||||
end |
||||
|
||||
it 'creates a report' do |
||||
report = Report.find_by(account: sender, target_account: flagged) |
||||
|
||||
expect(report).to_not be_nil |
||||
expect(report.comment).to eq 'Boo!!' |
||||
expect(report.status_ids).to eq [status.id] |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,25 @@ |
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe ReportService do |
||||
subject { described_class.new } |
||||
|
||||
let(:source_account) { Fabricate(:account) } |
||||
|
||||
context 'for a remote account' do |
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } |
||||
|
||||
before do |
||||
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) |
||||
end |
||||
|
||||
it 'sends ActivityPub payload when forward is true' do |
||||
subject.call(source_account, remote_account, forward: true) |
||||
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made |
||||
end |
||||
|
||||
it 'does not send anything when forward is false' do |
||||
subject.call(source_account, remote_account, forward: false) |
||||
expect(a_request(:post, 'http://example.com/inbox')).to_not have_been_made |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue