From 8dcf7b224ca2b96c3694e328a99b067b2caea2c9 Mon Sep 17 00:00:00 2001 From: fef Date: Fri, 2 Dec 2022 17:02:06 +0000 Subject: [PATCH] download remote custom emojis from reactions Emoji reactions containing custom emojis from remote instances were assumed to already have been downloaded and stored in the database. This might obviously not be the case. --- app/lib/activitypub/activity.rb | 26 +++++++++++++++++ app/lib/activitypub/activity/emoji_react.rb | 13 +++++++-- app/lib/activitypub/activity/like.rb | 32 ++++++++++++++------- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index a6b91f62da..29df445061 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -176,4 +176,30 @@ class ActivityPub::Activity Rails.logger.info("Rejected #{@json['type']} activity #{@json['id']} from #{@account.uri}#{@options[:relayed_through_actor] && "via #{@options[:relayed_through_actor].uri}"}") nil end + + # Ensure all emojis declared in the activity's tags are + # present in the database and downloaded to the local cache. + def process_emoji_tags + as_array(@object['tag']).each do |tag| + process_single_emoji(tag) if tag['type'] == 'Emoji' + end + end + + def process_single_emoji(tag) + parser = ActivityPub::Parser::CustomEmojiParser.new(tag) + return if parser.shortcode.blank? || parser.image_remote_url.blank? + + emoji = CustomEmoji.find_by(shortcode: parser.shortcode, domain: @account.domain) + return unless emoji.nil? || + parser.image_remote_url != emoji.image_remote_url || + (parser.updated_at && parser.updated_at >= emoji.updated_at) + + begin + emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: parser.shortcode, uri: parser.uri) + emoji.image_remote_url = parser.image_remote_url + emoji.save + rescue Seahorse::Client::NetworkingError => e + Rails.logger.warn "Error fetching emoji: #{e}" + end + end end diff --git a/app/lib/activitypub/activity/emoji_react.rb b/app/lib/activitypub/activity/emoji_react.rb index 595751aaae..c3d50b1e05 100644 --- a/app/lib/activitypub/activity/emoji_react.rb +++ b/app/lib/activitypub/activity/emoji_react.rb @@ -5,9 +5,16 @@ class ActivityPub::Activity::EmojiReact < ActivityPub::Activity original_status = status_from_uri(object_uri) name = @json['content'] return if original_status.nil? || - !original_status.account.local? || - delete_arrived_first?(@json['id']) || - @account.reacted?(original_status, name) + !original_status.account.local? || + delete_arrived_first?(@json['id']) || + @account.reacted?(original_status, name) + + if name =~ /^:.*:$/ + process_emoji_tags + + name.delete! ':' + return if CustomEmoji.find_by(shortcode: name, domain: @account.domain).nil? + end reaction = original_status.status_reactions.create!(account: @account, name: name) diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index 6af516d109..311a58d3cd 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -5,16 +5,7 @@ class ActivityPub::Activity::Like < ActivityPub::Activity original_status = status_from_uri(object_uri) return if original_status.nil? || !original_status.account.local? || delete_arrived_first?(@json['id']) - # misskey delivers reactions as likes and attaches the emoji in _misskey_reaction - mk_reaction = @json['_misskey_reaction'] - unless mk_reaction.nil? - custom_emoji = CustomEmoji.find_by(shortcode: mk_reaction, domain: @account.domain) - return if @account.reacted?(original_status, mk_reaction, custom_emoji) - - reaction = original_status.status_reactions.create!(account: @account, name: mk_reaction, custom_emoji: custom_emoji) - LocalNotificationWorker.perform_async(original_status.account_id, reaction.id, 'StatusReaction', 'reaction') - return - end + return if maybe_process_misskey_reaction(original_status) return if @account.favourited?(original_status) @@ -23,4 +14,25 @@ class ActivityPub::Activity::Like < ActivityPub::Activity LocalNotificationWorker.perform_async(original_status.account_id, favourite.id, 'Favourite', 'favourite') Trends.statuses.register(original_status) end + + # Misskey delivers reactions as likes with the emoji in _misskey_reaction + # see https://misskey-hub.net/ns.html#misskey-reaction for details + def maybe_process_misskey_reaction(original_status) + name = @json['_misskey_reaction'] + return false if name.nil? + + custom_emoji = nil + if name =~ /^:.*:$/ + process_emoji_tags + + name.delete! ':' + custom_emoji = CustomEmoji.find_by(shortcode: name, domain: @account.domain) + return false if custom_emoji.nil? # invalid custom emoji, treat it as a regular like + end + return true if @account.reacted?(original_status, name, custom_emoji) + + reaction = original_status.status_reactions.create!(account: @account, name: name, custom_emoji: custom_emoji) + LocalNotificationWorker.perform_async(original_status.account_id, reaction.id, 'StatusReaction', 'reaction') + return true + end end