mirror of
https://github.com/funamitech/mastodon
synced 2024-12-15 23:28:31 +09:00
178e151019
Conflicts: - `app/models/user_settings.rb`: Upstream added a constraint on a setting textually close to glitch-soc-only settings. Applied upstream's change. - `lib/sanitize_ext/sanitize_config.rb`: Upstream added support for the `translate` attribute on a few elements, where glitch-soc had a different set of allowed elements and attributes. Extended glitch-soc's allowed attributes with `translate` as upstream did. - `spec/validators/status_length_validator_spec.rb`: Upstream refactored to use RSpec's `instance_double` instead of `double`, but glitch-soc had changes to tests due to configurable max toot chars. Applied upstream's changes while keeping tests against configurable max toot chars.
171 lines
5.4 KiB
Ruby
171 lines
5.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class RemoveStatusService < BaseService
|
|
include Redisable
|
|
include Payloadable
|
|
include Lockable
|
|
|
|
# Delete a status
|
|
# @param [Status] status
|
|
# @param [Hash] options
|
|
# @option [Boolean] :redraft
|
|
# @option [Boolean] :immediate
|
|
# @option [Boolean] :preserve
|
|
# @option [Boolean] :original_removed
|
|
# @option [Boolean] :skip_streaming
|
|
def call(status, **options)
|
|
@payload = Oj.dump(event: :delete, payload: status.id.to_s)
|
|
@status = status
|
|
@account = status.account
|
|
@options = options
|
|
|
|
with_redis_lock("distribute:#{@status.id}") do
|
|
@status.discard_with_reblogs
|
|
|
|
StatusPin.find_by(status: @status)&.destroy
|
|
|
|
remove_from_self if @account.local?
|
|
remove_from_followers
|
|
remove_from_lists
|
|
|
|
# There is no reason to send out Undo activities when the
|
|
# cause is that the original object has been removed, since
|
|
# original object being removed implicitly removes reblogs
|
|
# of it. The Delete activity of the original is forwarded
|
|
# separately.
|
|
remove_from_remote_reach if @account.local? && !@options[:original_removed]
|
|
|
|
# Since reblogs don't mention anyone, don't get reblogged,
|
|
# favourited and don't contain their own media attachments
|
|
# or hashtags, this can be skipped
|
|
unless @status.reblog?
|
|
remove_from_mentions
|
|
remove_reblogs
|
|
remove_from_hashtags
|
|
remove_from_public
|
|
remove_from_media if @status.with_media?
|
|
remove_from_direct if status.direct_visibility?
|
|
remove_media
|
|
end
|
|
|
|
@status.destroy! if permanently?
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# The following FeedManager calls all do not result in redis publishes for
|
|
# streaming, as the `:update` option is false
|
|
|
|
def remove_from_self
|
|
FeedManager.instance.unpush_from_home(@account, @status)
|
|
FeedManager.instance.unpush_from_direct(@account, @status) if @status.direct_visibility?
|
|
end
|
|
|
|
def remove_from_followers
|
|
@account.followers_for_local_distribution.includes(:user).reorder(nil).find_each do |follower|
|
|
FeedManager.instance.unpush_from_home(follower, @status)
|
|
end
|
|
end
|
|
|
|
def remove_from_lists
|
|
@account.lists_for_local_distribution.select(:id, :account_id).includes(account: :user).reorder(nil).find_each do |list|
|
|
FeedManager.instance.unpush_from_list(list, @status)
|
|
end
|
|
end
|
|
|
|
def remove_from_mentions
|
|
# For limited visibility statuses, the mentions that determine
|
|
# who receives them in their home feed are a subset of followers
|
|
# and therefore the delete is already handled by sending it to all
|
|
# followers. Here we send a delete to actively mentioned accounts
|
|
# that may not follow the account
|
|
|
|
return if skip_streaming?
|
|
|
|
@status.active_mentions.find_each do |mention|
|
|
redis.publish("timeline:#{mention.account_id}", @payload)
|
|
end
|
|
end
|
|
|
|
def remove_from_remote_reach
|
|
# Followers, relays, people who got mentioned in the status,
|
|
# or who reblogged it from someone else might not follow
|
|
# the author and wouldn't normally receive the delete
|
|
# notification - so here, we explicitly send it to them
|
|
|
|
status_reach_finder = StatusReachFinder.new(@status, unsafe: true)
|
|
|
|
ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes, limit: 1_000) do |inbox_url|
|
|
[signed_activity_json, @account.id, inbox_url]
|
|
end
|
|
end
|
|
|
|
def signed_activity_json
|
|
@signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account, always_sign: true))
|
|
end
|
|
|
|
def remove_reblogs
|
|
# We delete reblogs of the status before the original status,
|
|
# because once original status is gone, reblogs will disappear
|
|
# without us being able to do all the fancy stuff
|
|
|
|
@status.reblogs.rewhere(deleted_at: [nil, @status.deleted_at]).includes(:account).reorder(nil).find_each do |reblog|
|
|
RemoveStatusService.new.call(reblog, original_removed: true, skip_streaming: skip_streaming?)
|
|
end
|
|
end
|
|
|
|
def remove_from_hashtags
|
|
@account.featured_tags.where(tag_id: @status.tags.map(&:id)).each do |featured_tag|
|
|
featured_tag.decrement(@status.id)
|
|
end
|
|
|
|
return unless @status.public_visibility?
|
|
|
|
return if skip_streaming?
|
|
|
|
@status.tags.map(&:name).each do |hashtag|
|
|
redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload)
|
|
redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if @status.local?
|
|
end
|
|
end
|
|
|
|
def remove_from_public
|
|
return unless @status.public_visibility?
|
|
|
|
return if skip_streaming?
|
|
|
|
redis.publish('timeline:public', @payload)
|
|
redis.publish(@status.local? ? 'timeline:public:local' : 'timeline:public:remote', @payload)
|
|
end
|
|
|
|
def remove_from_media
|
|
return unless @status.public_visibility?
|
|
|
|
return if skip_streaming?
|
|
|
|
redis.publish('timeline:public:media', @payload)
|
|
redis.publish(@status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', @payload)
|
|
end
|
|
|
|
def remove_from_direct
|
|
@status.active_mentions.each do |mention|
|
|
FeedManager.instance.unpush_from_direct(mention.account, @status) if mention.account.local?
|
|
end
|
|
end
|
|
|
|
def remove_media
|
|
return if @options[:redraft] || !permanently?
|
|
|
|
@status.media_attachments.destroy_all
|
|
end
|
|
|
|
def permanently?
|
|
@options[:immediate] || !(@options[:preserve] || @status.reported?)
|
|
end
|
|
|
|
def skip_streaming?
|
|
!!@options[:skip_streaming]
|
|
end
|
|
end
|