diff --git a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb index d7ca4f2da7..1f103beb71 100644 --- a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb +++ b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb @@ -27,7 +27,7 @@ class Api::V1::Statuses::EmojiReactionsController < Api::BaseController authorize @status, :show? if emoji_reaction.nil? - UnEmojiReactService.new.call(current_account.id, @status.id, emoji_reaction) if emoji_reaction.present? + UnEmojiReactService.new.call(current_account, @status, emoji_reaction) if emoji_reaction.present? else authorize @status, :show? end diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index 729a1ba0c6..0486d51f56 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -54,38 +54,11 @@ class ActivityPub::Activity::Like < ActivityPub::Activity Trends.statuses.register(@original_status) write_stream(reaction) - if @original_status.account.local? - NotifyService.new.call(@original_status.account, :emoji_reaction, reaction) - forward_for_emoji_reaction - relay_for_emoji_reaction - relay_friend_for_emoji_reaction - end + NotifyService.new.call(@original_status.account, :emoji_reaction, reaction) if @original_status.account.local? rescue Seahorse::Client::NetworkingError nil end - def forward_for_emoji_reaction - return if @json['signature'].blank? - - ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), @original_status.account.id, [@account.preferred_inbox_url]) - end - - def relay_for_emoji_reaction - return unless @json['signature'].present? && @original_status.public_visibility? - - ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url| - [Oj.dump(@json), @original_status.account.id, inbox_url] - end - end - - def relay_friend_for_emoji_reaction - return unless @json['signature'].present? && @original_status.distributable_friend? - - ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url| - [Oj.dump(@json), @original_status.account.id, inbox_url] - end - end - def shortcode return @shortcode if defined?(@shortcode) diff --git a/app/lib/activitypub/activity/undo.rb b/app/lib/activitypub/activity/undo.rb index f99d949c39..3ba655c91d 100644 --- a/app/lib/activitypub/activity/undo.rb +++ b/app/lib/activitypub/activity/undo.rb @@ -147,12 +147,6 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity if emoji_reaction emoji_reaction.destroy write_stream(emoji_reaction) - - if @original_status.account.local? - forward_for_undo_emoji_reaction - relay_for_undo_emoji_reaction - relay_friend_for_undo_emoji_reaction - end end else undo_like_original @@ -176,28 +170,6 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity @render_emoji_reaction ||= Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json) end - def forward_for_undo_emoji_reaction - return if @json['signature'].blank? - - ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), @original_status.account.id, [@account.preferred_inbox_url]) - end - - def relay_for_undo_emoji_reaction - return unless @json['signature'].present? && @original_status.public_visibility? - - ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url| - [Oj.dump(@json), @original_status.account.id, inbox_url] - end - end - - def relay_friend_for_undo_emoji_reaction - return unless @json['signature'].present? && @original_status.distributable_friend? - - ActivityPub::DeliveryWorker.push_bulk(FriendDomain.distributables.pluck(:inbox_url)) do |inbox_url| - [Oj.dump(@json), @original_status.account.id, inbox_url] - end - end - def shortcode return @shortcode if defined?(@shortcode) diff --git a/app/lib/status_reach_finder.rb b/app/lib/status_reach_finder.rb index 5fe775907f..08c625bbe7 100644 --- a/app/lib/status_reach_finder.rb +++ b/app/lib/status_reach_finder.rb @@ -150,7 +150,7 @@ class StatusReachFinder end def friend_inboxes - if @status.public_visibility? || @status.public_unlisted_visibility? || (@status.unlisted_visibility? && (@status.public_searchability? || @status.public_unlisted_searchability?)) + if @status.distributable_friend? DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: true).where.not(domain: AccountDomainBlock.where(account: @status.account).select(:domain)).pluck(:inbox_url)) else [] diff --git a/app/services/emoji_react_service.rb b/app/services/emoji_react_service.rb index f8bc622d1f..54e7a54a9e 100644 --- a/app/services/emoji_react_service.rb +++ b/app/services/emoji_react_service.rb @@ -46,29 +46,15 @@ class EmojiReactService < BaseService def create_notification status = @emoji_reaction.status + return unless status.account.local? + return unless status.account.user&.setting_enable_emoji_reaction - if status.account.local? - if status.account.user&.setting_enable_emoji_reaction - LocalNotificationWorker.perform_async(status.account_id, @emoji_reaction.id, 'EmojiReaction', 'reaction') if status.account.user&.setting_emoji_reaction_streaming_notify_impl2 - LocalNotificationWorker.perform_async(status.account_id, @emoji_reaction.id, 'EmojiReaction', 'emoji_reaction') - end - elsif status.account.activitypub? - ActivityPub::DeliveryWorker.perform_async(payload, @emoji_reaction.account_id, status.account.inbox_url) - end + LocalNotificationWorker.perform_async(status.account_id, @emoji_reaction.id, 'EmojiReaction', 'reaction') if status.account.user&.setting_emoji_reaction_streaming_notify_impl2 + LocalNotificationWorker.perform_async(status.account_id, @emoji_reaction.id, 'EmojiReaction', 'emoji_reaction') end def notify_to_followers - status = @emoji_reaction.status - - return unless status.account.local? - - ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url| - [payload, @status.account.id, inbox_url] - end - end - - def inboxes - StatusReachFinder.new(@status).all_inboxes + ActivityPub::EmojiReactionDistributionWorker.perform_async(@emoji_reaction.id) end def write_stream! diff --git a/app/services/un_emoji_react_service.rb b/app/services/un_emoji_react_service.rb index 6364938a99..1ec7e2867b 100644 --- a/app/services/un_emoji_react_service.rb +++ b/app/services/un_emoji_react_service.rb @@ -4,23 +4,23 @@ class UnEmojiReactService < BaseService include Redisable include Payloadable - def call(account_id, status_id, emoji_reaction = nil) - @status = Status.find(status_id) + def call(account, status, emoji_reaction = nil) + @account = account + @status = status if emoji_reaction emoji_reaction.destroy! - @status.touch # rubocop:disable Rails/SkipsModelValidations + status.touch # rubocop:disable Rails/SkipsModelValidations create_notification(emoji_reaction) if !@status.account.local? && @status.account.activitypub? - notify_to_followers(emoji_reaction) if @status.account.local? + notify_to_followers(emoji_reaction) write_stream(emoji_reaction) relay_for_undo_emoji_reaction!(emoji_reaction) relay_friend_for_undo_emoji_reaction!(emoji_reaction) else - account = Account.find(account_id) - bulk(account, @status) + bulk(account, status) end emoji_reaction end @@ -28,8 +28,8 @@ class UnEmojiReactService < BaseService private def bulk(account, status) - EmojiReaction.where(account: account).where(status: status).each do |emoji_reaction| - call(account.id, status.id, emoji_reaction) + EmojiReaction.where(account: account, status: status).each do |emoji_reaction| + call(account, status, emoji_reaction) end end @@ -38,7 +38,7 @@ class UnEmojiReactService < BaseService end def notify_to_followers(emoji_reaction) - ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), @status.account_id) + ActivityPub::RawDistributionWorker.perform_async(build_json(emoji_reaction), @account.id) end def write_stream(emoji_reaction) diff --git a/app/workers/activitypub/emoji_reaction_distribution_worker.rb b/app/workers/activitypub/emoji_reaction_distribution_worker.rb new file mode 100644 index 0000000000..06b1c73960 --- /dev/null +++ b/app/workers/activitypub/emoji_reaction_distribution_worker.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class ActivityPub::EmojiReactionDistributionWorker < ActivityPub::RawDistributionWorker + # Distribute an emoji reaction to servers that might have a copy of ohagi + def perform(emoji_reaction_id, options = {}) + @options = options.with_indifferent_access + @emoji_reaction = EmojiReaction.find(emoji_reaction_id) + @account = @emoji_reaction.account + @status = @emoji_reaction.status + + distribute! + rescue ActiveRecord::RecordNotFound + true + end + + protected + + def payload + @payload ||= Oj.dump(serialize_payload(@emoji_reaction, ActivityPub::EmojiReactionSerializer, signer: @account)) + end + + def inboxes + @inboxes ||= (@account.followers.inboxes + [@status.account.preferred_inbox_url].compact_blank + relay_inboxes + friend_inboxes).uniq + end + + def relay_inboxes + if @status.public_visibility? + Relay.enabled.pluck(:inbox_url) + else + [] + end + end + + def friend_inboxes + if @status.distributable_friend? + DeliveryFailureTracker.without_unavailable(FriendDomain.distributables.where(delivery_local: true).where.not(domain: AccountDomainBlock.where(account: @status.account).select(:domain)).pluck(:inbox_url)) + else + [] + end + end +end diff --git a/spec/services/emoji_react_service_spec.rb b/spec/services/emoji_react_service_spec.rb index 8ba0466ef1..21cdf93e0f 100644 --- a/spec/services/emoji_react_service_spec.rb +++ b/spec/services/emoji_react_service_spec.rb @@ -161,11 +161,46 @@ RSpec.describe EmojiReactService, type: :service do end end - context 'when has remote followers' do + context 'when remote status' do + let(:author) { Fabricate(:account, domain: 'author.foo.bar', uri: 'https://author.foo.bar/actor', inbox_url: 'https://author.foo.bar/inbox', protocol: 'activitypub') } + + before do + stub_request(:post, 'https://author.foo.bar/inbox') + end + + it 'react with emoji' do + expect(subject.count).to eq 1 + expect(a_request(:post, 'https://author.foo.bar/inbox').with(body: hash_including({ + type: 'Like', + actor: ActivityPub::TagManager.instance.uri_for(sender), + content: '😀', + }))).to have_been_made.once + end + + context 'when has followers' do + let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') } + + before do + bob.follow!(sender) + stub_request(:post, 'https://foo.bar/inbox') + end + + it 'react with emoji' do + expect(subject.count).to eq 1 + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + type: 'Like', + actor: ActivityPub::TagManager.instance.uri_for(sender), + content: '😀', + }))).to have_been_made.once + end + end + end + + context 'when sender has remote followers' do let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') } before do - bob.follow!(author) + bob.follow!(sender) stub_request(:post, 'https://foo.bar/inbox') end diff --git a/spec/services/un_emoji_react_service_spec.rb b/spec/services/un_emoji_react_service_spec.rb index 393e83b2e0..bf348943d3 100644 --- a/spec/services/un_emoji_react_service_spec.rb +++ b/spec/services/un_emoji_react_service_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' RSpec.describe UnEmojiReactService, type: :service do subject do - described_class.new.call(sender.id, status.id, emoji_reaction) + described_class.new.call(sender, status, emoji_reaction) EmojiReaction.where(status: status, account: sender) end @@ -82,12 +82,48 @@ RSpec.describe UnEmojiReactService, type: :service do end end - context 'when has remote followers' do + context 'when remote status' do + let(:author) { Fabricate(:account, domain: 'author.foo.bar', uri: 'https://author.foo.bar/actor', inbox_url: 'https://author.foo.bar/inbox', protocol: 'activitypub') } + let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') } + + before do + stub_request(:post, 'https://author.foo.bar/inbox') + end + + it 'react with emoji' do + expect(subject.count).to eq 0 + expect(a_request(:post, 'https://author.foo.bar/inbox').with(body: hash_including({ + type: 'Undo', + actor: ActivityPub::TagManager.instance.uri_for(sender), + content: '😀', + }))).to have_been_made.once + end + + context 'when has followers' do + let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') } + + before do + bob.follow!(sender) + stub_request(:post, 'https://foo.bar/inbox') + end + + it 'react with emoji' do + expect(subject.count).to eq 0 + expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({ + type: 'Undo', + actor: ActivityPub::TagManager.instance.uri_for(sender), + content: '😀', + }))).to have_been_made.once + end + end + end + + context 'when sender has remote followers' do let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') } let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') } before do - bob.follow!(author) + bob.follow!(sender) stub_request(:post, 'https://foo.bar/inbox') end