Add ability to auto-approve followbacks
Resolves: https://akkoma.dev/AkkomaGang/akkoma/issues/148 Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
cc6121a948
commit
23b2e044a5
10 changed files with 81 additions and 14 deletions
|
@ -149,6 +149,7 @@ Has these additional fields under the `pleroma` object:
|
|||
- `favicon`: nullable URL string, Favicon image of the user's instance
|
||||
- `avatar_description`: string, image description for user avatar, defaults to empty string
|
||||
- `header_description`: string, image description for user banner, defaults to empty string
|
||||
- `permit_followback`: boolean, whether follows from followed accounts are auto-approved
|
||||
|
||||
### Source
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ defmodule Pleroma.User do
|
|||
field(:location, :string)
|
||||
field(:language, :string)
|
||||
field(:last_move_at, :naive_datetime)
|
||||
field(:permit_followback, :boolean, default: false)
|
||||
|
||||
belongs_to(:domain, Domain)
|
||||
|
||||
|
@ -588,7 +589,8 @@ def update_changeset(struct, params \\ %{}) do
|
|||
:disclose_client,
|
||||
:birthday,
|
||||
:show_birthday,
|
||||
:location
|
||||
:location,
|
||||
:permit_followback
|
||||
]
|
||||
)
|
||||
|> validate_min_age()
|
||||
|
@ -1143,16 +1145,21 @@ def needs_update?(%User{local: false} = user) do
|
|||
|
||||
def needs_update?(_), do: true
|
||||
|
||||
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||
@spec can_direct_follow_local(User.t(), User.t()) :: true | false
|
||||
def can_direct_follow_local(%User{} = follower, %User{local: true} = followed) do
|
||||
!followed.is_locked || (followed.permit_followback and is_friend_of(follower, followed))
|
||||
end
|
||||
|
||||
@spec maybe_direct_follow(User.t(), User.t()) ::
|
||||
{:ok, User.t(), User.t()} | {:error, String.t()}
|
||||
|
||||
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
|
||||
follow(follower, followed, :follow_pending)
|
||||
end
|
||||
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
|
||||
follow(follower, followed)
|
||||
if can_direct_follow_local(follower, followed) do
|
||||
follow(follower, followed)
|
||||
else
|
||||
follow(follower, followed, :follow_pending)
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
|
||||
|
@ -1526,6 +1533,13 @@ def get_familiar_followers(%User{} = user, %User{} = current_user, page \\ nil)
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
def is_friend_of(%User{} = potential_friend, %User{local: true} = user) do
|
||||
user
|
||||
|> get_friends_query()
|
||||
|> where(id: ^potential_friend.id)
|
||||
|> Repo.exists?()
|
||||
end
|
||||
|
||||
def increase_note_count(%User{} = user) do
|
||||
User
|
||||
|> where(id: ^user.id)
|
||||
|
|
|
@ -101,7 +101,7 @@ def handle(
|
|||
%User{} = followed <- User.get_cached_by_ap_id(followed_user),
|
||||
{_, {:ok, _, _}, _, _} <-
|
||||
{:following, User.follow(follower, followed, :follow_pending), follower, followed} do
|
||||
if followed.local && !followed.is_locked do
|
||||
if followed.local && User.can_direct_follow_local(follower, followed) do
|
||||
{:ok, accept_data, _} = Builder.accept(followed, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true)
|
||||
end
|
||||
|
|
|
@ -822,6 +822,12 @@ defp update_credentials_request do
|
|||
nullable: true,
|
||||
description: "User's birthday will be visible"
|
||||
},
|
||||
permit_followback: %Schema{
|
||||
allOf: [BooleanLike],
|
||||
nullable: true,
|
||||
description:
|
||||
"Whether follow requests from accounts the user is already following are auto-approved (when locked)."
|
||||
},
|
||||
location: %Schema{
|
||||
type: :string,
|
||||
nullable: true,
|
||||
|
@ -858,7 +864,8 @@ defp update_credentials_request do
|
|||
discoverable: false,
|
||||
actor_type: "Person",
|
||||
show_birthday: false,
|
||||
birthday: "2001-02-12"
|
||||
birthday: "2001-02-12",
|
||||
permit_followback: true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -113,7 +113,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
description: "Favicon image of the user's instance"
|
||||
},
|
||||
avatar_description: %Schema{type: :string},
|
||||
header_description: %Schema{type: :string}
|
||||
header_description: %Schema{type: :string},
|
||||
permit_followback: %Schema{type: :boolean}
|
||||
}
|
||||
},
|
||||
source: %Schema{
|
||||
|
@ -208,7 +209,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
"settings_store" => %{
|
||||
"pleroma-fe" => %{}
|
||||
},
|
||||
"birthday" => "2001-02-12"
|
||||
"birthday" => "2001-02-12",
|
||||
"permit_followback" => true
|
||||
},
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
|
|
|
@ -232,6 +232,7 @@ def update_credentials(
|
|||
|> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
|
||||
|> Maps.put_if_present(:avatar_description, params[:avatar_description])
|
||||
|> Maps.put_if_present(:header_description, params[:header_description])
|
||||
|> Maps.put_if_present(:permit_followback, params[:permit_followback])
|
||||
|
||||
# What happens here:
|
||||
#
|
||||
|
|
|
@ -328,7 +328,8 @@ defp do_render("show.json", %{user: user} = opts) do
|
|||
location: user.location,
|
||||
is_local: user.local,
|
||||
avatar_description: avatar_description,
|
||||
header_description: header_description
|
||||
header_description: header_description,
|
||||
permit_followback: user.permit_followback
|
||||
}
|
||||
}
|
||||
|> maybe_put_role(user, opts[:for])
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddPermitFollowback do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
add(:permit_followback, :boolean, null: false, default: false)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1472,6 +1472,36 @@ test "directly follows a non-locked local user" do
|
|||
|
||||
assert User.following?(follower, followed)
|
||||
end
|
||||
|
||||
test "directly follows back a locked, but followback-allowing local user" do
|
||||
uopen = insert(:user, is_locked: false)
|
||||
uselective = insert(:user, is_locked: true, permit_followback: true)
|
||||
|
||||
assert {:ok, uselective, uopen, %{data: %{"state" => "accept"}}} =
|
||||
CommonAPI.follow(uselective, uopen)
|
||||
|
||||
assert User.get_follow_state(uselective, uopen) == :follow_accept
|
||||
|
||||
assert {:ok, uopen, uselective, %{data: %{"state" => "accept"}}} =
|
||||
CommonAPI.follow(uopen, uselective)
|
||||
|
||||
assert User.get_follow_state(uopen, uselective) == :follow_accept
|
||||
end
|
||||
|
||||
test "creates a pending request for locked, non-followback local user" do
|
||||
uopen = insert(:user, is_locked: false)
|
||||
ulocked = insert(:user, is_locked: true, permit_followback: false)
|
||||
|
||||
assert {:ok, ulocked, uopen, %{data: %{"state" => "accept"}}} =
|
||||
CommonAPI.follow(ulocked, uopen)
|
||||
|
||||
assert User.get_follow_state(ulocked, uopen) == :follow_accept
|
||||
|
||||
assert {:ok, uopen, ulocked, %{data: %{"state" => "pending"}}} =
|
||||
CommonAPI.follow(uopen, ulocked)
|
||||
|
||||
assert User.get_follow_state(uopen, ulocked) == :follow_pending
|
||||
end
|
||||
end
|
||||
|
||||
describe "unfollow/2" do
|
||||
|
|
|
@ -99,7 +99,8 @@ test "Represent a user account" do
|
|||
location: nil,
|
||||
is_local: true,
|
||||
avatar_description: "",
|
||||
header_description: ""
|
||||
header_description: "",
|
||||
permit_followback: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,7 +311,8 @@ test "Represent a Service(bot) account" do
|
|||
location: nil,
|
||||
is_local: true,
|
||||
avatar_description: "",
|
||||
header_description: ""
|
||||
header_description: "",
|
||||
permit_followback: false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue