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

FREE VoteMute : 1.2.1 1.2.1

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

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

namespace Oxide.Plugins
{
    [Info("VoteMute", "Sigilo", "1.2.1")]
    [Description("Player voting system for muting players")]
    class VoteMute : RustPlugin
    {
        #region Configuration
        
        private Configuration config;
        
        class Configuration
        {
            [JsonProperty("Required percentage (0-100)")]
            public float RequiredPercentage = 60f;
            
            [JsonProperty("Vote duration (seconds)")]
            public int VoteDuration = 120;
            
            [JsonProperty("Cooldown between votes (minutes)")]
            public int CooldownMinutes = 10;
            
            [JsonProperty("Mute duration (minutes)")]
            public int MuteDuration = 60;
            
            [JsonProperty("Discord Webhook URL")]
            public string WebhookUrl = "";
            
            [JsonProperty("Log to console")]
            public bool LogToConsole = false;

            [JsonProperty("Minimum players required for voting")]
            public int MinimumPlayers = 4;

            [JsonProperty("Allow voting to mute self")]
            public bool AllowSelfVote = true;

            [JsonProperty("Allow voting to mute admins")]
            public bool AllowAdminVote = false;

            [JsonProperty("Reminder interval (seconds)")]
            public int ReminderInterval = 20;
            
            [JsonProperty("Count only voters (if false, non-voters count as No)")]
            public bool CountOnlyVoters = true;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
            }
            catch
            {
                PrintError("Configuration file is corrupt, using defaults");
                LoadDefaultConfig();
            }
        }

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

        #endregion

        #region Initialization
        
        private const string permStartVote = "votemute.startvote";
        private const string permVote = "votemute.vote";
        private const string permUnmute = "votemute.unmute";
        private Dictionary<ulong, DateTime> lastVoteCall = new Dictionary<ulong, DateTime>();
        private VoteSession activeVote;
        private Dictionary<ulong, DateTime> mutedPlayers = new Dictionary<ulong, DateTime>();

        void Init()
        {
            permission.RegisterPermission(permStartVote, this);
            permission.RegisterPermission(permVote, this);
            permission.RegisterPermission(permUnmute, this);
            LoadDefaultMessages();
            LoadMuteData();
            timer.Every(60f, CheckMutes);
        }

        void Unload()
        {
            SaveMuteData();
        }
        
        protected override void LoadDefaultMessages()
        {
            var messages = new Dictionary<string, string>
            {
                ["VoteStarted"] = "<color=#E6BE8A>{2}</color> started a vote to mute <color=#B87333>{0}</color> ({1}m)",
                ["VoteDuration"] = "Vote ends in <color=#9CAF88>{0}</color>s",
                ["VoteInstructions"] = "Type <color=#E6BE8A>/yes</color> or <color=#E6BE8A>/no</color> to vote",
                ["VoteSuccess"] = "<color=#B87333>{3}</color> muted for {4}m (<color=#9CAF88>{0}/{1}</color> votes, {2}%)",
                ["VoteFailed"] = "Vote failed for <color=#B87333>{3}</color> (<color=#9CAF88>{0}/{1}</color> votes, {2}%)",
                ["Cooldown"] = "Wait {0}m before starting new vote",
                ["NoPermission"] = "No permission to start vote",
                ["InvalidTarget"] = "Player '<color=#B87333>{0}</color>' not found",
                ["VoteInProgress"] = "Vote already in progress",
                ["InvalidCommandMute"] = "Usage: <color=#E6BE8A>/votemute</color> <color=#B87333><player></color>",
                ["MinimumPlayers"] = "Need at least <color=#9CAF88>{0}</color> players online to start a vote",
                ["CannotVoteAdmin"] = "Cannot vote to mute admins",
                ["CannotVoteSelf"] = "Cannot vote to mute yourself",
                ["Muted"] = "You are muted",
                ["MutedRemaining"] = "You are muted, <color=#9CAF88>{0}</color>m remaining",
                ["MuteExpired"] = "<color=#708090>Your mute has expired</color>",
                ["UnmuteSuccess"] = "Unmuted <color=#B87333>{0}</color>",
                ["NotMuted"] = "<color=#B87333>{0}</color> is not muted",
                ["NoVotePermission"] = "You don't have permission to participate in votes",
                ["NoUnmutePermission"] = "You don't have permission to unmute players",
                ["VoteReminder"] = "Vote to mute <color=#B87333>{0}</color> - {1}s left",
                ["AlreadyVoted"] = "You have already voted"
            };

            lang.RegisterMessages(messages, this, "en");
            lang.RegisterMessages(messages, this);
        }

