using System; using System.Collections.Generic; using System.Linq; using Oxide.Core; using UnityEngine; using Oxide.Core.Libraries.Covalence; namespace Oxide.Plugins { [Info("CommandRateLimiter", "Calytic", "0.1.2", ResourceId = 1812)] public class CommandRateLimiter : CovalencePlugin { private int KickAfter; private int CooldownMS; private int ClearRateCountSeconds; private bool LogExcessiveUsage; private bool SendPlayerMessage; Dictionary lastRun = new Dictionary(); Dictionary rateTimer = new Dictionary(); Dictionary rateCount = new Dictionary(); List commandWhitelist = new List(); private Dictionary> spamLog = new Dictionary>(); void OnServerInitialized() { Config["CooldownMS"] = CooldownMS = GetConfig("CooldownMS", 195); Config["KickAfter"] = KickAfter = GetConfig("KickAfter", 10); Config["ClearRateCountSeconds"] = ClearRateCountSeconds = GetConfig("ClearRateCountSeconds", 5); Config["LogExcessiveUsage"] = LogExcessiveUsage = GetConfig("LogExcessiveUsage", false); Config["SendPlayerMessage"] = SendPlayerMessage = GetConfig("SendPlayerMessage", true); Config["CommandWhitelist"] = commandWhitelist = GetConfig("CommandWhitelist", GetDefaultWhitelist()); Config["Version"] = Version.ToString(); SaveConfig(); LoadMessages(); } List GetDefaultWhitelist() { return new List() { "add" }; } void LoadMessages() { lang.RegisterMessages(new Dictionary { {"Player Message", "You are doing that too often"}, {"Kick Message", "Spamming"}, }, this); } void LoadDefaultConfig() { Config["CooldownMS"] = 195; Config["KickAfter"] = 10; Config["ClearRateCountSeconds"] = 5; Config["LogExcessiveUsage"] = false; Config["SendPlayerMessage"] = true; Config["CommandWhitelist"] = GetDefaultWhitelist(); Config["Version"] = Version.ToString(); } private T GetConfig(string name, T defaultValue) { if (Config[name] == null) { return defaultValue; } return (T)Convert.ChangeType(Config[name], typeof(T)); } object OnServerCommand(ConsoleSystem.Arg arg) { if (arg.cmd == null) return null; if (arg.Player() == null) { return null; } IPlayer player = null; player = covalence.Players.FindPlayerById(arg.Player().UserIDString); if (player == null) return null; if (player.IsConnected == false) return null; if (arg.IsAdmin) return null; DateTime lastTime; if (lastRun.TryGetValue(player.Id, out lastTime)) { TimeSpan ts = DateTime.Now - lastTime; if (ts.TotalMilliseconds < CooldownMS) { if (arg.cmd.Name != null) { if (commandWhitelist.Contains(arg.cmd.Name)) { return null; } if (LogExcessiveUsage) { if (!spamLog.ContainsKey(player.Id)) { spamLog.Add(player.Id, new Dictionary()); timer.In(ClearRateCountSeconds, delegate() { List msgs = new List(); foreach (KeyValuePair kvp in spamLog[player.Id]) { msgs.Add(kvp.Key + " (" + kvp.Value + ")"); } string cmds = string.Join(", ", msgs.ToArray()); PrintWarning(player.Name + " (" + player.Id + ") spamming commands: " + cmds); spamLog.Remove(player.Id); }); } if (!spamLog[player.Id].ContainsKey(arg.cmd.Name)) { spamLog[player.Id].Add(arg.cmd.Name, 1); } else { spamLog[player.Id][arg.cmd.Name]++; } } } int c = 0; bool kicked = false; if (rateCount.TryGetValue(player.Id, out c)) { rateCount[player.Id]++; if (KickAfter > 0 && (c + 1) >= KickAfter) { player.Kick(GetMsg("Kick Message")); kicked = true; } } else { rateCount.Add(player.Id, 1); } if (ClearRateCountSeconds > 0) { Timer rtimer; if (rateTimer.TryGetValue(player.Id, out rtimer)) { if (!rtimer.Destroyed) { rtimer.Destroy(); } rateTimer.Remove(player.Id); } timer.In(ClearRateCountSeconds, delegate() { if (rateCount.TryGetValue(player.Id, out c)) { rateCount[player.Id] = 0; } }); } if (player != null && SendPlayerMessage && !kicked) { player.Reply(GetMsg("Player Message")); } } else { lastRun[player.Id] = DateTime.Now; } } else { lastRun.Add(player.Id, DateTime.Now); } return null; } void OnUserDisconnected(IPlayer player) { int c = 0; if (rateCount.TryGetValue(player.Id, out c)) { rateCount.Remove(player.Id); } } string GetMsg(string key, object userID = null) { return lang.GetMessage(key, this, userID == null ? null : userID.ToString()); } } }