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

FREE rServerMessages : 0.0.261 0.0.261

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins {
    [Info("rServerMessages", "Ftuoil Xelrash", "0.0.261")]
    [Description("Logs essential server events to Discord channels using webhooks")]
    public class rServerMessages : RustPlugin {
#region Variables
        [PluginReference]
        private readonly Plugin AntiSpam, BetterChatMute, UFilter;
        private readonly Queue<QueuedMessage> _queue = new();
        private readonly StringBuilder _sb = new();
        private int _retryCount = 0;
        private const int _maxRetries = 5;
        // Maximum retry attempts before giving up
        private object _resultCall;
        private QueuedMessage _nextMessage;
        private QueuedMessage _queuedMessage;
        private string[] _profanities;
        private Timer _timerQueue;
        private Timer _timerQueueCooldown;
        private bool _isProcessingQueue = false;
        // Prevent recursive queue processing
        private readonly List<Regex> _regexTags = new() {
            new("<color=.+?>", RegexOptions.Compiled), new("<size=.+?>", RegexOptions.Compiled)
        }
        ;
        private readonly List<string> _tags = new() {
            "</color>", "</size>", "<i>", "</i>", "<b>", "</b>"
        }
        ;
        // Simple entity type mappings for basic death messages
        private readonly Dictionary<string, string> _entityNames = new() {
            // Players
            ["BasePlayer"]
            = "Player",  // NPCs
            ["Scientist"]
            = "Scientist", ["ScientistNPC"] = "Scientist", ["HTNPlayer"] = "Heavy Scientist", ["NPCMurderer"] = "Murderer", ["scarecrow"] = "Scarecrow", ["ScientistNPCNew"] = "Scientist", ["tunneldweller"] = "Tunnel Dweller", ["underwaterdweller"] = "Underwater Dweller", ["ZombieNPC"] = "Zombie", ["GingerbreadNPC"] = "Gingerbread Man", ["NPCShopKeeper"] = "Shopkeeper",  // Animals
            ["Bear"]
            = "Bear", ["Boar"] = "Boar", ["Chicken"] = "Chicken", ["Stag"] = "Stag", ["Wolf"] = "Wolf", ["Wolf2"] = "Wolf", ["Polarbear"] = "Polar Bear", ["Horse"] = "Horse", ["RidableHorse"] = "Horse", ["FarmableAnimal"] = "Farm Animal", ["Crocodile"] = "Crocodile", ["SnakeHazard"] = "Snake", ["Panther"] = "Panther", ["Tiger"] = "Tiger", ["SimpleShark"] = "Shark", ["BaseFishNPC"] = "Fish",  // Vehicles/Military
            ["BaseHelicopter"]
            = "Patrol Helicopter", ["PatrolHelicopter"] = "Patrol Helicopter", ["BradleyAPC"] = "Bradley APC", ["Minicopter"] = "Minicopter", ["ScrapTransportHelicopter"] = "Scrap Helicopter", ["AttackHelicopter"] = "Attack Helicopter",  // Turrets/Traps
            ["AutoTurret"]
            = "Auto Turret", ["GunTrap"] = "Shotgun Trap", ["FlameTurret"] = "Flame Turret", ["NPCAutoTurret"] = "Sentry Turret", ["SamSite"] = "SAM Site", ["Landmine"] = "Landmine", ["BearTrap"] = "Bear Trap", ["TeslaCoil"] = "Tesla Coil",  // Structures
            ["Barricade"]
            = "Barricade", ["SimpleBuildingBlock"] = "Wall", ["CodeLock"] = "Code Lock",  // Fire/Heat
            ["FireBall"]
            = "Fire", ["BaseOven"] = "Heat Source", ["Campfire"] = "Campfire",  // Other
            ["BeeSwarmAI"]
            = "Bee Swarm"
        }
        ;
        private class QueuedMessage {
            public string WebhookUrl {
                get;
                set;
            }
            public string Message {
                get;
                set;
            }
            public DiscordMessage DiscordMessage {
                get;
                set;
            }
            public bool IsEmbed {
                get;
                set;
            }
        }
#endregion Variables
#region Initialization
        private void Init() {
            UnsubscribeHooks();
        }
        private void Unload() {
            _timerQueue?.Destroy();
            _timerQueueCooldown?.Destroy();
        }
        private void OnServerInitialized(bool isStartup) {
            if (isStartup && _configData.ServerStateSettings.Enabled) {
                DiscordSendMessage(Lang(LangKeys.Event.Initialized), _configData.GlobalSettings.ServerMessagesWebhook);
            }
            SubscribeHooks();
        }
        private void OnServerShutdown() {
            if (_configData.ServerStateSettings.Enabled) {
                string url = _configData.GlobalSettings.ServerMessagesWebhook;
                if (!string.IsNullOrEmpty(url)) {
                    webrequest.Enqueue(url, new DiscordMessage(Lang(LangKeys.Event.Shutdown)).ToJson(), DiscordSendMessageCallback, null, RequestMethod.POST, _headers);
                }
            }
        }
#endregion Initialization
#region Configuration
        private ConfigData _configData;
        private class ConfigData {
            [JsonProperty(PropertyName = "Global settings")]
            public GlobalSettings GlobalSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Air Event settings")]
            public EventSettings AirEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Airfield Event settings")]
            public EventSettings AirfieldEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Arctic Base Event settings")]
            public EventSettings ArcticBaseEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Armored Train Event settings")]
            public EventSettings ArmoredTrainEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Chat settings")]
            public EventSettings ChatSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Chat (Team) settings")]
            public EventSettings ChatTeamSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Christmas settings")]
            public EventSettings ChristmasSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Easter settings")]
            public EventSettings EasterSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Gas Station Event settings")]
            public EventSettings GasStationEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Halloween settings")]
            public EventSettings HalloweenSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Permissions settings")]
            public EventSettings PermissionsSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Player death settings")]
            public DeathSettings PlayerDeathSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Player connect advanced info settings")]
            public EventSettings PlayerConnectedInfoSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Player disconnect settings")]
            public EventSettings PlayerDisconnectedSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Raidable Bases settings")]
            public EventSettings RaidableBasesSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Rcon command settings")]
            public EventSettings RconCommandSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Rcon connection settings")]
            public EventSettings RconConnectionSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "SantaSleigh settings")]
            public EventSettings SantaSleighSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Server messages settings")]
            public EventSettings ServerMessagesSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Server state settings")]
            public EventSettings ServerStateSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Sputnik Event settings")]
            public EventSettings SputnikEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Supermarket Event settings")]
            public EventSettings SupermarketEventSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "User Banned settings")]
            public EventSettings UserBannedSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "User Kicked settings")]
            public EventSettings UserKickedSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "User Muted settings")]
            public EventSettings UserMutedSettings {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "User Name Updated settings")]
            public EventSettings UserNameUpdateSettings {
                get;
                set;
            }
            = new();
        }
        private class GlobalSettings {
            [JsonProperty(PropertyName = "Log to console?")]
            public bool LoggingEnabled {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Use AntiSpam plugin on chat messages")]
            public bool UseAntiSpam {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Use UFilter plugin on chat messages")]
            public bool UseUFilter {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Hide admin connect/disconnect messages")]
            public bool HideAdmin {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Hide NPC death messages")]
            public bool HideNPC {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Include death coordinates in death messages")]
            public bool IncludeDeathCoordinates {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Use Discord Embeds for death messages")]
            public bool UseEmbedForDeaths {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Use enhanced embeds for connections")]
            public bool UseEmbedForConnections {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Show country information (requires internet)")]
            public bool ShowCountryInfo {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Show server population in connection messages")]
            public bool ShowServerPopulation {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Show combat details in death messages")]
            public bool ShowCombatDetails {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Use enhanced embeds for server messages")]
            public bool UseEmbedForServerMessages {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Show kill distance in PvP deaths")]
            public bool ShowKillDistance {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "High damage threshold for special kills")]
            public float HighDamageThreshold {
                get;
                set;
            }
            = 75f;
            [JsonProperty(PropertyName = "Use enhanced embeds for RCON messages")]
            public bool UseEmbedForRcon {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Replacement string for tags")]
            public string TagsReplacement {
                get;
                set;
            }
            = "`";
            [JsonProperty(PropertyName = "Queue interval (1 message per ? seconds)")]
            public float QueueInterval {
                get;
                set;
            }
            = 1f;
            [JsonProperty(PropertyName = "Queue cooldown if connection error (seconds)")]
            public float QueueCooldown {
                get;
                set;
            }
            = 60f;
            [JsonProperty(PropertyName = "Public Chat Webhook URL")]
            public string PublicChatWebhook {
                get;
                set;
            }
            = string.Empty;
            [JsonProperty(PropertyName = "Private Admin Webhook URL")]
            public string PrivateAdminWebhook {
                get;
                set;
            }
            = string.Empty;
            [JsonProperty(PropertyName = "Server Messages Webhook URL")]
            public string ServerMessagesWebhook {
                get;
                set;
            }
            = string.Empty;
            [JsonProperty(PropertyName = "RCON command blacklist", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<string> RCONCommandBlacklist {
                get;
                set;
            }
            = new() {
                "serverinfo", "server.hostname", "server.headerimage", "server.description", "server.url", "playerlist", "status"
            }
            ;
            [JsonProperty(PropertyName = "RCON trusted IPs (hide connections from these)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<string> RCONTrustedIPs {
                get;
                set;
            }
            = new() {
                "127.0.0.1", "::1"
            }
            ;
            [JsonProperty(PropertyName = "Steam Web API Key (for profile data)")]
            public string SteamWebAPIKey {
                get;
                set;
            }
            = string.Empty;
        }
        private class EventSettings {
            [JsonProperty(PropertyName = "Enabled?")]
            public bool Enabled {
                get;
                set;
            }
            = false;
        }
        private class DeathSettings {
            [JsonProperty(PropertyName = "Enabled?")]
            public bool Enabled {
                get;
                set;
            }
            = false;
            [JsonProperty(PropertyName = "Enable PvP deaths")]
            public bool EnablePvP {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Enable PvE deaths")]
            public bool EnablePvE {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Enable suicide deaths")]
            public bool EnableSuicide {
                get;
                set;
            }
            = true;
            [JsonProperty(PropertyName = "Enable drowning deaths")]
            public bool EnableDrowning {
                get;
                set;
            }
            = true;
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                _configData = Config.ReadObject<ConfigData>();
                if (_configData == null) {
                    LoadDefaultConfig();
                }
                ValidateConfig();
                SaveConfig();
            }
            catch {
                PrintError("The configuration file is corrupted");
                LoadDefaultConfig();
                ValidateConfig();
                SaveConfig();
            }
        }
        protected override void LoadDefaultConfig() {
            PrintWarning("Creating a new configuration file");
            _configData = new ConfigData();
        }
        protected override void SaveConfig() => Config.WriteObject(_configData);
        private void ValidateConfig() {
            bool needsSave = false;
            // Validate Global Settings
            if (_configData.GlobalSettings == null) {
                _configData.GlobalSettings = new GlobalSettings();
                PrintWarning("GlobalSettings was null, reset to default");
                needsSave = true;
            }
            // Validate QueueInterval
            if (_configData.GlobalSettings.QueueInterval <= 0) {
                _configData.GlobalSettings.QueueInterval = 1f;
                PrintWarning("QueueInterval was invalid, reset to 1.0");
                needsSave = true;
            }
            // Validate QueueCooldown
            if (_configData.GlobalSettings.QueueCooldown <= 0) {
                _configData.GlobalSettings.QueueCooldown = 60f;
                PrintWarning("QueueCooldown was invalid, reset to 60.0");
                needsSave = true;
            }
            // Validate TagsReplacement
            if (string.IsNullOrEmpty(_configData.GlobalSettings.TagsReplacement)) {
                _configData.GlobalSettings.TagsReplacement = "`";
                PrintWarning("TagsReplacement was empty, reset to '`'");
                needsSave = true;
            }
            // Validate RCONCommandBlacklist
            if (_configData.GlobalSettings.RCONCommandBlacklist == null) {
                _configData.GlobalSettings.RCONCommandBlacklist = new List<string> {
                    "playerlist", "status"
                }
                ;
                PrintWarning("RCONCommandBlacklist was null, reset to default");
                needsSave = true;
            }
            // Validate RCONTrustedIPs
            if (_configData.GlobalSettings.RCONTrustedIPs == null) {
                _configData.GlobalSettings.RCONTrustedIPs = new List<string> {
                    "127.0.0.1", "::1"
                }
                ;
                PrintWarning("RCONTrustedIPs was null, reset to default");
                needsSave = true;
            }
            // Validate HighDamageThreshold
            if (_configData.GlobalSettings.HighDamageThreshold <= 0) {
                _configData.GlobalSettings.HighDamageThreshold = 75f;
                PrintWarning("HighDamageThreshold was invalid, reset to 75.0");
                needsSave = true;
            }
            // Validate Steam API Key
            if (string.IsNullOrEmpty(_configData.GlobalSettings.SteamWebAPIKey)) {
                // Silently skip Steam profile data if no API key configured
            }
            else if (_configData.GlobalSettings.SteamWebAPIKey.Length != 32) {
                PrintWarning("Steam Web API Key appears to be invalid (should be 32 characters).Steam profile data may not work.");
            }
            // Validate Webhook URLs
            if (!IsValidWebhookUrl(_configData.GlobalSettings.PublicChatWebhook) && !string.IsNullOrEmpty(_configData.GlobalSettings.PublicChatWebhook)) {
                PrintWarning("PublicChatWebhook URL appears to be invalid");
            }
            if (!IsValidWebhookUrl(_configData.GlobalSettings.PrivateAdminWebhook) && !string.IsNullOrEmpty(_configData.GlobalSettings.PrivateAdminWebhook)) {
                PrintWarning("PrivateAdminWebhook URL appears to be invalid");
            }
            if (!IsValidWebhookUrl(_configData.GlobalSettings.ServerMessagesWebhook) && !string.IsNullOrEmpty(_configData.GlobalSettings.ServerMessagesWebhook)) {
                PrintWarning("ServerMessagesWebhook URL appears to be invalid");
            }
            // Validate ALL Event Settings - Force creation if missing
            if (_configData.AirEventSettings == null) {
                _configData.AirEventSettings = new EventSettings();
                PrintWarning("AirEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.AirfieldEventSettings == null) {
                _configData.AirfieldEventSettings = new EventSettings();
                PrintWarning("AirfieldEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ArcticBaseEventSettings == null) {
                _configData.ArcticBaseEventSettings = new EventSettings();
                PrintWarning("ArcticBaseEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ArmoredTrainEventSettings == null) {
                _configData.ArmoredTrainEventSettings = new EventSettings();
                PrintWarning("ArmoredTrainEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ChatSettings == null) {
                _configData.ChatSettings = new EventSettings();
                PrintWarning("ChatSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ChatTeamSettings == null) {
                _configData.ChatTeamSettings = new EventSettings();
                PrintWarning("ChatTeamSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ChristmasSettings == null) {
                _configData.ChristmasSettings = new EventSettings();
                PrintWarning("ChristmasSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.EasterSettings == null) {
                _configData.EasterSettings = new EventSettings();
                PrintWarning("EasterSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.GasStationEventSettings == null) {
                _configData.GasStationEventSettings = new EventSettings();
                PrintWarning("GasStationEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.HalloweenSettings == null) {
                _configData.HalloweenSettings = new EventSettings();
                PrintWarning("HalloweenSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.PermissionsSettings == null) {
                _configData.PermissionsSettings = new EventSettings();
                PrintWarning("PermissionsSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.PlayerDeathSettings == null) {
                _configData.PlayerDeathSettings = new DeathSettings();
                PrintWarning("PlayerDeathSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.PlayerConnectedInfoSettings == null) {
                _configData.PlayerConnectedInfoSettings = new EventSettings();
                PrintWarning("PlayerConnectedInfoSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.PlayerDisconnectedSettings == null) {
                _configData.PlayerDisconnectedSettings = new EventSettings();
                PrintWarning("PlayerDisconnectedSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.RaidableBasesSettings == null) {
                _configData.RaidableBasesSettings = new EventSettings();
                PrintWarning("RaidableBasesSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.RconCommandSettings == null) {
                _configData.RconCommandSettings = new EventSettings();
                PrintWarning("RconCommandSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.RconConnectionSettings == null) {
                _configData.RconConnectionSettings = new EventSettings();
                PrintWarning("RconConnectionSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.SantaSleighSettings == null) {
                _configData.SantaSleighSettings = new EventSettings();
                PrintWarning("SantaSleighSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ServerMessagesSettings == null) {
                _configData.ServerMessagesSettings = new EventSettings();
                PrintWarning("ServerMessagesSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.ServerStateSettings == null) {
                _configData.ServerStateSettings = new EventSettings();
                PrintWarning("ServerStateSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.SputnikEventSettings == null) {
                _configData.SputnikEventSettings = new EventSettings();
                PrintWarning("SputnikEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.SupermarketEventSettings == null) {
                _configData.SupermarketEventSettings = new EventSettings();
                PrintWarning("SupermarketEventSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.UserBannedSettings == null) {
                _configData.UserBannedSettings = new EventSettings();
                PrintWarning("UserBannedSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.UserKickedSettings == null) {
                _configData.UserKickedSettings = new EventSettings();
                PrintWarning("UserKickedSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.UserMutedSettings == null) {
                _configData.UserMutedSettings = new EventSettings();
                PrintWarning("UserMutedSettings was null, created new instance");
                needsSave = true;
            }
            if (_configData.UserNameUpdateSettings == null) {
                _configData.UserNameUpdateSettings = new EventSettings();
                PrintWarning("UserNameUpdateSettings was null, created new instance");
                needsSave = true;
            }
            if (needsSave) {
                PrintWarning("Configuration validation completed - saving updated config");
            }
        }
        private bool IsValidWebhookUrl(string url) {
            if (string.IsNullOrEmpty(url)) return false;
            return url.StartsWith("https://discord.com/api/webhooks/") || url.StartsWith("https://discordapp.com/api/webhooks/");
        }
#endregion Configuration
#region Localization
        public string Lang(string key, string userIDString = null, params object[] args) {
            try {
                return string.Format(lang.GetMessage(key, this, userIDString).Replace("{time}", $"<t:{DateTimeOffset.Now.ToUnixTimeSeconds()}:t>"), args);
            }
            catch (Exception ex) {
                PrintError($"Lang Key '{key}' threw exception:\n{ex}");
                throw;
            }
        }
        private static class LangKeys {
            public static class Event {
                private const string Base = nameof(Event) + ".";
                public const string AirEvent = Base + nameof(AirEvent);
                public const string AirfieldEvent = Base + nameof(AirfieldEvent);
                public const string ArcticBaseEvent = Base + nameof(ArcticBaseEvent);
                public const string ArmoredTrainEvent = Base + nameof(ArmoredTrainEvent);
                public const string Chat = Base + nameof(Chat);
                public const string ChatTeam = Base + nameof(ChatTeam);
                public const string Christmas = Base + nameof(Christmas);
                public const string Death = Base + nameof(Death);
                public const string Easter = Base + nameof(Easter);
                public const string EasterWinner = Base + nameof(EasterWinner);
                public const string GasStationEvent = Base + nameof(GasStationEvent);
                public const string Halloween = Base + nameof(Halloween);
                public const string HalloweenWinner = Base + nameof(HalloweenWinner);
                public const string Initialized = Base + nameof(Initialized);
                public const string PlayerConnected = Base + nameof(PlayerConnected);
                public const string PlayerConnectedInfo = Base + nameof(PlayerConnectedInfo);
                public const string PlayerDisconnected = Base + nameof(PlayerDisconnected);
                public const string RconCommand = Base + nameof(RconCommand);
                public const string RconConnection = Base + nameof(RconConnection);
                public const string SantaSleigh = Base + nameof(SantaSleigh);
                public const string ServerMessage = Base + nameof(ServerMessage);
                public const string Shutdown = Base + nameof(Shutdown);
                public const string SputnikEvent = Base + nameof(SputnikEvent);
                public const string SupermarketEvent = Base + nameof(SupermarketEvent);
                public const string UserBanned = Base + nameof(UserBanned);
                public const string UserKicked = Base + nameof(UserKicked);
                public const string UserMuted = Base + nameof(UserMuted);
                public const string UserNameUpdated = Base + nameof(UserNameUpdated);
                public const string UserUnbanned = Base + nameof(UserUnbanned);
                public const string UserUnmuted = Base + nameof(UserUnmuted);
            }
            public static class Permission {
                private const string Base = nameof(Permission) + ".";
                public const string GroupCreated = Base + nameof(GroupCreated);
                public const string GroupDeleted = Base + nameof(GroupDeleted);
                public const string UserGroupAdded = Base + nameof(UserGroupAdded);
                public const string UserGroupRemoved = Base + nameof(UserGroupRemoved);
                public const string UserPermissionGranted = Base + nameof(UserPermissionGranted);
                public const string UserPermissionRevoked = Base + nameof(UserPermissionRevoked);
            }
            public static class Plugin {
                private const string Base = nameof(Plugin) + ".";
                public const string RaidableBaseCompleted = Base + nameof(RaidableBaseCompleted);
                public const string RaidableBaseEnded = Base + nameof(RaidableBaseEnded);
                public const string RaidableBaseStarted = Base + nameof(RaidableBaseStarted);
                public const string TimedGroupAdded = Base + nameof(TimedGroupAdded);
                public const string TimedGroupExtended = Base + nameof(TimedGroupExtended);
                public const string TimedPermissionExtended = Base + nameof(TimedPermissionExtended);
                public const string TimedPermissionGranted = Base + nameof(TimedPermissionGranted);
            }
            public static class Format {
                private const string Base = nameof(Format) + ".";
                public const string Day = Base + nameof(Day);
                public const string Days = Base + nameof(Days);
                public const string Easy = Base + nameof(Easy);
                public const string Expert = Base + nameof(Expert);
                public const string Hard = Base + nameof(Hard);
                public const string Hour = Base + nameof(Hour);
                public const string Hours = Base + nameof(Hours);
                public const string Medium = Base + nameof(Medium);
                public const string Minute = Base + nameof(Minute);
                public const string Minutes = Base + nameof(Minutes);
                public const string Nightmare = Base + nameof(Nightmare);
                public const string Second = Base + nameof(Second);
                public const string Seconds = Base + nameof(Seconds);
            }
        }
        protected override void LoadDefaultMessages() {
            lang.RegisterMessages(new Dictionary<string, string> {
                [LangKeys.Event.AirEvent]
                = ":helicopter: {time} Air Event started", [LangKeys.Event.AirfieldEvent] = ":airplane: {time} Airfield Event started", [LangKeys.Event.ArcticBaseEvent] = ":snowflake: {time} Arctic Base Event started", [LangKeys.Event.ArmoredTrainEvent] = ":train2: {time} Armored Train Event started", [LangKeys.Event.Chat] = ":speech_left: {time} **{0}**: {1}", [LangKeys.Event.ChatTeam] = ":busts_in_silhouette: {time} **{0}**: {1}", [LangKeys.Event.Christmas] = ":christmas_tree: {time} Christmas event started", [LangKeys.Event.Death] = ":skull: {time} {0}", [LangKeys.Event.Easter] = ":egg: {time} Easter event started", [LangKeys.Event.EasterWinner] = ":egg: {time} Easter event ended.The winner is `{0}`", [LangKeys.Event.GasStationEvent] = ":fuelpump: {time} Gas Station Event started", [LangKeys.Event.Halloween] = ":jack_o_lantern: {time} Halloween event started", [LangKeys.Event.HalloweenWinner] = ":jack_o_lantern: {time} Halloween event ended.The winner is `{0}`", [LangKeys.Event.Initialized] = ":ballot_box_with_check: {time} Server is online again!", [LangKeys.Event.PlayerConnectedInfo] = ":detective: {time} {0} connected.SteamID: `{1}` IP: `{2}`", [LangKeys.Event.PlayerDisconnected] = ":x: {time} {0} disconnected ({1}) SteamID: `{2}` IP: `{3}`", [LangKeys.Event.RconCommand] = ":satellite: {time} RCON command `{0}` is run from `{1}`", [LangKeys.Event.RconConnection] = ":satellite: {time} RCON connection is opened from `{0}`", [LangKeys.Event.SantaSleigh] = ":santa: {time} SantaSleigh Event started", [LangKeys.Event.ServerMessage] = ":desktop: {time} `{0}`", [LangKeys.Event.Shutdown] = ":stop_sign: {time} Server is shutting down!", [LangKeys.Event.SputnikEvent] = ":satellite_orbital: {time} Sputnik Event started", [LangKeys.Event.SupermarketEvent] = ":convenience_store: {time} Supermarket Event started", [LangKeys.Event.UserBanned] = ":no_entry: {time} Player `{0}` SteamID: `{1}` IP: `{2}` was banned: `{3}`", [LangKeys.Event.UserKicked] = ":hiking_boot: {time} Player `{0}` SteamID: `{1}` was kicked: `{2}`", [LangKeys.Event.UserMuted] = ":mute: {time} `{0}` was muted by `{1}` for `{2}` (`{3}`)", [LangKeys.Event.UserNameUpdated] = ":label: {time} `{0}` changed name to `{1}` SteamID: `{2}`", [LangKeys.Event.UserUnbanned] = ":ok: {time} Player `{0}` SteamID: `{1}` IP: `{2}` was unbanned", [LangKeys.Event.UserUnmuted] = ":speaker: {time} `{0}` was unmuted `{1}`", [LangKeys.Format.Day] = "day", [LangKeys.Format.Days] = "days", [LangKeys.Format.Easy] = "Easy", [LangKeys.Format.Expert] = "Expert", [LangKeys.Format.Hard] = "Hard", [LangKeys.Format.Hour] = "hour", [LangKeys.Format.Hours] = "hours", [LangKeys.Format.Medium] = "Medium", [LangKeys.Format.Minute] = "minute", [LangKeys.Format.Minutes] = "minutes", [LangKeys.Format.Nightmare] = "Nightmare", [LangKeys.Format.Second] = "second", [LangKeys.Format.Seconds] = "seconds", [LangKeys.Permission.GroupCreated] = ":family: {time} Group `{0}` has been created", [LangKeys.Permission.GroupDeleted] = ":family: {time} Group `{0}` has been deleted", [LangKeys.Permission.UserGroupAdded] = ":family: {time} `{0}` `{1}` is added to group `{2}`", [LangKeys.Permission.UserGroupRemoved] = ":family: {time} `{0}` `{1}` is removed from group `{2}`", [LangKeys.Permission.UserPermissionGranted] = ":key: {time} `{0}` `{1}` is granted `{2}`", [LangKeys.Permission.UserPermissionRevoked] = ":key: {time} `{0}` `{1}` is revoked `{2}`", [LangKeys.Plugin.RaidableBaseCompleted] = ":homes: {time} {1} Raidable Base owned by {2} at `{0}` has been raided by **{3}**", [LangKeys.Plugin.RaidableBaseEnded] = ":homes: {time} {1} Raidable Base at `{0}` has ended", [LangKeys.Plugin.RaidableBaseStarted] = ":homes: {time} {1} Raidable Base spawned at `{0}`", [LangKeys.Plugin.TimedGroupAdded] = ":timer: {time} `{0}` `{1}` is added to `{2}` for {3}", [LangKeys.Plugin.TimedGroupExtended] = ":timer: {time} `{0}` `{1}` timed group `{2}` is extended to {3}", [LangKeys.Plugin.TimedPermissionExtended] = ":timer: {time} `{0}` `{1}` timed permission `{2}` is extended to {3}", [LangKeys.Plugin.TimedPermissionGranted] = ":timer: {time} `{0}` `{1}` is granted `{2}` for {3}",
            }
            , this);
        }
#endregion Localization
#region Events Hooks
        private void OnBetterChatMuted(IPlayer target, IPlayer initiator, string reason) {
            LogToConsole($"{target.Name} was muted by {initiator.Name} for ever ({reason})");
            DiscordSendMessage(Lang(LangKeys.Event.UserMuted, null, ReplaceChars(target.Name), ReplaceChars(initiator.Name), "ever", ReplaceChars(reason)), _configData.GlobalSettings.PrivateAdminWebhook);
        }
        private void OnBetterChatMuteExpired(IPlayer player) {
            LogToConsole($"{player.Name} was unmuted by SERVER");
            DiscordSendMessage(Lang(LangKeys.Event.UserUnmuted, null, ReplaceChars(player.Name), "SERVER"), _configData.GlobalSettings.PrivateAdminWebhook);
        }
        private void OnBetterChatTimeMuted(IPlayer target, IPlayer initiator, TimeSpan time, string reason) {
            LogToConsole($"{target.Name} was muted by {initiator.Name} for {time.ToShortString()} ({reason})");
            DiscordSendMessage(Lang(LangKeys.Event.UserMuted, null, ReplaceChars(target.Name), ReplaceChars(initiator.Name), time.ToShortString(), ReplaceChars(reason)), _configData.GlobalSettings.PrivateAdminWebhook);
        }
        private void OnBetterChatUnmuted(IPlayer target, IPlayer initiator) {
            LogToConsole($"{target.Name} was unmuted by {initiator.Name}");
            DiscordSendMessage(Lang(LangKeys.Event.UserUnmuted, null, ReplaceChars(target.Name), ReplaceChars(initiator.Name)), _configData.GlobalSettings.PrivateAdminWebhook);
        }
        private void OnEntityDeath(BasePlayer player, HitInfo info) {
            if (!_configData.PlayerDeathSettings.Enabled || !player.IsValid()) {
                return;
            }
            if (_configData.GlobalSettings.HideNPC && (player.IsNpc || !player.userID.IsSteamId())) {
                return;
            }
            // Check if this death type should be reported
            string deathType = GetDeathType(player, info);
            if (!ShouldReportDeathType(deathType)) {
                return;
            }
            // Capture death position
            Vector3 deathPosition = player.transform.position;
            // Send enhanced death message with embeds if enabled
            if (_configData.GlobalSettings.UseEmbedForDeaths) {
                SendEnhancedDeathMessage(player, info, deathPosition);
            }
            else {
                // Fallback to simple text message
                string deathMessage = GetSimpleDeathMessage(player, info, deathPosition);
                string discordMessage = $":skull: <t:{DateTimeOffset.Now.ToUnixTimeSeconds()}:t> {deathMessage}";
                DiscordSendMessage(discordMessage, _configData.GlobalSettings.PrivateAdminWebhook);
            }
        }
        private void SendEnhancedDeathMessage(BasePlayer victim, HitInfo info, Vector3 deathPosition) {
            string victimName = ReplaceChars(victim.displayName);
            string deathType = GetDeathType(victim, info);
            string deathIcon = GetDeathIcon(victim, info);
            int embedColor = GetDeathColor(victim, info);
            var embed = new DiscordEmbed().SetColor(embedColor).SetTimestamp(DateTimeOffset.Now);
            string description = "";
            string weapon = GetWeaponName(info);
            // Check for drowning first
            if (IsDrowningDeath(info)) {
                description = $"**{victimName}** drowned";
                embed.SetTitle($"🌊 Drowning").AddField("Death Type", "Drowning", true);
            }
            // Build death description based on type
            else if (info?.Initiator == null) {
                description = $"**{victimName}** died";
                embed.SetTitle($"{deathIcon} Player Death").AddField("Death Type", "Unknown", true);
            }
            else if (info.Initiator is BasePlayer killer) {
                if (killer.userID == victim.userID) {
                    description = $"**{victimName}** committed suicide";
                    embed.SetTitle($"{deathIcon} Suicide").AddField("Death Type", "Suicide", true);
                    if (!string.IsNullOrEmpty(weapon)) {
                        embed.AddField("Method", weapon, true);
                    }
                }
                else {
                    // PvP Kill
                    float damage = info.damageTypes.Total();
                    float distance = Vector3.Distance(victim.transform.position, killer.transform.position);
                    bool isHighDamage = damage >= _configData.GlobalSettings.HighDamageThreshold;
                    bool isHeadshot = IsHeadshot(info);
                    string killerName = ReplaceChars(killer.displayName);
                    description = $"**{victimName}** was eliminated by **{killerName}**";
                    // Enhanced title based on kill type - prioritize headshot
                    string title;
                    if (isHeadshot) {
                        title = "🎯💀 Devastating Headshot!";
                    }
                    else if (isHighDamage) {
                        title = "🎯 Devastating Kill!";
                    }
                    else {
                        title = "⚔️ Player Eliminated";
                    }
                    embed.SetTitle(title).SetColor(embedColor).AddField("💀 Elimination", $"**Killer:** {killerName}\n**Victim:** {victimName}", false);
                    // Combat details
                    if (_configData.GlobalSettings.ShowCombatDetails) {
                        string combatDetails = GetCombatDetails(info, distance, damage, weapon, isHighDamage);
                        embed.AddField("🔫 Combat Stats", combatDetails, false);
                    }
                }
            }
            else {
                // PvE Death
                string entityName = GetEntityName(info.Initiator);
                description = $"**{victimName}** was killed by **{entityName}**";
                embed.SetTitle($"{deathIcon} Environmental Death").AddField("Death Type", "PvE", true).AddField("Killed By", entityName, true);
                if (!string.IsNullOrEmpty(weapon)) {
                    embed.AddField("Method", weapon, true);
                }
            }
            embed.SetDescription(description);
            // Add coordinates if enabled
            if (_configData.GlobalSettings.IncludeDeathCoordinates) {
                string gridPos = GetGridPosition(deathPosition);
                string coords = $"**Grid:** {gridPos}\n**Position:** {deathPosition.x:F1}, {deathPosition.y:F1}, {deathPosition.z:F1}";
                embed.AddField("📍 Death Location", coords, false);
                embed.AddField("🚁 Quick Teleport", $"`teleportpos {deathPosition.x:F1} {deathPosition.y:F1} {deathPosition.z:F1}`", false);
            }
            // Add separator
            embed.AddField("─────────────────────", "", false);
            var discordMessage = new DiscordMessage().AddEmbed(embed);
            DiscordSendEmbedMessage(discordMessage, _configData.GlobalSettings.PrivateAdminWebhook);
        }
        private string GetCombatDetails(HitInfo info, float distance, float damage, string weapon, bool isHighDamage) {
            string distanceCategory = GetDistanceCategory(distance);
            string bodyPart = GetBodyPartHit(info);
            bool isHeadshot = IsHeadshot(info);
            // Enhanced damage display with body part
            string damageInfo;
            if (isHeadshot) {
                damageInfo = $"**{damage:F1}** 💀 ({bodyPart} shot)";
            }
            else if (isHighDamage) {
                damageInfo = $"**{damage:F1}** 🎯 ({bodyPart} shot)";
            }
            else {
                damageInfo = $"{damage:F1} ({bodyPart} shot)";
            }
            string details = $"**Weapon:** {weapon ?? "Unknown"}\n**Damage:** {damageInfo}";
            if (_configData.GlobalSettings.ShowKillDistance) {
                details += $"\n**Range:** {distance:F1}m ({distanceCategory})";
            }
            // Add special indicators
            if (isHeadshot) {
                details += "\n💀 **HEADSHOT ELIMINATION!**";
            }
            else if (isHighDamage) {
                details += "\n🎯 **High Damage Hit!**";
            }
            if (distance > 150f) {
                details += "\n🏹 **Long Range Snipe!**";
            }
            else if (distance < 3f) {
                details += "\n💥 **Point Blank!**";
            }
            return details;
        }
        private string GetDistanceCategory(float distance) {
            return dis
// ... [Code preview truncated due to length. Full source in attached file] ...

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