Skip to content

Commit

Permalink
Add: #87 フォロワーのみに購読を許可する設定 (#239)
Browse files Browse the repository at this point in the history
* Wip: マイグレーション、設定など一式

* Fix test

* Fix test

* Fix: マスター用の設定を他サーバーに送信しないよう修正

* DTL、外部サーバーの情報受け入れのテストを追加

* スペルミスを修正

* Web画面に設定項目追加

* 既存の`master_settings`を上書きしないよう修正
  • Loading branch information
kmycode authored Nov 8, 2023
1 parent a7dec3c commit 76f2f2e
Show file tree
Hide file tree
Showing 26 changed files with 284 additions and 52 deletions.
1 change: 1 addition & 0 deletions .haml-lint_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ linters:
- 'app/views/settings/preferences/other/show.html.haml'
- 'app/views/settings/preferences/reaching/show.html.haml'
- 'app/views/settings/profiles/show.html.haml'
- 'app/views/settings/privacy_extra/show.html.haml'

# Offense count: 9
RuboCop:
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/settings/preferences/other_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class Settings::Preferences::OtherController < Settings::Preferences::BaseContro
include DtlHelper

def show
@dtl_enabled = DTL_ENABLED
@dtl_tag = DTL_TAG
@dtl_enabled = dtl_enabled?
@dtl_tag = dtl_tag_name
end

private
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/settings/privacy_extra_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def update
private

def account_params
params.require(:account).permit(:dissubscribable, settings: UserSettings.keys)
params.require(:account).permit(:subscription_policy, settings: UserSettings.keys)
end

def set_account
Expand Down
9 changes: 7 additions & 2 deletions app/helpers/dtl_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# frozen_string_literal: true

module DtlHelper
DTL_ENABLED = ENV.fetch('DTL_ENABLED', 'false') == 'true'
DTL_TAG = ENV.fetch('DTL_TAG', 'kmyblue')
def dtl_enabled?
ENV.fetch('DTL_ENABLED', 'false') == 'true'
end

def dtl_tag_name
ENV.fetch('DTL_TAG', 'kmyblue')
end
end
2 changes: 1 addition & 1 deletion app/lib/account_statuses_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def results
private

def initial_scope
if (suspended? || (domain_block&.reject_send_dissubscribable && @account.dissubscribable)) || domain_block&.reject_send_media || blocked?
if (suspended? || (domain_block&.reject_send_dissubscribable && !@account.all_subscribable?)) || domain_block&.reject_send_media || blocked?
Status.none
elsif anonymous?
account.statuses.where(visibility: %i(public unlisted public_unlisted))
Expand Down
9 changes: 8 additions & 1 deletion app/lib/activitypub/tag_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,14 @@ def limited_scope(status)
end

def subscribable_by(account)
account.dissubscribable ? [] : [COLLECTIONS[:public]]
case account.subscription_policy
when :allow
[COLLECTIONS[:public]]
when :followers_only
[account_followers_url(account)]
else
[]
end
end

def searchable_by(status)
Expand Down
2 changes: 1 addition & 1 deletion app/lib/status_reach_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def banned_domains_of_status(status)
blocks = DomainBlock.where(domain: nil)
blocks = blocks.or(DomainBlock.where(reject_send_not_public_searchability: true)) if status.compute_searchability != 'public'
blocks = blocks.or(DomainBlock.where(reject_send_public_unlisted: true)) if status.public_unlisted_visibility?
blocks = blocks.or(DomainBlock.where(reject_send_dissubscribable: true)) if status.account.dissubscribable
blocks = blocks.or(DomainBlock.where(reject_send_dissubscribable: true)) unless status.account.all_subscribable?
blocks = blocks.or(DomainBlock.where(reject_send_media: true)) if status.with_media?
blocks = blocks.or(DomainBlock.where(reject_send_sensitive: true)) if (status.with_media? && status.sensitive) || status.spoiler_text?
blocks.pluck(:domain).uniq
Expand Down
3 changes: 2 additions & 1 deletion app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
# requested_review_at :datetime
# group_allow_private_message :boolean
# searchability :integer default("direct"), not null
# dissubscribable :boolean default(FALSE), not null
# settings :jsonb
# indexable :boolean default(FALSE), not null
# master_settings :jsonb
#

class Account < ApplicationRecord
Expand Down Expand Up @@ -88,6 +88,7 @@ class Account < ApplicationRecord
include AccountSearch
include AccountStatusesSearch
include AccountOtherSettings
include AccountMasterSettings

