Resurrect MRF.QuietReply
This was not working correctly because the Publisher was stripping the public address from the cc when federating unlisted activities
This commit is contained in:
parent
774e0cb172
commit
33cf49e860
2 changed files with 202 additions and 0 deletions
62
lib/pleroma/web/activity_pub/mrf/quiet_reply.ex
Normal file
62
lib/pleroma/web/activity_pub/mrf/quiet_reply.ex
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# 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),
|
||||||
|
false <- match?([], cc),
|
||||||
|
%User{follower_address: followers_collection, local: true} <-
|
||||||
|
User.get_by_ap_id(actor) do
|
||||||
|
updated_to =
|
||||||
|
to
|
||||||
|
|> Kernel.++([followers_collection])
|
||||||
|
|> 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
|
||||||
140
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
140
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
# 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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_to = [batman.ap_id, robin.follower_address]
|
||||||
|
expected_cc = [Pleroma.Constants.as_public()]
|
||||||
|
|
||||||
|
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||||
|
|
||||||
|
assert expected_to == filtered["to"]
|
||||||
|
assert expected_cc == filtered["cc"]
|
||||||
|
assert expected_to == filtered["object"]["to"]
|
||||||
|
assert expected_cc == 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