1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# frozen_string_literal: true
class FanOutOnWriteService < BaseService
# Push a status into home and mentions feeds
# @param [Status] status
def call(status, delayed = false)
raise Mastodon::RaceConditionError if status.visibility.nil?
deliver_to_self(status) if status.account.local?
return if delayed
render_anonymous_payload(status)
if status.direct_visibility?
deliver_to_mentioned_followers(status)
deliver_to_direct_timelines(status)
deliver_to_own_conversation(status)
elsif status.limited_visibility?
deliver_to_mentioned_followers(status)
elsif status.local_visibility?
deliver_to_followers(status)
return if status.reblog? && !Setting.show_reblogs_in_public_timelines
deliver_to_lists(status)
deliver_to_local(status) unless filtered?(status)
else
deliver_to_followers(status)
deliver_to_lists(status)
return if status.reblog? && !Setting.show_reblogs_in_public_timelines
return if filtered?(status) || (status.reblog? && filtered?(status.reblog))
if !status.reblog? && status.distributable?
deliver_to_hashtags(status)
deliver_to_public(status) if status.curated
end
if status.relayed?
status = Status.find(status.reblog_of_id)
return if filtered?(status)
render_anonymous_payload(status)
end
return unless status.network? && status.public_visibility? && !status.reblog
deliver_to_local(status)
end
end
private
def filtered?(status)
status.account.silenced? || !Setting.show_replies_in_public_timelines && status.reply? && status.in_reply_to_account_id != status.account_id
end
def deliver_to_self(status)
Rails.logger.debug "Delivering status #{status.id} to author"
FeedManager.instance.push_to_home(status.account, status)
end
def deliver_to_followers(status)
Rails.logger.debug "Delivering status #{status.id} to followers"
status.account.followers_for_local_distribution.select(:id).reorder(nil).find_in_batches do |followers|
FeedInsertWorker.push_bulk(followers) do |follower|
[status.id, follower.id, :home]
end
end
end
def deliver_to_lists(status)
Rails.logger.debug "Delivering status #{status.id} to lists"
List.where(account_id: status.account.id, show_self: true).select(:id).reorder(nil).find_in_batches do |lists|
FeedInsertWorker.push_bulk(lists) do |list|
[status.id, list.id, :list]
end
end
status.account.lists_for_local_distribution.select(:id).reorder(nil).find_in_batches do |lists|
FeedInsertWorker.push_bulk(lists) do |list|
[status.id, list.id, :list]
end
end
end
def deliver_to_mentioned_followers(status)
Rails.logger.debug "Delivering status #{status.id} to limited followers"
FeedInsertWorker.push_bulk(status.mentions.includes(:account).map(&:account).select { |mentioned_account| mentioned_account.local? && mentioned_account.following?(status.account) }) do |follower|
[status.id, follower.id, :home]
end
end
def render_anonymous_payload(status)
@payload = InlineRenderer.render(status, nil, :status)
@payload = Oj.dump(event: :update, payload: @payload)
end
def deliver_to_hashtags(status)
Rails.logger.debug "Delivering status #{status.id} to hashtags"
status.tags.reject { |t| t.private }.pluck(:name).each do |hashtag|
Redis.current.publish("timeline:hashtag:#{hashtag}", @payload)
Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local?
end
end
def deliver_to_public(status)
Rails.logger.debug "Delivering status #{status.id} to public timeline"
Redis.current.publish('timeline:public', @payload)
Redis.current.publish('timeline:public:media', @payload) if status.media_attachments.any?
end
def deliver_to_local(status)
Rails.logger.debug "Delivering status #{status.id} to local timeline"
Redis.current.publish('timeline:public:local', @payload)
Redis.current.publish('timeline:public:local:media', @payload) if status.media_attachments.any?
end
def deliver_to_direct_timelines(status)
Rails.logger.debug "Delivering status #{status.id} to direct timelines"
status.mentions.includes(:account).each do |mention|
Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?
end
Redis.current.publish("timeline:direct:#{status.account.id}", @payload) if status.account.local?
end
def deliver_to_own_conversation(status)
AccountConversation.add_status(status.account, status)
end
end
|