]> cat aescling's git repositories - mastodon.git/blob - app/services/remove_status_service.rb
Merge branch 'master' into glitch-soc/merge-upstream
[mastodon.git] / app / services / remove_status_service.rb
1 # frozen_string_literal: true
2
3 class RemoveStatusService < BaseService
4 include StreamEntryRenderer
5
6 def call(status, **options)
7 @payload = Oj.dump(event: :delete, payload: status.id.to_s)
8 @status = status
9 @account = status.account
10 @tags = status.tags.pluck(:name).to_a
11 @mentions = status.active_mentions.includes(:account).to_a
12 @reblogs = status.reblogs.to_a
13 @stream_entry = status.stream_entry
14 @options = options
15
16 remove_from_self if status.account.local?
17 remove_from_followers
18 remove_from_lists
19 remove_from_affected
20 remove_reblogs
21 remove_from_hashtags
22 remove_from_public
23 remove_from_media if status.media_attachments.any?
24 remove_from_direct if status.direct_visibility?
25
26 @status.destroy!
27
28 # There is no reason to send out Undo activities when the
29 # cause is that the original object has been removed, since
30 # original object being removed implicitly removes reblogs
31 # of it. The Delete activity of the original is forwarded
32 # separately.
33 return if !@account.local? || @options[:original_removed]
34
35 remove_from_remote_followers
36 remove_from_remote_affected
37 end
38
39 private
40
41 def remove_from_self
42 FeedManager.instance.unpush_from_home(@account, @status)
43 end
44
45 def remove_from_followers
46 @account.followers_for_local_distribution.reorder(nil).find_each do |follower|
47 FeedManager.instance.unpush_from_home(follower, @status)
48 end
49 end
50
51 def remove_from_lists
52 @account.lists_for_local_distribution.select(:id, :account_id).reorder(nil).find_each do |list|
53 FeedManager.instance.unpush_from_list(list, @status)
54 end
55 end
56
57 def remove_from_affected
58 @mentions.map(&:account).select(&:local?).each do |account|
59 Redis.current.publish("timeline:#{account.id}", @payload)
60 end
61 end
62
63 def remove_from_remote_affected
64 # People who got mentioned in the status, or who
65 # reblogged it from someone else might not follow
66 # the author and wouldn't normally receive the
67 # delete notification - so here, we explicitly
68 # send it to them
69
70 target_accounts = (@mentions.map(&:account).reject(&:local?) + @reblogs.map(&:account).reject(&:local?))
71 target_accounts << @status.reblog.account if @status.reblog? && !@status.reblog.account.local?
72 target_accounts.uniq!(&:id)
73
74 # Ostatus
75 NotificationWorker.push_bulk(target_accounts.select(&:ostatus?).uniq(&:domain)) do |target_account|
76 [salmon_xml, @account.id, target_account.id]
77 end
78
79 # ActivityPub
80 ActivityPub::DeliveryWorker.push_bulk(target_accounts.select(&:activitypub?).uniq(&:inbox_url)) do |target_account|
81 [signed_activity_json, @account.id, target_account.inbox_url]
82 end
83 end
84
85 def remove_from_remote_followers
86 # OStatus
87 Pubsubhubbub::RawDistributionWorker.perform_async(salmon_xml, @account.id)
88
89 # ActivityPub
90 ActivityPub::DeliveryWorker.push_bulk(@account.followers.inboxes) do |inbox_url|
91 [signed_activity_json, @account.id, inbox_url]
92 end
93
94 relay! if relayable?
95 end
96
97 def relayable?
98 @status.public_visibility?
99 end
100
101 def relay!
102 ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
103 [signed_activity_json, @account.id, inbox_url]
104 end
105 end
106
107 def salmon_xml
108 @salmon_xml ||= stream_entry_to_xml(@stream_entry)
109 end
110
111 def signed_activity_json
112 @signed_activity_json ||= Oj.dump(ActivityPub::LinkedDataSignature.new(activity_json).sign!(@account))
113 end
114
115 def activity_json
116 @activity_json ||= ActiveModelSerializers::SerializableResource.new(
117 @status,
118 serializer: @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer,
119 adapter: ActivityPub::Adapter
120 ).as_json
121 end
122
123 def remove_reblogs
124 # We delete reblogs of the status before the original status,
125 # because once original status is gone, reblogs will disappear
126 # without us being able to do all the fancy stuff
127
128 @reblogs.each do |reblog|
129 RemoveStatusService.new.call(reblog, original_removed: true)
130 end
131 end
132
133 def remove_from_hashtags
134 return unless @status.public_visibility?
135
136 @tags.each do |hashtag|
137 Redis.current.publish("timeline:hashtag:#{hashtag}", @payload)
138 Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if @status.local?
139 end
140 end
141
142 def remove_from_public
143 return unless @status.public_visibility?
144
145 Redis.current.publish('timeline:public', @payload)
146 Redis.current.publish('timeline:public:local', @payload) if @status.local?
147 end
148
149 def remove_from_media
150 return unless @status.public_visibility?
151
152 Redis.current.publish('timeline:public:media', @payload)
153 Redis.current.publish('timeline:public:local:media', @payload) if @status.local?
154 end
155
156 def remove_from_direct
157 @mentions.each do |mention|
158 Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?
159 end
160 Redis.current.publish("timeline:direct:#{@account.id}", @payload) if @account.local?
161 end
162
163 def redis
164 Redis.current
165 end
166 end
This page took 0.104658 seconds and 4 git commands to generate.