alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.Federator
alias Pleroma.Workers.TransmogrifierWorker
|> fix_attachments()
|> fix_context()
|> fix_in_reply_to(options)
+ |> fix_quote_url(options)
|> fix_emoji()
|> fix_tag()
|> fix_content_map()
|> Map.put("cc", final_cc)
end
- # if as:Public is addressed, then make sure the followers collection is also addressed
- # so that the activities will be delivered to local users.
- def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
- recipients = to ++ cc
-
- if followers_collection not in recipients do
- cond do
- Pleroma.Constants.as_public() in cc ->
- to = to ++ [followers_collection]
- Map.put(object, "to", to)
-
- Pleroma.Constants.as_public() in to ->
- cc = cc ++ [followers_collection]
- Map.put(object, "cc", cc)
-
- true ->
- object
- end
- else
- object
- end
- end
-
def fix_addressing(object) do
{:ok, %User{follower_address: follower_collection}} =
object
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
|> fix_explicit_addressing(follower_collection)
- |> fix_implicit_addressing(follower_collection)
+ |> CommonFixes.fix_implicit_addressing(follower_collection)
end
def fix_actor(%{"attributedTo" => actor} = object) do
|> Map.drop(["conversation", "inReplyToAtomUri"])
else
e ->
- Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
+ Logger.warn("Couldn't fetch reply@#{inspect(in_reply_to_id)}, error: #{inspect(e)}")
object
end
else
def fix_in_reply_to(object, _options), do: object
+ def fix_quote_url(object, options \\ [])
+
+ def fix_quote_url(%{"quoteUri" => quote_url} = object, options)
+ when not is_nil(quote_url) do
+ depth = (options[:depth] || 0) + 1
+
+ if Federator.allowed_thread_distance?(depth) do
+ with {:ok, quoted_object} <- get_obj_helper(quote_url, options),
+ %Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do
+ object
+ |> Map.put("quoteUri", quoted_object.data["id"])
+ else
+ e ->
+ Logger.warn("Couldn't fetch quote@#{inspect(quote_url)}, error: #{inspect(e)}")
+ object
+ end
+ else
+ object
+ end
+ end
+
+ # Soapbox
+ def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do
+ object
+ |> Map.put("quoteUri", quote_url)
+ |> Map.delete("quoteUrl")
+ |> fix_quote_url(options)
+ end
+
+ # Old Fedibird (bug)
+ # https://github.com/fedibird/mastodon/issues/9
+ def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do
+ object
+ |> Map.put("quoteUri", quote_url)
+ |> Map.delete("quoteURL")
+ |> fix_quote_url(options)
+ end
+
+ def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do
+ object
+ |> Map.put("quoteUri", quote_url)
+ |> Map.delete("_misskey_quote")
+ |> fix_quote_url(options)
+ end
+
+ def fix_quote_url(object, _), do: object
+
defp prepare_in_reply_to(in_reply_to) do
cond do
is_bitstring(in_reply_to) ->
def fix_tag(object), do: object
# content map usually only has one language so this will do for now.
- def fix_content_map(%{"contentMap" => content_map} = object) do
+ def fix_content_map(%{"contentMap" => content_map} = object) when is_map(content_map) do
content_groups = Map.to_list(content_map)
- {_, content} = Enum.at(content_groups, 0)
- Map.put(object, "content", content)
+ if Enum.empty?(content_groups) do
+ object
+ else
+ {_, content} = Enum.at(content_groups, 0)
+
+ Map.put(object, "content", content)
+ end
end
def fix_content_map(object), do: object
def handle_incoming(
%{
"type" => "Like",
- "_misskey_reaction" => reaction,
- "tag" => _
- } = data,
- options
- ) do
- data
- |> Map.put("type", "EmojiReact")
- |> Map.put("content", reaction)
- |> handle_incoming(options)
- end
-
- def handle_incoming(
- %{
- "type" => "Like",
- "_misskey_reaction" => reaction
+ "content" => reaction
} = data,
options
) do
- data
- |> Map.put("type", "EmojiReact")
- |> Map.put("content", reaction)
- |> handle_incoming(options)
+ if Pleroma.Emoji.is_unicode_emoji?(reaction) or Pleroma.Emoji.matches_shortcode?(reaction) do
+ data
+ |> Map.put("type", "EmojiReact")
+ |> handle_incoming(options)
+ else
+ data
+ |> Map.delete("content")
+ |> handle_incoming(options)
+ end
end
def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
options
)
- when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page} do
+ when objtype in ~w{Question Answer Audio Video Event Article Note Page} do
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
object =
|> strip_internal_fields()
|> fix_type(fetch_options)
|> fix_in_reply_to(fetch_options)
+ |> fix_quote_url(fetch_options)
+
+ # Only change the Create's context if the object's context has been modified.
+ data =
+ if data["object"]["context"] != object["context"] do
+ data
+ |> Map.put("object", object)
+ |> Map.put("context", object["context"])
+ else
+ Map.put(data, "object", object)
+ end
- data = Map.put(data, "object", object)
options = Keyword.put(options, :local, false)
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
def set_reply_to_uri(obj), do: obj
+ def set_quote_url(%{"quoteUri" => quote} = object) when is_binary(quote) do
+ Map.put(object, "quoteUrl", quote)
+ end
+
+ def set_quote_url(obj), do: obj
+
@doc """
Serialized Mastodon-compatible `replies` collection containing _self-replies_.
Based on Mastodon's ActivityPub::NoteSerializer#replies.
|> prepare_attachments
|> set_conversation
|> set_reply_to_uri
+ |> set_quote_url()
|> set_replies
|> strip_internal_fields
|> strip_internal_tags
|> set_type
+ |> maybe_process_history
+ end
+
+ defp maybe_process_history(%{"formerRepresentations" => %{"orderedItems" => history}} = object) do
+ processed_history =
+ Enum.map(
+ history,
+ fn
+ item when is_map(item) -> prepare_object(item)
+ item -> item
+ end
+ )
+
+ put_in(object, ["formerRepresentations", "orderedItems"], processed_history)
+ end
+
+ defp maybe_process_history(object) do
+ object
end
# @doc
{:ok, data}
end
+ def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
+ when objtype in Pleroma.Constants.updatable_object_types() do
+ object =
+ object
+ |> prepare_object
+
+ data =
+ data
+ |> Map.put("object", object)
+ |> Map.merge(Utils.make_json_ld_header())
+ |> Map.delete("bcc")
+
+ {:ok, data}
+ end
+
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
object =
object_id
Map.put(data, "object", external_url)
else
{:fetch, e} ->
- Logger.error("Couldn't fetch #{object} #{inspect(e)}")
+ Logger.error("Couldn't fetch fixed_object@#{object} #{inspect(e)}")
data
_ ->
Map.put(object, "attributedTo", attributed_to)
end
- # TODO: Revisit this
- def prepare_attachments(%{"type" => "ChatMessage"} = object), do: object
-
def prepare_attachments(object) do
attachments =
object