Fix processing of mentions for post edits with an existing corresponding silent mention (#33227)
This commit is contained in:
		
							parent
							
								
									afcfc64007
								
							
						
					
					
						commit
						533477e77c
					
				@ -188,40 +188,30 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def update_mentions!
 | 
					  def update_mentions!
 | 
				
			||||||
    previous_mentions = @status.active_mentions.includes(:account).to_a
 | 
					 | 
				
			||||||
    current_mentions  = []
 | 
					 | 
				
			||||||
    unresolved_mentions = []
 | 
					    unresolved_mentions = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @raw_mentions.each do |href|
 | 
					    currently_mentioned_account_ids = @raw_mentions.filter_map do |href|
 | 
				
			||||||
      next if href.blank?
 | 
					      next if href.blank?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      account   = ActivityPub::TagManager.instance.uri_to_resource(href, Account)
 | 
					      account   = ActivityPub::TagManager.instance.uri_to_resource(href, Account)
 | 
				
			||||||
      account ||= ActivityPub::FetchRemoteAccountService.new.call(href, request_id: @request_id)
 | 
					      account ||= ActivityPub::FetchRemoteAccountService.new.call(href, request_id: @request_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      next if account.nil?
 | 
					      account&.id
 | 
				
			||||||
 | 
					 | 
				
			||||||
      mention   = previous_mentions.find { |x| x.account_id == account.id }
 | 
					 | 
				
			||||||
      mention ||= account.mentions.new(status: @status)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      current_mentions << mention
 | 
					 | 
				
			||||||
    rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
 | 
					    rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError
 | 
				
			||||||
      # Since previous mentions are about already-known accounts,
 | 
					      # Since previous mentions are about already-known accounts,
 | 
				
			||||||
      # they don't try to resolve again and won't fall into this case.
 | 
					      # they don't try to resolve again and won't fall into this case.
 | 
				
			||||||
      # In other words, this failure case is only for new mentions and won't
 | 
					      # In other words, this failure case is only for new mentions and won't
 | 
				
			||||||
      # affect `removed_mentions` so they can safely be retried asynchronously
 | 
					      # affect `removed_mentions` so they can safely be retried asynchronously
 | 
				
			||||||
      unresolved_mentions << href
 | 
					      unresolved_mentions << href
 | 
				
			||||||
 | 
					      nil
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    current_mentions.each do |mention|
 | 
					    @status.mentions.upsert_all(currently_mentioned_account_ids.map { |id| { account_id: id, silent: false } }, unique_by: %w(status_id account_id))
 | 
				
			||||||
      mention.save if mention.new_record?
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # If previous mentions are no longer contained in the text, convert them
 | 
					    # If previous mentions are no longer contained in the text, convert them
 | 
				
			||||||
    # to silent mentions, since withdrawing access from someone who already
 | 
					    # to silent mentions, since withdrawing access from someone who already
 | 
				
			||||||
    # received a notification might be more confusing
 | 
					    # received a notification might be more confusing
 | 
				
			||||||
    removed_mentions = previous_mentions - current_mentions
 | 
					    @status.mentions.where.not(account_id: currently_mentioned_account_ids).update_all(silent: true)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Mention.where(id: removed_mentions.map(&:id)).update_all(silent: true) unless removed_mentions.empty?
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Queue unresolved mentions for later
 | 
					    # Queue unresolved mentions for later
 | 
				
			||||||
    unresolved_mentions.uniq.each do |uri|
 | 
					    unresolved_mentions.uniq.each do |uri|
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ class ProcessMentionsService < BaseService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return unless @status.local?
 | 
					    return unless @status.local?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @previous_mentions = @status.active_mentions.includes(:account).to_a
 | 
					    @previous_mentions = @status.mentions.includes(:account).to_a
 | 
				
			||||||
    @current_mentions  = []
 | 
					    @current_mentions  = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Status.transaction do
 | 
					    Status.transaction do
 | 
				
			||||||
@ -57,6 +57,8 @@ class ProcessMentionsService < BaseService
 | 
				
			|||||||
      mention ||= @current_mentions.find  { |x| x.account_id == mentioned_account.id }
 | 
					      mention ||= @current_mentions.find  { |x| x.account_id == mentioned_account.id }
 | 
				
			||||||
      mention ||= @status.mentions.new(account: mentioned_account)
 | 
					      mention ||= @status.mentions.new(account: mentioned_account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      mention.silent = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      @current_mentions << mention
 | 
					      @current_mentions << mention
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      "@#{mentioned_account.acct}"
 | 
					      "@#{mentioned_account.acct}"
 | 
				
			||||||
@ -78,7 +80,7 @@ class ProcessMentionsService < BaseService
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @current_mentions.each do |mention|
 | 
					    @current_mentions.each do |mention|
 | 
				
			||||||
      mention.save if mention.new_record? && @save_records
 | 
					      mention.save if (mention.new_record? || mention.silent_changed?) && @save_records
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # If previous mentions are no longer contained in the text, convert them
 | 
					    # If previous mentions are no longer contained in the text, convert them
 | 
				
			||||||
@ -86,7 +88,7 @@ class ProcessMentionsService < BaseService
 | 
				
			|||||||
    # received a notification might be more confusing
 | 
					    # received a notification might be more confusing
 | 
				
			||||||
    removed_mentions = @previous_mentions - @current_mentions
 | 
					    removed_mentions = @previous_mentions - @current_mentions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Mention.where(id: removed_mentions.map(&:id)).update_all(silent: true) unless removed_mentions.empty?
 | 
					    Mention.where(id: removed_mentions.map(&:id), silent: false).update_all(silent: true) unless removed_mentions.empty?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def mention_undeliverable?(mentioned_account)
 | 
					  def mention_undeliverable?(mentioned_account)
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@ class MentionResolveWorker
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return if account.nil?
 | 
					    return if account.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    status.mentions.create!(account: account, silent: false)
 | 
					    status.mentions.upsert({ account_id: account.id, silent: false }, unique_by: %w(status_id account_id))
 | 
				
			||||||
  rescue ActiveRecord::RecordNotFound
 | 
					  rescue ActiveRecord::RecordNotFound
 | 
				
			||||||
    # Do nothing
 | 
					    # Do nothing
 | 
				
			||||||
  rescue Mastodon::UnexpectedResponseError => e
 | 
					  rescue Mastodon::UnexpectedResponseError => e
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
 | 
				
			|||||||
  let(:media_attachments) { [] }
 | 
					  let(:media_attachments) { [] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before do
 | 
					  before do
 | 
				
			||||||
    mentions.each { |a| Fabricate(:mention, status: status, account: a) }
 | 
					    mentions.each { |(account, silent)| Fabricate(:mention, status: status, account: account, silent: silent) }
 | 
				
			||||||
    tags.each { |t| status.tags << t }
 | 
					    tags.each { |t| status.tags << t }
 | 
				
			||||||
    media_attachments.each { |m| status.media_attachments << m }
 | 
					    media_attachments.each { |m| status.media_attachments << m }
 | 
				
			||||||
    stub_request(:get, bogus_mention).to_raise(HTTP::ConnectionError)
 | 
					    stub_request(:get, bogus_mention).to_raise(HTTP::ConnectionError)
 | 
				
			||||||
@ -280,7 +280,19 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'when originally with mentions' do
 | 
					    context 'when originally with mentions' do
 | 
				
			||||||
      let(:mentions) { [alice, bob] }
 | 
					      let(:mentions) { [[alice, false], [bob, false]] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      before do
 | 
				
			||||||
 | 
					        subject.call(status, json, json)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'updates mentions' do
 | 
				
			||||||
 | 
					        expect(status.active_mentions.reload.map(&:account_id)).to eq [alice.id]
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when originally with silent mentions' do
 | 
				
			||||||
 | 
					      let(:mentions) { [[alice, true], [bob, true]] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        subject.call(status, json, json)
 | 
					        subject.call(status, json, json)
 | 
				
			||||||
 | 
				
			|||||||
@ -150,6 +150,14 @@ RSpec.describe UpdateStatusService do
 | 
				
			|||||||
        .to eq [bob.id]
 | 
					        .to eq [bob.id]
 | 
				
			||||||
      expect(status.mentions.pluck(:account_id))
 | 
					      expect(status.mentions.pluck(:account_id))
 | 
				
			||||||
        .to contain_exactly(alice.id, bob.id)
 | 
					        .to contain_exactly(alice.id, bob.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Going back when a mention was switched to silence should still be possible
 | 
				
			||||||
 | 
					      subject.call(status, status.account_id, text: 'Hello @alice')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status.active_mentions.pluck(:account_id))
 | 
				
			||||||
 | 
					        .to eq [alice.id]
 | 
				
			||||||
 | 
					      expect(status.mentions.pluck(:account_id))
 | 
				
			||||||
 | 
					        .to contain_exactly(alice.id, bob.id)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user