aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorlain <lain@soykaf.club>2019-10-16 16:16:39 +0200
committerlain <lain@soykaf.club>2019-10-16 16:16:39 +0200
commit6e4f52f8a2e510273149acbaf629521d1b4aec2e (patch)
tree0f6210fe8dfe0b716f2fc9721df0d5a8f807294f /lib
parente3b4a3e96b2ffbc6d920155cd41687414045d4d6 (diff)
downloadpleroma-6e4f52f8a2e510273149acbaf629521d1b4aec2e.tar.gz
Introduce new ingestion pipeline structure, implement internal Likes with it.
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex35
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex43
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex57
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex28
-rw-r--r--lib/pleroma/web/common_api/common_api.ex29
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex6
6 files changed, 189 insertions, 9 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 364452b5d..f4fc45926 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -18,6 +18,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.SideEffects
alias Pleroma.Web.Streamer
alias Pleroma.Web.WebFinger
alias Pleroma.Workers.BackgroundWorker
@@ -123,6 +125,38 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def increase_poll_votes_if_vote(_create_data), do: :noop
+ @spec common_pipeline(map(), keyword()) :: {:ok, Activity.t(), keyword()} | {:error, any()}
+ def common_pipeline(object, meta) do
+ with {_, {:ok, validated_object, meta}} <-
+ {:validate_object, ObjectValidator.validate(object, meta)},
+ {_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
+ {_, {:ok, %Activity{} = activity, meta}} <-
+ {:persist_object, persist(mrfd_object, meta)},
+ {_, {:ok, %Activity{} = activity, meta}} <-
+ {:execute_side_effects, SideEffects.handle(activity, meta)} do
+ {:ok, activity, meta}
+ else
+ e -> {:error, e}
+ end
+ end
+
+ # TODO rewrite in with style
+ @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+ def persist(object, meta) do
+ local = Keyword.get(meta, :local)
+ {recipients, _, _} = get_recipients(object)
+
+ {:ok, activity} =
+ Repo.insert(%Activity{
+ data: object,
+ local: local,
+ recipients: recipients,
+ actor: object["actor"]
+ })
+
+ {:ok, activity, meta}
+ end
+
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
with nil <- Activity.normalize(map),
map <- lazy_put_activity_defaults(map, fake),
@@ -130,6 +164,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
{:ok, map} <- MRF.filter(map),
{recipients, _, _} = get_recipients(map),
+ # ???
{:fake, false, map, recipients} <- {:fake, fake, map, recipients},
:ok <- Containment.contain_child(map),
{:ok, map, object} <- insert_full_object(map) do
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
new file mode 100644
index 000000000..1787f1510
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -0,0 +1,43 @@
+defmodule Pleroma.Web.ActivityPub.Builder do
+ @moduledoc """
+ This module builds the objects. Meant to be used for creating local objects.
+
+ This module encodes our addressing policies and general shape of our objects.
+ """
+
+ alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.User
+ alias Pleroma.Object
+
+ @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
+ def like(actor, object) do
+ object_actor = User.get_cached_by_ap_id(object.data["actor"])
+
+ # Address the actor of the object, and our actor's follower collection if the post is public.
+ to =
+ if Visibility.is_public?(object) do
+ [actor.follower_address, object.data["actor"]]
+ else
+ [object.data["actor"]]
+ end
+
+ # CC everyone who's been addressed in the object, except ourself and the object actor's
+ # follower collection
+ cc =
+ (object.data["to"] ++ (object.data["cc"] || []))
+ |> List.delete(actor.ap_id)
+ |> List.delete(object_actor.follower_address)
+
+ {:ok,
+ %{
+ "id" => Utils.generate_activity_id(),
+ "actor" => actor.ap_id,
+ "type" => "Like",
+ "object" => object.data["id"],
+ "to" => to,
+ "cc" => cc,
+ "context" => object.data["context"]
+ }, []}
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
new file mode 100644
index 000000000..8ecad0dec
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidator do
+ @moduledoc """
+ This module is responsible for validating an object (which can be an activity)
+ and checking if it is both well formed and also compatible with our view of
+ the system.
+ """
+
+ alias Pleroma.User
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.Utils
+
+ def validate_id(object, meta) do
+ with {_, true} <- {:id_presence, Map.has_key?(object, "id")} do
+ {:ok, object, meta}
+ else
+ e -> {:error, e}
+ end
+ end
+
+ def validate_actor(object, meta) do
+ with {_, %User{}} <- {:actor_validation, User.get_cached_by_ap_id(object["actor"])} do
+ {:ok, object, meta}
+ else
+ e -> {:error, e}
+ end
+ end
+
+ def common_validations(object, meta) do
+ with {_, {:ok, object, meta}} <- {:validate_id, validate_id(object, meta)},
+ {_, {:ok, object, meta}} <- {:validate_actor, validate_actor(object, meta)} do
+ {:ok, object, meta}
+ else
+ e -> {:error, e}
+ end
+ end
+
+ @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+ def validate(object, meta)
+
+ def validate(%{"type" => "Like"} = object, meta) do
+ with {:ok, object, meta} <- common_validations(object, meta),
+ {_, %Object{} = liked_object} <- {:find_liked_object, Object.normalize(object["object"])},
+ {_, nil} <- {:existing_like, Utils.get_existing_like(object["actor"], liked_object)} do
+ {:ok, object, meta}
+ else
+ e -> {:error, e}
+ end
+ end
+
+ def validate(object, meta) do
+ common_validations(object, meta)
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
new file mode 100644
index 000000000..6d3e77a62
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -0,0 +1,28 @@
+defmodule Pleroma.Web.ActivityPub.SideEffects do
+ @moduledoc """
+ This module looks at an inserted object and executes the side effects that it
+ implies. For example, a `Like` activity will increase the like count on the
+ liked object, a `Follow` activity will add the user to the follower
+ collection, and so on.
+ """
+ alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Object
+ alias Pleroma.Notification
+
+ def handle(object, meta \\ [])
+
+ # Tasks this handles:
+ # - Add like to object
+ # - Set up notification
+ def handle(%{data: %{"type" => "Like"}} = object, meta) do
+ liked_object = Object.get_by_ap_id(object.data["object"])
+ Utils.add_like_to_object(object, liked_object)
+ Notification.create_notifications(object)
+ {:ok, object, meta}
+ end
+
+ # Nothing to do
+ def handle(object, meta) do
+ {:ok, object, meta}
+ end
+end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 386408d51..466beb724 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.ThreadMute
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
@@ -17,6 +18,7 @@ defmodule Pleroma.Web.CommonAPI do
import Pleroma.Web.CommonAPI.Utils
require Pleroma.Constants
+ require Logger
def follow(follower, followed) do
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@@ -98,16 +100,31 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- def favorite(id_or_ap_id, user) do
- with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
- object <- Object.normalize(activity),
- nil <- Utils.get_existing_like(user.ap_id, object) do
- ActivityPub.like(user, object)
+ @spec favorite(User.t(), binary()) :: {:ok, Activity.t()} | {:error, any()}
+ def favorite(%User{} = user, id) do
+ with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
+ {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
+ {_, {:ok, %Activity{} = activity, _meta}} <-
+ {:common_pipeline,
+ ActivityPub.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
+ {:ok, activity}
else
- _ -> {:error, dgettext("errors", "Could not favorite")}
+ e ->
+ Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
+ {:error, dgettext("errors", "Could not favorite")}
end
end
+ # def favorite(id_or_ap_id, user) do
+ # with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
+ # object <- Object.normalize(activity),
+ # nil <- Utils.get_existing_like(user.ap_id, object) do
+ # ActivityPub.like(user, object)
+ # else
+ # _ -> {:error, dgettext("errors", "Could not favorite")}
+ # end
+ # end
+
def unfavorite(id_or_ap_id, user) do
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id) do
object = Object.normalize(activity)
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index e5d016f63..4b4482aa8 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -201,9 +201,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/favourite"
- def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
+ def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
+ with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),
+ %Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
end
end