2017-08-09 04:52:15 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class ActivityPub::Activity::Undo < ActivityPub::Activity
|
|
|
|
def perform
|
|
|
|
case @object['type']
|
|
|
|
when 'Announce'
|
|
|
|
undo_announce
|
2018-08-17 23:24:56 +09:00
|
|
|
when 'Accept'
|
|
|
|
undo_accept
|
2017-08-09 04:52:15 +09:00
|
|
|
when 'Follow'
|
|
|
|
undo_follow
|
|
|
|
when 'Like'
|
|
|
|
undo_like
|
2022-12-03 17:24:23 +09:00
|
|
|
when 'EmojiReact'
|
|
|
|
undo_emoji_react
|
2017-08-09 04:52:15 +09:00
|
|
|
when 'Block'
|
|
|
|
undo_block
|
Fix not handling Undo on some activity types when they aren't inlined (#14346)
* Fix not handling Undo on some activity types when they aren't inlined
When receiving an Undo for a non-inlined activity, try looking it up in
database using the URI. The queries are ad-hoc because we don't have a global
index of object URIs, and not all activity types are stored in database with
an index on their URI.
Announces are just statuses, and have an index on URIs, so this check can
be done efficiently.
Accepts cannot be handled at all because we don't record their URI at any
point.
Follows don't have an index on URI, but they have an index on the issuing
account, which should make such queries largely manageable.
Likes don't have an index on URI, they have an index on the issuing account,
but the number of favs per account may be very high, so I decided not to
handle that.
Blocks don't have an index on URI, but they have an index on the issuing
account, which should make such queries largely manageable.
In all cases, if an Undo could not be handled properly, we call `delete_later!`
because that does not require us to know more than the URI of the undone
property.
* Add tests
* Make newer blocks overwrite older ones
Allows re-synchronizing block info by re-blocking and un-blocking again
when the original Undo Block has been lost.
2020-07-22 18:45:35 +09:00
|
|
|
when nil
|
|
|
|
handle_reference
|
2017-08-09 04:52:15 +09:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
Fix not handling Undo on some activity types when they aren't inlined (#14346)
* Fix not handling Undo on some activity types when they aren't inlined
When receiving an Undo for a non-inlined activity, try looking it up in
database using the URI. The queries are ad-hoc because we don't have a global
index of object URIs, and not all activity types are stored in database with
an index on their URI.
Announces are just statuses, and have an index on URIs, so this check can
be done efficiently.
Accepts cannot be handled at all because we don't record their URI at any
point.
Follows don't have an index on URI, but they have an index on the issuing
account, which should make such queries largely manageable.
Likes don't have an index on URI, they have an index on the issuing account,
but the number of favs per account may be very high, so I decided not to
handle that.
Blocks don't have an index on URI, but they have an index on the issuing
account, which should make such queries largely manageable.
In all cases, if an Undo could not be handled properly, we call `delete_later!`
because that does not require us to know more than the URI of the undone
property.
* Add tests
* Make newer blocks overwrite older ones
Allows re-synchronizing block info by re-blocking and un-blocking again
when the original Undo Block has been lost.
2020-07-22 18:45:35 +09:00
|
|
|
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
|
|
|
|
|
2017-08-09 04:52:15 +09:00
|
|
|
def undo_announce
|
2018-10-04 06:44:13 +09:00
|
|
|
return if object_uri.nil?
|
|
|
|
|
2017-10-14 21:42:09 +09:00
|
|
|
status = Status.find_by(uri: object_uri, account: @account)
|
|
|
|
status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present?
|
2017-08-09 04:52:15 +09:00
|
|
|
|
|
|
|
if status.nil?
|
|
|
|
delete_later!(object_uri)
|
|
|
|
else
|
|
|
|
RemoveStatusService.new.call(status)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-17 23:24:56 +09:00
|
|
|
def undo_accept
|
|
|
|
::Follow.find_by(target_account: @account, uri: target_uri)&.revoke_request!
|
|
|
|
end
|
|
|
|
|
2017-08-09 04:52:15 +09:00
|
|
|
def undo_follow
|
|
|
|
target_account = account_from_uri(target_uri)
|
|
|
|
|
|
|
|
return if target_account.nil? || !target_account.local?
|
|
|
|
|
|
|
|
if @account.following?(target_account)
|
|
|
|
@account.unfollow!(target_account)
|
2017-09-03 03:44:41 +09:00
|
|
|
elsif @account.requested?(target_account)
|
|
|
|
FollowRequest.find_by(account: @account, target_account: target_account)&.destroy
|
2017-08-09 04:52:15 +09:00
|
|
|
else
|
|
|
|
delete_later!(object_uri)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def undo_like
|
|
|
|
status = status_from_uri(target_uri)
|
|
|
|
|
|
|
|
return if status.nil? || !status.account.local?
|
|
|
|
|
|
|
|
if @account.favourited?(status)
|
|
|
|
favourite = status.favourites.where(account: @account).first
|
|
|
|
favourite&.destroy
|
|
|
|
else
|
|
|
|
delete_later!(object_uri)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-03 17:24:23 +09:00
|
|
|
def undo_emoji_react
|
|
|
|
name = @object['content']
|
2023-05-15 06:49:18 +09:00
|
|
|
tags = @object['tag']
|
2022-12-03 17:24:23 +09:00
|
|
|
return if name.nil?
|
|
|
|
|
|
|
|
status = status_from_uri(target_uri)
|
2023-05-15 06:49:18 +09:00
|
|
|
name.delete! ':'
|
2022-12-03 17:24:23 +09:00
|
|
|
|
|
|
|
return if status.nil? || !status.account.local?
|
|
|
|
|
2023-05-15 06:49:18 +09:00
|
|
|
custom_emoji = nil
|
|
|
|
emoji_tag = as_array(tags).find { |tag| tag['type'] == 'Emoji' }
|
|
|
|
|
|
|
|
if emoji_tag
|
|
|
|
custom_emoji_parser = ActivityPub::Parser::CustomEmojiParser.new(emoji_tag)
|
|
|
|
return if custom_emoji_parser.shortcode.blank? || custom_emoji_parser.image_remote_url.blank?
|
|
|
|
|
|
|
|
custom_emoji = CustomEmoji.find_by(shortcode: custom_emoji_parser.shortcode, domain: @account.domain)
|
|
|
|
end
|
|
|
|
|
|
|
|
if @account.reacted?(status, name, custom_emoji)
|
2022-12-03 17:24:23 +09:00
|
|
|
reaction = status.status_reactions.where(account: @account, name: name).first
|
|
|
|
reaction&.destroy
|
|
|
|
else
|
|
|
|
delete_later!(object_uri)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-09 04:52:15 +09:00
|
|
|
def undo_block
|
|
|
|
target_account = account_from_uri(target_uri)
|
|
|
|
|
|
|
|
return if target_account.nil? || !target_account.local?
|
|
|
|
|
|
|
|
if @account.blocking?(target_account)
|
|
|
|
UnblockService.new.call(@account, target_account)
|
|
|
|
else
|
|
|
|
delete_later!(object_uri)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def target_uri
|
2017-08-22 05:57:34 +09:00
|
|
|
@target_uri ||= value_or_id(@object['object'])
|
2017-08-09 04:52:15 +09:00
|
|
|
end
|
|
|
|
end
|