enum protocol: { ostatus: 0, activitypub: 1 }
enum suspension_origin: { local: 0, remote: 1 }, _prefix: true
Expand Down
28 changes: 28 additions & 0 deletions app/models/concerns/account_master_settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module AccountMasterSettings
extend ActiveSupport::Concern

included do
def subscription_policy
return master_settings['subscription_policy']&.to_sym || :allow if master_settings.present?

# allow, followers_only, block
:allow
end

def subscription_policy=(val)
self.master_settings = (master_settings.nil? ? {} : master_settings).merge({ 'subscription_policy' => val })
end

def all_subscribable?
subscription_policy == :allow
end

def public_master_settings
{
'subscription_policy' => subscription_policy,
}
end
end
end
2 changes: 1 addition & 1 deletion app/models/concerns/account_other_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def public_settings
end

def public_settings_for_local
public_settings
public_settings.merge(public_master_settings)
end
end
end
2 changes: 1 addition & 1 deletion app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def reported?
end

def dtl?
tags.where(name: DTL_TAG).exists?
(%w(public public_unlisted login).include?(visibility) || (unlisted_visibility? && public_searchability?)) && tags.where(name: dtl_tag_name).exists?
end

def emojis
Expand Down
2 changes: 1 addition & 1 deletion app/policies/status_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def server_blocking_domain_of_status?(status)
else
(@domain_block.reject_send_not_public_searchability && status.compute_searchability != 'public') ||
(@domain_block.reject_send_public_unlisted && status.public_unlisted_visibility?) ||
(@domain_block.reject_send_dissubscribable && status.account.dissubscribable) ||
(@domain_block.reject_send_dissubscribable && !status.account.all_subscribable?) ||
(@domain_block.detect_invalid_subscription && status.public_unlisted_visibility? && status.account.user&.setting_reject_public_unlisted_subscription) ||
(@domain_block.detect_invalid_subscription && status.public_visibility? && status.account.user&.setting_reject_unlisted_subscription) ||
(@domain_block.reject_send_media && status.with_media?) ||
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/initial_state_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def meta
trends_as_landing_page: Setting.trends_as_landing_page,
status_page_url: Setting.status_page_url,
sso_redirect: sso_redirect,
dtl_tag: DTL_ENABLED ? DTL_TAG : nil,
dtl_tag: dtl_enabled? ? dtl_tag_name : nil,
enable_local_privacy: Setting.enable_public_unlisted_visibility,
enable_local_timeline: Setting.enable_local_timeline,
}
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/rest/account_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def discoverable
end

def subscribable
!object.dissubscribable
object.all_subscribable?
end

def moved_to_account
Expand Down
19 changes: 14 additions & 5 deletions app/services/activitypub/process_account_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ def create_account
@account.suspension_origin = :local if auto_suspend?
@account.silenced_at = domain_block.created_at if auto_silence?
@account.searchability = :direct # not null
@account.dissubscribable = false # not null

set_immediate_protocol_attributes!

Expand Down Expand Up @@ -125,8 +124,8 @@ def set_immediate_attributes!
@account.discoverable = @json['discoverable'] || false
@account.indexable = @json['indexable'] || false
@account.searchability = searchability_from_audience
@account.dissubscribable = !subscribable(@account.note)
@account.settings = other_settings
@account.master_settings = (@account.master_settings || {}).merge(master_settings(@account.note))
@account.memorial = @json['memorial'] || false
end

Expand Down Expand Up @@ -320,14 +319,24 @@ def subscribable_by
@subscribable_by = as_array(@json['subscribableBy']).map { |x| value_or_id(x) }
end

def subscribable(note)
def subscription_policy(note)
if subscribable_by.nil?
note.exclude?('[subscribable:no]')
note.include?('[subscribable:no]') ? :block : :allow
elsif subscribable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
:allow
elsif subscribable_by.include?(@account.followers_url)
:followers_only
else
subscribable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
:block
end
end

def master_settings(note)
{
'subscription_policy' => subscription_policy(note),
}
end

def other_settings
return {} unless @json['otherSetting'].is_a?(Array)

Expand Down
14 changes: 8 additions & 6 deletions app/services/delivery_antenna_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ def call(status, update, **options)
private

