Add emoji policy to remove emojis matching certain urls
https://git.pleroma.social/pleroma/pleroma/-/issues/2775
This commit is contained in:
parent
7da6a82dbd
commit
28ff828caa
3 changed files with 287 additions and 0 deletions
|
@ -408,6 +408,12 @@
|
|||
federated_timeline_removal: [],
|
||||
replace: []
|
||||
|
||||
config :pleroma, :mrf_emoji,
|
||||
remove_url: [],
|
||||
remove_shortcode: [],
|
||||
federated_timeline_removal_url: [],
|
||||
federated_timeline_removal_shortcode: []
|
||||
|
||||
config :pleroma, :mrf_hashtag,
|
||||
sensitive: ["nsfw"],
|
||||
reject: [],
|
||||
|
|
158
lib/pleroma/web/activity_pub/emoji_policy.ex
Normal file
158
lib/pleroma/web/activity_pub/emoji_policy.ex
Normal file
|
@ -0,0 +1,158 @@
|
|||
# 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.EmojiPolicy do
|
||||
require Pleroma.Constants
|
||||
|
||||
@moduledoc "Reject or force-unlisted emojis with certain URLs or names"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp config_remove_url do
|
||||
Pleroma.Config.get([:mrf_emoji, :remove_url], [])
|
||||
end
|
||||
|
||||
@impl Pleroma.Web.ActivityPub.MRF.Policy
|
||||
def filter(%{"type" => type, "object" => %{} = object} = message)
|
||||
when type in ["Create", "Update"] do
|
||||
with object <- process_remove(object, :url, config_remove_url()) do
|
||||
{:ok, Map.put(message, "object", object)}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Web.ActivityPub.MRF.Policy
|
||||
def filter(%{"type" => type} = object) when type in Pleroma.Constants.actor_types() do
|
||||
with object <- process_remove(object, :url, config_remove_url()) do
|
||||
{:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Web.ActivityPub.MRF.Policy
|
||||
def filter(message) do
|
||||
{:ok, message}
|
||||
end
|
||||
|
||||
defp match_string?(string, pattern) when is_binary(pattern) do
|
||||
string == pattern
|
||||
end
|
||||
|
||||
defp match_string?(string, %Regex{} = pattern) do
|
||||
String.match?(string, pattern)
|
||||
end
|
||||
|
||||
defp match_any?(string, patterns) do
|
||||
Enum.any?(patterns, &match_string?(string, &1))
|
||||
end
|
||||
|
||||
defp process_remove(object, :url, patterns) do
|
||||
processed_tag =
|
||||
Enum.filter(
|
||||
object["tag"],
|
||||
fn
|
||||
%{"type" => "Emoji", "icon" => %{"url" => url}} when is_binary(url) ->
|
||||
not match_any?(url, patterns)
|
||||
|
||||
_ ->
|
||||
true
|
||||
end
|
||||
)
|
||||
|
||||
processed_emoji =
|
||||
if object["emoji"] do
|
||||
object["emoji"]
|
||||
|> Enum.reduce(%{}, fn {name, url}, acc ->
|
||||
if not match_any?(url, patterns) do
|
||||
Map.put(acc, name, url)
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
if processed_emoji do
|
||||
object
|
||||
|> Map.put("tag", processed_tag)
|
||||
|> Map.put("emoji", processed_emoji)
|
||||
else
|
||||
object
|
||||
|> Map.put("tag", processed_tag)
|
||||
end
|
||||
end
|
||||
|
||||
@impl Pleroma.Web.ActivityPub.MRF.Policy
|
||||
def describe do
|
||||
# This horror is needed to convert regex sigils to strings
|
||||
mrf_emoji =
|
||||
Pleroma.Config.get(:mrf_emoji, [])
|
||||
|> Enum.map(fn {key, value} ->
|
||||
{key,
|
||||
Enum.map(value, fn
|
||||
pattern ->
|
||||
if not is_binary(pattern) do
|
||||
inspect(pattern)
|
||||
else
|
||||
pattern
|
||||
end
|
||||
end)}
|
||||
end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
{:ok, %{mrf_emoji: mrf_emoji}}
|
||||
end
|
||||
|
||||
@impl Pleroma.Web.ActivityPub.MRF.Policy
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_emoji,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.EmojiPolicy",
|
||||
label: "MRF Emoji",
|
||||
description:
|
||||
"Reject or force-unlisted emojis whose URLs or names match a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
|
||||
children: [
|
||||
%{
|
||||
key: :remove_url,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in emoji whose URL matches being removed from the message. This will apply to both statuses and user profiles.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :remove_shortcode,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in emoji whose shortcode matches being removed from the message. This will apply to both statuses and user profiles.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal_url,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message with emojis whose URLs match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal_shortcode,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message with emojis whose shortcodes match being removed from federated timelines (a.k.a unlisted). This will apply only to statuses.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
123
test/pleroma/web/activity_pub/mrf/emoji_policy_test.exs
Normal file
123
test/pleroma/web/activity_pub/mrf/emoji_policy_test.exs
Normal file
|
@ -0,0 +1,123 @@
|
|||
# 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.EmojiPolicyTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.EmojiPolicy
|
||||
|
||||
setup do: clear_config(:mrf_emoji)
|
||||
|
||||
setup do
|
||||
clear_config([:mrf_emoji], %{
|
||||
remove_url: [],
|
||||
remove_shortcode: [],
|
||||
federated_timeline_removal_url: [],
|
||||
federated_timeline_removal_shortcode: []
|
||||
})
|
||||
end
|
||||
|
||||
@emoji_tags [
|
||||
%{
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => "https://example.org/emoji/biribiri/mikoto_smile2.png"
|
||||
},
|
||||
"id" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
|
||||
"name" => ":mikoto_smile2:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z"
|
||||
},
|
||||
%{
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => "https://example.org/emoji/biribiri/mikoto_smile3.png"
|
||||
},
|
||||
"id" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
|
||||
"name" => ":mikoto_smile3:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z"
|
||||
},
|
||||
%{
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
|
||||
},
|
||||
"id" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
|
||||
"name" => ":nekomimi_girl_emoji_007:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z"
|
||||
},
|
||||
%{
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => "https://example.org/test.png"
|
||||
},
|
||||
"id" => "https://example.org/test.png",
|
||||
"name" => ":test:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z"
|
||||
}
|
||||
]
|
||||
|
||||
@misc_tags [%{"type" => "Placeholder"}]
|
||||
|
||||
@user_data %{
|
||||
"type" => "Person",
|
||||
"id" => "https://example.org/placeholder",
|
||||
"name" => "lol",
|
||||
"tag" => @emoji_tags ++ @misc_tags
|
||||
}
|
||||
|
||||
@status_data %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://example.org/placeholder",
|
||||
"content" => "lol",
|
||||
"tag" => @emoji_tags ++ @misc_tags,
|
||||
"emoji" => %{
|
||||
"mikoto_smile2" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
|
||||
"mikoto_smile3" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
|
||||
"nekomimi_girl_emoji_007" =>
|
||||
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
|
||||
"test" => "https://example.org/test.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe "remove_url" do
|
||||
setup do
|
||||
clear_config([:mrf_emoji, :remove_url], [
|
||||
"https://example.org/test.png",
|
||||
~r{/biribiri/mikoto_smile[23]\.png},
|
||||
"nekomimi_girl_emoji"
|
||||
])
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "processes user" do
|
||||
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data)
|
||||
|
||||
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
|
||||
|
||||
assert %{"tag" => ^expected_tags} = filtered
|
||||
end
|
||||
|
||||
test "processes status" do
|
||||
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
|
||||
|
||||
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
|
||||
|
||||
expected_emoji = %{
|
||||
"nekomimi_girl_emoji_007" =>
|
||||
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
|
||||
}
|
||||
|
||||
assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue