mirror of
https://github.com/mastodon/mastodon
synced 2024-11-24 15:16:19 +09:00
ddd30f331c
* Add silent column to mentions * Save silent mentions in ActivityPub Create handler and optimize it Move networking calls out of the database transaction * Add "limited" visibility level masked as "private" in the API Unlike DMs, limited statuses are pushed into home feeds. The access control rules between direct and limited statuses is almost the same, except for counter and conversation logic * Ensure silent column is non-null, add spec * Ensure filters don't check silent mentions for blocks/mutes As those are "this person is also allowed to see" rather than "this person is involved", therefore does not warrant filtering * Clean up code * Use Status#active_mentions to limit returned mentions * Fix code style issues * Use Status#active_mentions in Notification And remove stream_entry eager-loading from Notification
100 lines
3.2 KiB
Ruby
100 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class FanOutOnWriteService < BaseService
|
|
# Push a status into home and mentions feeds
|
|
# @param [Status] status
|
|
def call(status)
|
|
raise Mastodon::RaceConditionError if status.visibility.nil?
|
|
|
|
render_anonymous_payload(status)
|
|
|
|
if status.direct_visibility?
|
|
deliver_to_own_conversation(status)
|
|
elsif status.limited_visibility?
|
|
deliver_to_mentioned_followers(status)
|
|
else
|
|
deliver_to_self(status) if status.account.local?
|
|
deliver_to_followers(status)
|
|
deliver_to_lists(status)
|
|
end
|
|
|
|
return if status.account.silenced? || !status.public_visibility? || status.reblog?
|
|
|
|
deliver_to_hashtags(status)
|
|
|
|
return if status.reply? && status.in_reply_to_account_id != status.account_id
|
|
|
|
deliver_to_public(status)
|
|
deliver_to_media(status) if status.media_attachments.any?
|
|
end
|
|
|
|
private
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
status.mentions.includes(:account).each do |mention|
|
|
mentioned_account = mention.account
|
|
next if !mentioned_account.local? || !mentioned_account.following?(status.account) || FeedManager.instance.filter?(:home, status, mention.account_id)
|
|
FeedManager.instance.push_to_home(mentioned_account, status)
|
|
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.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:local', @payload) if status.local?
|
|
end
|
|
|
|
def deliver_to_media(status)
|
|
Rails.logger.debug "Delivering status #{status.id} to media timeline"
|
|
|
|
Redis.current.publish('timeline:public:media', @payload)
|
|
Redis.current.publish('timeline:public:local:media', @payload) if status.local?
|
|
end
|
|
|
|
def deliver_to_own_conversation(status)
|
|
AccountConversation.add_status(status.account, status)
|
|
end
|
|
end
|