Unilateral remove from followers (#232)
authorfloatingghost <hannah@coffee-and-dreams.uk>
Wed, 19 Oct 2022 10:01:14 +0000 (10:01 +0000)
committerfloatingghost <hannah@coffee-and-dreams.uk>
Wed, 19 Oct 2022 10:01:14 +0000 (10:01 +0000)
from https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3647/

Co-authored-by: marcin mikołajczak <git@mkljczk.pl>
Co-authored-by: Tusooa Zhu <tusooa@kazv.moe>
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/232

CHANGELOG.md
lib/pleroma/web/api_spec/operations/account_operation.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/router.ex
test/pleroma/user_test.exs
test/pleroma/web/activity_pub/activity_pub_test.exs
test/pleroma/web/mastodon_api/controllers/account_controller_test.exs

index f1c2e44609ca2ac3552295acbed1f835e6749886..b3e4f371f64905699186560f17023625ad26f292 100644 (file)
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## Added
 - Officially supported docker release
+- Ability to remove followers unilaterally without a block
 
 ## Changes
 - Follows no longer override domain blocks, a domain block is final
index b1f4932ee17b0c30f206c520ea24f57e9676634b..b305dc1ea8fb3d544efe37dae11a4bd0185be856 100644 (file)
@@ -334,6 +334,22 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
     }
   end
 
+  def remove_from_followers_operation do
+    %Operation{
+      tags: ["Account actions"],
+      summary: "Remove from followers",
+      operationId: "AccountController.remove_from_followers",
+      security: [%{"oAuth" => ["follow", "write:follows"]}],
+      description: "Remove the given account from followers",
+      parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
+      responses: %{
+        200 => Operation.response("Relationship", "application/json", AccountRelationship),
+        400 => Operation.response("Error", "application/json", ApiError),
+        404 => Operation.response("Error", "application/json", ApiError)
+      }
+    }
+  end
+
   def note_operation do
     %Operation{
       tags: ["Account actions"],
index ed7fb802ac16be008b413a42b1f9d10660cef5f0..946c8544fe2fbd64a5c8e38598816cecf3fd5f27 100644 (file)
@@ -76,15 +76,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow]
+    %{scopes: ["follow", "write:follows"]}
+    when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers]
   )
 
   plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
 
   plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
 
-  @relationship_actions [:follow, :unfollow]
-  @needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a
+  @relationship_actions [:follow, :unfollow, :remove_from_followers]
+  @needs_account ~W(followers following lists follow unfollow mute unmute block unblock note remove_from_followers)a
 
   plug(
     RateLimiter,
@@ -447,6 +448,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
     end
   end
 
+  @doc "POST /api/v1/accounts/:id/remove_from_followers"
+  def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
+    {:error, "Can not unfollow yourself"}
+  end
+
+  def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do
+    with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
+      render(conn, "relationship.json", user: followed, target: follower)
+    else
+      nil ->
+        render_error(conn, :not_found, "Record not found")
+    end
+  end
+
   @doc "POST /api/v1/follows"
   def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
     case User.get_cached_by_nickname(uri) do
index 838599c4d65158e5b6e0f9aa9e5a87dd5e4c152a..71a9e4d29febdcac4223d043bd874eaa1c6e6066 100644 (file)
@@ -509,6 +509,7 @@ defmodule Pleroma.Web.Router do
     post("/accounts/:id/mute", AccountController, :mute)
     post("/accounts/:id/unmute", AccountController, :unmute)
     post("/accounts/:id/note", AccountController, :note)
+    post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers)
 
     get("/conversations", ConversationController, :index)
     post("/conversations/:id/read", ConversationController, :mark_as_read)
index 0272e31429b82c72bfd020041024f35096aef699..f11aec1364f2ae1d2256a39e99eca62f6c9e92fa 100644 (file)
@@ -311,7 +311,7 @@ defmodule Pleroma.UserTest do
   describe "unfollow/2" do
     setup do: clear_config([:instance, :external_user_synchronization])
 
-    test "unfollow with syncronizes external user" do
+    test "unfollow with synchronizes external user" do
       clear_config([:instance, :external_user_synchronization], true)
 
       followed =
@@ -2260,7 +2260,7 @@ defmodule Pleroma.UserTest do
       assert other_user.follower_count == 1
     end
 
-    test "syncronizes the counters with the remote instance for the followed when enabled" do
+    test "synchronizes the counters with the remote instance for the followed when enabled" do
       clear_config([:instance, :external_user_synchronization], false)
 
       user = insert(:user)
@@ -2282,7 +2282,7 @@ defmodule Pleroma.UserTest do
       assert other_user.follower_count == 437
     end
 
-    test "syncronizes the counters with the remote instance for the follower when enabled" do
+    test "synchronizes the counters with the remote instance for the follower when enabled" do
       clear_config([:instance, :external_user_synchronization], false)
 
       user = insert(:user)
index ec562ac7b2e8abcd9caf57c89eb5efd1a0e2334d..c7b3334f3dac0339db9917d2a55cf75b10297979 100644 (file)
@@ -1632,7 +1632,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   end
 
   describe "fetch_follow_information_for_user" do
-    test "syncronizes following/followers counters" do
+    test "synchronizes following/followers counters" do
       user =
         insert(:user,
           local: false,
index dcdff6c094472e9e26011be83638ef3fc6430f7c..5b4f124021a1fa8620b9f1f83eb329486cbcde5d 100644 (file)
@@ -1921,4 +1921,48 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
              |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
              |> json_response_and_validate_schema(200)
   end
+
+  describe "remove from followers" do
+    setup do: oauth_access(["follow"])
+
+    test "removing user from followers", %{conn: conn, user: user} do
+      %{id: other_user_id} = other_user = insert(:user)
+
+      CommonAPI.follow(other_user, user)
+
+      assert %{"id" => ^other_user_id, "followed_by" => false} =
+               conn
+               |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
+               |> json_response_and_validate_schema(200)
+
+      refute User.following?(other_user, user)
+    end
+
+    test "removing remote user from followers", %{conn: conn, user: user} do
+      %{id: other_user_id} = other_user = insert(:user, local: false)
+
+      CommonAPI.follow(other_user, user)
+
+      assert User.following?(other_user, user)
+
+      assert %{"id" => ^other_user_id, "followed_by" => false} =
+               conn
+               |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
+               |> json_response_and_validate_schema(200)
+
+      refute User.following?(other_user, user)
+    end
+
+    test "removing user from followers errors", %{user: user, conn: conn} do
+      # self remove
+      conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers")
+
+      assert %{"error" => "Can not unfollow yourself"} =
+               json_response_and_validate_schema(conn_res, 400)
+
+      # remove non existing user
+      conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers")
+      assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
+    end
+  end
 end