Change auto-following admin-selected accounts, show in recommendations (#16078)

This commit is contained in:
Eugen Rochko 2021-04-24 17:01:43 +02:00 committed by GitHub
parent 863ae47b51
commit daccc07dc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 228 additions and 128 deletions

View file

@ -1,17 +1,28 @@
# frozen_string_literal: true
class AccountSuggestions
class Suggestion < ActiveModelSerializers::Model
attributes :account, :source
end
SOURCES = [
AccountSuggestions::SettingSource,
AccountSuggestions::PastInteractionsSource,
AccountSuggestions::GlobalSource,
].freeze
def self.get(account, limit)
suggestions = PotentialFriendshipTracker.get(account, limit).map { |target_account| Suggestion.new(account: target_account, source: :past_interaction) }
suggestions.concat(FollowRecommendation.get(account, limit - suggestions.size, suggestions.map { |suggestion| suggestion.account.id }).map { |target_account| Suggestion.new(account: target_account, source: :global) }) if suggestions.size < limit
suggestions
SOURCES.each_with_object([]) do |source_class, suggestions|
source_suggestions = source_class.new.get(
account,
skip_account_ids: suggestions.map(&:account_id),
limit: limit - suggestions.size
)
suggestions.concat(source_suggestions)
end
end
def self.remove(account, target_account_id)
PotentialFriendshipTracker.remove(account.id, target_account_id)
SOURCES.each do |source_class|
source = source_class.new
source.remove(account, target_account_id)
end
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
class AccountSuggestions::GlobalSource < AccountSuggestions::Source
def key
:global
end
def get(account, skip_account_ids: [], limit: 40)
account_ids = account_ids_for_locale(account.user_locale) - [account.id] - skip_account_ids
as_ordered_suggestions(
scope(account).where(id: account_ids),
account_ids
).take(limit)
end
def remove(_account, _target_account_id)
nil
end
private
def scope(account)
Account.searchable
.followable_by(account)
.not_excluded_by_account(account)
.not_domain_blocked_by_account(account)
end
def account_ids_for_locale(locale)
Redis.current.zrevrange("follow_recommendations:#{locale}", 0, -1).map(&:to_i)
end
def to_ordered_list_key(account)
account.id
end
end

View file

@ -0,0 +1,36 @@
# frozen_string_literal: true
class AccountSuggestions::PastInteractionsSource < AccountSuggestions::Source
include Redisable
def key
:past_interactions
end
def get(account, skip_account_ids: [], limit: 40)
account_ids = account_ids_for_account(account.id, limit + skip_account_ids.size) - skip_account_ids
as_ordered_suggestions(
scope.where(id: account_ids),
account_ids
).take(limit)
end
def remove(account, target_account_id)
redis.zrem("interactions:#{account.id}", target_account_id)
end
private
def scope
Account.searchable
end
def account_ids_for_account(account_id, limit)
redis.zrevrange("interactions:#{account_id}", 0, limit).map(&:to_i)
end
def to_ordered_list_key(account)
account.id
end
end

View file

@ -0,0 +1,68 @@
# frozen_string_literal: true
class AccountSuggestions::SettingSource < AccountSuggestions::Source
def key
:staff
end
def get(account, skip_account_ids: [], limit: 40)
return [] unless setting_enabled?
as_ordered_suggestions(
scope(account).where(setting_to_where_condition).where.not(id: skip_account_ids),
usernames_and_domains
).take(limit)
end
def remove(_account, _target_account_id)
nil
end
private
def scope(account)
Account.searchable
.followable_by(account)
.not_excluded_by_account(account)
.not_domain_blocked_by_account(account)
.where(locked: false)
.where.not(id: account.id)
end
def usernames_and_domains
@usernames_and_domains ||= setting_to_usernames_and_domains
end
def setting_enabled?
setting.present?
end
def setting_to_where_condition
usernames_and_domains.map do |(username, domain)|
Arel::Nodes::Grouping.new(
Account.arel_table[:username].lower.eq(username.downcase).and(
Account.arel_table[:domain].lower.eq(domain&.downcase)
)
)
end.reduce(:or)
end
def setting_to_usernames_and_domains
setting.split(',').map do |str|
username, domain = str.strip.gsub(/\A@/, '').split('@', 2)
domain = nil if TagManager.instance.local_domain?(domain)
next if username.blank?
[username, domain]
end.compact
end
def setting
Setting.bootstrap_timeline_accounts
end
def to_ordered_list_key(account)
[account.username, account.domain]
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
class AccountSuggestions::Source
def key
raise NotImplementedError
end
def get(_account, **kwargs)
raise NotImplementedError
end
def remove(_account, target_account_id)
raise NotImplementedError
end
protected
def as_ordered_suggestions(scope, ordered_list)
return [] if ordered_list.empty?
map = scope.index_by(&method(:to_ordered_list_key))
ordered_list.map { |ordered_list_key| map[ordered_list_key] }.compact.map do |account|
AccountSuggestions::Suggestion.new(
account: account,
source: key
)
end
end
def to_ordered_list_key(_account)
raise NotImplementedError
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AccountSuggestions::Suggestion < ActiveModelSerializers::Model
attributes :account, :source
delegate :id, to: :account, prefix: true
end

View file

@ -21,19 +21,4 @@ class FollowRecommendation < ApplicationRecord
def readonly?
true
end
def self.get(account, limit, exclude_account_ids = [])
account_ids = Redis.current.zrevrange("follow_recommendations:#{account.user_locale}", 0, -1).map(&:to_i) - exclude_account_ids - [account.id]
return [] if account_ids.empty? || limit < 1
accounts = Account.followable_by(account)
.not_excluded_by_account(account)
.not_domain_blocked_by_account(account)
.where(id: account_ids)
.limit(limit)
.index_by(&:id)
account_ids.map { |id| accounts[id] }.compact
end
end

View file

@ -16,7 +16,6 @@ class Form::AdminSettings
open_deletion
timeline_preview
show_staff_badge
enable_bootstrap_timeline_accounts
bootstrap_timeline_accounts
theme
min_invite_role
@ -41,7 +40,6 @@ class Form::AdminSettings
open_deletion
timeline_preview
show_staff_badge
enable_bootstrap_timeline_accounts
activity_api_enabled
peers_api_enabled
show_known_fediverse_at_about_page