First
[anni] / lib / pleroma / web / activity_pub / mrf / hellthread_policy.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
6   alias Pleroma.User
7
8   require Pleroma.Constants
9
10   @moduledoc "Block messages with too much mentions (configurable)"
11
12   @behaviour Pleroma.Web.ActivityPub.MRF.Policy
13
14   defp delist_message(message, threshold) when threshold > 0 do
15     follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
16     to = message["to"] || []
17     cc = message["cc"] || []
18
19     follower_collection? = Enum.member?(to ++ cc, follower_collection)
20
21     message =
22       case get_recipient_count(message) do
23         {:public, recipients}
24         when follower_collection? and recipients > threshold ->
25           message
26           |> Map.put("to", [follower_collection])
27           |> Map.put("cc", [Pleroma.Constants.as_public()])
28
29         {:public, recipients} when recipients > threshold ->
30           message
31           |> Map.put("to", [])
32           |> Map.put("cc", [Pleroma.Constants.as_public()])
33
34         _ ->
35           message
36       end
37
38     {:ok, message}
39   end
40
41   defp delist_message(message, _threshold), do: {:ok, message}
42
43   defp reject_message(message, threshold) when threshold > 0 do
44     with {_, recipients} <- get_recipient_count(message) do
45       if recipients > threshold do
46         {:reject, "[HellthreadPolicy] #{recipients} recipients is over the limit of #{threshold}"}
47       else
48         {:ok, message}
49       end
50     end
51   end
52
53   defp reject_message(message, _threshold), do: {:ok, message}
54
55   defp get_recipient_count(message) do
56     recipients = (message["to"] || []) ++ (message["cc"] || [])
57     follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
58
59     if Enum.member?(recipients, Pleroma.Constants.as_public()) do
60       recipients =
61         recipients
62         |> List.delete(Pleroma.Constants.as_public())
63         |> List.delete(follower_collection)
64
65       {:public, length(recipients)}
66     else
67       recipients =
68         recipients
69         |> List.delete(follower_collection)
70
71       {:not_public, length(recipients)}
72     end
73   end
74
75   @impl true
76   def filter(%{"type" => "Create", "object" => %{"type" => object_type}} = message)
77       when object_type in ~w{Note Article} do
78     reject_threshold =
79       Pleroma.Config.get(
80         [:mrf_hellthread, :reject_threshold],
81         Pleroma.Config.get([:mrf_hellthread, :threshold])
82       )
83
84     delist_threshold = Pleroma.Config.get([:mrf_hellthread, :delist_threshold])
85
86     with {:ok, message} <- reject_message(message, reject_threshold),
87          {:ok, message} <- delist_message(message, delist_threshold) do
88       {:ok, message}
89     else
90       e -> e
91     end
92   end
93
94   @impl true
95   def filter(message), do: {:ok, message}
96
97   @impl true
98   def describe,
99     do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
100
101   @impl true
102   def config_description do
103     %{
104       key: :mrf_hellthread,
105       related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
106       label: "MRF Hellthread",
107       description: "Block messages with excessive user mentions",
108       children: [
109         %{
110           key: :delist_threshold,
111           type: :integer,
112           description:
113             "Number of mentioned users after which the message gets removed from timelines and" <>
114               "disables notifications. Set to 0 to disable.",
115           suggestions: [10]
116         },
117         %{
118           key: :reject_threshold,
119           type: :integer,
120           description:
121             "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
122           suggestions: [20]
123         }
124       ]
125     }
126   end
127 end