diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index a07115544bfd..0a797aa02a8d 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Afk; using Content.Server.Discord; using Content.Server.GameTicking; +using Content.Server.Players.RateLimiting; using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Mind; @@ -27,6 +28,8 @@ namespace Content.Server.Administration.Systems [UsedImplicitly] public sealed partial class BwoinkSystem : SharedBwoinkSystem { + private const string RateLimitKey = "AdminHelp"; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IConfigurationManager _config = default!; @@ -35,6 +38,7 @@ public sealed partial class BwoinkSystem : SharedBwoinkSystem [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly SharedMindSystem _minds = default!; [Dependency] private readonly IAfkManager _afkManager = default!; + [Dependency] private readonly PlayerRateLimitManager _rateLimit = default!; [GeneratedRegex(@"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")] private static partial Regex DiscordRegex(); @@ -80,6 +84,22 @@ public override void Initialize() SubscribeLocalEvent(OnGameRunLevelChanged); SubscribeNetworkEvent(OnClientTypingUpdated); + + _rateLimit.Register( + RateLimitKey, + new RateLimitRegistration + { + CVarLimitPeriodLength = CCVars.AhelpRateLimitPeriod, + CVarLimitCount = CCVars.AhelpRateLimitCount, + PlayerLimitedAction = PlayerRateLimitedAction + }); + } + + private void PlayerRateLimitedAction(ICommonSession obj) + { + RaiseNetworkEvent( + new BwoinkTextMessage(obj.UserId, default, Loc.GetString("bwoink-system-rate-limited"), playSound: false), + obj.Channel); } private void OnOverrideChanged(string obj) @@ -395,6 +415,9 @@ protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySes return; } + if (_rateLimit.CountAction(eventArgs.SenderSession, RateLimitKey) != RateLimitStatus.Allowed) + return; + var escapedText = FormattedMessage.EscapeText(message.Text); string bwoinkText; diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index f20ea21491dc..1a1a7f02262d 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -883,6 +883,25 @@ public static readonly CVarDef public static readonly CVarDef AdminBypassMaxPlayers = CVarDef.Create("admin.bypass_max_players", true, CVar.SERVERONLY); + /* + * AHELP + */ + + /// + /// Ahelp rate limit values are accounted in periods of this size (seconds). + /// After the period has passed, the count resets. + /// + /// + public static readonly CVarDef AhelpRateLimitPeriod = + CVarDef.Create("ahelp.rate_limit_period", 2, CVar.SERVERONLY); + + /// + /// How many ahelp messages are allowed in a single rate limit period. + /// + /// + public static readonly CVarDef AhelpRateLimitCount = + CVarDef.Create("ahelp.rate_limit_count", 10, CVar.SERVERONLY); + /* * Explosions */ diff --git a/Resources/Locale/en-US/administration/bwoink.ftl b/Resources/Locale/en-US/administration/bwoink.ftl index 474af89c268c..3a92f58ad18b 100644 --- a/Resources/Locale/en-US/administration/bwoink.ftl +++ b/Resources/Locale/en-US/administration/bwoink.ftl @@ -14,3 +14,5 @@ bwoink-system-typing-indicator = {$players} {$count -> admin-bwoink-play-sound = Bwoink? bwoink-title-none-selected = None selected + +bwoink-system-rate-limited = System: you are sending messages too quickly.