MRF: create MRF.Policy behaviour separate from MRF module
[akkoma] / lib / pleroma / web / activity_pub / mrf / hashtag_policy.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
6 require Pleroma.Constants
7
8 alias Pleroma.Config
9 alias Pleroma.Object
10
11 @moduledoc """
12 Reject, TWKN-remove or Set-Sensitive messsages with specific hashtags (without the leading #)
13
14 Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
15 """
16
17 @behaviour Pleroma.Web.ActivityPub.MRF.Policy
18
19 defp check_reject(message, hashtags) do
20 if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
21 {:reject, "[HashtagPolicy] Matches with rejected keyword"}
22 else
23 {:ok, message}
24 end
25 end
26
27 defp check_ftl_removal(%{"to" => to} = message, hashtags) do
28 if Pleroma.Constants.as_public() in to and
29 Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match ->
30 match in hashtags
31 end) do
32 to = List.delete(to, Pleroma.Constants.as_public())
33 cc = [Pleroma.Constants.as_public() | message["cc"] || []]
34
35 message =
36 message
37 |> Map.put("to", to)
38 |> Map.put("cc", cc)
39 |> Kernel.put_in(["object", "to"], to)
40 |> Kernel.put_in(["object", "cc"], cc)
41
42 {:ok, message}
43 else
44 {:ok, message}
45 end
46 end
47
48 defp check_ftl_removal(message, _hashtags), do: {:ok, message}
49
50 defp check_sensitive(message, hashtags) do
51 if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
52 {:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
53 else
54 {:ok, message}
55 end
56 end
57
58 @impl true
59 def filter(%{"type" => "Create", "object" => object} = message) do
60 hashtags = Object.hashtags(%Object{data: object})
61
62 if hashtags != [] do
63 with {:ok, message} <- check_reject(message, hashtags),
64 {:ok, message} <- check_ftl_removal(message, hashtags),
65 {:ok, message} <- check_sensitive(message, hashtags) do
66 {:ok, message}
67 end
68 else
69 {:ok, message}
70 end
71 end
72
73 @impl true
74 def filter(message), do: {:ok, message}
75
76 @impl true
77 def describe do
78 mrf_hashtag =
79 Config.get(:mrf_hashtag)
80 |> Enum.into(%{})
81
82 {:ok, %{mrf_hashtag: mrf_hashtag}}
83 end
84
85 @impl true
86 def config_description do
87 %{
88 key: :mrf_hashtag,
89 related_policy: "Pleroma.Web.ActivityPub.MRF.HashtagPolicy",
90 label: "MRF Hashtag",
91 description: @moduledoc,
92 children: [
93 %{
94 key: :reject,
95 type: {:list, :string},
96 description: "A list of hashtags which result in message being rejected.",
97 suggestions: ["foo"]
98 },
99 %{
100 key: :federated_timeline_removal,
101 type: {:list, :string},
102 description:
103 "A list of hashtags which result in message being removed from federated timelines (a.k.a unlisted).",
104 suggestions: ["foo"]
105 },
106 %{
107 key: :sensitive,
108 type: {:list, :string},
109 description:
110 "A list of hashtags which result in message being set as sensitive (a.k.a NSFW/R-18)",
111 suggestions: ["nsfw", "r18"]
112 }
113 ]
114 }
115 end
116 end