From b527b6895815d491dce91fbde16601f7f6e57e21 Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Apr 2023 17:23:04 +0900 Subject: [PATCH 1/5] Add antennas to menu --- .../mastodon/features/compose/components/action_bar.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/mastodon/features/compose/components/action_bar.jsx b/app/javascript/mastodon/features/compose/components/action_bar.jsx index 71208e57f7..ed338c1acb 100644 --- a/app/javascript/mastodon/features/compose/components/action_bar.jsx +++ b/app/javascript/mastodon/features/compose/components/action_bar.jsx @@ -17,6 +17,7 @@ const messages = defineMessages({ domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, + antennas: { id: 'navigation_bar.antennas', defaultMessage: 'Antennas' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, }); @@ -54,6 +55,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' }); + menu.push({ text: intl.formatMessage(messages.antennas), href: '/antennas' }); menu.push(null); menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout }); From 9db152db38bc7c4b562f2ee96df6bfe18777eaee Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Apr 2023 11:23:54 +0900 Subject: [PATCH 2/5] Fix queries count in fan_out antennas --- app/models/account_statuses_cleanup_policy.rb | 2 ++ app/models/antenna.rb | 34 +++++++++---------- app/services/fan_out_on_write_service.rb | 18 ++++++---- ...20230426013738_add_excludes_to_antennas.rb | 7 ++++ db/schema.rb | 7 +++- 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 db/migrate/20230426013738_add_excludes_to_antennas.rb diff --git a/app/models/account_statuses_cleanup_policy.rb b/app/models/account_statuses_cleanup_policy.rb index 14ce00abbc..8d507c48fd 100644 --- a/app/models/account_statuses_cleanup_policy.rb +++ b/app/models/account_statuses_cleanup_policy.rb @@ -18,6 +18,8 @@ # min_reblogs :integer # created_at :datetime not null # updated_at :datetime not null +# min_emojis :integer +# keep_self_emoji :boolean default(TRUE), not null # class AccountStatusesCleanupPolicy < ApplicationRecord include Redisable diff --git a/app/models/antenna.rb b/app/models/antenna.rb index 0b1d3c1d0e..0d7a218e02 100644 --- a/app/models/antenna.rb +++ b/app/models/antenna.rb @@ -17,6 +17,9 @@ # updated_at :datetime not null # expires_at :datetime # with_media_only :boolean default(FALSE), not null +# exclude_domains :jsonb +# exclude_accounts :jsonb +# exclude_tags :jsonb # class Antenna < ApplicationRecord include Expireable @@ -86,7 +89,7 @@ class Antenna < ApplicationRecord def keywords_raw=(raw) keywords = raw.split(/\R/).filter { |r| r.present? && r.length >= 2 }.uniq self[:keywords] = keywords - self[:any_keywords] = !keywords.any? && !exclude_keywords&.any? + self[:any_keywords] = !keywords.any? end def exclude_keywords_raw @@ -98,7 +101,6 @@ class Antenna < ApplicationRecord def exclude_keywords_raw=(raw) exclude_keywords = raw.split(/\R/).filter { |r| r.present? }.uniq self[:exclude_keywords] = exclude_keywords - self[:any_keywords] = !keywords&.any? && !exclude_keywords.any? end def tags_raw @@ -118,18 +120,19 @@ class Antenna < ApplicationRecord end def exclude_tags_raw - antenna_tags.where(exclude: true).map(&:tag).map(&:name).join("\n") + return '' if !exclude_tags.present? + Tag.where(id: exclude_tags).map(&:name).join("\n") end def exclude_tags_raw=(raw) return if exclude_tags_raw == raw + tags = [] tag_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('#') ? r[1..-1] : r }.uniq - - antenna_tags.where(exclude: true).destroy_all Tag.find_or_create_by_names(tag_names).each do |tag| - antenna_tags.create!(tag: tag, exclude: true) + tags << tag.id end + self[:exclude_tags] = tags end def domains_raw @@ -149,18 +152,15 @@ class Antenna < ApplicationRecord end def exclude_domains_raw - antenna_domains.where(exclude: true).map(&:name).join("\n") + return '' if !exclude_domains.present? + exclude_domains.join("\n") end def exclude_domains_raw=(raw) return if exclude_domains_raw == raw domain_names = raw.split(/\R/).filter { |r| r.present? }.uniq - - antenna_domains.where(exclude: true).destroy_all - domain_names.each do |domain| - antenna_domains.create!(name: domain, exclude: true) - end + self[:exclude_domains] = domain_names end def accounts_raw @@ -186,7 +186,8 @@ class Antenna < ApplicationRecord end def exclude_accounts_raw - antenna_accounts.where(exclude: true).map(&:account).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n") + return '' if !exclude_accounts.present? + Account.where(id: exclude_accounts).map { |account| account.domain ? "@#{account.username}@#{account.domain}" : "@#{account.username}" }.join("\n") end def exclude_accounts_raw=(raw) @@ -194,16 +195,15 @@ class Antenna < ApplicationRecord account_names = raw.split(/\R/).filter { |r| r.present? }.map { |r| r.start_with?('@') ? r[1..-1] : r }.uniq - hit = false - antenna_accounts.where(exclude: true).destroy_all + accounts = [] account_names.each do |name| username, domain = name.split('@') account = Account.find_by(username: username, domain: domain) if account.present? - antenna_accounts.create!(account: account, exclude: true) - hit = true + accounts << account.id end end + self[:exclude_accounts] = accounts end end diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index d29b051d0d..00ccf2dcc0 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -118,30 +118,34 @@ class FanOutOnWriteService < BaseService def deliver_to_antennas! lists = [] + tag_ids = @status.tags.pluck(:id) + domain = @account.domain || Rails.configuration.x.local_domain + antennas = Antenna.availables antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.availables.left_joins(:antenna_accounts) .where(antenna_accounts: { exclude: false, account: @status.account })) antennas = antennas.left_joins(:antenna_domains) .where(any_domains: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains) .where(antenna_domains: { exclude: false, name: @status.account.domain })) antennas = antennas.left_joins(:antenna_tags) .where(any_tags: true) .or(Antenna.availables.left_joins(:antenna_accounts).left_joins(:antenna_domains).left_joins(:antenna_tags).where(antenna_tags: { exclude: false, tag: @status.tags })) antennas = antennas.where(account: @status.account.followers) if @status.visibility.to_sym == :unlisted antennas = antennas.where(with_media_only: false) if !@status.with_media? + antennas = antennas.where.not(account: @status.account.blocking) + antennas = antennas.includes(:antenna_accounts).includes(:antenna_domains).includes(:antenna_tags) antennas.in_batches do |ans| ans.each do |antenna| next if !antenna.enabled? - next if @status.account.blocking?(antenna.account) next if antenna.keywords.any? && !([nil, :public].include?(@status.searchability&.to_sym)) next if antenna.keywords.any? && !antenna.keywords.any? { |keyword| @status.text.include?(keyword) } - next if antenna.exclude_keywords.any? && antenna.exclude_keywords.any? { |keyword| @status.text.include?(keyword) } - next if antenna.antenna_accounts.where(exclude: true, account: @status.account).any? - next if antenna.antenna_domains.where(exclude: true, name: @status.account.domain).any? - next if antenna.antenna_tags.where(exclude: true, tag: @status.tags).any? - lists << antenna.list + next if antenna.exclude_keywords&.any? { |keyword| @status.text.include?(keyword) } + next if antenna.exclude_accounts&.include?(@status.account_id) + next if antenna.exclude_domains&.include?(domain) + next if antenna.exclude_tags&.any? { |tag_id| tag_ids.include?(tag_id) } + lists << antenna.list_id end end lists = lists.uniq if lists.any? FeedInsertWorker.push_bulk(lists) do |list| - [@status.id, list.id, 'list', { 'update' => update? }] + [@status.id, list, 'list', { 'update' => update? }] end end end diff --git a/db/migrate/20230426013738_add_excludes_to_antennas.rb b/db/migrate/20230426013738_add_excludes_to_antennas.rb new file mode 100644 index 0000000000..f38f53207b --- /dev/null +++ b/db/migrate/20230426013738_add_excludes_to_antennas.rb @@ -0,0 +1,7 @@ +class AddExcludesToAntennas < ActiveRecord::Migration[6.1] + def change + add_column :antennas, :exclude_domains, :jsonb + add_column :antennas, :exclude_accounts, :jsonb + add_column :antennas, :exclude_tags, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index e1f54b00c7..aef6f3736e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_04_23_233429) do +ActiveRecord::Schema.define(version: 2023_04_26_013738) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -117,6 +117,8 @@ ActiveRecord::Schema.define(version: 2023_04_23_233429) do t.integer "min_reblogs" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.integer "min_emojis" + t.boolean "keep_self_emoji", default: true, null: false t.index ["account_id"], name: "index_account_statuses_cleanup_policies_on_account_id" end @@ -300,6 +302,9 @@ ActiveRecord::Schema.define(version: 2023_04_23_233429) do t.datetime "updated_at", null: false t.datetime "expires_at" t.boolean "with_media_only", default: false, null: false + t.jsonb "exclude_domains" + t.jsonb "exclude_accounts" + t.jsonb "exclude_tags" t.index ["account_id"], name: "index_antennas_on_account_id" t.index ["any_accounts"], name: "index_antennas_on_any_accounts" t.index ["any_domains"], name: "index_antennas_on_any_domains" From ae2452abeaa94d5e06a5fd1ff2fec24253e3100e Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Apr 2023 11:26:27 +0900 Subject: [PATCH 3/5] Remove translation text --- config/locales/ja.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 8c8e8ec21c..1457dedab2 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -955,7 +955,7 @@ ja: accounts_raw: 絞り込むアカウント available: 有効 description: アンテナは、サーバーが認識した全ての公開・ローカル公開投稿のうち、検索許可が「公開」または明示的に設定されていないもの(検索許可システムに対応していないサーバーからの投稿)、かつ購読を拒否していないすべてのアカウントからの投稿が対象です。検出された投稿は、指定したリストに追加されます。 - domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です。自身のサーバーを指定することはできません + domains_hint: ドメインとは、アカウントIDやサイトのURLのうち「kmy.blue」「example.com」に該当する部分です domains_raw: 絞り込むドメイン exclude_accounts_raw: 除外するアカウント exclude_domains_raw: 除外するドメイン From 8f8e0ec88c8c87746f12a157b18dc5983c834c5b Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Apr 2023 11:30:36 +0900 Subject: [PATCH 4/5] Fix removing list with antenna --- app/controllers/api/v1/lists_controller.rb | 1 + config/locales/en.yml | 1 + config/locales/ja.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb index 843ca2ec2b..3c78862736 100644 --- a/app/controllers/api/v1/lists_controller.rb +++ b/app/controllers/api/v1/lists_controller.rb @@ -31,6 +31,7 @@ class Api::V1::ListsController < Api::BaseController end def destroy + raise Mastodon::ValidationError, I18n.t('antennas.errors.remove_list_with_antenna') if Antenna.where(list_id: @list.id).any? @list.destroy! render_empty end diff --git a/config/locales/en.yml b/config/locales/en.yml index efadfb8e92..adf772e152 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -994,6 +994,7 @@ en: empty_contexts: No contexts! You must set any context filters invalid_context: None or invalid context supplied invalid_list_owner: This list is not yours + remove_list_with_antenna: Cannot remove list because this list is related to antenna. index: contexts: Antennas in %{contexts} delete: Delete diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 1457dedab2..1e97eabbf6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -950,6 +950,7 @@ ja: errors: empty_contexts: 絞り込み条件が1つも指定されていないため無効です(除外条件はカウントされません) invalid_list_owner: これはあなたのリストではありません + remove_list_with_antenna: アンテナが関連付けられているリストは削除できません edit: accounts_hint: ローカルアカウントの場合は「@info」、リモートアカウントの場合は「@info@example.com」の形式で指定します。サーバーが認識していないアカウントは保存時に自動的に削除されます。 accounts_raw: 絞り込むアカウント From a18b0578bfd47917ed191891fad0c73e67139944 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Apr 2023 11:35:42 +0900 Subject: [PATCH 5/5] Remove translation phrases --- config/locales/ja.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 1e97eabbf6..f5b4346f66 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -941,7 +941,7 @@ ja: hint_html: 他のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。エイリアス自体は無害で、取り消すことができます。引っ越しは以前のアカウント側から開始する必要があります。 remove: エイリアスを削除 antennas: - beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。この機能の存在は外部に積極的に宣伝しないよう、ご協力をお願いします。 + beta: アンテナ機能はベータ版です。今後、予告なく全データリセット・機能削除を行う場合があります。 contexts: account: アカウント domain: ドメイン