extend reject MRF to check if originating instance is blocked
[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 @impl true
20 def history_awareness, do: :manual
21
22 defp check_reject(message, hashtags) do
23 if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
24 {:reject, "[HashtagPolicy] Matches with rejected keyword"}
25 else
26 {:ok, message}
27 end
28 end
29
30 defp check_ftl_removal(%{"to" => to} = message, hashtags) do
31 if Pleroma.Constants.as_public() in to and
32 Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match ->
33 match in hashtags
34 end) do
35 to = List.delete(to, Pleroma.Constants.as_public())
36 cc = [Pleroma.Constants.as_public() | message["cc"] || []]
37
38 message =
39 message
40 |> Map.put("to", to)
41 |> Map.put("cc", cc)
42 |> Kernel.put_in(["object", "to"], to)
43 |> Kernel.put_in(["object", "cc"], cc)
44
45 {:ok, message}
46 else
47 {:ok, message}
48 end
49 end
50
51 defp check_ftl_removal(message, _hashtags), do: {:ok, message}
52
53 defp check_sensitive(message) do
54 {:ok, new_object} =
55 Object.Updater.do_with_history(message["object"], fn object ->
56 hashtags = Object.hashtags(%Object{data: object})
57
58 if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
59 {:ok, Map.put(object, "sensitive", true)}
60 else
61 {:ok, object}
62 end
63 end)
64
65 {:ok, Map.put(message, "object", new_object)}
66 end
67
68 @impl true
69 def filter(%{"type" => type, "object" => object} = message) when type in ["Create", "Update"] do
70 history_items =
71 with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do
72 items
73 else
74 _ -> []
75 end
76
77 historical_hashtags =
78 Enum.reduce(history_items, [], fn item, acc ->
79 acc ++ Object.hashtags(%Object{data: item})
80 end)
81
82 hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags
83
84 if hashtags != [] do
85 with {:ok, message} <- check_reject(message, hashtags),
86 {:ok, message} <-
87 (if "type" == "Create" do
88 check_ftl_removal(message, hashtags)
89 else
90 {:ok, message}
91 end),
92 {:ok, message} <- check_sensitive(message) do
93 {:ok, message}
94 end
95 else
96 {:ok, message}
97 end
98 end
99
100 @impl true
101 def filter(message), do: {:ok, message}
102
103 @impl true
104 def describe do
105 mrf_hashtag =
106 Config.get(:mrf_hashtag)
107 |> Enum.into(%{})
108
109 {:ok, %{mrf_hashtag: mrf_hashtag}}
110 end
111
112 @impl true
113 def config_description do
114 %{
115 key: :mrf_hashtag,
116 related_policy: "Pleroma.Web.ActivityPub.MRF.HashtagPolicy",
117 label: "MRF Hashtag",
118 description: @moduledoc,
119 children: [
120 %{
121 key: :reject,
122 type: {:list, :string},
123 description: "A list of hashtags which result in message being rejected.",
124 suggestions: ["foo"]
125 },
126 %{
127 key: :federated_timeline_removal,
128 type: {:list, :string},
129 description:
130 "A list of hashtags which result in message being removed from federated timelines (a.k.a unlisted).",
131 suggestions: ["foo"]
132 },
133 %{
134 key: :sensitive,
135 type: {:list, :string},
136 description:
137 "A list of hashtags which result in message being set as sensitive (a.k.a NSFW/R-18)",
138 suggestions: ["nsfw", "r18"]
139 }
140 ]
141 }
142 end
143 end