diff options
Diffstat (limited to 'app/services/revoke_status_service.rb')
-rw-r--r-- | app/services/revoke_status_service.rb | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/app/services/revoke_status_service.rb b/app/services/revoke_status_service.rb new file mode 100644 index 000000000..f4762631c --- /dev/null +++ b/app/services/revoke_status_service.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +class RevokeStatusService < BaseService + include Redisable + include Payloadable + + # Unpublish a status from a given set of local accounts' timelines and public, if visibility changed. + # @param [Status] status + # @param [Enumerable] account_ids + def call(status, account_ids) + @payload = Oj.dump(event: :delete, payload: status.id.to_s) + @status = status + @account = status.account + @account_ids = account_ids + @mentions = status.mentions.where(account_id: account_ids) + @reblogs = status.reblogs.where(account_id: account_ids) + + RedisLock.acquire(lock_options) do |lock| + if lock.acquired? + remove_from_followers + remove_from_lists + remove_from_affected + remove_reblogs + remove_from_hashtags + remove_from_public + remove_from_media + remove_from_direct if status.direct_visibility? + else + raise Mastodon::RaceConditionError + end + end + end + + private + + def remove_from_followers + @account.followers_for_local_distribution.where(id: @account_ids).reorder(nil).find_each do |follower| + FeedManager.instance.unpush_from_home(follower, @status) + end + end + + def remove_from_lists + @account.lists_for_local_distribution.where(account_id: @account_ids).select(:id, :account_id).reorder(nil).find_each do |list| + FeedManager.instance.unpush_from_list(list, @status) + end + end + + def remove_from_affected + @mentions.map(&:account).select(&:local?).each do |account| + redis.publish("timeline:#{account.id}", @payload) + end + end + + def remove_reblogs + @reblogs.each do |reblog| + RemoveStatusService.new.call(reblog) + end + end + + def remove_from_hashtags + @account.featured_tags.where(tag_id: @status.tags.pluck(:id)).each do |featured_tag| + featured_tag.decrement(@status.id) + end + + return unless @status.distributable? + + @tags.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 if @status.distributable? + + redis.publish('timeline:public', @payload) + redis.publish('timeline:public:local', @payload) if @status.local? + redis.publish('timeline:public:remote', @payload) + end + + def remove_from_media + return if @status.distributable? + + redis.publish('timeline:public:media', @payload) + redis.publish('timeline:public:local:media', @payload) if @status.local? + redis.publish('timeline:public:remote:media', @payload) + end + + def remove_from_direct + @mentions.each do |mention| + FeedManager.instance.unpush_from_direct(mention.account, @status) if mention.account.local? + end + end + + def lock_options + { redis: Redis.current, key: "distribute:#{@status.id}" } + end +end |