Иконка ресурса

FREE AutoReportBan : 1.3.1 1.3.1

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Threading.Tasks;

//  ____  _       _ _       
// / ___|(_) __ _(_) | ___  
// \___ \| |/ _` | | |/ _ \ 
//  ___) | | (_| | | | (_) |
// |____/|_|\__, |_|_|\___/ 
//          |___/             
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾
// ✦    RUST PLUGINS   ✦
// ✦     Sigilo.dev    ✦
// ☽✧✵✧✵✧✵✧✵✧✵✧✵✧☾

namespace Oxide.Plugins
{
    [Info("AutoReportBan", "Sigilo", "1.3.1")]
    [Description("Automatically bans players who receive multiple reports within a time window using Rust's native F7 report system")]
    class AutoReportBan : RustPlugin
    {
        #region Configuration

        private Configuration config;

        private class Configuration
        {
            [JsonProperty("Reports threshold")]
            public int ReportsThreshold = 10;

            [JsonProperty("Time window (minutes)")]
            public int TimeWindowMinutes = 30;

            [JsonProperty("Ban reason template")]
            public string BanReasonTemplate = "Banned by server";

            [JsonProperty("Ban duration (minutes, 0 = permanent)")]
            public int BanDurationMinutes = 0;

            [JsonProperty("Debug mode")]
            public bool DebugMode = false;
            
            [JsonProperty("Discord Webhook URL")]
            public string WebhookUrl = "";
            
            [JsonProperty("Report Discord Webhook URL")]
            public string ReportWebhookUrl = "";
            
            [JsonProperty("Discord Bot Name")]
            public string DiscordBotName = "Auto Report Ban";
            
            [JsonProperty("Server Name")]
            public string ServerName = "Rust Server";

