Merge branch 'ecto-rollback-in-test-env' into 'develop'
[akkoma] / lib / pleroma / web / activity_pub / mrf / hellthread_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.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
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