        #endregion

        #region Voting System
        
        class VoteSession
        {
            public string TargetName;
            public ulong TargetId;
            public Dictionary<ulong, bool> Votes = new Dictionary<ulong, bool>();
            public Timer VoteTimer;
            public Timer ReminderTimer;
            public int RemainingTime;
        }

        [ChatCommand("votemute")]
        void CmdVoteMute(BasePlayer player, string command, string[] args)
        {
            StartVote(player, args);
        }

        void StartVote(BasePlayer initiator, string[] args)
        {
            if (!permission.UserHasPermission(initiator.UserIDString, permStartVote))
            {
                SendReply(initiator, Lang("NoPermission", initiator.UserIDString));
                return;
            }

            if (BasePlayer.activePlayerList.Count < config.MinimumPlayers)
            {
                SendReply(initiator, Lang("MinimumPlayers", initiator.UserIDString, config.MinimumPlayers));
                return;
            }

            if (activeVote != null)
            {
                SendReply(initiator, Lang("VoteInProgress", initiator.UserIDString));
                return;
            }

            if (args.Length == 0)
            {
                SendReply(initiator, Lang("InvalidCommandMute", initiator.UserIDString));
                return;
            }

            if (lastVoteCall.TryGetValue(initiator.userID, out DateTime lastCall) && 
                (DateTime.Now - lastCall).TotalMinutes < config.CooldownMinutes)
            {
                var remainingTime = config.CooldownMinutes - (DateTime.Now - lastCall).TotalMinutes;
                SendReply(initiator, Lang("Cooldown", initiator.UserIDString, Math.Round(remainingTime, 2)));
                return;
            }

            var target = FindPlayer(args[0]);
            if (target == null)
            {
                SendReply(initiator, Lang("InvalidTarget", initiator.UserIDString, args[0]));
                return;
            }

            if (!config.AllowSelfVote && target.userID == initiator.userID)
            {
                SendReply(initiator, Lang("CannotVoteSelf", initiator.UserIDString));
                return;
            }

            if (!config.AllowAdminVote && target.IsAdmin)
            {
                SendReply(initiator, Lang("CannotVoteAdmin", initiator.UserIDString));
                return;
            }

            activeVote = new VoteSession
            {
                TargetName = target.displayName,
                TargetId = target.userID,
                RemainingTime = config.VoteDuration,
                VoteTimer = timer.Once(config.VoteDuration, EndVote)
            };

            lastVoteCall[initiator.userID] = DateTime.Now;

            BroadcastChat(Lang("VoteStarted", null, target.displayName, config.MuteDuration, initiator.displayName));
            BroadcastChat(Lang("VoteDuration", null, config.VoteDuration));
            BroadcastChat(Lang("VoteInstructions"));

            if (config.ReminderInterval > 0 && config.ReminderInterval < config.VoteDuration)
            {
                activeVote.RemainingTime = config.VoteDuration;
                activeVote.ReminderTimer = timer.Every(config.ReminderInterval, () =>
                {
                    activeVote.RemainingTime -= config.ReminderInterval;
                    
                    if (activeVote.RemainingTime <= 5) return;
                    
                    BroadcastChat(Lang("VoteReminder", null, activeVote.TargetName, activeVote.RemainingTime));
                    BroadcastChat(Lang("VoteInstructions"));
                });
            }
        }

        [ChatCommand("yes")]
        void VoteYes(BasePlayer player, string command, string[] args)
        {
            RegisterVote(player, true);
        }

        [ChatCommand("no")]
        void VoteNo(BasePlayer player, string command, string[] args)
        {
            RegisterVote(player, false);
        }

