From da1cb1d3c46d5d8daea1e86330c039ccdc9bf72d Mon Sep 17 00:00:00 2001 From: Mint <> Date: Sun, 16 Apr 2023 17:46:36 +0300 Subject: [PATCH] Somewhat smarter rate limiting --- config/config.exs | 2 + .../activity_pub/mrf/high_roller_policy.ex | 52 ++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/config/config.exs b/config/config.exs index e86fe5641..f1dfd5ee6 100644 --- a/config/config.exs +++ b/config/config.exs @@ -446,6 +446,8 @@ config :pleroma, :mrf_inline_quote, prefix: "RT" config :pleroma, :mrf_high_roller, user: "blockbot", + timeout: 60, + global_threshold: 5, actor_blacklist: [], domain_greylist: [], domain_blacklist: [], diff --git a/lib/pleroma/web/activity_pub/mrf/high_roller_policy.ex b/lib/pleroma/web/activity_pub/mrf/high_roller_policy.ex index b09037907..f75756076 100644 --- a/lib/pleroma/web/activity_pub/mrf/high_roller_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/high_roller_policy.ex @@ -58,9 +58,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do @impl true def filter(message) do - with {:error, _} <- Cachex.stats(:highroller), do: Cachex.start(:highroller) - {_, n} = Cachex.fetch(:highroller, message["actor"], fn(i) -> {:commit, :os.system_time(:seconds)-1} end) - t = :os.system_time(:seconds) + with {:error, _} <- Cachex.stats(:highroller), do: Cachex.start(:highroller, [ stats: true ]) + systime = :os.system_time(:seconds) with {true, action, object} <- is_block_or_unblock(message), %User{} = actor <- User.get_cached_by_ap_id(message["actor"]), @@ -69,6 +68,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do false <- Enum.member?(Config.get([:mrf_high_roller, :domain_blacklist]), URI.parse(message["actor"]).host), true <- recipient.local do + {_, actiontime} = Cachex.fetch(:highroller, actor.nickname<>","<>recipient.nickname<>","<>action, fn(_i) -> {:commit, :os.system_time(:seconds)-1} end) + {_, globalcount} = Cachex.fetch(:highroller, "global:"<>actor.nickname, fn(_i) -> {:commit, 0} end) + blocker = if(Config.get([:mrf_high_roller, :tag_blocking_actor]) && !Enum.member?(Config.get([:mrf_high_roller, :domain_greylist]), URI.parse(message["actor"]).host)) do "@" <> actor.nickname else @@ -90,15 +92,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do end ) - if t > n do - Cachex.put(:highroller, message["actor"], t+15) + if (systime > actiontime && globalcount < Config.get([:mrf_high_roller, :global_threshold])) do + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.put(:highroller, actor.nickname<>","<>recipient.nickname<>","<>action, systime+Config.get([:mrf_high_roller, :timeout])) CommonAPI.post(User.get_by_nickname(Config.get([:mrf_high_roller, :user])), %{ status: msg, visibility: Config.get([:mrf_high_roller, :block_visibility]) }) else Logger.warn("Rate-limited incoming block notif! #{inspect(message)}") - Cachex.incr(:highroller, message["actor"], 30*(1+(n-t))) + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.incr(:highroller, actor.nickname<>","<>recipient.nickname<>","<>action, 30*(1+(systime-actiontime))) end end @@ -109,6 +113,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do false <- Enum.member?(Config.get([:mrf_high_roller, :domain_blacklist]), URI.parse(message["actor"]).host), true <- recipient.local do + {_, actiontime} = Cachex.fetch(:highroller, actor.nickname<>","<>recipient.nickname<>",report", fn(_i) -> {:commit, :os.system_time(:seconds)-1} end) + {_, globalcount} = Cachex.fetch(:highroller, "global:"<>actor.nickname, fn(_i) -> {:commit, 0} end) + reporter = if(Config.get([:mrf_high_roller, :tag_reporting_actor]) && !Enum.member?(Config.get([:mrf_high_roller, :domain_greylist]), URI.parse(message["actor"]).host)) do "@" <> actor.nickname else @@ -149,15 +156,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do _ -> "" end - if t > n do - Cachex.put(:highroller, message["actor"], t+15) + if (systime > actiontime && globalcount < Config.get([:mrf_high_roller, :global_threshold])) do + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.put(:highroller, actor.nickname<>","<>recipient.nickname<>",report", systime+Config.get([:mrf_high_roller, :timeout])) CommonAPI.post(User.get_by_nickname(Config.get([:mrf_high_roller, :user])), %{ status: msg <> comment <> posts, visibility: Config.get([:mrf_high_roller, :report_visibility]) }) else Logger.warn("Rate-limited incoming report notif! #{inspect(message)}") - Cachex.incr(:highroller, message["actor"], 30*(1+(n-t))) + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.incr(:highroller, actor.nickname<>","<>recipient.nickname<>",report", 30*(1+(systime-actiontime))) end end @@ -168,6 +177,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do false <- Enum.member?(Config.get([:mrf_high_roller, :domain_blacklist]), URI.parse(message["actor"]).host), true <- recipient.local do + {_, actiontime} = Cachex.fetch(:highroller, actor.nickname<>","<>recipient.nickname<>",unfollow", fn(_i) -> {:commit, :os.system_time(:seconds)-1} end) + {_, globalcount} = Cachex.fetch(:highroller, "global:"<>actor.nickname, fn(_i) -> {:commit, 0} end) + unfollower = if(Config.get([:mrf_high_roller, :tag_unfollowing_actor]) && !Enum.member?(Config.get([:mrf_high_roller, :domain_greylist]), URI.parse(message["actor"]).host)) do "@" <> actor.nickname else @@ -188,15 +200,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do end ) - if t > n do - Cachex.put(:highroller, message["actor"], t+15) + if (systime > actiontime && globalcount < Config.get([:mrf_high_roller, :global_threshold])) do + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.put(:highroller, actor.nickname<>","<>recipient.nickname<>",unfollow", systime+Config.get([:mrf_high_roller, :timeout])) CommonAPI.post(User.get_by_nickname(Config.get([:mrf_high_roller, :user])), %{ status: msg, visibility: Config.get([:mrf_high_roller, :unfollow_visibility]) }) else Logger.warn("Rate-limited incoming unfollow notif! #{inspect(message)}") - Cachex.incr(:highroller, message["actor"], 30*(1+(n-t))) + Cachex.incr(:highroller, "global:"<>actor.nickname, globalcount+1) + Cachex.incr(:highroller, actor.nickname<>","<>recipient.nickname<>",unfollow", 30*(1+(systime-actiontime))) end end @@ -218,6 +232,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.HighRollerPolicy do description: "Account from which notifications would be sent", suggestions: ["blockbot"] }, + %{ + key: :timeout, + type: :integer, + label: "Timeout", + description: "Timeout (in seconds) between which no new notifications of the same type can be sent", + suggestions: [60] + }, + %{ + key: :global_threshold, + type: :integer, + label: "Global threshold", + description: "Global threshold of the actions for the actor", + suggestions: [5] + }, %{ key: :actor_blacklist, type: {:list, :string},