about summary refs log tree commit diff
path: root/app/lib/activitypub
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib/activitypub')
-rw-r--r--app/lib/activitypub/activity/announce.rb9
-rw-r--r--app/lib/activitypub/activity/create.rb60
-rw-r--r--app/lib/activitypub/adapter.rb6
-rw-r--r--app/lib/activitypub/tag_manager.rb6
4 files changed, 60 insertions, 21 deletions
diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb
index c4da405c7..4516454e1 100644
--- a/app/lib/activitypub/activity/announce.rb
+++ b/app/lib/activitypub/activity/announce.rb
@@ -11,7 +11,12 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
 
     return status unless status.nil?
 
-    status = Status.create!(account: @account, reblog: original_status, uri: @json['id'])
+    status = Status.create!(
+      account: @account,
+      reblog: original_status,
+      uri: @json['id'],
+      created_at: @json['published'] || Time.now.utc
+    )
     distribute(status)
     status
   end
@@ -20,6 +25,8 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
 
   def fetch_remote_original_status
     if object_uri.start_with?('http')
+      return if ActivityPub::TagManager.instance.local_uri?(object_uri)
+
       ActivityPub::FetchRemoteStatusService.new.call(object_uri)
     elsif @object['url'].present?
       ::FetchRemoteStatusService.new.call(@object['url'])
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 9a34484f5..4e19b3096 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -4,26 +4,31 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   def perform
     return if delete_arrived_first?(object_uri) || unsupported_object_type?
 
-    status = find_existing_status
+    RedisLock.acquire(lock_options) do |lock|
+      if lock.acquired?
+        @status = find_existing_status
+        process_status if @status.nil?
+      end
+    end
 
-    return status unless status.nil?
+    @status
+  end
+
+  private
 
+  def process_status
     ApplicationRecord.transaction do
-      status = Status.create!(status_params)
+      @status = Status.create!(status_params)
 
-      process_tags(status)
-      process_attachments(status)
+      process_tags(@status)
+      process_attachments(@status)
     end
 
-    resolve_thread(status)
-    distribute(status)
-    forward_for_reply if status.public_visibility? || status.unlisted_visibility?
-
-    status
+    resolve_thread(@status)
+    distribute(@status)
+    forward_for_reply if @status.public_visibility? || @status.unlisted_visibility?
   end
 
-  private
-
   def find_existing_status
     status   = status_from_uri(object_uri)
     status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present?
@@ -56,11 +61,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
         process_hashtag tag, status
       when 'Mention'
         process_mention tag, status
+      when 'Emoji'
+        process_emoji tag, status
       end
     end
   end
 
   def process_hashtag(tag, status)
+    return if tag['name'].blank?
+
     hashtag = tag['name'].gsub(/\A#/, '').mb_chars.downcase
     hashtag = Tag.where(name: hashtag).first_or_initialize(name: hashtag)
 
@@ -68,17 +77,32 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   end
 
   def process_mention(tag, status)
+    return if tag['href'].blank?
+
     account = account_from_uri(tag['href'])
     account = FetchRemoteAccountService.new.call(tag['href']) if account.nil?
     return if account.nil?
     account.mentions.create(status: status)
   end
 
+  def process_emoji(tag, _status)
+    return if tag['name'].blank? || tag['href'].blank?
+
+    shortcode = tag['name'].delete(':')
+    emoji     = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain)
+
+    return if !emoji.nil? || skip_download?
+
+    emoji = CustomEmoji.new(domain: @account.domain, shortcode: shortcode)
+    emoji.image_remote_url = tag['href']
+    emoji.save
+  end
+
   def process_attachments(status)
     return unless @object['attachment'].is_a?(Array)
 
     @object['attachment'].each do |attachment|
-      next if unsupported_media_type?(attachment['mediaType'])
+      next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank?
 
       href             = Addressable::URI.parse(attachment['url']).normalize.to_s
       media_attachment = MediaAttachment.create(status: status, account: status.account, remote_url: href)
@@ -88,6 +112,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
       media_attachment.file_remote_url = href
       media_attachment.save
     end
+  rescue Addressable::URI::InvalidURIError => e
+    Rails.logger.debug e
   end
 
   def resolve_thread(status)
@@ -97,8 +123,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
 
   def conversation_from_uri(uri)
     return nil if uri.nil?
-    return Conversation.find_by(id: TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if TagManager.instance.local_id?(uri)
-    Conversation.find_by(uri: uri) || Conversation.create!(uri: uri)
+    return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri)
+    Conversation.find_by(uri: uri) || Conversation.create(uri: uri)
   end
 
   def visibility_from_audience
@@ -182,4 +208,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     return unless @json['signature'].present? && reply_to_local?
     ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id)
   end
+
+  def lock_options
+    { redis: Redis.current, key: "create:#{@object['id']}" }
+  end
 end
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index 6ed66a239..790d2025c 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -14,6 +14,8 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
         'atomUri'                   => 'ostatus:atomUri',
         'inReplyToAtomUri'          => 'ostatus:inReplyToAtomUri',
         'conversation'              => 'ostatus:conversation',
+        'toot'                      => 'http://joinmastodon.org/ns#',
+        'Emoji'                     => 'toot:Emoji',
       },
     ],
   }.freeze
@@ -28,7 +30,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
 
   def serializable_hash(options = nil)
     options = serialization_options(options)
-    serialized_hash = CONTEXT.merge(ActiveModelSerializers::Adapter::Attributes.new(serializer, instance_options).serializable_hash(options))
-    self.class.transform_key_casing!(serialized_hash, instance_options)
+    serialized_hash = ActiveModelSerializers::Adapter::Attributes.new(serializer, instance_options).serializable_hash(options)
+    CONTEXT.merge(self.class.transform_key_casing!(serialized_hash, instance_options))
   end
 end
diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb
index 929e87852..4ec3b8c56 100644
--- a/app/lib/activitypub/tag_manager.rb
+++ b/app/lib/activitypub/tag_manager.rb
@@ -37,7 +37,7 @@ class ActivityPub::TagManager
   end
 
   def activity_uri_for(target)
-    return nil unless %i(note comment activity).include?(target.object_type) && target.local?
+    raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
 
     activity_account_status_url(target.account, target)
   end
@@ -98,8 +98,8 @@ class ActivityPub::TagManager
       else
         StatusFinder.new(uri).status
       end
-    elsif ::TagManager.instance.local_id?(uri)
-      klass.find_by(id: ::TagManager.instance.unique_tag_to_local_id(uri, klass.to_s))
+    elsif OStatus::TagManager.instance.local_id?(uri)
+      klass.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, klass.to_s))
     else
       klass.find_by(uri: uri.split('#').first)
     end