        void RegisterVote(BasePlayer voter, bool vote)
        {
            if (activeVote == null) return;
            
            if (!permission.UserHasPermission(voter.UserIDString, permVote))
            {
                SendReply(voter, Lang("NoVotePermission", voter.UserIDString));
                return;
            }
            
            if (activeVote.Votes.ContainsKey(voter.userID))
            {
                SendReply(voter, Lang("AlreadyVoted", voter.UserIDString));
                return;
            }
            
            activeVote.Votes[voter.userID] = vote;
            SendReply(voter, $"You voted {(vote ? "<color=#E6BE8A>YES</color>" : "<color=#708090>NO</color>")}");
        }

        void EndVote()
        {
            if (activeVote == null) return;

            if (activeVote.ReminderTimer != null)
            {
                activeVote.ReminderTimer.Destroy();
            }

            int participants;
            float votePercentage;
            var yesVotes = activeVote.Votes.Count(v => v.Value);
            var totalVotes = activeVote.Votes.Count;
            
            if (config.CountOnlyVoters)
            {
                participants = totalVotes > 0 ? totalVotes : 1;
                votePercentage = (yesVotes * 100.0f) / participants;
            }
            else
            {
                participants = BasePlayer.activePlayerList.Count;
                votePercentage = (yesVotes * 100.0f) / participants;
            }
            
            var requiredVotes = (int)Math.Ceiling(participants * (config.RequiredPercentage / 100));

            try
            {
                if (yesVotes >= requiredVotes)
                {
                    ExecuteMute(activeVote);
                    BroadcastChat(Lang("VoteSuccess", null, yesVotes, participants, Math.Round(votePercentage, 1), activeVote.TargetName, config.MuteDuration));
                    SendDiscordEmbed("Vote Passed",
                        $"Player **{activeVote.TargetName}** has been muted.",
                        3066993,
                        new List<Dictionary<string, object>>
                        {
                            new Dictionary<string, object> { { "name", "Player ID" }, { "value", activeVote.TargetId.ToString() }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Mute Duration" }, { "value", $"{config.MuteDuration} minutes" }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Vote Result" }, { "value", $"{yesVotes}/{participants} ({Math.Round(votePercentage, 1)}%)" }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Count Mode" }, { "value", config.CountOnlyVoters ? "Only Voters" : "All Players" }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Server" }, { "value", ConVar.Server.hostname }, { "inline", true } }
                        }
                    );
                }
                else
                {
                    BroadcastChat(Lang("VoteFailed", null, yesVotes, participants, Math.Round(votePercentage, 1), activeVote.TargetName));
                    SendDiscordEmbed("Vote Failed",
                        $"Failed to mute player **{activeVote.TargetName}**.",
                        15158332,
                        new List<Dictionary<string, object>>
                        {
                            new Dictionary<string, object> { { "name", "Vote Result" }, { "value", $"{yesVotes}/{participants} ({Math.Round(votePercentage, 1)}%)" }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Count Mode" }, { "value", config.CountOnlyVoters ? "Only Voters" : "All Players" }, { "inline", true } },
                            new Dictionary<string, object> { { "name", "Server" }, { "value", ConVar.Server.hostname }, { "inline", true } }
                        }
                    );
                }
            }
            catch (Exception ex)
            {
                PrintError($"Error in EndVote: {ex.Message}");
            }
            finally
            {
                activeVote = null;
            }
        }

        void ExecuteMute(VoteSession vote)
        {
            try
            {
                mutedPlayers[vote.TargetId] = DateTime.Now.AddMinutes(config.MuteDuration);
                var target = BasePlayer.FindByID(vote.TargetId);
                if (target != null)
                {
                    target.IPlayer.Reply(Lang("Muted", target.UserIDString));
                    target.SendNetworkUpdate();
                }

                SaveMuteData();
            }
            catch (Exception ex)
            {
                PrintError($"Error in ExecuteMute: {ex.Message}");
            }
        }

        void CheckMutes()
        {
            var now = DateTime.Now;
            foreach (var entry in mutedPlayers.Where(entry => entry.Value <= now).ToList())
            {
                mutedPlayers.Remove(entry.Key);
                var player = BasePlayer.FindByID(entry.Key);
                player?.IPlayer.Reply(Lang("MuteExpired", player.UserIDString));
            }
            SaveMuteData();
        }

        #endregion

        #region Helper Methods
        
        BasePlayer FindPlayer(string search)
        {
            return BasePlayer.activePlayerList
                .FirstOrDefault(p => p.displayName.Contains(search, StringComparison.OrdinalIgnoreCase) 
                                   || p.UserIDString == search);
        }