def delivery!
must_dtl_tag = @account.dissubscribable
return if must_dtl_tag && !DTL_ENABLED
subscription_policy = @account.subscription_policy

dtl_post = @status.dtl? && dtl_enabled?
return if subscription_policy == :block && (!dtl_post || !@account.user&.setting_dtl_force_subscribable)

tag_ids = @status.tags.pluck(:id)
domain = @account.domain
Expand All @@ -38,8 +40,8 @@ def delivery!
antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.left_joins(:antenna_accounts).where(antenna_accounts: { account: @account }))

antennas = Antenna.where(id: antennas.select(:id))
if must_dtl_tag
dtl_tag = Tag.find_or_create_by_names(DTL_TAG).first
if subscription_policy == :block
dtl_tag = Tag.find_or_create_by_names(dtl_tag_name).first
return if !dtl_tag || tag_ids.exclude?(dtl_tag.id)

antennas = antennas.left_joins(:antenna_tags).where(antenna_tags: { tag_id: dtl_tag.id })
Expand Down Expand Up @@ -125,9 +127,9 @@ def delivery_ltl!
def followers_only?
case @status.visibility.to_sym
when :public, :public_unlisted, :login, :limited
false
@status.account.subscription_policy == :followers_only
when :unlisted
@status.compute_searchability != 'public'
@status.compute_searchability != 'public' || @status.account.subscription_policy == :followers_only
else
true
end
Expand Down
4 changes: 2 additions & 2 deletions app/services/fan_out_on_write_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ def fan_out_to_local_recipients!
when :public, :unlisted, :public_unlisted, :login, :private
deliver_to_all_followers!
deliver_to_lists!
deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && DTL_ENABLED && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: DTL_TAG))
deliver_to_antennas!
deliver_to_stl_antennas! if Setting.enable_local_timeline
deliver_to_ltl_antennas! if Setting.enable_local_timeline
when :limited
deliver_to_lists_mentioned_accounts_only!
deliver_to_antennas! unless @account.dissubscribable
deliver_to_antennas!
deliver_to_stl_antennas! if Setting.enable_local_timeline
deliver_to_mentioned_followers!
else
Expand Down
4 changes: 2 additions & 2 deletions app/services/post_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ def load_circle
end

def overwrite_dtl_post
return unless DTL_ENABLED
return unless dtl_enabled?

raw_tags = Extractor.extract_hashtags(@text)
return if raw_tags.exclude?(DTL_TAG)
return if raw_tags.exclude?(dtl_tag_name)
return unless %i(public public_unlisted unlisted).include?(@visibility)

@visibility = @account.user&.setting_dtl_force_visibility if %i(public public_unlisted unlisted).include?(@account.user&.setting_dtl_force_visibility)
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/privacy_extra/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
= ff.input :link_preview, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_link_preview'), hint: I18n.t('simple_form.hints.defaults.setting_link_preview')

