diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index be5b4f3029..3daaa6f687 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -20,7 +20,7 @@ class Settings::ProfilesController < Settings::BaseController private def account_params - params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :hide_collections, fields_attributes: [:name, :value]) + params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :my_actor_type, :group_message_following_only, :discoverable, :hide_collections, fields_attributes: [:name, :value]) end def set_account diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 1537980891..6b93233c0d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1290,7 +1290,7 @@ body > [data-popper-placement] { transition: transform .2s ease; &:hover { - transform: scale(1.4); + transform: scale(1.2); } img { diff --git a/app/models/account.rb b/app/models/account.rb index 1ff083e54a..c32b08282f 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -50,6 +50,7 @@ # trendable :boolean # reviewed_at :datetime # requested_review_at :datetime +# group_message_following_only :boolean # class Account < ApplicationRecord @@ -171,8 +172,20 @@ class Account < ApplicationRecord actor_type == 'Group' end + def group=(val) + self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Group' : 'Person' + end + alias group group? + def my_actor_type + actor_type == 'Service' ? 'bot' : actor_type == 'Group' ? 'group' : 'person' + end + + def my_actor_type=(val) + self.actor_type = val == 'bot' ? 'Service' : val == 'group' ? 'Group' : 'Person' + end + def acct local? ? username : "#{username}@#{domain}" end diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index ac7372f745..b89a834631 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -182,6 +182,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService update_tags! update_mentions! update_emojis! + join_group! end def update_tags! @@ -218,6 +219,10 @@ class ActivityPub::ProcessStatusUpdateService < BaseService Mention.where(id: removed_mentions.map(&:id)).update_all(silent: true) unless removed_mentions.empty? end + def join_group! + GroupReblogService.call(@status) + end + def update_emojis! return if skip_download? diff --git a/app/services/group_reblog_service.rb b/app/services/group_reblog_service.rb new file mode 100644 index 0000000000..b9f375b8cb --- /dev/null +++ b/app/services/group_reblog_service.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class GroupReblogService < BaseService + + def call(status) + visibility = status.visibility.to_sym + return nil if visibility != :public && visibility != :public_unlisted && visibility != :unlisted + + accounts = status.mentions.map(&:account) | status.active_mentions.map(&:account) + + accounts.each do |account| + next unless account.local? + next if account.group_message_following_only && !account.following?(status.account) + + ReblogService.new.call(account, status, { visibility: status.visibility }) if account.group? + end + end +end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index d1e750c9c4..f9349670d9 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -122,6 +122,7 @@ class PostStatusService < BaseService DistributionWorker.perform_async(@status.id) ActivityPub::DistributionWorker.perform_async(@status.id) PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll + GroupReblogService.new.call(@status) end def validate_media! diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index 3067b37370..87897a6465 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -27,7 +27,10 @@ = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked') .fields-group - = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot') + = f.input :my_actor_type, collection: ['person', 'bot', 'group'],label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.#{item}"), content_tag(:span, I18n.t("simple_form.hints.defaults.#{item}"), class: 'hint')]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label + + .fields-group + = f.input :group_message_following_only, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.group_message_following_only') .fields-group = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable'), recommended: true diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 96b0131efe..a7b66807bd 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -40,12 +40,15 @@ en: discoverable: Allow your account to be discovered by strangers through recommendations, trends and other features email: You will be sent a confirmation e-mail fields: You can have up to 4 items displayed as a table on your profile + group: Reps sent to this account will be automatically BT'd and distributed to all accounts you follow! + group_message_following_only: Effective as an anti-troll/spam measure, but requires the effort to follow up with subscribers header: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px inbox_url: Copy the URL from the frontpage of the relay you want to use irreversible: Filtered posts will disappear irreversibly, even if filter is later removed locale: The language of the user interface, e-mails and push notifications locked: Manually control who can follow you by approving follow requests password: Use at least 8 characters + person: This is normal account phrase: Will be matched regardless of casing in text or content warning of a post scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones. setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts) @@ -179,6 +182,8 @@ en: email: E-mail address expires_in: Expire after fields: Profile metadata + group: This is a group account + group_message_following_only: For group accounts, BT only mentions from people you are following header: Header honeypot: "%{label} (do not fill in)" inbox_url: URL of the relay inbox @@ -186,10 +191,12 @@ en: locale: Interface language locked: Require follow requests max_uses: Max number of uses + my_actor_type: Account type new_password: New password note: Bio otp_attempt: Two-factor code password: Password + person: This is a normal account phrase: Keyword or phrase setting_advanced_layout: Enable advanced web interface setting_aggregate_reblogs: Group boosts in timelines diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index f7e2cb9545..2ad4e03e11 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -40,12 +40,15 @@ ja: discoverable: レコメンド、トレンド、その他の機能により、あなたのアカウントを他の人から見つけられるようにします email: 確認のメールが送信されます fields: プロフィールに表として4つまでの項目を表示することができます + group: このアカウントに送られたリプは自動でBTされ、フォローしている全てのアカウントに配信されます + group_message_following_only: 荒らし・スパム対策として有効ですが、加入者をフォローする手間が発生します header: "%{size}までのPNG、GIF、JPGが利用可能です。 %{dimensions}pxまで縮小されます" inbox_url: 使用したいリレーサーバーのトップページからURLをコピーします irreversible: フィルターが後で削除されても、除外された投稿は元に戻せなくなります locale: ユーザーインターフェース、メールやプッシュ通知の言語 locked: フォロワーを手動で承認する必要があります password: 少なくとも8文字は入力してください + person: これは人が使用している通常のアカウントです phrase: 投稿内容の大文字小文字や閲覧注意に関係なく一致 scopes: アプリの API に許可するアクセス権を選択してください。最上位のスコープを選択する場合、個々のスコープを選択する必要はありません。 setting_aggregate_reblogs: 最近ブーストされた投稿が新たにブーストされても表示しません (設定後受信したものにのみ影響) @@ -179,6 +182,8 @@ ja: email: メールアドレス expires_in: 有効期限 fields: プロフィール補足情報 + group: これはグループアカウントです + group_message_following_only: グループアカウントの場合、自分がフォローしている相手からのメンションのみをBTする header: ヘッダー honeypot: "%{label} (入力しない)" inbox_url: リレーサーバーの inbox URL @@ -186,10 +191,12 @@ ja: locale: 言語 locked: 承認制アカウントにする max_uses: 使用できる回数 + my_actor_type: アカウントの種類 new_password: 新しいパスワード note: プロフィール otp_attempt: 二要素認証コード password: パスワード + person: これは通常のアカウントです phrase: キーワードまたはフレーズ setting_advanced_layout: 上級者向けUIを有効にする setting_aggregate_reblogs: ブーストをまとめる diff --git a/db/migrate/20230314021909_add_group_message_following_only_to_accounts.rb b/db/migrate/20230314021909_add_group_message_following_only_to_accounts.rb new file mode 100644 index 0000000000..40f843afd8 --- /dev/null +++ b/db/migrate/20230314021909_add_group_message_following_only_to_accounts.rb @@ -0,0 +1,5 @@ +class AddGroupMessageFollowingOnlyToAccounts < ActiveRecord::Migration[6.1] + def change + add_column :accounts, :group_message_following_only, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 93a923bbe2..f2e9cac6c0 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_03_08_061833) do +ActiveRecord::Schema.define(version: 2023_03_14_021909) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -186,6 +186,7 @@ ActiveRecord::Schema.define(version: 2023_03_08_061833) do t.boolean "trendable" t.datetime "reviewed_at" t.datetime "requested_review_at" + t.boolean "group_message_following_only" t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "lower((username)::text), COALESCE(lower((domain)::text), ''::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id", where: "(moved_to_account_id IS NOT NULL)"