diff --git a/lib/vonbraun/activity_pub/handler/undo.ex b/lib/vonbraun/activity_pub/handler/undo.ex index 026786c..6e4f591 100644 --- a/lib/vonbraun/activity_pub/handler/undo.ex +++ b/lib/vonbraun/activity_pub/handler/undo.ex @@ -1,11 +1,35 @@ defmodule Vonbraun.ActivityPub.Handler.Undo do @behaviour Vonbraun.ActivityPub.HandlerBehaviour + require Logger + alias Vonbraun.Ecto.Schema.Actor + alias Vonbraun.ActivityPub.Object + import Vonbraun.ActivityPub.Handler.Accept, only: [extract_follow_object_actor: 1] + @verb "Undo" def type, do: @verb # Lots of different kinds of things can be undone. + + def handle(%{"type" => @verb, "actor" => actor_id, "object" => object = %{"type" => "Follow"}}) do + with {:actor, {:ok, follow_actor_id}} <- {:actor, extract_follow_object_actor(object)}, + {:match, true} <- {:match, follow_actor_id == Object.my_id()}, + {:asked, {:ok, %Actor{:following_state => nil}}} <- + {:asked, Actor.remove_follower(actor_id)} do + {:ok, :removed_follower} + else + {:asked, {:error, error}} -> + {:error, error} + + {:match, false} -> + {:ok, :unauthorized} + + {:actor, {:error, _error}} -> + {:ok, :unauthorized} + end + end + def handle(%{"type" => @verb}, %{}) do {:ok, :unknown} end diff --git a/lib/vonbraun/activity_pub/handler_behaviour.ex b/lib/vonbraun/activity_pub/handler_behaviour.ex index 541d29b..25f91e4 100644 --- a/lib/vonbraun/activity_pub/handler_behaviour.ex +++ b/lib/vonbraun/activity_pub/handler_behaviour.ex @@ -1,5 +1,5 @@ defmodule Vonbraun.ActivityPub.HandlerBehaviour do @callback type() :: String.t() @callback handle(activity :: map(), actor_object :: map()) :: - :ok | {:ok, atom()} | {:error, any()} + {:ok, atom()} | {:error, any()} end diff --git a/lib/vonbraun/ecto/schema/actor.ex b/lib/vonbraun/ecto/schema/actor.ex index 7f303ad..a3537c8 100644 --- a/lib/vonbraun/ecto/schema/actor.ex +++ b/lib/vonbraun/ecto/schema/actor.ex @@ -49,6 +49,11 @@ defmodule Vonbraun.Ecto.Schema.Actor do end end + @doc """ + Mark a remote actor as following me, or pending follow, depending on if + approval of followers is enabled. Tries to intelligently ignore repeat and + spam follows. + """ @spec maybe_add_follower(String.t()) :: {:ok, __MODULE__.t()} | {:error, Changeset.t()} def maybe_add_follower(id) when is_binary(id) do with {:actor, {:ok, actor = %{:follows_me_state => follows_me_state, :blocked => blocked?}}} <- @@ -79,6 +84,9 @@ defmodule Vonbraun.Ecto.Schema.Actor do end end + @doc """ + Mark a remote actor as no longer following me. + """ @spec remove_follower(String.t()) :: {:ok, __MODULE__.t() | nil} | {:error, Changeset.t()} def remove_follower(id) when is_binary(id) do case Repo.get(__MODULE__, id) do @@ -86,10 +94,11 @@ defmodule Vonbraun.Ecto.Schema.Actor do {:ok, nil} actor = %{:follows_me_state => follows_me_state} -> - if follows_me_state == "accepted" do + if follows_me_state in ["accepted", "pending"] do changeset(actor, %{follows_me_state: nil, follows_me_ts: DateTime.now!("Etc/UTC")}) |> Repo.update() else + # nil or rejected already. {:ok, actor} end end