2019-10-02 01:59:43 +00:00
|
|
|
defmodule Clacks.Inbox do
|
2019-10-02 13:47:04 +00:00
|
|
|
require Logger
|
2020-05-24 20:26:10 +00:00
|
|
|
alias Clacks.{Repo, Activity, Object, Actor, ActivityPub, Notification}
|
2019-10-02 01:59:43 +00:00
|
|
|
|
2021-08-27 02:19:46 +00:00
|
|
|
@spec store_object(map()) :: {:ok, Object.t()}
|
|
|
|
defp store_object(%{"id" => ap_id} = object) do
|
|
|
|
changeset = Object.changeset(Object.get_cached_by_ap_id(ap_id) || %Object{}, %{data: object})
|
|
|
|
{:ok, object} = Repo.insert_or_update(changeset)
|
|
|
|
{:ok, object}
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec store_activity(map(), boolean()) :: {:ok, Activity.t()} | {:error, term()}
|
2020-04-26 22:55:44 +00:00
|
|
|
defp store_activity(%{"actor" => actor, "id" => ap_id} = activity, local \\ false)
|
|
|
|
when is_binary(actor) do
|
2021-08-26 23:20:27 +00:00
|
|
|
# remove the embedded object (if there is one) from the activity
|
2021-08-25 20:11:08 +00:00
|
|
|
activity_without_embedded_object =
|
|
|
|
case Map.get(activity, "object") do
|
|
|
|
%{"id" => object_id} ->
|
2021-08-26 23:20:27 +00:00
|
|
|
# todo: this assumes we already have stored the object
|
2021-08-25 20:11:08 +00:00
|
|
|
Map.put(activity, "object", object_id)
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
activity
|
|
|
|
end
|
|
|
|
|
2019-10-02 01:59:43 +00:00
|
|
|
changeset =
|
2020-04-26 22:55:44 +00:00
|
|
|
Activity.changeset(Activity.get_cached_by_ap_id(ap_id) || %Activity{}, %{
|
2021-08-25 20:11:08 +00:00
|
|
|
data: activity_without_embedded_object,
|
2019-10-02 13:47:04 +00:00
|
|
|
local: local,
|
2021-08-26 23:20:27 +00:00
|
|
|
actor_ap_id: actor
|
2019-10-02 01:59:43 +00:00
|
|
|
})
|
|
|
|
|
2020-05-24 20:26:10 +00:00
|
|
|
case Repo.insert_or_update(changeset) do
|
|
|
|
{:ok, activity} ->
|
|
|
|
Notification.process_notifications_for_incoming(activity)
|
|
|
|
|
|
|
|
{:ok, activity}
|
|
|
|
|
|
|
|
{:error, reason} ->
|
|
|
|
{:error, reason}
|
|
|
|
end
|
2019-10-02 13:47:04 +00:00
|
|
|
end
|
|
|
|
|
2019-10-02 14:28:06 +00:00
|
|
|
@spec handle(activity :: map()) :: :ok | {:error, reason :: any()}
|
|
|
|
|
|
|
|
def handle(%{"type" => "Create", "object" => object} = activity) do
|
2020-04-27 02:25:43 +00:00
|
|
|
object = Clacks.Inbox.Transformer.restrict_incoming_object(object)
|
2019-10-02 13:47:04 +00:00
|
|
|
|
2021-08-27 02:19:46 +00:00
|
|
|
with {:ok, _object} <- store_object(object),
|
2020-04-27 02:25:43 +00:00
|
|
|
{:ok, _activity} <- store_activity(activity) do
|
|
|
|
:ok
|
|
|
|
else
|
2019-10-02 13:49:59 +00:00
|
|
|
{:error, changeset} ->
|
2020-04-27 02:25:43 +00:00
|
|
|
Logger.error("Couldn't store object or activity: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store activity"}
|
2019-10-02 13:49:59 +00:00
|
|
|
end
|
2019-10-02 01:59:43 +00:00
|
|
|
end
|
|
|
|
|
2019-10-02 14:28:06 +00:00
|
|
|
def handle(%{"type" => "Follow", "object" => followed_id, "actor" => follower_id} = activity)
|
2019-10-02 01:59:43 +00:00
|
|
|
when is_binary(followed_id) do
|
|
|
|
followed = Actor.get_by_ap_id(followed_id)
|
2019-10-02 13:47:04 +00:00
|
|
|
follower = Actor.get_by_ap_id(follower_id)
|
|
|
|
|
|
|
|
store_activity(activity)
|
|
|
|
|
2020-04-26 20:25:48 +00:00
|
|
|
new_followers = [follower_id | followed.followers] |> Enum.uniq()
|
|
|
|
changeset = Actor.changeset(followed, %{followers: new_followers})
|
2019-10-02 13:47:04 +00:00
|
|
|
|
|
|
|
case Repo.update(changeset) do
|
|
|
|
{:error, changeset} ->
|
2020-04-26 22:55:44 +00:00
|
|
|
Logger.error("Couldn't store updated followers: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store updated followers"}
|
2019-10-02 13:47:04 +00:00
|
|
|
|
|
|
|
{:ok, _followed} ->
|
|
|
|
accept = ActivityPub.accept_follow(activity)
|
|
|
|
|
|
|
|
case store_activity(accept, true) do
|
|
|
|
{:error, changeset} ->
|
|
|
|
Logger.error("Couldn't store Accept activity: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store Accept activity"}
|
|
|
|
|
2021-08-25 20:11:08 +00:00
|
|
|
{:ok, accept} ->
|
2019-10-02 13:47:04 +00:00
|
|
|
ActivityPub.Federator.federate(accept, follower.data["inbox"])
|
|
|
|
end
|
|
|
|
end
|
2019-10-02 01:59:43 +00:00
|
|
|
end
|
2019-10-02 14:28:06 +00:00
|
|
|
|
2020-04-26 22:55:44 +00:00
|
|
|
def handle(
|
|
|
|
%{
|
|
|
|
"type" => "Accept",
|
|
|
|
"actor" => followee_id,
|
|
|
|
"object" => %{"type" => "Follow", "id" => follow_activity_id, "actor" => follower_id}
|
|
|
|
} = activity
|
|
|
|
) do
|
|
|
|
followee = Actor.get_by_ap_id(followee_id)
|
|
|
|
|
|
|
|
store_activity(activity)
|
|
|
|
|
|
|
|
follow_activity = Activity.get_cached_by_ap_id(follow_activity_id)
|
|
|
|
|
|
|
|
changeset =
|
|
|
|
Activity.changeset(follow_activity, %{
|
|
|
|
data: %{follow_activity.data | "state" => "accepted"}
|
|
|
|
})
|
|
|
|
|
|
|
|
case Repo.update(changeset) do
|
|
|
|
{:error, changeset} ->
|
|
|
|
Logger.error("Couldn't store updated Follow activity: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store updated Follow activity"}
|
|
|
|
|
|
|
|
{:ok, _follow_activity} ->
|
|
|
|
new_followers = [follower_id | followee.followers] |> Enum.uniq()
|
|
|
|
changeset = Actor.changeset(followee, %{followers: new_followers})
|
|
|
|
|
|
|
|
case Repo.update(changeset) do
|
|
|
|
{:error, changeset} ->
|
|
|
|
Logger.error("Couldn't store updated followers: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store updated followers"}
|
|
|
|
|
|
|
|
{:ok, _followee} ->
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-27 02:28:48 +00:00
|
|
|
def handle(
|
|
|
|
%{
|
|
|
|
"type" => "Undo",
|
|
|
|
"actor" => follower_id,
|
|
|
|
"object" => %{"type" => "Follow", "object" => followee_id}
|
|
|
|
} = activity
|
|
|
|
)
|
|
|
|
when is_binary(followee_id) do
|
|
|
|
followee = Actor.get_by_ap_id(followee_id)
|
|
|
|
|
|
|
|
store_activity(activity)
|
|
|
|
|
|
|
|
changeset =
|
|
|
|
Actor.changeset(followee, %{
|
|
|
|
followers: List.delete(followee.followers, follower_id)
|
|
|
|
})
|
|
|
|
|
|
|
|
case Repo.update(changeset) do
|
|
|
|
{:error, changeset} ->
|
|
|
|
Logger.error("Couldn't store updated followers: #{inspect(changeset)}")
|
|
|
|
{:error, "Couldn't store updated followers"}
|
|
|
|
|
|
|
|
{:ok, _followee} ->
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-02 14:28:06 +00:00
|
|
|
# as a fallback, just store the activity
|
|
|
|
def handle(activity) do
|
2020-04-26 23:28:22 +00:00
|
|
|
Logger.debug("Unhandled activity: #{inspect(activity)}")
|
|
|
|
|
2019-10-02 14:28:06 +00:00
|
|
|
case store_activity(activity) do
|
|
|
|
{:error, changeset} ->
|
|
|
|
Logger.error("Could not store activity: #{inspect(changeset)}")
|
|
|
|
{:error, "Could not store activity"}
|
|
|
|
|
|
|
|
{:ok, _activity} ->
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
end
|
2019-10-02 01:59:43 +00:00
|
|
|
end
|