mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-24 22:07:52 -04:00
Add expiring blocks
- `/api/v1/accounts/:id/block` now has a "duration" parameter - `/api/v1/blocks` returns "block_expires_at" to indicate when the block will expire - MuteExpireWorker also processes block expiration - Remove unused OpenAPI parameters from mute endpoint - Add pleroma:block_expiration to nodeinfo features
This commit is contained in:
parent
1775a4db08
commit
51a0cee405
12 changed files with 112 additions and 33 deletions
1
changelog.d/expiring-blocks.add
Normal file
1
changelog.d/expiring-blocks.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add `duration` to the block endpoint, which makes block expire
|
|
@ -1708,7 +1708,9 @@ defmodule Pleroma.User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def block(%User{} = blocker, %User{} = blocked) do
|
def block(blocker, blocked, params \\ %{})
|
||||||
|
|
||||||
|
def block(%User{} = blocker, %User{} = blocked, params) do
|
||||||
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
||||||
blocker =
|
blocker =
|
||||||
if following?(blocker, blocked) do
|
if following?(blocker, blocked) do
|
||||||
|
@ -1738,12 +1740,33 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
{:ok, blocker} = update_follower_count(blocker)
|
{:ok, blocker} = update_follower_count(blocker)
|
||||||
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
|
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
|
||||||
add_to_block(blocker, blocked)
|
|
||||||
|
duration = Map.get(params, :duration, 0)
|
||||||
|
|
||||||
|
expires_at =
|
||||||
|
if duration > 0 do
|
||||||
|
DateTime.utc_now()
|
||||||
|
|> DateTime.add(duration)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
user_block = add_to_block(blocker, blocked, expires_at)
|
||||||
|
|
||||||
|
if duration > 0 do
|
||||||
|
Pleroma.Workers.MuteExpireWorker.new(
|
||||||
|
%{"op" => "unblock_user", "blocker_id" => blocker.id, "blocked_id" => blocked.id},
|
||||||
|
scheduled_at: expires_at
|
||||||
|
)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
user_block
|
||||||
end
|
end
|
||||||
|
|
||||||
# helper to handle the block given only an actor's AP id
|
# helper to handle the block given only an actor's AP id
|
||||||
def block(%User{} = blocker, %{ap_id: ap_id}) do
|
def block(%User{} = blocker, %{ap_id: ap_id}, params) do
|
||||||
block(blocker, get_cached_by_ap_id(ap_id))
|
block(blocker, get_cached_by_ap_id(ap_id), params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unblock(%User{} = blocker, %User{} = blocked) do
|
def unblock(%User{} = blocker, %User{} = blocked) do
|
||||||
|
@ -2779,10 +2802,10 @@ defmodule Pleroma.User do
|
||||||
set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
|
set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec add_to_block(User.t(), User.t()) ::
|
@spec add_to_block(User.t(), User.t(), integer() | nil) ::
|
||||||
{:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
|
{:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
|
||||||
defp add_to_block(%User{} = user, %User{} = blocked) do
|
defp add_to_block(%User{} = user, %User{} = blocked, expires_at) do
|
||||||
with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do
|
with {:ok, relationship} <- UserRelationship.create_block(user, blocked, expires_at) do
|
||||||
@cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
@cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||||
{:ok, relationship}
|
{:ok, relationship}
|
||||||
end
|
end
|
||||||
|
|
|
@ -327,8 +327,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
}, []}
|
}, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec block(User.t(), User.t()) :: {:ok, map(), keyword()}
|
@spec block(User.t(), User.t(), map()) :: {:ok, map(), keyword()}
|
||||||
def block(blocker, blocked) do
|
def block(blocker, blocked, params) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%{
|
%{
|
||||||
"id" => Utils.generate_activity_id(),
|
"id" => Utils.generate_activity_id(),
|
||||||
|
@ -336,7 +336,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
"actor" => blocker.ap_id,
|
"actor" => blocker.ap_id,
|
||||||
"object" => blocked.ap_id,
|
"object" => blocked.ap_id,
|
||||||
"to" => [blocked.ap_id]
|
"to" => [blocked.ap_id]
|
||||||
}, []}
|
}, Keyword.new(params)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||||
|
|
|
@ -145,7 +145,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
) do
|
) do
|
||||||
with %User{} = blocker <- User.get_cached_by_ap_id(blocking_user),
|
with %User{} = blocker <- User.get_cached_by_ap_id(blocking_user),
|
||||||
%User{} = blocked <- User.get_cached_by_ap_id(blocked_user) do
|
%User{} = blocked <- User.get_cached_by_ap_id(blocked_user) do
|
||||||
User.block(blocker, blocked)
|
User.block(blocker, blocked, Enum.into(meta, %{}))
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
|
|
|
@ -284,18 +284,6 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||||
:query,
|
:query,
|
||||||
%Schema{allOf: [BooleanLike], default: true},
|
%Schema{allOf: [BooleanLike], default: true},
|
||||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||||
),
|
|
||||||
Operation.parameter(
|
|
||||||
:duration,
|
|
||||||
:query,
|
|
||||||
%Schema{type: :integer},
|
|
||||||
"Expire the mute in `duration` seconds. Default 0 for infinity"
|
|
||||||
),
|
|
||||||
Operation.parameter(
|
|
||||||
:expires_in,
|
|
||||||
:query,
|
|
||||||
%Schema{type: :integer, default: 0},
|
|
||||||
"Deprecated, use `duration` instead"
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
|
@ -323,16 +311,37 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
summary: "Block",
|
summary: "Block",
|
||||||
operationId: "AccountController.block",
|
operationId: "AccountController.block",
|
||||||
|
requestBody: request_body("Parameters", block_request()),
|
||||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||||
description:
|
description:
|
||||||
"Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
|
"Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
|
||||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
parameters: [
|
||||||
|
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
|
||||||
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp block_request do
|
||||||
|
%Schema{
|
||||||
|
title: "AccountBlockRequest",
|
||||||
|
description: "POST body for blocking an account",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
duration: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
nullable: true,
|
||||||
|
description: "Expire the mute in `duration` seconds. Default 0 for infinity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"duration" => 86_400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def unblock_operation do
|
def unblock_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
|
|
@ -34,6 +34,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
id: FlakeID,
|
id: FlakeID,
|
||||||
locked: %Schema{type: :boolean},
|
locked: %Schema{type: :boolean},
|
||||||
mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true},
|
mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true},
|
||||||
|
block_expires_at: %Schema{type: :string, format: "date-time", nullable: true},
|
||||||
note: %Schema{type: :string, format: :html},
|
note: %Schema{type: :string, format: :html},
|
||||||
statuses_count: %Schema{type: :integer},
|
statuses_count: %Schema{type: :integer},
|
||||||
url: %Schema{type: :string, format: :uri},
|
url: %Schema{type: :string, format: :uri},
|
||||||
|
|
|
@ -27,9 +27,9 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@spec block(User.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
|
@spec block(User.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
|
||||||
def block(blocked, blocker) do
|
def block(blocked, blocker, params \\ %{}) do
|
||||||
with {:ok, block_data, _} <- Builder.block(blocker, blocked),
|
with {:ok, block_data, meta} <- Builder.block(blocker, blocked, params),
|
||||||
{:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
|
{:ok, block, _} <- Pipeline.common_pipeline(block_data, meta ++ [local: true]) do
|
||||||
{:ok, block}
|
{:ok, block}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -501,8 +501,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/accounts/:id/block"
|
@doc "POST /api/v1/accounts/:id/block"
|
||||||
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
def block(
|
||||||
with {:ok, _activity} <- CommonAPI.block(blocked, blocker) do
|
%{
|
||||||
|
assigns: %{user: blocker, account: blocked},
|
||||||
|
private: %{open_api_spex: %{body_params: params}}
|
||||||
|
} = conn,
|
||||||
|
_params
|
||||||
|
) do
|
||||||
|
with {:ok, _activity} <- CommonAPI.block(blocked, blocker, params) do
|
||||||
render(conn, "relationship.json", user: blocker, target: blocked)
|
render(conn, "relationship.json", user: blocker, target: blocked)
|
||||||
else
|
else
|
||||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||||
|
@ -607,7 +613,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
users: users,
|
users: users,
|
||||||
for: user,
|
for: user,
|
||||||
as: :user,
|
as: :user,
|
||||||
embed_relationships: embed_relationships?(params)
|
embed_relationships: embed_relationships?(params),
|
||||||
|
blocks: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -340,6 +340,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
|> maybe_put_unread_notification_count(user, opts[:for])
|
|> maybe_put_unread_notification_count(user, opts[:for])
|
||||||
|> maybe_put_email_address(user, opts[:for])
|
|> maybe_put_email_address(user, opts[:for])
|
||||||
|> maybe_put_mute_expires_at(user, opts[:for], opts)
|
|> maybe_put_mute_expires_at(user, opts[:for], opts)
|
||||||
|
|> maybe_put_block_expires_at(user, opts[:for], opts)
|
||||||
|> maybe_show_birthday(user, opts[:for])
|
|> maybe_show_birthday(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -476,6 +477,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
|
|
||||||
defp maybe_put_mute_expires_at(data, _, _, _), do: data
|
defp maybe_put_mute_expires_at(data, _, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_block_expires_at(data, %User{} = user, target, %{blocks: true}) do
|
||||||
|
Map.put(
|
||||||
|
data,
|
||||||
|
:block_expires_at,
|
||||||
|
UserRelationship.get_block_expire_date(target, user)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_block_expires_at(data, _, _, _), do: data
|
||||||
|
|
||||||
defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do
|
defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in([:pleroma, :birthday], user.birthday)
|
|> Kernel.put_in([:pleroma, :birthday], user.birthday)
|
||||||
|
|
|
@ -157,7 +157,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
||||||
"pleroma:bookmark_folders",
|
"pleroma:bookmark_folders",
|
||||||
if Pleroma.Language.LanguageDetector.configured?() do
|
if Pleroma.Language.LanguageDetector.configured?() do
|
||||||
"pleroma:language_detection"
|
"pleroma:language_detection"
|
||||||
end
|
end,
|
||||||
|
"pleroma:block_expiration"
|
||||||
]
|
]
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,9 +5,13 @@
|
||||||
defmodule Pleroma.Workers.MuteExpireWorker do
|
defmodule Pleroma.Workers.MuteExpireWorker do
|
||||||
use Oban.Worker, queue: :background
|
use Oban.Worker, queue: :background
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do
|
def perform(%Job{
|
||||||
Pleroma.User.unmute(muter_id, mutee_id)
|
args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}
|
||||||
|
}) do
|
||||||
|
User.unmute(muter_id, mutee_id)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,6 +22,17 @@ defmodule Pleroma.Workers.MuteExpireWorker do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform(%Job{
|
||||||
|
args: %{"op" => "unblock_user", "blocker_id" => blocker_id, "blocked_id" => blocked_id}
|
||||||
|
}) do
|
||||||
|
Pleroma.Web.CommonAPI.unblock(
|
||||||
|
User.get_cached_by_id(blocked_id),
|
||||||
|
User.get_cached_by_id(blocker_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def timeout(_job), do: :timer.seconds(5)
|
def timeout(_job), do: :timer.seconds(5)
|
||||||
end
|
end
|
||||||
|
|
|
@ -111,6 +111,17 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "add expiring block", %{blocker: blocker, blocked: blocked} do
|
||||||
|
{:ok, _} = CommonAPI.block(blocked, blocker, %{expires_in: 60})
|
||||||
|
assert User.blocks?(blocker, blocked)
|
||||||
|
|
||||||
|
worker = Pleroma.Workers.MuteExpireWorker
|
||||||
|
args = %{"op" => "unblock_user", "blocker_id" => blocker.id, "blocked_id" => blocked.id}
|
||||||
|
|
||||||
|
assert :ok = perform_job(worker, args)
|
||||||
|
refute User.blocks?(blocker, blocked)
|
||||||
|
end
|
||||||
|
|
||||||
test "it blocks and does not federate if outgoing blocks are disabled", %{
|
test "it blocks and does not federate if outgoing blocks are disabled", %{
|
||||||
blocker: blocker,
|
blocker: blocker,
|
||||||
blocked: blocked
|
blocked: blocked
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue