From fe0a99586782dc85650bd24fb8f7d66e4fdca1fc Mon Sep 17 00:00:00 2001 From: KMY Date: Fri, 13 Sep 2024 08:48:21 +0900 Subject: [PATCH] =?UTF-8?q?#842=20=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E5=88=A9=E7=94=A8=E3=81=97?= =?UTF-8?q?=E3=81=9F=E7=B5=B5=E6=96=87=E5=AD=97=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E4=BB=96=E3=82=B5=E3=83=BC?= =?UTF-8?q?=E3=83=90=E3=83=BC=E3=81=8B=E3=82=89=E3=81=AE=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=85=A5=E3=82=8C=E3=81=AB=E3=81=8A=E3=81=84=E3=81=A6=E3=80=81?= =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AEuri=E3=81=A0=E3=81=91=E6=8C=87=E5=AE=9A=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9FActivity=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/activitypub/activity/like.rb | 65 ++++++++++++++----- .../20240912234211_add_custom_emoji_index.rb | 27 ++++++++ db/schema.rb | 3 +- lib/tasks/dangerous.rake | 2 + spec/lib/activitypub/activity/like_spec.rb | 14 ++++ 5 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 db/migrate/20240912234211_add_custom_emoji_index.rb diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index 4f149857c3..bb17dc44c6 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -77,6 +77,8 @@ class ActivityPub::Activity::Like < ActivityPub::Activity end def process_emoji(tag) + return process_emoji_by_uri(as_array(tag)[0]) if tag.is_a?(String) || tag.is_a?(Array) + custom_emoji_parser = ActivityPub::Parser::CustomEmojiParser.new(tag) return if custom_emoji_parser.shortcode.blank? || custom_emoji_parser.image_remote_url.blank? @@ -100,35 +102,62 @@ class ActivityPub::Activity::Like < ActivityPub::Activity custom_emoji_parser = original_emoji_parser(custom_emoji_parser) if @account.domain != domain return if custom_emoji_parser.nil? - begin - emoji ||= CustomEmoji.new( - domain: domain, - shortcode: custom_emoji_parser.shortcode, - uri: custom_emoji_parser.uri - ) - emoji.image_remote_url = custom_emoji_parser.image_remote_url - emoji.license = custom_emoji_parser.license - emoji.is_sensitive = custom_emoji_parser.is_sensitive - emoji.aliases = custom_emoji_parser.aliases - emoji.save - rescue Seahorse::Client::NetworkingError => e - Rails.logger.warn "Error storing emoji: #{e}" - end - - emoji + update_custom_emoji!(emoji, custom_emoji_parser, domain) end def original_emoji_parser(custom_emoji_parser) - uri = custom_emoji_parser.uri + fetch_original_emoji_parser(custom_emoji_parser.uri, custom_emoji_parser.shortcode || '') + end + + def fetch_original_emoji_parser(uri, shortcode = nil) emoji = fetch_resource_without_id_validation(uri) return nil unless emoji parser = ActivityPub::Parser::CustomEmojiParser.new(emoji) - return nil unless parser.uri == uri && custom_emoji_parser.shortcode == parser.shortcode + return nil unless parser.uri == uri + return nil if shortcode.present? && shortcode != parser.shortcode parser end + def process_emoji_by_uri(uri) + return if uri.blank? + + domain = URI.split(uri)[2] || @account.domain + + if domain == Rails.configuration.x.local_domain || domain == Rails.configuration.x.web_domain + # Block overwriting remote-but-local data + return CustomEmoji.find_by(id: ActivityPub::TagManager.instance.uri_to_local_id) + end + + return if domain.present? && skip_download?(domain) + + custom_emoji_parser = nil + custom_emoji_parser = fetch_original_emoji_parser(uri) if @account.domain != domain + custom_emoji_parser ||= CustomEmoji.find_by(uri: uri) + return if custom_emoji_parser.nil? + + update_custom_emoji!(CustomEmoji.find_by(uri: uri), custom_emoji_parser, domain) + end + + def update_custom_emoji!(emoji, custom_emoji_parser, domain) + emoji ||= CustomEmoji.new( + domain: domain, + shortcode: custom_emoji_parser.shortcode, + uri: custom_emoji_parser.uri + ) + emoji.image_remote_url = custom_emoji_parser.image_remote_url + emoji.license = custom_emoji_parser.license + emoji.is_sensitive = custom_emoji_parser.is_sensitive + emoji.aliases = custom_emoji_parser.aliases + emoji.save + + emoji + rescue Seahorse::Client::NetworkingError => e + Rails.logger.warn "Error storing emoji: #{e}" + emoji + end + def skip_download?(domain) return true if DomainBlock.reject_media?(domain) return false if @account.domain == domain diff --git a/db/migrate/20240912234211_add_custom_emoji_index.rb b/db/migrate/20240912234211_add_custom_emoji_index.rb new file mode 100644 index 0000000000..38eac7d23e --- /dev/null +++ b/db/migrate/20240912234211_add_custom_emoji_index.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class AddCustomEmojiIndex < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + class CustomEmoji < ApplicationRecord + end + + def up + duplications = CustomEmoji.where('uri IN (SELECT uri FROM custom_emojis GROUP BY uri HAVING COUNT(*) > 1)') + .to_a.group_by(&:uri).to_h + + if duplications.any? + CustomEmoji.transaction do + duplications.each do |h| + h[1].drop(1).each(&:destroy) + end + end + end + + add_index :custom_emojis, :uri, unique: true, algorithm: :concurrently + end + + def down + remove_index :custom_emojis, :uri + end +end diff --git a/db/schema.rb b/db/schema.rb index 5e21d3b1dd..fb7bc792ca 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[7.1].define(version: 2024_08_28_123604) do +ActiveRecord::Schema[7.1].define(version: 2024_09_12_234211) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -515,6 +515,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_08_28_123604) do t.string "license" t.integer "image_file_size" t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true + t.index ["uri"], name: "index_custom_emojis_on_uri", unique: true end create_table "custom_filter_keywords", force: :cascade do |t| diff --git a/lib/tasks/dangerous.rake b/lib/tasks/dangerous.rake index fcc2a7b52c..bcfa15aae9 100644 --- a/lib/tasks/dangerous.rake +++ b/lib/tasks/dangerous.rake @@ -14,6 +14,7 @@ namespace :dangerous do end target_migrations = %w( + 20240912234211 20240828123604 20240709063700 20240426233435 @@ -189,6 +190,7 @@ namespace :dangerous do index_statuses_on_conversation_id index_preview_cards_vacuum index_media_attachments_vacuum + index_custom_emojis_on_uri ) prompt.say 'Processing...' diff --git a/spec/lib/activitypub/activity/like_spec.rb b/spec/lib/activitypub/activity/like_spec.rb index 369be4caa2..3270660f9a 100644 --- a/spec/lib/activitypub/activity/like_spec.rb +++ b/spec/lib/activitypub/activity/like_spec.rb @@ -204,6 +204,20 @@ RSpec.describe ActivityPub::Activity::Like do expect(subject.first.custom_emoji.domain).to eq 'example.com' expect(sender.favourited?(status)).to be false end + + context 'without tag info' do + let(:tag) { 'https://example.com/aaa' } + + it 'create emoji reaction' do + expect(subject.count).to eq 1 + expect(subject.first.name).to eq 'tinking' + expect(subject.first.account).to eq sender + expect(subject.first.custom_emoji).to_not be_nil + expect(subject.first.custom_emoji.shortcode).to eq 'tinking' + expect(subject.first.custom_emoji.domain).to eq 'example.com' + expect(sender.favourited?(status)).to be false + end + end end context 'with custom emoji and update license from non-original server account' do