A module to handle coding from internal to wire ActivityPub and back.
"""
alias Pleroma.Activity
- alias Pleroma.User
alias Pleroma.Object
alias Pleroma.Repo
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.Visibility
import Ecto.Query
end
def fix_addressing_list(map, field) do
- if is_binary(map[field]) do
- map
- |> Map.put(field, [map[field]])
- else
- map
+ cond do
+ is_binary(map[field]) ->
+ Map.put(map, field, [map[field]])
+
+ is_nil(map[field]) ->
+ Map.put(map, field, [])
+
+ true ->
+ map
end
end
|> fix_explicit_addressing(explicit_mentions)
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
+ "https://www.w3.org/ns/activitystreams#Public" in cc ->
+ to = to ++ [followers_collection]
+ Map.put(object, "to", to)
+
+ "https://www.w3.org/ns/activitystreams#Public" in to ->
+ cc = cc ++ [followers_collection]
+ Map.put(object, "cc", cc)
+
+ true ->
+ object
+ end
+ else
+ object
+ end
+ end
+
+ def fix_implicit_addressing(object, _), do: object
+
def fix_addressing(object) do
+ %User{} = user = User.get_or_fetch_by_ap_id(object["actor"])
+ followers_collection = User.ap_followers(user)
+
object
|> fix_addressing_list("to")
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
|> fix_explicit_addressing
+ |> fix_implicit_addressing(followers_collection)
end
def fix_actor(%{"attributedTo" => actor} = object) do
end
end
+ # Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them
+ # with nil ID.
+ def handle_incoming(%{"type" => "Flag", "object" => objects, "actor" => actor} = data) do
+ with context <- data["context"] || Utils.generate_context_id(),
+ content <- data["content"] || "",
+ %User{} = actor <- User.get_cached_by_ap_id(actor),
+
+ # Reduce the object list to find the reported user.
+ %User{} = account <-
+ Enum.reduce_while(objects, nil, fn ap_id, _ ->
+ with %User{} = user <- User.get_cached_by_ap_id(ap_id) do
+ {:halt, user}
+ else
+ _ -> {:cont, nil}
+ end
+ end),
+
+ # Remove the reported user from the object list.
+ statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
+ params = %{
+ actor: actor,
+ context: context,
+ account: account,
+ statuses: statuses,
+ content: content,
+ additional: %{
+ "cc" => [account.ap_id]
+ }
+ }
+
+ ActivityPub.flag(params)
+ end
+ end
+
# disallow objects with bogus IDs
def handle_incoming(%{"id" => nil}), do: :error
def handle_incoming(%{"id" => ""}), do: :error
with actor <- get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
- public <- ActivityPub.is_public?(data),
+ public <- Visibility.is_public?(data),
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
{:ok, activity}
else
if object = Object.normalize(id), do: {:ok, object}, else: nil
end
- def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) when is_binary(inReplyTo) do
- with false <- String.starts_with?(inReplyTo, "http"),
- {:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do
- Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo)
+ def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
+ with false <- String.starts_with?(in_reply_to, "http"),
+ {:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
+ Map.put(object, "inReplyTo", replied_to_object["external_url"] || in_reply_to)
else
_e -> object
end
def prepare_outgoing(%{"type" => _type} = data) do
data =
data
+ |> strip_internal_fields
|> maybe_fix_object_url
|> Map.merge(Utils.make_json_ld_header())
end
def add_attributed_to(object) do
- attributedTo = object["attributedTo"] || object["actor"]
+ attributed_to = object["attributedTo"] || object["actor"]
object
- |> Map.put("attributedTo", attributedTo)
+ |> Map.put("attributedTo", attributed_to)
end
def add_likes(%{"id" => id, "like_count" => likes} = object) do
"announcements",
"announcement_count",
"emoji",
- "context_id"
+ "context_id",
+ "deleted_activity_id"
])
end
defp strip_internal_tags(object), do: object
defp user_upgrade_task(user) do
- old_follower_address = User.ap_followers(user)
+ # we pass a fake user so that the followers collection is stripped away
+ old_follower_address = User.ap_followers(%User{nickname: user.nickname})
q =
from(