            [JsonProperty("Reporter cooldown after successful ban (minutes)")]
            public int ReporterCooldownMinutes = 60;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) throw new Exception("Could not read config file");
                SaveConfig();
                PrintDebug("Configuration loaded successfully");
            }
            catch (Exception ex)
            {
                PrintError($"Error loading config: {ex.Message}");
                config = new Configuration();
            }
        }

        protected override void LoadDefaultConfig() => config = new Configuration();

        protected override void SaveConfig() => Config.WriteObject(config);

        #endregion

        #region Data Storage

        private class PlayerReport
        {
            public ulong targetId;
            public string reporterId;
            public string type;
            public string reason;
            public string description;
            public DateTime Timestamp;
        }

        private class StoredData
        {
            public Dictionary<string, List<PlayerReport>> PlayerReports = new Dictionary<string, List<PlayerReport>>();
            public HashSet<string> WhitelistedPlayers = new HashSet<string>();
            public Dictionary<string, DateTime> ReporterCooldowns = new Dictionary<string, DateTime>();
        }

        private StoredData storedData;
        private Dictionary<ulong, DateTime> tempBans = new Dictionary<ulong, DateTime>();
        private Timer cleanupTimer;
        private Timer tempBanTimer;

        private void SaveData() 
        {
            if (storedData == null)
            {
                PrintDebug("Creating new StoredData before saving");
                storedData = new StoredData();
            }
            
            try
            {
                Interface.Oxide.DataFileSystem.WriteObject("AutoReportBan/data", storedData);
                PrintDebug($"Data saved to disk. Current player count in storage: {storedData.PlayerReports.Count}");
                
                int totalReports = 0;
                foreach (var reports in storedData.PlayerReports.Values)
                {
                    totalReports += reports.Count;
                }
                PrintDebug($"Total reports in storage: {totalReports}");
                
                var dataCheck = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("AutoReportBan/data");
                if (dataCheck != null && dataCheck.PlayerReports != null)
                {
                    int checkTotalReports = 0;
                    foreach (var reports in dataCheck.PlayerReports.Values)
                    {
                        checkTotalReports += reports.Count;
                    }
                    PrintDebug($"Data verification - Read back {dataCheck.PlayerReports.Count} players with {checkTotalReports} total reports from storage");
                }
                else
                {
                    PrintError("WARNING: Data verification failed - could not read back data file");
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error saving data: {ex.Message}");
                PrintError($"Stack trace: {ex.StackTrace}");
            }
        }

        private void SaveTempBanData()
        {
            Interface.Oxide.DataFileSystem.WriteObject("AutoReportBan/tempbans", tempBans);
        }

        private void LoadData()
        {
            try
            {
                storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>("AutoReportBan/data");
                if (storedData == null)
                {
                    PrintDebug("No existing data found, creating new data file");
                    storedData = new StoredData();
                    SaveData();
                }
                else
                {
                    PrintDebug($"Data loaded successfully. Found {storedData.PlayerReports.Count} players with reports");
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error loading data: {ex.Message}");
                storedData = new StoredData();
            }
            
            try
            {
                tempBans = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, DateTime>>("AutoReportBan/tempbans");
                if (tempBans == null)
                {
                    PrintDebug("No existing temp ban data found, creating new temp ban file");
                    tempBans = new Dictionary<ulong, DateTime>();
                    SaveTempBanData();
                }
                else
                {
                    PrintDebug($"Temp ban data loaded successfully. Found {tempBans.Count} temporary bans");
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error loading temp ban data: {ex.Message}");
                tempBans = new Dictionary<ulong, DateTime>();
            }
        }

        #endregion

        #region Hooks

        private void Init()
        {
            LoadConfig();
            LoadData();
            
            AddCovalenceCommand("listreports", "ConsoleListReports", "autoreportban.admin");
            AddCovalenceCommand("checkplayer", "ConsoleCheckPlayer", "autoreportban.admin");
            AddCovalenceCommand("testreport", "ConsoleTestReport", "autoreportban.admin");
            AddCovalenceCommand("testwebhook", "ConsoleTestWebhook", "autoreportban.admin");
            AddCovalenceCommand("cleanreports", "ConsoleCleanReports", "autoreportban.admin");
            AddCovalenceCommand("arb_addwl", "ConsoleWhitelistAdd", "autoreportban.admin");
            AddCovalenceCommand("arb_removewl", "ConsoleWhitelistRemove", "autoreportban.admin");
            AddCovalenceCommand("arb_listwl", "ConsoleWhitelistList", "autoreportban.admin");
            
            cleanupTimer = timer.Every(300f, CleanupOldReports);
            tempBanTimer = timer.Every(60f, CheckTempBans);
            
            CleanupOldReports();
            
            Puts("AutoReportBan initialized");
            
            Subscribe("OnPlayerReported");
        }

        private void Unload()
        {
            if (cleanupTimer != null && !cleanupTimer.Destroyed) cleanupTimer.Destroy();
            if (tempBanTimer != null && !tempBanTimer.Destroyed) tempBanTimer.Destroy();
            SaveData();
            SaveTempBanData();
            PrintDebug("Plugin unloaded, data saved to disk");
        }

        private void ConsoleListReports(IPlayer player, string command, string[] args)
        {
            if (player == null)
            {
                if (storedData?.PlayerReports == null || storedData.PlayerReports.Count == 0)
                {
                    Puts("No active reports in the system.");
                    return;
                }

                Puts("Current Active Reports:");
                foreach (var kvp in storedData.PlayerReports)
                {
                    var reports = GetRecentReports(kvp.Key);
                    if (reports.Count == 0) continue;

                    BasePlayer targetPlayer = null;
                    if (ulong.TryParse(kvp.Key, out ulong targetSteamId))
                    {
                        targetPlayer = RelationshipManager.FindByID(targetSteamId);
                    }
                    string targetName = targetPlayer?.displayName ?? "Unknown";
                    
                    var uniqueReporters = reports.Select(r => r.reporterId).Distinct().Count();
                    var groupedReports = reports
                        .GroupBy(r => r.type)
                        .ToDictionary(g => g.Key, g => g.Count());
                    
                    Puts($"\n{targetName} [{kvp.Key}]:");
                    Puts($"Total reports: {reports.Count} from {uniqueReporters} unique reporters");
                    foreach (var group in groupedReports)
                    {
                        Puts($"- {group.Key}: {group.Value}");
                    }
                    
                    var oldestReport = reports.Min(r => r.Timestamp);
                    var timeLeft = oldestReport.AddMinutes(config.TimeWindowMinutes) - DateTime.UtcNow;
                    Puts($"Time until oldest report expires: {timeLeft.Minutes}m {timeLeft.Seconds}s");
                }
                return;
            }

            if (!player.HasPermission("autoreportban.admin")) return;

            if (storedData?.PlayerReports == null || storedData.PlayerReports.Count == 0)
            {
                player.Reply("No active reports in the system.");
                return;
            }

            player.Reply("Current Active Reports:");
            foreach (var kvp in storedData.PlayerReports)
            {
                var reports = GetRecentReports(kvp.Key);
                if (reports.Count == 0) continue;

                BasePlayer targetPlayer = null;
                if (ulong.TryParse(kvp.Key, out ulong targetSteamId))
                {
                    targetPlayer = RelationshipManager.FindByID(targetSteamId);
                }
                string targetName = targetPlayer?.displayName ?? "Unknown";
                
                var uniqueReporters = reports.Select(r => r.reporterId).Distinct().Count();
                var groupedReports = reports
                    .GroupBy(r => r.type)
                    .ToDictionary(g => g.Key, g => g.Count());
                
                player.Reply($"\n{targetName} [{kvp.Key}]:");
                player.Reply($"Total reports: {reports.Count} from {uniqueReporters} unique reporters");
                foreach (var group in groupedReports)
                {
                    player.Reply($"- {group.Key}: {group.Value}");
                }
                
                var oldestReport = reports.Min(r => r.Timestamp);
                var timeLeft = oldestReport.AddMinutes(config.TimeWindowMinutes) - DateTime.UtcNow;
                player.Reply($"Time until oldest report expires: {timeLeft.Minutes}m {timeLeft.Seconds}s");
            }
        }

        private void ConsoleCheckPlayer(IPlayer player, string command, string[] args)
        {
            if (player == null)
            {
                if (args.Length == 0)
                {
                    Puts("Usage: checkplayer <name/steamid>");
                    return;
                }

                string consoleTargetId = args[0];
                BasePlayer consoleTargetPlayer = null;

                if (consoleTargetId.Length == 17 && ulong.TryParse(consoleTargetId, out ulong consoleSteamId))
                {
                    consoleTargetPlayer = RelationshipManager.FindByID(consoleSteamId);
                }
                else
                {
                    IPlayer iPlayer = covalence.Players.FindPlayer(consoleTargetId);
                    if (iPlayer != null && ulong.TryParse(iPlayer.Id, out ulong foundSteamId))
                    {
                        consoleTargetPlayer = RelationshipManager.FindByID(foundSteamId);
                    }
                }

                if (consoleTargetPlayer == null)
                {
                    Puts($"Could not find player: {consoleTargetId}");
                    return;
                }

                string consolePlayerTargetId = consoleTargetPlayer.UserIDString;
                var consoleReports = GetRecentReports(consolePlayerTargetId);
                if (consoleReports.Count == 0)
                {
                    Puts($"No active reports found for {consoleTargetPlayer.displayName}");
                    return;
                }

                Puts($"\nReports for {consoleTargetPlayer.displayName} [{consolePlayerTargetId}]:");
                Puts($"Total active reports: {consoleReports.Count}");
                
                var consoleGroupedReports = consoleReports
                    .GroupBy(r => r.type)
                    .ToDictionary(g => g.Key, g => g.Count());
                
                foreach (var group in consoleGroupedReports)
                {
                    Puts($"- {group.Key}: {group.Value}");
                }

                var consoleUniqueReporters = consoleReports.Select(r => r.reporterId).Distinct().Count();
                Puts($"Unique reporters: {consoleUniqueReporters}");
                
                var consoleOldestReport = consoleReports.Min(r => r.Timestamp);
                var consoleTimeLeft = consoleOldestReport.AddMinutes(config.TimeWindowMinutes) - DateTime.UtcNow;
                Puts($"Time until oldest report expires: {consoleTimeLeft.Minutes}m {consoleTimeLeft.Seconds}s");

                foreach (var consoleReport in consoleReports.OrderByDescending(r => r.Timestamp))
                {
                    BasePlayer consoleReporterPlayer = null;
                    if (ulong.TryParse(consoleReport.reporterId, out ulong reporterSteamId))
                    {
                        consoleReporterPlayer = RelationshipManager.FindByID(reporterSteamId);
                    }
                    string consoleReporterName = consoleReporterPlayer?.displayName ?? "Unknown";
                    Puts($"- {consoleReporterName}: {consoleReport.reason} ({(DateTime.UtcNow - consoleReport.Timestamp).Minutes}m ago)");
                }
                return;
            }

            if (!player.HasPermission("autoreportban.admin")) return;
            
            if (args.Length == 0)
            {
                player.Reply("Usage: checkplayer <name/steamid>");
                return;
            }

            string targetIdentifier = args[0];
            BasePlayer targetPlayer = null;

            if (targetIdentifier.Length == 17 && ulong.TryParse(targetIdentifier, out ulong steamId))
            {
                targetPlayer = RelationshipManager.FindByID(steamId);
            }
            else
            {
                IPlayer iPlayer = covalence.Players.FindPlayer(targetIdentifier);
                if (iPlayer != null && ulong.TryParse(iPlayer.Id, out ulong foundSteamId))
                {
                    targetPlayer = RelationshipManager.FindByID(foundSteamId);
                }
            }

            if (targetPlayer == null)
            {
                player.Reply($"Could not find player: {targetIdentifier}");
                return;
            }

            string targetId = targetPlayer.UserIDString;
            var reports = GetRecentReports(targetId);
            if (reports.Count == 0)
            {
                player.Reply($"No active reports found for {targetPlayer.displayName}");
                return;
            }

            player.Reply($"\nReports for {targetPlayer.displayName} [{targetId}]:");
            player.Reply($"Total active reports: {reports.Count}");
            
            var groupedReports = reports
                .GroupBy(r => r.type)
                .ToDictionary(g => g.Key, g => g.Count());
            
            foreach (var group in groupedReports)
            {
                player.Reply($"- {group.Key}: {group.Value}");
            }

            var uniqueReporters = reports.Select(r => r.reporterId).Distinct().Count();
            player.Reply($"Unique reporters: {uniqueReporters}");
            
            var oldestReport = reports.Min(r => r.Timestamp);
            var timeLeft = oldestReport.AddMinutes(config.TimeWindowMinutes) - DateTime.UtcNow;
            player.Reply($"Time until oldest report expires: {timeLeft.Minutes}m {timeLeft.Seconds}s");

            foreach (var report in reports.OrderByDescending(r => r.Timestamp))
            {
                BasePlayer reporterPlayer = null;
                if (ulong.TryParse(report.reporterId, out ulong reporterSteamId))
                {
                    reporterPlayer = RelationshipManager.FindByID(reporterSteamId);
                }
                string reporterName = reporterPlayer?.displayName ?? "Unknown";
                player.Reply($"- {reporterName}: {report.reason} ({(DateTime.UtcNow - report.Timestamp).Minutes}m ago)");
            }
        }

        private void ConsoleTestReport(IPlayer player, string command, string[] args)
        {
            if (player != null && !player.HasPermission("autoreportban.admin")) return;
            
            if (args.Length < 2)
            {
                Puts("Usage: testreport <reporter_id/name> <target_id/name>");
                return;
            }
            
            string reporterIdentifier = args[0];
            string targetIdentifier = args[1];
            
            IPlayer reporter = null;
            if (reporterIdentifier.Length == 17 && ulong.TryParse(reporterIdentifier, out _))
            {
                reporter = covalence.Players.FindPlayerById(reporterIdentifier);
            }
            else
            {
                reporter = covalence.Players.FindPlayer(reporterIdentifier);
            }
            
            if (reporter == null)
            {
                Puts($"Could not find reporter: {reporterIdentifier}");
                return;
            }
            
            IPlayer target = null;
            if (targetIdentifier.Length == 17 && ulong.TryParse(targetIdentifier, out _))
            {
                target = covalence.Players.FindPlayerById(targetIdentifier);
            }
            else
            {
                target = covalence.Players.FindPlayer(targetIdentifier);
            }
            
            if (target == null)
            {
                Puts($"Could not find target: {targetIdentifier}");
                return;
            }
            
            Puts($"Creating test report: {reporter.Name} reporting {target.Name}");
            
            ProcessPlayerReport(reporter, target, "[cheat] Test report from console", "Test description text");
            
            Puts("Test report created and processed");
            
            var reports = GetRecentReports(target.Id);
            Puts($"Current reports for {target.Name}: {reports.Count}");
        }

        private void ConsoleTestWebhook(IPlayer player, string command, string[] args)
        {
            if (player != null && !player.HasPermission("autoreportban.admin")) return;
            
            bool hasMainWebhook = !string.IsNullOrEmpty(config.WebhookUrl);
            bool hasReportWebhook = !string.IsNullOrEmpty(config.ReportWebhookUrl);
            
            if (!hasMainWebhook && !hasReportWebhook)
            {
                string message = player != null 
                    ? "Error: No Discord webhook URLs are configured. Please set at least one in the config file."
                    : "Error: No Discord webhook URLs are configured. Please set at least one in the config file.";
                
                if (player != null) player.Reply(message);
                else Puts(message);
                
                return;
            }
            
            var responseBuilder = new System.Text.StringBuilder("Testing Discord webhook(s)..\n");
            
            if (hasMainWebhook)
            {
                responseBuilder.AppendLine("- Main webhook (bans): Sending test message");
                
                var banFields = new List<Dictionary<string, object>>
                {
                    new Dictionary<string, object>
                    {
                        {"name", "Test Field 1"},
                        {"value", "This is a test field"},
                        {"inline", true}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Test Field 2"},
                        {"value", "This is another test field"},
                        {"inline", true}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Server Info"},
                        {"value", $"Players Online: {BasePlayer.activePlayerList.Count}"},
                        {"inline", false}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Server"},
                        {"value", config.ServerName},
                        {"inline", false}
                    }
                };
                
                SendDiscordEmbed("Test Ban Webhook", "This is a test message from the AutoReportBan plugin (Ban webhook).", 3447003, banFields);
            }
            
            if (hasReportWebhook)
            {
                responseBuilder.AppendLine("- Report webhook (F7 reports): Sending test message");
                
                var reportFields = new List<Dictionary<string, object>>
                {
                    new Dictionary<string, object>
                    {
                        {"name", "Reported Player"},
                        {"value", $"[TestPlayer](https://steamcommunity.com/profiles/76561198000000000) [76561198000000000]"},
                        {"inline", true}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Reporter"},
                        {"value", $"[TestReporter](https://steamcommunity.com/profiles/76561198000000001) [76561198000000001]"},
                        {"inline", true}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Report Type"},
                        {"value", "cheat"},
                        {"inline", true}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Report Reason"},
                        {"value", "[cheat] This is a test report reason"},
                        {"inline", false}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Current Status"},
                        {"value", $"1/{config.ReportsThreshold} unique reporters (1 total reports) in the last {config.TimeWindowMinutes} minutes"},
                        {"inline", false}
                    },
                    new Dictionary<string, object>
                    {
                        {"name", "Server"},
                        {"value", config.ServerName},
                        {"inline", false}
                    }
                };
                
                SendDiscordEmbedToUrl("Test Report Webhook", "This is a test message from the AutoReportBan plugin (Report webhook).", 16750848, reportFields, config.ReportWebhookUrl);
            }
            
            responseBuilder.AppendLine("Test webhook(s) sent. Please check your Discord channel(s).");
            
            string finalResponse = responseBuilder.ToString();
            if (player != null) player.Reply(finalResponse);
            else Puts(finalResponse);
        }

        private void ConsoleCleanReports(IPlayer player, string command, string[] args)
        {
            if (player != null && !player.HasPermission("autoreportban.admin")) return;
            
            player.Reply("Manually triggering cleanup of expired reports...");
            CleanupOldReports();
            player.Reply("Cleanup complete. Check console for details.");
        }

        private void CleanupOldReports()
        {
            if (storedData?.PlayerReports == null) return;
            
            PrintDebug("Starting cleanup of old reports...");
            
            var expiredCooldowns = storedData.ReporterCooldowns.Where(cd => cd.Value < DateTime.UtcNow).ToList();
            if (expiredCooldowns.Count > 0)
            {
                PrintDebug($"Cleaning up {expiredCooldowns.Count} expired reporter cooldowns.");
                foreach (var entry in expiredCooldowns)
                {
                    storedData.ReporterCooldowns.Remove(entry.Key);
                }
            }
            
            DateTime cutoffTime = DateTime.UtcNow.AddMinutes(-config.TimeWindowMinutes);
            int cleanedReports = 0;
            var keysToRemove = new List<string>();
            
            foreach (var playerID in storedData.PlayerReports.Keys)
            {
                var reports = storedData.PlayerReports[playerID];
                int oldCount = reports.Count;
                
                var expiredReports = reports.Where(r => r.Timestamp < cutoffTime).ToList();
                
                if (expiredReports.Count > 0)
                {
                    PrintDebug($"Found {expiredReports.Count} expired reports for player {playerID}");
                    
                    foreach (var report in expiredReports)
                    {
                        reports.Remove(report);
                    }
                    
                    cleanedReports += expiredReports.Count;
                    
                    if (reports.Count == 0)
                    {
                        keysToRemove.Add(playerID);
                        PrintDebug($"All reports expired for player {playerID}, marking for removal");
                    }
                }
            }
            
            foreach (var key in keysToRemove)
            {
                storedData.PlayerReports.Remove(key);
                PrintDebug($"Removed player {key} from storage (all reports expired)");
            }
            
            if (cleanedReports > 0 || keysToRemove.Count > 0)
            {
                PrintDebug($"Cleanup complete: Removed {cleanedReports} expired reports from {keysToRemove.Count} players");
                SaveData();
            }
            else
            {
                PrintDebug("No expired reports found during cleanup");
            }
        }

        private void CheckTempBans()
        {
            if (tempBans.Count == 0) return;
            
            var now = DateTime.Now;
            var expiredBans = tempBans.Where(entry => entry.Value <= now).ToList();
            
            if (expiredBans.Count > 0)
            {
                PrintDebug($"Checking temporary bans: Found {expiredBans.Count} expired bans to remove");
                
                foreach (var entry in expiredBans)
                {
                    try
                    {
                        ulong steamId = entry.Key;
                        string unbanCommand = $"unban {steamId}";
                        PrintDebug($"Executing unban command: {unbanCommand}");
                        ConsoleSystem.Run(ConsoleSystem.Option.Server, unbanCommand);
                        
                        var targetName = covalence.Players.FindPlayerById(steamId.ToString())?.Name ?? "Unknown";
                        Puts($"Temporary ban expired: Unbanned player {targetName} [{steamId}]");
                        
                        tempBans.Remove(steamId);
                        
                        if (!string.IsNullOrEmpty(config.WebhookUrl))
                        {
                            var fields = new List<Dictionary<string, object>>
                            {
                                new Dictionary<string, object> 
                                { 
                                    {"name", "Player"}, 
                                    {"value", $"{targetName} [{steamId}]"}, 
                                    {"inline", true} 
                                },
                                new Dictionary<string, object> 
                                { 
                                    {"name", "Ban Duration"}, 
                                    {"value", $"{config.BanDurationMinutes} minutes"}, 
                                    {"inline", true} 
                                },
                                new Dictionary<string, object> 
                                { 
                                    {"name", "Unbanned At"}, 
                                    {"value", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, 
                                    {"inline", false} 
                                }
                            };
                            
                            SendDiscordEmbed("Temporary Ban Expired", "A player's temporary ban has expired and they have been unbanned.", 3447003, fields);
                        }
                    }
                    catch (Exception ex)
                    {
                        PrintError($"Error removing temporary ban: {ex.Message}");
                    }
                }
                
                SaveTempBanData();
            }
        }

        private void PrintDebug(string message)
        {
            if (config.DebugMode)
            {
                Puts($"[DEBUG] {message}");
            }
        }

        private async Task SendDiscordEmbed(string title, string description, int color, List<Dictionary<string, object>> fields)
        {
            if (string.IsNullOrEmpty(config.WebhookUrl)) return;
            await SendDiscordEmbedToUrl(title, description, color, fields, config.WebhookUrl);
        }

        private async Task SendDiscordEmbedToUrl(string title, string description, int color, List<Dictionary<string, object>> fields, string webhookUrl)
        {
            if (string.IsNullOrEmpty(webhookUrl)) return;

            var embed = new Dictionary<string, object>
            {
                {"title", title},
                {"description", description},
                {"color", color},
                {"timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")}
            };

            if (fields != null && fields.Count > 0)
            {
                embed["fields"] = fields;
            }

            var payloadObj = new Dictionary<string, object>
            {
                {"username", config.DiscordBotName},
                {"embeds", new object[] { embed } }
            };

            var payload = JsonConvert.SerializeObject(payloadObj);

            try
            {
                webrequest.EnqueuePost(
                    webhookUrl.TrimEnd('/'),
                    payload,
                    (code, response) =>
                    {
                        if (code != 204 && code != 200)
                            PrintError($"Discord webhook error: {code} - {response}");
                        else
                            PrintDebug("Discord webhook notification sent successfully");
                    },
                    this,
                    new Dictionary<string, string>
                    {
                        ["Content-Type"] = "application/json"
                    }
                );
            }
            catch (Exception ex)
            {
                PrintError($"Discord embed log failed: {ex.Message}");
            }
        }

        private class ReportData
        {
            public string PlayerId;
            public string PlayerName;
            public string TargetId;
            public string TargetName;
            public string Subject;
            public string Message;
            public string Type;
        }

        [HookMethod("OnPlayerReported")]
        private void OnPlayerReport(string reportJson)
        {
            try
            {
                PrintDebug($"Raw report received: {reportJson}");
                
                if (string.IsNullOrEmpty(reportJson))
                {
                    PrintDebug("Report JSON is null or empty");
                    return;
                }
                
                var report = JsonConvert.DeserializeObject<ReportData>(reportJson);
                
                if (report == null)
                {
                    PrintDebug("Failed to deserialize report JSON");
                    return;
                }
                
                PrintDebug($"Deserialized report - Reporter: {report.PlayerName}[{report.PlayerId}], Target: {report.TargetName}[{report.TargetId}], Type: {report.Type}, Message: {report.Message}");
                
                var covReporter = covalence.Players.FindPlayerById(report.PlayerId);
                var covReported = covalence.Players.FindPlayerById(report.TargetId);
                
                if (covReporter == null)
                {
                    PrintDebug($"Could not find reporter with ID {report.PlayerId} using Covalence");
                    return;
                }

                if (covReported == null)
                {
                    PrintDebug($"Could not find target player with ID {report.TargetId} using Covalence");
                    return;
                }

                PrintDebug($"Found players via Covalence - Reporter: {covReporter.Name}, Target: {covReported.Name}");
                
                ProcessPlayerReport(covReporter, covReported, report.Subject, report.Message);
                PrintDebug($"Report successfully processed and stored");
                
                var storedReports = GetRecentReports(report.TargetId);
                PrintDebug($"Current stored reports for {report.TargetName}: {storedReports.Count}");
                
                SaveData();
                PrintDebug("Data saved after processing report");
            }
            catch (Exception ex)
            {
                PrintError($"Error processing report: {ex.Message}");
                PrintError($"Stack trace: {ex.StackTrace}");
            }
        }

        private void ProcessPlayerReport(IPlayer reporter, IPlayer reportedPlayer, string reportReason, string description = "")
        {
            try
            {
                if (reporter == null || reportedPlayer == null)
                {
                    PrintDebug("ProcessPlayerReport: Reporter or reported player is null");
                    return;
                }

                if (storedData.WhitelistedPlayers.Contains(reportedPlayer.Id))
                {
                    PrintDebug($"Report against {reportedPlayer.Name} blocked because they are whitelisted.");
                    return;
                }

                if (storedData.ReporterCooldowns.TryGetValue(reporter.Id, out DateTime cooldownExpiry) && cooldownExpiry > DateTime.UtcNow)
                {
                    var timeLeft = cooldownExpiry - DateTime.UtcNow;
                    Puts($"BLOCKED: {reporter.Name} is on a reporter cooldown for another {timeLeft.Minutes}m {timeLeft.Seconds}s.");
                    return;
                }

                string reportType = DetermineReportType(reportReason);
                string cleanedReason = CleanReportReason(reportReason);
                string reportedID = reportedPlayer.Id;
                string reporterID = reporter.Id;
                
                Puts($"{reporter.Name} reported {reportedPlayer.Name} for {reportReason}");
                
                if (storedData == null)
                {
                    PrintDebug("StoredData was null, initializing new instance");
                    storedData = new StoredData();
                }
                
                if (!storedData.PlayerReports.ContainsKey(reportedID))
                {
                    PrintDebug($"Creating new report list for {reportedPlayer.Name}");
                    storedData.PlayerReports[reportedID] = new List<PlayerReport>();
                }
                
                var recentReports = GetRecentReports(reportedID);
                
                if (recentReports.Any(r => r.reporterId == reporterID))
                {
                    Puts($"BLOCKED: {reporter.Name} attempted to report {reportedPlayer.Name} again within the {config.TimeWindowMinutes} minute window");
                    return;
                }
                
                var report = new PlayerReport
                {
                    targetId = ulong.Parse(reportedID),
                    reporterId = reporterID,
                    type = reportType,
                    reason = cleanedReason,
                    description = description,
                    Timestamp = DateTime.UtcNow
                };
                
                storedData.PlayerReports[reportedID].Add(report);
                PrintDebug($"Report added - Current reports for {reportedPlayer.Name}: {storedData.PlayerReports[reportedID].Count}");
                
                var currentReports = GetRecentReports(reportedID);
                var uniqueReporters = currentReports.Select(r => r.reporterId).Distinct().Count();
                Puts($"Current status for {reportedPlayer.Name}: {uniqueReporters} unique reporters in the last {config.TimeWindowMinutes} minutes");
                
                SaveData();
                
                if (!string.IsNullOrEmpty(config.ReportWebhookUrl))
                {
                    SendReportToDiscord(reporter, reportedPlayer, cleanedReason, reportType, description, uniqueReporters, currentReports.Count);
                }
                
                CheckReportThreshold(reportedID);
            }
            catch (Exception ex)
            {
                PrintError($"Error in ProcessPlayerReport: {ex.Message}");
                PrintError($"Stack trace: {ex.StackTrace}");
            }
        }

        private string DetermineReportType(string reportReason)
        {
            string lowerReason = reportReason.ToLower();
            
            if (lowerReason.StartsWith("[cheat") || lowerReason.Contains("hack") || lowerReason.Contains("aim"))
                return "cheat";
            if (lowerReason.StartsWith("[abuse") || lowerReason.StartsWith("[abusive") || lowerReason.Contains("exploit"))
                return "abuse";
            if (lowerReason.StartsWith("[name") || lowerReason.Contains("offensive"))
                return "name";
            if (lowerReason.StartsWith("[grief") || lowerReason.Contains("block"))
                return "grief";
            
            return "other";
        }

        private string CleanReportReason(string reportReason)
        {
            if (reportReason.StartsWith("["))
            {
                int closeBracketIndex = reportReason.IndexOf("]");
                if (closeBracketIndex > 0 && closeBracketIndex + 1 < reportReason.Length)
                {
                    return reportReason.Substring(closeBracketIndex + 1).Trim();
                }
            }
            return reportReason;
        }

        private List<PlayerReport> GetRecentReports(string playerID)
        {
            List<PlayerReport> reportsList;
            if (!storedData.PlayerReports.TryGetValue(playerID, out reportsList))
            {
                return new List<PlayerReport>();
            }

            DateTime cutoffTime = DateTime.UtcNow.AddMinutes(-config.TimeWindowMinutes);
            return reportsList
                .Where(r => r.Timestamp >= cutoffTime)
                .ToList();
        }

        private void CheckReportThreshold(string reportedID)
        {
            PrintDebug($"Checking report threshold for player ID: {reportedID}");
            
            var recentReports = GetRecentReports(reportedID);
            var uniqueReporterIds = recentReports.Select(r => r.reporterId).Distinct().ToList();
            var uniqueReportersCount = uniqueReporterIds.Count;
            
            PrintDebug($"Found {recentReports.Count} total reports from {uniqueReportersCount} unique reporters");
            
            if (uniqueReportersCount < config.ReportsThreshold)
            {
                PrintDebug($"Threshold check: Not enough unique reporters ({uniqueReportersCount}/{config.ReportsThreshold})");
                return;
            }
            
            PrintDebug($"Threshold reached! Proceeding with ban for player ID: {reportedID}");
            
            IPlayer reportedPlayer = covalence.Players.FindPlayerById(reportedID);
            string playerName = reportedPlayer?.Name ?? "Unknown";
            
            string banReason = config.BanReasonTemplate;
            string banDurationText = config.BanDurationMinutes > 0 ? $"{config.BanDurationMinutes} minutes" : "Permanent";
            
            try
            {
                ulong steamId;
                if (!ulong.TryParse(reportedID, out steamId))
                {
                    PrintError($"Failed to parse Steam ID from {reportedID}");
                    return;
                }
                
                PrintDebug($"Parsed Steam ID: {steamId} from player ID: {reportedID}");
                
                if (config.BanDurationMinutes > 0)
                {
                    tempBans[steamId] = DateTime.Now.AddMinutes(config.BanDurationMinutes);
                    SaveTempBanData();
                    
                    string banCommand = $"banid {steamId} {config.BanDurationMinutes * 60} \"{banReason}\"";
                    PrintDebug($"Executing temporary ban command: {banCommand}");
                    ConsoleSystem.Run(ConsoleSystem.Option.Server, banCommand);
                    Puts($"AUTO-BANNED {playerName} [{steamId}] for {banDurationText}");
                }
                else
                {
                    string banCommand = $"banid {steamId} 0 \"{banReason}\"";
                    PrintDebug($"Executing permanent ban command: {banCommand}");
                    ConsoleSystem.Run(ConsoleSystem.Option.Server, banCommand);
                    Puts($"AUTO-BANNED {playerName} [{steamId}] PERMANENTLY with reason: {banReason}");
                }
                
                var basePlayer = RelationshipManager.FindByID(steamId);
                if (basePlayer != null)
                {
                    basePlayer.Kick(banReason);
                    Puts($"Kicked {playerName} from the server");
                }
                
                LogToFile("bans", $"{DateTime.Now} - Player: {playerName} [{steamId}] - {banReason} - Ban duration: {banDurationText}", this);
                
                if (storedData.PlayerReports.ContainsKey(reportedID))
                {
                    storedData.PlayerReports[reportedID].Clear();
                    PrintDebug($"Cleared reports for banned player {playerName}");
                    SaveData();
                }
                
                if (!string.IsNullOrEmpty(config.WebhookUrl))
                {
                    var reportTypes = recentReports.Select(r => r.type).Distinct();
                    string reportTypesText = string.Join(", ", reportTypes);
                    
                    var recentReportDetails = recentReports
                        .OrderByDescending(r => r.Timestamp)
                        .Take(5)
                        .Select(r => {
                            var reporterName = covalence.Players.FindPlayerById(r.reporterId)?.Name ?? "Unknown";
                            return $"• {reporterName}: {r.type} - {r.reason}";
                        })
                        .ToList();
                    
                    string recentReportsText = recentReportDetails.Count > 0 
                        ? string.Join("\n", recentReportDetails) 
                        : "No detailed report information available";
                    
                    var fields = new List<Dictionary<string, object>>
                    {
                        new Dictionary<string, object>
                        {
                            {"name", "Player"},
                            {"value", $"{playerName} [{steamId}]"},
                            {"inline", true}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Report Count"},
                            {"value", $"{uniqueReportersCount} unique reporters"},
                            {"inline", true}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Ban Duration"},
                            {"value", banDurationText},
                            {"inline", true}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Report Types"},
                            {"value", reportTypesText},
                            {"inline", false}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Recent Reports"},
                            {"value", recentReportsText},
                            {"inline", false}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Ban Reason"},
                            {"value", banReason},
                            {"inline", false}
                        },
                        new Dictionary<string, object>
                        {
                            {"name", "Server"},
                            {"value", config.ServerName},
                            {"inline", false}
                        }
                    };
                    
                    SendDiscordEmbed("Player Auto-Banned", $"A player has been automatically banned after r
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху