Make MRF Keyword history-aware
This commit is contained in:
parent
04ded94a50
commit
eba9b0760f
4 changed files with 231 additions and 12 deletions
|
@ -197,4 +197,44 @@ def make_new_object_data_from_update_object(original_data, new_data) do
|
|||
used_history_in_new_object?: used_history_in_new_object?
|
||||
}
|
||||
end
|
||||
|
||||
defp for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do
|
||||
new_items =
|
||||
Enum.map(items, fun)
|
||||
|> Enum.reduce_while(
|
||||
{:ok, []},
|
||||
fn
|
||||
{:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}}
|
||||
e, _acc -> {:halt, e}
|
||||
end
|
||||
)
|
||||
|
||||
case new_items do
|
||||
{:ok, items} -> {:ok, Map.put(history, "orderedItems", items)}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
defp for_each_history_item(history, _, _) do
|
||||
{:ok, history}
|
||||
end
|
||||
|
||||
def do_with_history(object, fun) do
|
||||
with history <- object["formerRepresentations"],
|
||||
object <- Map.drop(object, ["formerRepresentations"]),
|
||||
{_, {:ok, object}} <- {:main_body, fun.(object)},
|
||||
{_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
|
||||
object =
|
||||
if history do
|
||||
Map.put(object, "formerRepresentations", history)
|
||||
else
|
||||
object
|
||||
end
|
||||
|
||||
{:ok, object}
|
||||
else
|
||||
{:main_body, e} -> e
|
||||
{:history_items, e} -> e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,24 +27,46 @@ defp object_payload(%{} = object) do
|
|||
end
|
||||
|
||||
defp check_reject(%{"object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
with {:ok, _new_object} <-
|
||||
Pleroma.Object.Updater.do_with_history(object, fn object ->
|
||||
payload = object_payload(object)
|
||||
|
||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
{:reject, "[KeywordPolicy] Matches with rejected keyword"}
|
||||
else
|
||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
{:reject, "[KeywordPolicy] Matches with rejected keyword"}
|
||||
else
|
||||
{:ok, message}
|
||||
end
|
||||
end) do
|
||||
{:ok, message}
|
||||
else
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do
|
||||
check_keyword = fn object ->
|
||||
payload = object_payload(object)
|
||||
|
||||
if Pleroma.Constants.as_public() in to and
|
||||
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
{:should_delist, nil}
|
||||
else
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
||||
|
||||
should_delist? = fn object ->
|
||||
with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do
|
||||
false
|
||||
else
|
||||
_ -> true
|
||||
end
|
||||
end
|
||||
|
||||
if Pleroma.Constants.as_public() in to and should_delist?.(object) do
|
||||
to = List.delete(to, Pleroma.Constants.as_public())
|
||||
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
|
||||
|
||||
|
@ -59,8 +81,12 @@ defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_ftl_removal(message) do
|
||||
{:ok, message}
|
||||
end
|
||||
|
||||
defp check_replace(%{"object" => %{} = object} = message) do
|
||||
object =
|
||||
replace_kw = fn object ->
|
||||
["content", "name", "summary"]
|
||||
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|
||||
|> Enum.reduce(object, fn field, object ->
|
||||
|
@ -73,6 +99,10 @@ defp check_replace(%{"object" => %{} = object} = message) do
|
|||
|
||||
Map.put(object, field, data)
|
||||
end)
|
||||
|> (fn object -> {:ok, object} end).()
|
||||
end
|
||||
|
||||
{:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw)
|
||||
|
||||
message = Map.put(message, "object", object)
|
||||
|
||||
|
@ -80,7 +110,8 @@ defp check_replace(%{"object" => %{} = object} = message) do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do
|
||||
def filter(%{"type" => type, "object" => %{"content" => _content}} = message)
|
||||
when type in ["Create", "Update"] do
|
||||
with {:ok, message} <- check_reject(message),
|
||||
{:ok, message} <- check_ftl_removal(message),
|
||||
{:ok, message} <- check_replace(message) do
|
||||
|
|
|
@ -79,6 +79,54 @@ test "rejects if regex matches in summary" do
|
|||
KeywordPolicy.filter(message)
|
||||
end)
|
||||
end
|
||||
|
||||
test "rejects if string matches in history" do
|
||||
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||
KeywordPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "rejects Updates" do
|
||||
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||
KeywordPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delisting from ftl based on keywords" do
|
||||
|
@ -157,6 +205,31 @@ test "delists if regex matches in summary" do
|
|||
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
|
||||
end)
|
||||
end
|
||||
|
||||
test "delists if string matches in history" do
|
||||
clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"])
|
||||
|
||||
message = %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, result} = KeywordPolicy.filter(message)
|
||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
|
||||
refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "replacing keywords" do
|
||||
|
@ -221,5 +294,63 @@ test "replaces keyword if regex matches in summary" do
|
|||
result == "ZFS is free software"
|
||||
end)
|
||||
end
|
||||
|
||||
test "replaces keyword if string matches in history" do
|
||||
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"object" => %{
|
||||
"content" => "ZFS is opensource",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => "ZFS is free software",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||
}
|
||||
}
|
||||
}} = KeywordPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "replaces keyword in Updates" do
|
||||
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"object" => %{
|
||||
"content" => "ZFS is opensource",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => "ZFS is free software",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||
}
|
||||
}
|
||||
}} = KeywordPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1647,5 +1647,22 @@ test "editing a post that copied a remote title with remote emoji should keep th
|
|||
|
||||
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
||||
end
|
||||
|
||||
test "respects MRF" do
|
||||
user = insert(:user)
|
||||
|
||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||
clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
|
||||
assert Object.normalize(activity).data["summary"] == "mewmew 1"
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "mewmew 2"
|
||||
assert Map.get(updated_object.data, "summary", "") == ""
|
||||
assert Map.has_key?(updated_object.data, "updated")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue