Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `app/javascript/mastodon/actions/compose.js`: Not a “real” conflict, but change too close to a change we made to fix the vanilla WebUI locally pushing authored local-only toots in the public TL view.
This commit is contained in:
@@ -157,6 +157,34 @@ class ActivityPub::Activity
|
||||
fetch_remote_original_status
|
||||
end
|
||||
|
||||
def dereference_object!
|
||||
return unless @object.is_a?(String)
|
||||
return if invalid_origin?(@object)
|
||||
|
||||
object = fetch_resource(@object, true, signed_fetch_account)
|
||||
return unless object.present? && object.is_a?(Hash) && supported_context?(object)
|
||||
|
||||
@object = object
|
||||
end
|
||||
|
||||
def signed_fetch_account
|
||||
first_mentioned_local_account || first_local_follower
|
||||
end
|
||||
|
||||
def first_mentioned_local_account
|
||||
audience = (as_array(@json['to']) + as_array(@json['cc'])).uniq
|
||||
local_usernames = audience.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }
|
||||
.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
|
||||
|
||||
return if local_usernames.empty?
|
||||
|
||||
Account.local.where(username: local_usernames).first
|
||||
end
|
||||
|
||||
def first_local_follower
|
||||
@account.followers.local.first
|
||||
end
|
||||
|
||||
def follow_request_from_object
|
||||
@follow_request ||= FollowRequest.find_by(target_account: @account, uri: object_uri) unless object_uri.nil?
|
||||
end
|
||||
|
@@ -4,25 +4,32 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
|
||||
def perform
|
||||
return reject_payload! if delete_arrived_first?(@json['id']) || !related_to_local_activity?
|
||||
|
||||
original_status = status_from_object
|
||||
RedisLock.acquire(lock_options) do |lock|
|
||||
if lock.acquired?
|
||||
original_status = status_from_object
|
||||
|
||||
return reject_payload! if original_status.nil? || !announceable?(original_status)
|
||||
return reject_payload! if original_status.nil? || !announceable?(original_status)
|
||||
|
||||
status = Status.find_by(account: @account, reblog: original_status)
|
||||
@status = Status.find_by(account: @account, reblog: original_status)
|
||||
|
||||
return status unless status.nil?
|
||||
return @status unless @status.nil?
|
||||
|
||||
status = Status.create!(
|
||||
account: @account,
|
||||
reblog: original_status,
|
||||
uri: @json['id'],
|
||||
created_at: @json['published'],
|
||||
override_timestamps: @options[:override_timestamps],
|
||||
visibility: visibility_from_audience
|
||||
)
|
||||
@status = Status.create!(
|
||||
account: @account,
|
||||
reblog: original_status,
|
||||
uri: @json['id'],
|
||||
created_at: @json['published'],
|
||||
override_timestamps: @options[:override_timestamps],
|
||||
visibility: visibility_from_audience
|
||||
)
|
||||
|
||||
distribute(status)
|
||||
status
|
||||
distribute(@status)
|
||||
else
|
||||
raise Mastodon::RaceConditionError
|
||||
end
|
||||
end
|
||||
|
||||
@status
|
||||
end
|
||||
|
||||
private
|
||||
@@ -54,4 +61,8 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
|
||||
def reblog_of_local_status?
|
||||
status_from_uri(object_uri)&.account&.local?
|
||||
end
|
||||
|
||||
def lock_options
|
||||
{ redis: Redis.current, key: "announce:#{@object['id']}" }
|
||||
end
|
||||
end
|
||||
|
@@ -4,7 +4,12 @@ class ActivityPub::Activity::Block < ActivityPub::Activity
|
||||
def perform
|
||||
target_account = account_from_uri(object_uri)
|
||||
|
||||
return if target_account.nil? || !target_account.local? || @account.blocking?(target_account)
|
||||
return if target_account.nil? || !target_account.local?
|
||||
|
||||
if @account.blocking?(target_account)
|
||||
@account.block_relationships.find_by(target_account: target_account).update(uri: @json['id']) if @json['id'].present?
|
||||
return
|
||||
end
|
||||
|
||||
UnfollowService.new.call(target_account, @account) if target_account.following?(@account)
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
def perform
|
||||
dereference_object!
|
||||
|
||||
case @object['type']
|
||||
when 'EncryptedMessage'
|
||||
create_encrypted_message
|
||||
|
@@ -13,11 +13,62 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
|
||||
undo_like
|
||||
when 'Block'
|
||||
undo_block
|
||||
when nil
|
||||
handle_reference
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def handle_reference
|
||||
# Some implementations do not inline the object, and as we don't have a
|
||||
# global index, we have to guess what object it is.
|
||||
return if object_uri.nil?
|
||||
|
||||
try_undo_announce || try_undo_accept || try_undo_follow || try_undo_like || try_undo_block || delete_later!(object_uri)
|
||||
end
|
||||
|
||||
def try_undo_announce
|
||||
status = Status.where.not(reblog_of_id: nil).find_by(uri: object_uri, account: @account)
|
||||
if status.present?
|
||||
RemoveStatusService.new.call(status)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def try_undo_accept
|
||||
# We can't currently handle `Undo Accept` as we don't record `Accept`'s uri
|
||||
false
|
||||
end
|
||||
|
||||
def try_undo_follow
|
||||
follow = @account.follow_requests.find_by(uri: object_uri) || @account.active_relationships.find_by(uri: object_uri)
|
||||
|
||||
if follow.present?
|
||||
follow.destroy
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def try_undo_like
|
||||
# There is an index on accounts, but an account may have *many* favs, so this may be too costly
|
||||
false
|
||||
end
|
||||
|
||||
def try_undo_block
|
||||
block = @account.block_relationships.find_by(uri: object_uri)
|
||||
if block.present?
|
||||
UnblockService.new.call(@account, block.target_account)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def undo_announce
|
||||
return if object_uri.nil?
|
||||
|
||||
|
@@ -4,6 +4,8 @@ class ActivityPub::Activity::Update < ActivityPub::Activity
|
||||
SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze
|
||||
|
||||
def perform
|
||||
dereference_object!
|
||||
|
||||
if equals_or_includes_any?(@object['type'], SUPPORTED_TYPES)
|
||||
update_account
|
||||
elsif equals_or_includes_any?(@object['type'], %w(Question))
|
||||
|
@@ -7,6 +7,7 @@ module Mastodon
|
||||
class HostValidationError < ValidationError; end
|
||||
class LengthValidationError < ValidationError; end
|
||||
class DimensionsValidationError < ValidationError; end
|
||||
class StreamValidationError < ValidationError; end
|
||||
class RaceConditionError < Error; end
|
||||
class RateLimitExceededError < Error; end
|
||||
|
||||
|
@@ -139,9 +139,15 @@ class FeedManager
|
||||
end
|
||||
|
||||
def clear_from_timeline(account, target_account)
|
||||
# Clear from timeline all statuses from or mentionning target_account
|
||||
timeline_key = key(:home, account.id)
|
||||
timeline_status_ids = redis.zrange(timeline_key, 0, -1)
|
||||
target_statuses = Status.where(id: timeline_status_ids, account: target_account)
|
||||
statuses = Status.where(id: timeline_status_ids).select(:id, :reblog_of_id, :account_id).to_a
|
||||
reblogged_ids = Status.where(id: statuses.map(&:reblog_of_id).compact, account: target_account).pluck(:id)
|
||||
with_mentions_ids = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact, account: target_account).pluck(:status_id)
|
||||
target_statuses = statuses.filter do |status|
|
||||
status.account_id == target_account.id || reblogged_ids.include?(status.reblog_of_id) || with_mentions_ids.include?(status.id) || with_mentions_ids.include?(status.reblog_of_id)
|
||||
end
|
||||
|
||||
target_statuses.each do |status|
|
||||
unpush_from_home(account, status)
|
||||
|
Reference in New Issue
Block a user