diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 6276304a75..e4220abc45 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.Mind; +using Content.Shared.Database; // Starlight using Content.Shared.Players.RateLimiting; using JetBrains.Annotations; using Robust.Server.Player; @@ -38,6 +39,7 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IBanManager _banManager = default!; // Starlight [Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerLocator _playerLocator = default!; @@ -80,6 +82,14 @@ namespace Content.Server.Administration.Systems // Should be shorter than DescriptionMax private const ushort MessageLengthCap = 3000; + // Begin Starlight additions + private readonly TimeSpan _messageCooldown = TimeSpan.FromSeconds(2); + + private readonly Queue<(NetUserId Channel, string Text, TimeSpan Timestamp)> _recentMessages = new(); + private const int MaxRecentMessages = 10; + private const int SpamCheckMessageCount = 3; + // End Starlight additions + // Text to be used to cut off messages that are too long. Should be shorter than MessageLengthCap private const string TooLongText = "... **(too long)**"; @@ -726,6 +736,18 @@ namespace Content.Server.Administration.Systems return; } + // Begin Starlight Changes + var currentTime = _timing.RealTime; + + if (IsOnCooldown(message.UserId, currentTime)) + return; + + if (IsSpam(message.UserId, message.Text)) + _banManager.CreateServerBan(senderSession.UserId, senderSession.Name, null, null, null, 0, NoteSeverity.High, "Automatic AHELP Antispam system Ban, If this ban is wrong, file an appeal."); + + AddToRecentMessages(message.UserId, message.Text, currentTime); + // End Starlight Changes + if (_rateLimit.CountAction(eventArgs.SenderSession, RateLimitKey) != RateLimitStatus.Allowed) return; @@ -993,6 +1015,43 @@ namespace Content.Server.Administration.Systems /// public bool OnCall; } + + // Begin Starlight Changes + private void AddToRecentMessages(NetUserId channelId, string text, TimeSpan timestamp) + { + _recentMessages.Enqueue((channelId, text, timestamp)); + + if (_recentMessages.Count > MaxRecentMessages) + { + _recentMessages.Dequeue(); + } + } + + private bool IsOnCooldown(NetUserId channelId, TimeSpan currentTime) + { + var lastMessage = _recentMessages + .Where(msg => msg.Channel == channelId) + .OrderByDescending(msg => msg.Timestamp) + .FirstOrDefault(); + + return lastMessage != default && (currentTime - lastMessage.Timestamp) < _messageCooldown; + } + + private bool IsSpam(NetUserId channelId, string text) + { + var recentMessages = _recentMessages + .Where(msg => msg.Channel == channelId) + .OrderByDescending(msg => msg.Timestamp) + .Take(10); + + return recentMessages.All(msg => msg.Text == text) && recentMessages.Count() >= 5; + } + + public IEnumerable<(NetUserId Channel, string Text, TimeSpan Timestamp)> GetRecentMessages() + { + return _recentMessages; + } + // End Starlight Changes } public sealed class AHelpMessageParams