You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
5.5 KiB

  1. # frozen_string_literal: true
  2. class ActivityPub::Activity
  3. include JsonLdHelper
  4. include Redisable
  5. SUPPORTED_TYPES = %w(Note Question).freeze
  6. CONVERTED_TYPES = %w(Image Audio Video Article Page Event).freeze
  7. def initialize(json, account, **options)
  8. @json = json
  9. @account = account
  10. @object = @json['object']
  11. @options = options
  12. end
  13. def perform
  14. raise NotImplementedError
  15. end
  16. class << self
  17. def factory(json, account, **options)
  18. @json = json
  19. klass&.new(json, account, **options)
  20. end
  21. private
  22. def klass
  23. case @json['type']
  24. when 'Create'
  25. ActivityPub::Activity::Create
  26. when 'Announce'
  27. ActivityPub::Activity::Announce
  28. when 'Delete'
  29. ActivityPub::Activity::Delete
  30. when 'Follow'
  31. ActivityPub::Activity::Follow
  32. when 'Like'
  33. ActivityPub::Activity::Like
  34. when 'Block'
  35. ActivityPub::Activity::Block
  36. when 'Update'
  37. ActivityPub::Activity::Update
  38. when 'Undo'
  39. ActivityPub::Activity::Undo
  40. when 'Accept'
  41. ActivityPub::Activity::Accept
  42. when 'Reject'
  43. ActivityPub::Activity::Reject
  44. when 'Flag'
  45. ActivityPub::Activity::Flag
  46. when 'Add'
  47. ActivityPub::Activity::Add
  48. when 'Remove'
  49. ActivityPub::Activity::Remove
  50. when 'Move'
  51. ActivityPub::Activity::Move
  52. end
  53. end
  54. end
  55. protected
  56. def status_from_uri(uri)
  57. ActivityPub::TagManager.instance.uri_to_resource(uri, Status)
  58. end
  59. def account_from_uri(uri)
  60. ActivityPub::TagManager.instance.uri_to_resource(uri, Account)
  61. end
  62. def object_uri
  63. @object_uri ||= begin
  64. str = value_or_id(@object)
  65. if str&.start_with?('bear:')
  66. Addressable::URI.parse(str).query_values['u']
  67. else
  68. str
  69. end
  70. end
  71. end
  72. def unsupported_object_type?
  73. @object.is_a?(String) || !(supported_object_type? || converted_object_type?)
  74. end
  75. def supported_object_type?
  76. equals_or_includes_any?(@object['type'], SUPPORTED_TYPES)
  77. end
  78. def converted_object_type?
  79. equals_or_includes_any?(@object['type'], CONVERTED_TYPES)
  80. end
  81. def delete_arrived_first?(uri)
  82. redis.exists?("delete_upon_arrival:#{@account.id}:#{uri}")
  83. end
  84. def delete_later!(uri)
  85. redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, true)
  86. end
  87. def status_from_object
  88. # If the status is already known, return it
  89. status = status_from_uri(object_uri)
  90. return status unless status.nil?
  91. # If the boosted toot is embedded and it is a self-boost, handle it like a Create
  92. unless unsupported_object_type?
  93. actor_id = value_or_id(first_of_value(@object['attributedTo']))
  94. if actor_id == @account.uri
  95. return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
  96. end
  97. end
  98. fetch_remote_original_status
  99. end
  100. def dereference_object!
  101. return unless @object.is_a?(String)
  102. dereferencer = ActivityPub::Dereferencer.new(@object, permitted_origin: @account.uri, signature_account: signed_fetch_account)
  103. @object = dereferencer.object unless dereferencer.object.nil?
  104. end
  105. def signed_fetch_account
  106. return Account.find(@options[:delivered_to_account_id]) if @options[:delivered_to_account_id].present?
  107. first_mentioned_local_account || first_local_follower
  108. end
  109. def first_mentioned_local_account
  110. audience = (as_array(@json['to']) + as_array(@json['cc'])).map { |x| value_or_id(x) }.uniq
  111. local_usernames = audience.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }
  112. .map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
  113. return if local_usernames.empty?
  114. Account.local.where(username: local_usernames).first
  115. end
  116. def first_local_follower
  117. @account.followers.local.first
  118. end
  119. def follow_request_from_object
  120. @follow_request ||= FollowRequest.find_by(target_account: @account, uri: object_uri) unless object_uri.nil?
  121. end
  122. def follow_from_object
  123. @follow ||= ::Follow.find_by(target_account: @account, uri: object_uri) unless object_uri.nil?
  124. end
  125. def fetch_remote_original_status
  126. if object_uri.start_with?('http')
  127. return if ActivityPub::TagManager.instance.local_uri?(object_uri)
  128. ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first)
  129. elsif @object['url'].present?
  130. ::FetchRemoteStatusService.new.call(@object['url'])
  131. end
  132. end
  133. def lock_or_return(key, expire_after = 2.hours.seconds)
  134. yield if redis.set(key, true, nx: true, ex: expire_after)
  135. ensure
  136. redis.del(key)
  137. end
  138. def lock_or_fail(key, expire_after = 15.minutes.seconds)
  139. RedisLock.acquire({ redis: Redis.current, key: key, autorelease: expire_after }) do |lock|
  140. if lock.acquired?
  141. yield
  142. else
  143. raise Mastodon::RaceConditionError
  144. end
  145. end
  146. end
  147. def fetch?
  148. !@options[:delivery]
  149. end
  150. def followed_by_local_accounts?
  151. @account.passive_relationships.exists? || @options[:relayed_through_account]&.passive_relationships&.exists?
  152. end
  153. def requested_through_relay?
  154. @options[:relayed_through_account] && Relay.find_by(inbox_url: @options[:relayed_through_account].inbox_url)&.enabled?
  155. end
  156. def reject_payload!
  157. Rails.logger.info("Rejected #{@json['type']} activity #{@json['id']} from #{@account.uri}#{@options[:relayed_through_account] && "via #{@options[:relayed_through_account].uri}"}")
  158. nil
  159. end
  160. end