        string Lang(string key, string userId = null, params object[] args)
        {
            try 
            {
                var message = lang.GetMessage(key, this, userId);
                return args == null || args.Length == 0 ? message : string.Format(message, args);
            }
            catch (Exception ex)
            {
                PrintError($"Lang format error for key '{key}': {ex.Message}");
                return key;
            }
        }

        void BroadcastChat(string message)
        {
            foreach (var player in BasePlayer.activePlayerList)
                SendReply(player, message);
            
            if (config.LogToConsole)
                Puts(message);
        }

        async Task SendDiscordEmbed(string title, string description, int color, List<Dictionary<string, object>> fields)
        {
            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", "Vote Mute System"},
                {"embeds", new object[] { embed } }
            };

            var payload = JsonConvert.SerializeObject(payloadObj);

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

        void LoadMuteData()
        {
            mutedPlayers = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, DateTime>>(Name) ?? new Dictionary<ulong, DateTime>();
        }

        void SaveMuteData()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, mutedPlayers);
        }

        #endregion

        #region Hooks
        
        [PluginReference]
        private Plugin BetterChat;

        private readonly HashSet<ulong> mutedReplied = new HashSet<ulong>();

        private bool MuteCheck(IPlayer player, bool sendMessage = true)
        {
            ulong id = ulong.Parse(player.Id);
            if (mutedPlayers.TryGetValue(id, out DateTime muteExpiry))
            {
                if (DateTime.Now >= muteExpiry)
                {
                    mutedPlayers.Remove(id);
                    SaveMuteData();
                    return false;
                }
                if (sendMessage && !mutedReplied.Contains(id))
                {
                    int timeRemaining = (int)Math.Ceiling((muteExpiry - DateTime.Now).TotalMinutes);
                    if (timeRemaining < 1) timeRemaining = 1;
                    mutedReplied.Add(id);
                    player.Reply(string.Format(Lang("MutedRemaining", player.Id), timeRemaining));
                    timer.Once(0.5f, () => mutedReplied.Remove(id));
                }
                return true;
            }
            return false;
        }

        object OnPlayerVoice(BasePlayer player)
        {
            if (MuteCheck(player.IPlayer, sendMessage: false))
                return true;
            return null;
        }

        object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)
        {
            if (BetterChat != null)
                return null;
            
            if (MuteCheck(player.IPlayer))
                return false;
            
            return null;
        }

        object OnUserChat(IPlayer player, string message)
        {
            if (MuteCheck(player))
                return true;
            return null;
        }

        void OnBetterChat(Dictionary<string, object> data)
        {
            IPlayer player = data["Player"] as IPlayer;
            if (player == null) return;
            if (MuteCheck(player, sendMessage: false))
                data["CancelOption"] = 2;
        }

        object OnTeamChat(BasePlayer player, string message)
        {
            if (MuteCheck(player.IPlayer))
                return false;
            return null;
        }

        void OnPlayerDisconnected(BasePlayer player)
        {
            if (activeVote?.TargetId == player.userID)
                EndVote();
        }

        #endregion

        [ChatCommand("unmute")]
        void CmdUnmute(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, permUnmute))
            {
                SendReply(player, Lang("NoUnmutePermission", player.UserIDString));
                return;
            }

            if (args.Length == 0)
            {
                SendReply(player, Lang("InvalidTarget", player.UserIDString));
                return;
            }

            var target = FindPlayer(args[0]);
            if (target == null)
            {
                SendReply(player, Lang("InvalidTarget", player.UserIDString, args[0]));
                return;
            }

            if (!mutedPlayers.ContainsKey(target.userID))
            {
                SendReply(player, Lang("NotMuted", player.UserIDString, target.displayName));
                return;
            }

            mutedPlayers.Remove(target.userID);
            SaveMuteData();
            
            string message = Lang("UnmuteSuccess", player.UserIDString, target.displayName);
            SendReply(player, message);
            SendDiscordEmbed("Player Unmuted",
                $"Admin **{player.displayName}** unmuted player **{target.displayName}**.",
                3447003,
                new List<Dictionary<string, object>>
                {
                    new Dictionary<string, object> { { "name", "Admin" }, { "value", player.displayName }, { "inline", true } },
                    new Dictionary<string, object> { { "name", "Player" }, { "value", target.displayName }, { "inline", true } }
                }
            );
        }
    }
}

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