.fields-group
= f.input :dissubscribable, as: :boolean, wrapper: :with_label, kmyblue: true, hint: t('simple_form.hints.defaults.dissubscribable')
= f.input :subscription_policy, kmyblue: true, collection: %w(allow followers_only block), label_method: ->(item) { safe_join([t("simple_form.labels.subscription_policy.#{item}")]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: t('simple_form.labels.defaults.subscription_policy'), hint: t('simple_form.hints.defaults.subscription_policy')

.fields-group
= ff.input :allow_quote, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_allow_quote'), hint: false
Expand Down
10 changes: 7 additions & 3 deletions config/locales/simple_form.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ en:
digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
discoverable: Allow your account to be discovered by strangers through recommendations, trends and other features
discoverable_local: Disable the setting on federated servers. The setting is available this server only for avoiding full-text search on other servers
dissubscribable: Your post is not picked by antenna
email: You will be sent a confirmation e-mail
group: Reps sent to this account will be automatically BT'd and distributed to all accounts you follow!
group_allow_private_message: Posts are duplicated and cannot be edited or deleted by the post
Expand Down Expand Up @@ -76,6 +75,7 @@ en:
setting_single_ref_to_quote: If this server does not have target post, target server maybe cannot read your quote
setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
subscription_policy: Your post is not picked by antenna
username: You can use letters, numbers, and underscores
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
domain_allow:
Expand Down Expand Up @@ -143,6 +143,10 @@ en:
settings:
indexable: Your profile page may appear in search results on Google, Bing, and others.
show_application: You will always be able to see which app published your post regardless.
subscription_policy:
allow: Allow
block: Block
followers_only: Followers only
tag:
name: You can only change the casing of the letters, for example, to make it more readable
user:
Expand Down Expand Up @@ -208,7 +212,6 @@ en:
discoverable: Suggest account to others
discoverable_local: Disallow suggesting account on other servers
display_name: Display name
dissubscribable: Reject any subscriptions
email: E-mail address
expires_in: Expire after
fields: Extra fields
Expand Down Expand Up @@ -249,7 +252,7 @@ en:
setting_display_media_expand: Show more medias
setting_display_media_hide_all: Hide all
setting_display_media_show_all: Show all
setting_dtl_force_subscribable: Ignore your dissubscribable setting when using the DTL tag
setting_dtl_force_subscribable: Ignore your subscribability setting when using the DTL tag
setting_dtl_force_searchability: Post searchability
setting_dtl_force_visibility: Post visibility
setting_emoji_reaction_streaming_notify_impl2: Enable stamp notification compat with Nyastodon, Catstodon, glitch-soc
Expand Down Expand Up @@ -298,6 +301,7 @@ en:
setting_use_public_index: Include permitted accounts post to results of search
severity: Severity
sign_in_token_attempt: Security code
subscription_policy: Subscribability
title: Title
type: Import type
username: Username
Expand Down
8 changes: 6 additions & 2 deletions config/locales/simple_form.ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ ja:
digest: 長期間使用していない場合と不在時に返信を受けた場合のみ送信されます
discoverable: レコメンド、トレンド、その他の機能により、あなたのアカウントを他の人から見つけられるようにします。なおkmyblueのローカルユーザーはこの設定をオンにしても全文検索結果には掲載されません。全文検索結果への掲載には、投稿の検索許可設定を変更する必要があります
discoverable_local: 上記設定を当サーバー内でのみ適用するようにします。他のサーバーの全文検索結果への掲載を回避できますが、レコメンド、トレンドなどその他の機能への掲載も回避されます
dissubscribable: あなたの投稿はすべてのアンテナに掲載されなくなります。Misskeyのアンテナを拒否することはできません。Mastodonの一部のサーバーもこの設定に対応しますが、挙動が一部kmyblueと異なる場合があります
email: 確認のメールが送信されます
group: このアカウントに送られたメンションは自動でBTされ、フォローしている全てのアカウントに配信されます
group_allow_private_message: 投稿は複製されるため、投稿者が編集・削除することはできません
Expand Down Expand Up @@ -90,6 +89,7 @@ ja:
setting_unsafe_limited_distribution: Mastodon 3.5、4.0、4.1のサーバーにも限定投稿(相互のみ)が届くようになりますが、安全でない方法で送信します
setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています
setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします
subscription_policy: あなたの投稿はこの設定の範囲外にあるアカウントのアンテナに掲載されなくなります。Misskeyのアンテナを拒否することはできません。Mastodonの一部のサーバーもこの設定に対応しますが、挙動が一部kmyblueと異なる場合があります
username: アルファベット大文字と小文字、数字、アンダーバー「_」が使えます
whole_word: キーワードまたはフレーズが英数字のみの場合、単語全体と一致する場合のみ適用されるようになります
domain_allow:
Expand Down Expand Up @@ -222,7 +222,6 @@ ja:
discoverable: ディレクトリに掲載する
discoverable_local: 他サーバーのディレクトリに掲載しない
display_name: 表示名
dissubscribable: 購読を拒否する
email: メールアドレス
expires_in: 有効期限
fields: プロフィール補足情報
Expand Down Expand Up @@ -314,6 +313,7 @@ ja:
setting_use_public_index: Mastodonの標準設定によって検索が許可されたアカウントの公開投稿を検索結果に含める
severity: 重大性
sign_in_token_attempt: セキュリティコード
subscription_policy: 購読許可
title: タイトル
type: インポートする項目
username: ユーザー名
Expand Down Expand Up @@ -420,6 +420,10 @@ ja:
settings:
indexable: 検索エンジンからアクセスできるようにする
show_application: 投稿に使ったアプリを開示する
subscription_policy:
allow: 全員に許可
block: 全員拒否
followers_only: フォロワーにのみ許可
tag:
listable: 検索とディレクトリへの使用を許可する
name: ハッシュタグ
Expand Down
Loading

0 comments on commit 76f2f2e

Please sign in to comment.