alias Pleroma.Activity
alias Pleroma.EarmarkRenderer
alias Pleroma.FollowingRelationship
+ alias Pleroma.Maps
+ alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
|> Map.put("conversation", context)
end
- defp add_if_present(map, _key, nil), do: map
-
- defp add_if_present(map, key, value) do
- Map.put(map, key, value)
- end
-
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
attachments =
Enum.map(attachment, fn data ->
attachment_url =
%{"href" => href}
- |> add_if_present("mediaType", media_type)
- |> add_if_present("type", Map.get(url || %{}, "type"))
+ |> Maps.put_if_present("mediaType", media_type)
+ |> Maps.put_if_present("type", Map.get(url || %{}, "type"))
%{"url" => [attachment_url]}
- |> add_if_present("mediaType", media_type)
- |> add_if_present("type", data["type"])
- |> add_if_present("name", data["name"])
+ |> Maps.put_if_present("mediaType", media_type)
+ |> Maps.put_if_present("type", data["type"])
+ |> Maps.put_if_present("name", data["name"])
end)
Map.put(object, "attachment", attachments)
User.get_cached_by_ap_id(Containment.get_actor(%{"actor" => followed})),
{:ok, %User{} = follower} <-
User.get_or_fetch_by_ap_id(Containment.get_actor(%{"actor" => follower})),
- {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
+ {:ok, activity} <-
+ ActivityPub.follow(follower, followed, id, false, skip_notify_and_stream: true) do
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
{_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
{_, false} <- {:user_locked, User.locked?(followed)},
:noop
end
+ ActivityPub.notify_and_stream(activity)
{:ok, activity}
else
_e ->
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do
+ User.update_follower_count(followed)
+ User.update_following_count(follower)
+
+ Notification.update_notification_type(followed, follow_activity)
+
ActivityPub.accept(%{
to: follow_activity.data["to"],
type: "Accept",
activity_id: id
})
else
- _e -> :error
+ _e ->
+ :error
end
end
|> handle_incoming(options)
end
- def handle_incoming(%{"type" => "Like"} = data, _options) do
- with :ok <- ObjectValidator.fetch_actor_and_object(data),
- {:ok, activity, _meta} <-
- Pipeline.common_pipeline(data, local: false) do
- {:ok, activity}
- else
- e -> {:error, e}
- end
- end
-
def handle_incoming(
- %{
- "type" => "EmojiReact",
- "object" => object_id,
- "actor" => _actor,
- "id" => id,
- "content" => emoji
- } = data,
+ %{"type" => "Create", "object" => %{"type" => "ChatMessage"}} = data,
_options
) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id),
- {:ok, activity, _object} <-
- ActivityPub.react_with_emoji(actor, object, emoji, activity_id: id, local: false) do
+ with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
+ {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
- else
- _e -> :error
end
end
- def handle_incoming(
- %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
- _options
- ) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_embedded_obj_helper(object_id, actor),
- public <- Visibility.is_public?(data),
- {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
+ def handle_incoming(%{"type" => type} = data, _options)
+ when type in ["Like", "EmojiReact", "Announce"] do
+ with :ok <- ObjectValidator.fetch_actor_and_object(data),
+ {:ok, activity, _meta} <-
+ Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
else
- _e -> :error
+ e -> {:error, e}
end
end
end
end
- # TODO: We presently assume that any actor on the same origin domain as the object being
- # deleted has the rights to delete that object. A better way to validate whether or not
- # the object should be deleted is to refetch the object URI, which should return either
- # an error or a tombstone. This would allow us to verify that a deletion actually took
- # place.
def handle_incoming(
- %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data,
+ %{"type" => "Delete"} = data,
_options
) do
- object_id = Utils.get_ap_id(object_id)
-
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id),
- :ok <- Containment.contain_origin(actor.ap_id, object.data),
- {:ok, activity} <-
- ActivityPub.delete(object, local: false, activity_id: id, actor: actor.ap_id) do
+ with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
else
- nil ->
- case User.get_cached_by_ap_id(object_id) do
- %User{ap_id: ^actor} = user ->
- User.delete(user)
-
- nil ->
- :error
+ {:error, {:validate_object, _}} = e ->
+ # Check if we have a create activity for this
+ with {:ok, object_id} <- Types.ObjectID.cast(data["object"]),
+ %Activity{data: %{"actor" => actor}} <-
+ Activity.create_by_object_ap_id(object_id) |> Repo.one(),
+ # We have one, insert a tombstone and retry
+ {:ok, tombstone_data, _} <- Builder.tombstone(actor, object_id),
+ {:ok, _tombstone} <- Object.create(tombstone_data) do
+ handle_incoming(data)
+ else
+ _ -> e
end
-
- _e ->
- :error
- end
- end
-
- def handle_incoming(
- %{
- "type" => "Undo",
- "object" => %{"type" => "Announce", "object" => object_id},
- "actor" => _actor,
- "id" => id
- } = data,
- _options
- ) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id),
- {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do
- {:ok, activity}
- else
- _e -> :error
end
end
end
end
- def handle_incoming(
- %{
- "type" => "Undo",
- "object" => %{"type" => "Block", "object" => blocked},
- "actor" => blocker,
- "id" => id
- } = _data,
- _options
- ) do
- with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
- {:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker),
- {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do
- User.unblock(blocker, blocked)
- {:ok, activity}
- else
- _e -> :error
- end
- end
-
- def handle_incoming(
- %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data,
- _options
- ) do
- with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
- {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker),
- {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
- User.unfollow(blocker, blocked)
- User.block(blocker, blocked)
- {:ok, activity}
- else
- _e -> :error
- end
- end
-
def handle_incoming(
%{
"type" => "Undo",
} = data,
_options
)
- when type in ["Like", "EmojiReact"] do
+ when type in ["Like", "EmojiReact", "Announce", "Block"] do
with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
end
end
end
+ def handle_incoming(
+ %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data,
+ _options
+ ) do
+ with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
+ {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker),
+ {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
+ User.unfollow(blocker, blocked)
+ User.block(blocker, blocked)
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
def handle_incoming(
%{
"type" => "Move",
Map.put(object, "tag", tags)
end
+ # TODO These should be added on our side on insertion, it doesn't make much
+ # sense to regenerate these all the time
def add_mention_tags(object) do
- {enabled_receivers, disabled_receivers} = Utils.get_notified_from_object(object)
- potential_receivers = enabled_receivers ++ disabled_receivers
- mentions = Enum.map(potential_receivers, &build_mention_tag/1)
+ to = object["to"] || []
+ cc = object["cc"] || []
+ mentioned = User.get_users_from_set(to ++ cc, local_only: false)
+
+ mentions = Enum.map(mentioned, &build_mention_tag/1)
tags = object["tag"] || []
Map.put(object, "tag", tags ++ mentions)
Map.put(object, "conversation", object["context"])
end
+ def set_sensitive(%{"sensitive" => true} = object) do
+ object
+ end
+
def set_sensitive(object) do
tags = object["tag"] || []
Map.put(object, "sensitive", "nsfw" in tags)
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