198 lines
7.1 KiB
Ruby
198 lines
7.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ActivityPub::Activity::Like < ActivityPub::Activity
|
|
include Redisable
|
|
include Lockable
|
|
include JsonLdHelper
|
|
include NgRuleHelper
|
|
|
|
def perform
|
|
@original_status = status_from_uri(object_uri)
|
|
|
|
return if @original_status.nil? || delete_arrived_first?(@json['id']) || block_domain? || reject_favourite?
|
|
|
|
if shortcode.nil? || !Setting.enable_emoji_reaction
|
|
process_favourite
|
|
else
|
|
process_emoji_reaction
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def reject_favourite?
|
|
@reject_favourite ||= DomainBlock.reject_favourite?(@account.domain)
|
|
end
|
|
|
|
def process_favourite
|
|
return if @account.favourited?(@original_status)
|
|
return unless check_invalid_reaction_for_ng_rule! @account, uri: @json['id'], reaction_type: 'favourite', recipient: @original_status.account, target_status: @original_status
|
|
|
|
favourite = @original_status.favourites.create!(account: @account, uri: @json['id'])
|
|
|
|
LocalNotificationWorker.perform_async(@original_status.account_id, favourite.id, 'Favourite', 'favourite')
|
|
Trends.statuses.register(@original_status)
|
|
end
|
|
|
|
def process_emoji_reaction
|
|
return if !@original_status.account.local? && !Setting.receive_other_servers_emoji_reaction
|
|
return if (silence_domain? || @account.silenced?) && (!@original_status.local? || !@original_status.account.following?(@account))
|
|
|
|
# custom emoji
|
|
emoji = nil
|
|
if emoji_tag.present?
|
|
emoji = process_emoji(emoji_tag)
|
|
return if emoji.nil?
|
|
end
|
|
|
|
return unless check_invalid_reaction_for_ng_rule! @account, uri: @json['id'], reaction_type: 'emoji_reaction', emoji_reaction_name: emoji&.shortcode || shortcode, emoji_reaction_origin_domain: emoji&.domain, recipient: @original_status.account, target_status: @original_status
|
|
|
|
reaction = nil
|
|
|
|
with_redis_lock("emoji_reaction:#{@original_status.id}") do
|
|
return if EmojiReaction.where(account: @account, status: @original_status).count >= EmojiReaction::EMOJI_REACTION_PER_REMOTE_ACCOUNT_LIMIT
|
|
return if EmojiReaction.find_by(account: @account, status: @original_status, name: shortcode)
|
|
|
|
reaction = @original_status.emoji_reactions.create!(account: @account, name: shortcode, custom_emoji: emoji, uri: @json['id'])
|
|
end
|
|
|
|
Trends.statuses.register(@original_status)
|
|
write_stream(reaction)
|
|
|
|
NotifyService.new.call(@original_status.account, :emoji_reaction, reaction) if @original_status.account.local?
|
|
rescue Seahorse::Client::NetworkingError, ActiveRecord::RecordInvalid
|
|
nil
|
|
end
|
|
|
|
def shortcode
|
|
return @shortcode if defined?(@shortcode)
|
|
|
|
@shortcode = begin
|
|
if @json['_misskey_reaction'] == '⭐'
|
|
nil
|
|
else
|
|
@json['content']&.delete(':')
|
|
end
|
|
end
|
|
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?
|
|
|
|
domain = URI.split(custom_emoji_parser.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(shortcode: custom_emoji_parser.shortcode, domain: nil)
|
|
end
|
|
|
|
return if domain.present? && skip_download?(domain)
|
|
|
|
emoji = CustomEmoji.find_by(shortcode: custom_emoji_parser.shortcode, domain: domain)
|
|
|
|
return emoji unless emoji.nil? ||
|
|
custom_emoji_parser.image_remote_url != emoji.image_remote_url ||
|
|
(custom_emoji_parser.updated_at && custom_emoji_parser.updated_at >= emoji.updated_at) ||
|
|
custom_emoji_parser.license != emoji.license
|
|
|
|
custom_emoji_parser = original_emoji_parser(custom_emoji_parser) if @account.domain != domain
|
|
return if custom_emoji_parser.nil?
|
|
|
|
update_custom_emoji!(emoji, custom_emoji_parser, domain)
|
|
end
|
|
|
|
def original_emoji_parser(custom_emoji_parser)
|
|
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
|
|
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
|
|
|
|
DomainBlock.blocked?(domain) || (DomainBlock.silence?(domain) && !@original_status.account.following?(@account))
|
|
end
|
|
|
|
def block_domain?
|
|
DomainBlock.blocked?(@account.domain)
|
|
end
|
|
|
|
def silence_domain?
|
|
DomainBlock.silence?(@account.domain)
|
|
end
|
|
|
|
def misskey_favourite?
|
|
misskey_shortcode = @json['_misskey_reaction']&.delete(':')
|
|
|
|
misskey_shortcode == shortcode && misskey_shortcode == '⭐'
|
|
end
|
|
|
|
def emoji_tag
|
|
return @emoji_tag if defined?(@emoji_tag)
|
|
|
|
@emoji_tag = @json['tag'].is_a?(Array) ? @json['tag']&.first : @json['tag']
|
|
end
|
|
|
|
def write_stream(emoji_reaction)
|
|
emoji_group = @original_status.emoji_reactions_grouped_by_name(nil, force: true)
|
|
.find { |reaction_group| reaction_group['name'] == emoji_reaction.name && (!reaction_group.key?(:domain) || reaction_group['domain'] == emoji_reaction.custom_emoji&.domain) }
|
|
emoji_group['status_id'] = @original_status.id.to_s
|
|
DeliveryEmojiReactionWorker.perform_async(render_emoji_reaction(emoji_group), @original_status.id, emoji_reaction.account_id) if @original_status.local? ? Setting.streaming_local_emoji_reaction : Setting.streaming_other_servers_emoji_reaction
|
|
end
|
|
|
|
def render_emoji_reaction(emoji_group)
|
|
@render_emoji_reaction ||= Oj.dump(event: :emoji_reaction, payload: emoji_group.to_json)
|
|
end
|
|
end
|