using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.ValveConstants.Protobuf; using CS2_SimpleAdmin.Managers; using CS2_SimpleAdmin.Menus; using CS2_SimpleAdminApi; namespace CS2_SimpleAdmin; public partial class CS2_SimpleAdmin { [RequiresPermissions("@css/ban")] [CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnBanCommand(CCSPlayerController? caller, CommandInfo command) { var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName; if (command.ArgCount < 2) return; var targets = GetTarget(command); if (targets == null) return; var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList(); if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0) { return; } var reason = command.ArgCount >= 3 ? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim() : _localizer?["sa_unknown"] ?? "Unknown"; reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason; var time = Helper.ParsePenaltyTime(command.GetArg(2)); playersToTarget.ForEach(player => { if (!caller.CanTarget(player)) return; if (time < 0 && caller != null && caller.IsValid && Config.OtherSettings.ShowBanMenuIfNoTime) { DurationMenu.OpenMenu(caller, $"{_localizer?["sa_ban"] ?? "Ban"}: {player.PlayerName}", player, ManagePlayersMenu.BanMenu); return; } Ban(caller, player, time, reason, callerName, BanManager, command); }); } internal void Ban(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, BanManager? banManager = null, CommandInfo? command = null, bool silent = false) { if (Database == null || !player.IsValid || !player.UserId.HasValue) return; if (!caller.CanTarget(player)) return; if (!CheckValidBan(caller, time)) return; // Set default caller name if not provided callerName = !string.IsNullOrEmpty(caller?.PlayerName) ? caller.PlayerName : (_localizer?["sa_console"] ?? "Console"); // Get player and admin information var playerInfo = PlayersInfo[player.UserId.Value]; var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; // Asynchronously handle banning logic Task.Run(async () => { int? penaltyId = await BanManager.BanPlayer(playerInfo, adminInfo, reason, time); SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Ban, reason, time, penaltyId); }); // Determine message keys and arguments based on ban time var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0 ? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm", [reason, "CALLER"], ["CALLER", player.PlayerName, reason]) : ("sa_player_ban_message_time", "sa_admin_ban_message_time", new object[] { reason, time, "CALLER" }, new object[] { "CALLER", player.PlayerName, reason, time }); // Display center message to the player Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs); // Display admin activity message if necessary if (caller == null || !SilentPlayers.Contains(caller.Slot)) { Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs); } // Schedule a kick timer if (player.UserId.HasValue) { Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED, Config.OtherSettings.KickTime); } // Execute ban command if necessary if (UnlockedCommands) { Server.ExecuteCommand($"banid 1 {new SteamID(player.SteamID).SteamId3}"); } if (!silent) { if (command == null) Helper.LogCommand(caller, $"css_ban {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}"); else Helper.LogCommand(caller, command); } Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer); } internal void AddBan(CCSPlayerController? caller, SteamID steamid, int time, string reason, BanManager? banManager = null) { // Set default caller name if not provided var callerName = !string.IsNullOrEmpty(caller?.PlayerName) ? caller.PlayerName : (_localizer?["sa_console"] ?? "Console"); var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64.ToString()); if (player != null && player.IsValid) { if (!caller.CanTarget(player)) return; Ban(caller, player, time, reason, callerName, silent: true); //command.ReplyToCommand($"Banned player {player.PlayerName}."); } else { if (!caller.CanTarget(steamid)) return; // Asynchronous ban operation if player is not online or not found Task.Run(async () => { int? penaltyId = await BanManager.AddBanBySteamid(steamid.SteamId64.ToString(), adminInfo, reason, time); SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Ban, reason, time, penaltyId); }); Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Ban, _localizer); } } [RequiresPermissions("@css/ban")] [CommandHelper(minArgs: 1, usage: " [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command) { if (Database == null) return; var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console"; if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return; if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null) { command.ReplyToCommand("Invalid SteamID64."); return; } var steamid = steamId.SteamId64.ToString(); var reason = command.ArgCount >= 3 ? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim() : _localizer?["sa_unknown"] ?? "Unknown"; reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason; var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2))); if (!CheckValidBan(caller, time)) return; var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; var player = Helper.GetPlayerFromSteamid64(steamid); if (player != null && player.IsValid) { if (!caller.CanTarget(player)) return; Ban(caller, player, time, reason, callerName, silent: true); //command.ReplyToCommand($"Banned player {player.PlayerName}."); } else { if (!caller.CanTarget(new SteamID(steamId.SteamId64))) return; // Asynchronous ban operation if player is not online or not found Task.Run(async () => { int? penaltyId = await BanManager.AddBanBySteamid(steamid, adminInfo, reason, time); SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Ban, reason, time, penaltyId); }); Helper.SendDiscordPenaltyMessage(caller, steamid, reason, time, PenaltyType.Ban, _localizer); command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline."); } Helper.LogCommand(caller, command); if (UnlockedCommands) Server.ExecuteCommand($"banid 1 {steamId.SteamId3}"); } [RequiresPermissions("@css/ban")] [CommandHelper(minArgs: 1, usage: " [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command) { if (Database == null) return; var callerName = caller?.PlayerName ?? _localizer?["sa_console"] ?? "Console"; if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return; var ipAddress = command.GetArg(1); if (!Helper.IsValidIp(ipAddress)) { command.ReplyToCommand($"Invalid IP address."); return; } var reason = command.ArgCount >= 3 ? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim() : _localizer?["sa_unknown"] ?? "Unknown"; reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason; var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2))); if (!CheckValidBan(caller, time)) return; var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; var player = Helper.GetPlayerFromIp(ipAddress); if (player != null && player.IsValid) { if (!caller.CanTarget(player)) return; Ban(caller, player, time, reason, callerName, silent: true); } else { // Asynchronous ban operation if player is not online or not found Task.Run(async () => { await BanManager.AddBanByIp(ipAddress, adminInfo, reason, time); }); command.ReplyToCommand($"Player with ip {ipAddress} is not online. Ban has been added offline."); } Helper.LogCommand(caller, command); } private bool CheckValidBan(CCSPlayerController? caller, int duration) { if (caller == null) return true; var canPermBan = AdminManager.PlayerHasPermissions(new SteamID(caller.SteamID), "@css/permban"); if (duration <= 0 && canPermBan == false) { caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}"); return false; } if (duration <= Config.OtherSettings.MaxBanDuration || canPermBan) return true; caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_max_duration_exceeded", Config.OtherSettings.MaxBanDuration]}"); return false; } [RequiresPermissions("@css/unban")] [CommandHelper(minArgs: 1, usage: " [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command) { if (Database == null) return; var callerSteamId = caller?.SteamID.ToString() ?? _localizer?["sa_console"] ?? "Console"; if (command.GetArg(1).Length <= 1) { command.ReplyToCommand($"Too short pattern to search."); return; } var pattern = command.GetArg(1); var reason = command.ArgCount >= 2 ? string.Join(" ", Enumerable.Range(2, command.ArgCount - 2).Select(command.GetArg)).Trim() : _localizer?["sa_unknown"] ?? "Unknown"; reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason; Task.Run(async () => await BanManager.UnbanPlayer(pattern, callerSteamId, reason)); Helper.LogCommand(caller, command); command.ReplyToCommand($"Unbanned player with pattern {pattern}."); } [RequiresPermissions("@css/kick")] [CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnWarnCommand(CCSPlayerController? caller, CommandInfo command) { if (Database == null) return; var callerName = caller == null ? _localizer?["sa_console"] ?? "Console" : caller.PlayerName; if (command.ArgCount < 2) return; var targets = GetTarget(command); if (targets == null) return; var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList(); if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0) { return; } WarnManager warnManager = new(Database); var time = Math.Max(0, Helper.ParsePenaltyTime(command.GetArg(2))); var reason = command.ArgCount >= 3 ? string.Join(" ", Enumerable.Range(3, command.ArgCount - 3).Select(command.GetArg)).Trim() : _localizer?["sa_unknown"] ?? "Unknown"; reason = string.IsNullOrWhiteSpace(reason) ? _localizer?["sa_unknown"] ?? "Unknown" : reason; playersToTarget.ForEach(player => { if (caller!.CanTarget(player)) { Warn(caller, player, time, reason, callerName, warnManager, command); } }); } internal void Warn(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, WarnManager? warnManager = null, CommandInfo? command = null) { if (Database == null || !player.IsValid || !player.UserId.HasValue) return; if (!caller.CanTarget(player)) return; if (!CheckValidBan(caller, time)) return; // Set default caller name if not provided callerName = !string.IsNullOrEmpty(caller?.PlayerName) ? caller.PlayerName : (_localizer?["sa_console"] ?? "Console"); // Freeze player pawn if alive if (player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE) { player.PlayerPawn?.Value?.Freeze(); AddTimer(5.0f, () => player.PlayerPawn?.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); } // Get player and admin information var playerInfo = PlayersInfo[player.UserId.Value]; var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; // Asynchronously handle warning logic Task.Run(async () => { warnManager ??= new WarnManager(Database); int? penaltyId = await warnManager.WarnPlayer(playerInfo, adminInfo, reason, time); SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Warn, reason, time, penaltyId); // Check for warn thresholds and execute punish command if applicable var totalWarns = await warnManager.GetPlayerWarnsCount(player.SteamID.ToString()); if (Config.WarnThreshold.Count > 0) { string? punishCommand = null; var lastKey = Config.WarnThreshold.Keys.Max(); if (totalWarns >= lastKey) punishCommand = Config.WarnThreshold[lastKey]; else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value)) punishCommand = value; if (!string.IsNullOrEmpty(punishCommand)) { await Server.NextFrameAsync(() => { Server.ExecuteCommand(punishCommand.Replace("USERID", playerInfo.UserId.ToString()).Replace("STEAMID64", playerInfo.SteamId?.ToString())); }); } } }); // Determine message keys and arguments based on warning time var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0 ? ("sa_player_warn_message_perm", "sa_admin_warn_message_perm", new object[] { reason, "CALLER" }, new object[] { "CALLER", player.PlayerName, reason }) : ("sa_player_warn_message_time", "sa_admin_warn_message_time", [reason, time, "CALLER"], ["CALLER", player.PlayerName, reason, time]); // Display center message to the playser Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs); // Display admin activity message if necessary if (caller == null || !SilentPlayers.Contains(caller.Slot)) { Helper.ShowAdminActivity(activityMessageKey, callerName, false, adminActivityArgs); } // Log the warning command if (command == null) Helper.LogCommand(caller, $"css_warn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}"); else Helper.LogCommand(caller, command); // Send Discord notification for the warning Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer); } internal void AddWarn(CCSPlayerController? caller, SteamID steamid, int time, string reason, WarnManager? warnManager = null) { // Set default caller name if not provided var callerName = !string.IsNullOrEmpty(caller?.PlayerName) ? caller.PlayerName : (_localizer?["sa_console"] ?? "Console"); var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null; var player = Helper.GetPlayerFromSteamid64(steamid.SteamId64.ToString()); if (player != null && player.IsValid) { if (!caller.CanTarget(player)) return; Warn(caller, player, time, reason, callerName); //command.ReplyToCommand($"Banned player {player.PlayerName}."); } else { if (!caller.CanTarget(steamid)) return; // Asynchronous ban operation if player is not online or not found Task.Run(async () => { int? penaltyId = await WarnManager.AddWarnBySteamid(steamid.SteamId64.ToString(), adminInfo, reason, time); SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamid, adminInfo, PenaltyType.Warn, reason, time, penaltyId); // Check for warn thresholds and execute punish command if applicable var totalWarns = await WarnManager.GetPlayerWarnsCount(steamid.SteamId64.ToString()); if (Config.WarnThreshold.Count > 0) { string? punishCommand = null; var lastKey = Config.WarnThreshold.Keys.Max(); if (totalWarns >= lastKey) punishCommand = Config.WarnThreshold[lastKey]; else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value)) punishCommand = value; if (!string.IsNullOrEmpty(punishCommand)) { await Server.NextFrameAsync(() => { Server.ExecuteCommand(punishCommand.Replace("STEAMID64", steamid.SteamId64.ToString())); }); } } }); Helper.SendDiscordPenaltyMessage(caller, steamid.SteamId64.ToString(), reason, time, PenaltyType.Warn, _localizer); } } [RequiresPermissions("@css/kick")] [CommandHelper(minArgs: 1, usage: "", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command) { if (Database == null) return; if (command.GetArg(1).Length <= 1) { command.ReplyToCommand($"Too short pattern to search."); return; } var pattern = command.GetArg(1); Task.Run(async () => await WarnManager.UnwarnPlayer(pattern)); Helper.LogCommand(caller, command); command.ReplyToCommand($"Unwarned player with pattern {pattern}."); } }