Merge branch 'resurrect-mrf-quietreply' into 'develop'
Resurrect MRF.QuietReply See merge request pleroma/pleroma!4368
This commit is contained in:
commit
f6c9b003fa
3 changed files with 201 additions and 0 deletions
1
changelog.d/mrf-quietreply.add
Normal file
1
changelog.d/mrf-quietreply.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Added MRF.QuietReply which prevents replies to public posts from being published to the timelines
|
||||||
61
lib/pleroma/web/activity_pub/mrf/quiet_reply.ex
Normal file
61
lib/pleroma/web/activity_pub/mrf/quiet_reply.ex
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.QuietReply do
|
||||||
|
@moduledoc """
|
||||||
|
QuietReply alters the scope of activities from local users when replying by enforcing them to be "Unlisted" or "Quiet Public". This delivers the activity to all the expected recipients and instances, but it will not be published in the Federated / The Whole Known Network timelines. It will still be published to the Home timelines of the user's followers and visible to anyone who opens the thread.
|
||||||
|
"""
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def history_awareness, do: :auto
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(
|
||||||
|
%{
|
||||||
|
"type" => "Create",
|
||||||
|
"to" => to,
|
||||||
|
"cc" => cc,
|
||||||
|
"object" => %{
|
||||||
|
"actor" => actor,
|
||||||
|
"type" => "Note",
|
||||||
|
"inReplyTo" => in_reply_to
|
||||||
|
}
|
||||||
|
} = activity
|
||||||
|
) do
|
||||||
|
with true <- is_binary(in_reply_to),
|
||||||
|
true <- Pleroma.Constants.as_public() in to,
|
||||||
|
%User{follower_address: followers_collection, local: true} <-
|
||||||
|
User.get_by_ap_id(actor) do
|
||||||
|
updated_to =
|
||||||
|
[followers_collection | to]
|
||||||
|
|> Kernel.--([Pleroma.Constants.as_public()])
|
||||||
|
|
||||||
|
updated_cc =
|
||||||
|
[Pleroma.Constants.as_public() | cc]
|
||||||
|
|> Kernel.--([followers_collection])
|
||||||
|
|
||||||
|
updated_activity =
|
||||||
|
activity
|
||||||
|
|> Map.put("to", updated_to)
|
||||||
|
|> Map.put("cc", updated_cc)
|
||||||
|
|> put_in(["object", "to"], updated_to)
|
||||||
|
|> put_in(["object", "cc"], updated_cc)
|
||||||
|
|
||||||
|
{:ok, updated_activity}
|
||||||
|
else
|
||||||
|
_ -> {:ok, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(activity), do: {:ok, activity}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def describe, do: {:ok, %{}}
|
||||||
|
end
|
||||||
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.QuietReplyTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF.QuietReply
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
test "replying to public post is forced to be quiet" do
|
||||||
|
batman = insert(:user, nickname: "batman")
|
||||||
|
robin = insert(:user, nickname: "robin")
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||||
|
|
||||||
|
reply = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"to" => [
|
||||||
|
batman.ap_id,
|
||||||
|
Pleroma.Constants.as_public()
|
||||||
|
],
|
||||||
|
"cc" => [robin.follower_address],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"content" => "@batman Wait up, I forgot my spandex!",
|
||||||
|
"to" => [
|
||||||
|
batman.ap_id,
|
||||||
|
Pleroma.Constants.as_public()
|
||||||
|
],
|
||||||
|
"cc" => [robin.follower_address],
|
||||||
|
"inReplyTo" => Object.normalize(post).data["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||||
|
|
||||||
|
assert batman.ap_id in filtered["to"]
|
||||||
|
assert batman.ap_id in filtered["object"]["to"]
|
||||||
|
assert robin.follower_address in filtered["to"]
|
||||||
|
assert robin.follower_address in filtered["object"]["to"]
|
||||||
|
assert Pleroma.Constants.as_public() in filtered["cc"]
|
||||||
|
assert Pleroma.Constants.as_public() in filtered["object"]["cc"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "replying to unlisted post is unmodified" do
|
||||||
|
batman = insert(:user, nickname: "batman")
|
||||||
|
robin = insert(:user, nickname: "robin")
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!", visibility: "private"})
|
||||||
|
|
||||||
|
reply = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"to" => [batman.ap_id],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"content" => "@batman Wait up, I forgot my spandex!",
|
||||||
|
"to" => [batman.ap_id],
|
||||||
|
"cc" => [],
|
||||||
|
"inReplyTo" => Object.normalize(post).data["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||||
|
|
||||||
|
assert match?(^filtered, reply)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "replying direct is unmodified" do
|
||||||
|
batman = insert(:user, nickname: "batman")
|
||||||
|
robin = insert(:user, nickname: "robin")
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||||
|
|
||||||
|
reply = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"to" => [batman.ap_id],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"content" => "@batman Wait up, I forgot my spandex!",
|
||||||
|
"to" => [batman.ap_id],
|
||||||
|
"cc" => [],
|
||||||
|
"inReplyTo" => Object.normalize(post).data["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||||
|
|
||||||
|
assert match?(^filtered, reply)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "replying followers-only is unmodified" do
|
||||||
|
batman = insert(:user, nickname: "batman")
|
||||||
|
robin = insert(:user, nickname: "robin")
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||||
|
|
||||||
|
reply = %{
|
||||||
|
"type" => "Create",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"to" => [batman.ap_id, robin.follower_address],
|
||||||
|
"cc" => [],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => robin.ap_id,
|
||||||
|
"content" => "@batman Wait up, I forgot my spandex!",
|
||||||
|
"to" => [batman.ap_id, robin.follower_address],
|
||||||
|
"cc" => [],
|
||||||
|
"inReplyTo" => Object.normalize(post).data["id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||||
|
|
||||||
|
assert match?(^filtered, reply)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "non-reply posts are unmodified" do
|
||||||
|
batman = insert(:user, nickname: "batman")
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(post)
|
||||||
|
|
||||||
|
assert match?(^filtered, post)
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue