summaryrefslogtreecommitdiffstats
path: root/app/models/account_suggestions
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/account_suggestions')
-rw-r--r--app/models/account_suggestions/global_source.rb37
-rw-r--r--app/models/account_suggestions/past_interactions_source.rb36
-rw-r--r--app/models/account_suggestions/setting_source.rb68
-rw-r--r--app/models/account_suggestions/source.rb34
-rw-r--r--app/models/account_suggestions/suggestion.rb7
5 files changed, 182 insertions, 0 deletions
diff --git a/app/models/account_suggestions/global_source.rb b/app/models/account_suggestions/global_source.rb
new file mode 100644
index 00000000000..ac764de50f5
--- /dev/null
+++ b/app/models/account_suggestions/global_source.rb
@@ -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
diff --git a/app/models/account_suggestions/past_interactions_source.rb b/app/models/account_suggestions/past_interactions_source.rb
new file mode 100644
index 00000000000..d169394f11a
--- /dev/null
+++ b/app/models/account_suggestions/past_interactions_source.rb
@@ -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
diff --git a/app/models/account_suggestions/setting_source.rb b/app/models/account_suggestions/setting_source.rb
new file mode 100644
index 00000000000..be9eff23350
--- /dev/null
+++ b/app/models/account_suggestions/setting_source.rb
@@ -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
diff --git a/app/models/account_suggestions/source.rb b/app/models/account_suggestions/source.rb
new file mode 100644
index 00000000000..bd1068d201e
--- /dev/null
+++ b/app/models/account_suggestions/source.rb
@@ -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
diff --git a/app/models/account_suggestions/suggestion.rb b/app/models/account_suggestions/suggestion.rb
new file mode 100644
index 00000000000..2c6f4d27f5c
--- /dev/null
+++ b/app/models/account_suggestions/suggestion.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AccountSuggestions::Suggestion < ActiveModelSerializers::Model
+ attributes :account, :source
+
+ delegate :id, to: :account, prefix: true
+end