using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Facepunch.Extend; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oxide.Core; using Oxide.Core.Configuration; using Oxide.Core.Libraries; using Oxide.Core.Plugins; using UnityEngine; using UnityEngine.Networking; namespace Oxide.Plugins { [Info(_PluginName, _PluginAuthor, _PluginVersion)] [Description(_PluginDescription)] public class EasyVoteLite : RustPlugin { // Plugin Metadata private const string _PluginName = "EasyVoteLite"; private const string _PluginAuthor = "BippyMiester"; private const string _PluginVersion = "3.0.2"; private const string _PluginDescription = "Voting System"; // Misc Variables private IEnumerator coroutine; private void Init() { ConsoleLog($"{_PluginName} has been initialized..."); _config = Config.ReadObject(); LoadMessages(); } private void OnServerInitialized() { } private void Loaded() { } private void Unload() { if (coroutine != null) ServerMgr.Instance.StopCoroutine(coroutine); } #region HelperFunctions private void HandleClaimWebRequestCallback(int code, string response, BasePlayer player, string url, string serverName, string site) { if (code != 200) { ConsoleError($"An error occurred while trying to check the claim status of the player {player.displayName}:{player.UserIDString}"); ConsoleWarn($"URL: {url}"); ConsoleWarn($"HTTP Code: {code} | Response: {response} | Server Name: {serverName}"); ConsoleWarn("This error could be due to a malformed or incorrect server token, id, or player id / username issue. Most likely its due to your server key being incorrect. Check that you server key is correct."); return; } _Debug("------------------------------"); _Debug($"Site: {site}"); _Debug($"Code: {code}"); _Debug($"Response: {response}"); _Debug($"URL: {url}"); _Debug($"ServerName: {serverName}"); _Debug("------------------------------"); // Handle Every Reward if (response == "1") { HandleVoteCount(player); player.ChatMessage(_lang("ThankYou", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix], player.voteCount().ToString(), site)); // Handle Discord Announcements if (_config.Discord[ConfigDefaultKeys.DiscordEnabled].ToBool()) { coroutine = DiscordSendMessage(_lang("DiscordWebhookMessage", player.UserIDString, player.displayName, serverName, site)); ServerMgr.Instance.StartCoroutine(coroutine); } // Handle Global Annonucements if (_config.NotificationSettings[ConfigDefaultKeys.GlobalChatAnnouncements] == "true") { server.Broadcast(_lang("GlobalChatAnnouncements", player.UserIDString, player.displayName, player.voteCount().ToString()), _config.PluginSettings[ConfigDefaultKeys.Prefix]); } } else { player.ChatMessage(_lang("ClaimStatus", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix], serverName, site, "Not Voted")); } } private void HandleStatusWebRequestCallback(int code, string response, BasePlayer player, string url, string serverName, string site) { // If the error code isn't a successful code if (code != 200) { ConsoleError($"An error occurred while trying to check the claim status of the player {player.displayName}:{player.UserIDString}"); ConsoleWarn($"URL: {url}"); ConsoleWarn($"HTTP Code: {code} | Response: {response} | Server Name: {serverName}"); ConsoleWarn("This error could be due to a malformed or incorrect server token, id, or player id / username issue. Most likely its due to your server key being incorrect. Check that you server key is correct."); return; } _Debug("------------------------------"); _Debug($"Site: {site}"); _Debug($"Code: {code}"); _Debug($"Response: {response}"); _Debug($"URL: {url}"); _Debug($"ServerName: {serverName}"); _Debug("------------------------------"); // Handle all other sites if (response == "0") { player.ChatMessage(_lang("NoRewards", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix], serverName, site)); } // Handle a player needs to claim a reward else if (response == "1") { player.ChatMessage(_lang("RememberClaim", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix], site)); } // Handle a player has already voted else if (response == "2") { player.ChatMessage(_lang("AlreadyVoted", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix], site)); } } private void HandleVoteCount(BasePlayer player) { // Grab the current vote count of the player int playerVoteCount = (int) DataFile[player.UserIDString]; _Debug($"Player: {player.displayName}/{player.UserIDString} | Current VoteCount: {playerVoteCount}"); // Increase the players vote count by 1 playerVoteCount += 1; DataFile[player.UserIDString] = playerVoteCount; SaveDataFile(DataFile); _Debug($"Player: {player.displayName}/{player.UserIDString} | Updated Vote Count: {playerVoteCount}"); // Handle giving rewards to the player based on cumulative boolean value if (_config.PluginSettings[ConfigDefaultKeys.RewardIsCumulative] == "true") { GiveCumulativeRewards(player, (int) DataFile[player.UserIDString]); } else { GiveNormalRewards(player, (int) DataFile[player.UserIDString]); } } private void GiveCumulativeRewards(BasePlayer player, int playerVoteCount) { _Debug($"Giving Cumulative Rewards to player: {player.displayName} | {player.UserIDString}"); // Handle the every reward GiveEveryReward(player); // Handle the first reward GiveFirstReward(player); // Handle all subsequent rewards foreach (KeyValuePair> rewards in _config.Rewards) { if (rewards.Key.ToInt() <= playerVoteCount) { GiveSubsequentReward(player, rewards.Value); } } } private void GiveNormalRewards(BasePlayer player, int playerVoteCount) { _Debug($"Giving Normal Rewards to player: {player.displayName} | {player.UserIDString}"); // Handle the every reward GiveEveryReward(player); // Handle the first reward if (playerVoteCount == 1) { GiveFirstReward(player); } // Handle all subsequent rewards foreach (KeyValuePair> rewards in _config.Rewards) { if (rewards.Key.ToInt() == playerVoteCount) { GiveSubsequentReward(player, rewards.Value); } } } private void GiveEveryReward(BasePlayer player) { foreach (string rewardCommand in _config.Rewards["@"]) { string command = ParseRewardCommand(player, rewardCommand); _Debug($"Giving player {player.displayName}/{player.UserIDString} their every time reward for voting."); _Debug($"Reward Command: {command}"); rust.RunServerCommand(command); } } private void GiveFirstReward(BasePlayer player) { foreach (string rewardCommand in _config.Rewards["first"]) { string command = ParseRewardCommand(player, rewardCommand); _Debug($"Giving player {player.displayName}/{player.UserIDString} their first time reward for voting."); _Debug($"Reward Command: {command}"); rust.RunServerCommand(command); } } private void GiveSubsequentReward(BasePlayer player, List rewardsList) { foreach (string rewardCommand in rewardsList) { string command = ParseRewardCommand(player, rewardCommand); _Debug($"Giving player {player.displayName}/{player.UserIDString} their {player.voteCount()} time reward for voting."); _Debug($"Reward Command: {command}"); rust.RunServerCommand(command); } } private string ParseRewardCommand(BasePlayer player, string command) { return command .Replace("{playerid}", player.UserIDString) .Replace("{playername}", player.displayName); } private void CheckIfPlayerDataExists(BasePlayer player) { _Debug($"{player._name} has connected... Checking if data exists."); // If the player data entry is null then we need to create a new entry if (DataFile[player.UserIDString] == null) { _Debug($"{player._name} data does not exist. Creating new entry now."); DataFile[player.UserIDString] = 0; SaveDataFile(DataFile); _Debug($"{player._name} Data has been created."); } } private void ResetAllVoteData() { foreach (KeyValuePair player in DataFile.ToList()) { DataFile[player.Key] = 0; _Debug($"Player {player.Key} vote count reset..."); } SaveDataFile(DataFile); } private void CheckVotingStatus(BasePlayer player) { var timeout = 5000; player.ChatMessage(_lang("PleaseWait", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix])); // Loop through the vote sites api list foreach (KeyValuePair> kvp in _config.VoteSitesAPI) { // Loop through all the servers foreach (KeyValuePair> serverskvp in _config.Servers) { // Loop through all the ID's and Keys foreach (KeyValuePair serveridkeys in serverskvp.Value) { // If the key of the api is equal to the server key if (Equals(kvp.Key, serveridkeys.Key)) { string[] idkey = serveridkeys.Value.Split(':'); string url = ""; if (kvp.Value[ConfigDefaultKeys.apiUsername] == "true") { url = string.Format(kvp.Value[ConfigDefaultKeys.apiStatus], idkey[1], player.displayName); } else if (serveridkeys.Key == "Rustservers.gg") { url = string.Format(kvp.Value[ConfigDefaultKeys.apiStatus], idkey[1], player.UserIDString, idkey[0]); } else { url = string.Format(kvp.Value[ConfigDefaultKeys.apiStatus], idkey[1], player.UserIDString); } webrequest.Enqueue(url, null, (code, response) => HandleStatusWebRequestCallback(code, response, player, url, serverskvp.Key, serveridkeys.Key), this, RequestMethod.GET, null, timeout, (code, response, exception) => HandleStatusWebRequestCallback(code, response, player, url, serverskvp.Key, serveridkeys.Key)); } } } } } #endregion #region Hooks private void OnPlayerConnected(BasePlayer player) { // Check if the player data is present in the data file CheckIfPlayerDataExists(player); // Check voting status if (!_config.NotificationSettings[ConfigDefaultKeys.OnPlayerSleepEnded].ToBool() && _config.NotificationSettings[ConfigDefaultKeys.OnPlayerConnected].ToBool()) { CheckVotingStatus(player); } } private void OnNewSave(string filename) { ConsoleLog("New map data detected!"); if (_config.PluginSettings[ConfigDefaultKeys.ClearRewardsOnWipe] == "true") { _Debug("Wiping all votes from data file"); ResetAllVoteData(); } } private void OnPlayerSleepEnded(BasePlayer player) { // Check the voting status when the player wakes up if (_config.NotificationSettings[ConfigDefaultKeys.OnPlayerSleepEnded].ToBool()) { CheckVotingStatus(player); } } #endregion #region ChatCommands [ChatCommand("rewardlist")] private void RewardListChatCommand(BasePlayer player, string command, string[] args) { player.ChatMessage(_config.PluginSettings[ConfigDefaultKeys.Prefix] + "The following rewards are given for voting!"); foreach (KeyValuePair kvp in _config.RewardDescriptions) { player.ChatMessage(kvp.Value); } } [ChatCommand("vote")] private void VoteChatCommand(BasePlayer player, string command, string[] args) { player.ChatMessage(_lang("VoteList", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix])); foreach (KeyValuePair> kvp in _config.VoteSitesAPI) { foreach (KeyValuePair> serverskvp in _config.Servers) { foreach (KeyValuePair serveridkeys in serverskvp.Value) { if (Equals(kvp.Key, serveridkeys.Key)) { string[] parts = serveridkeys.Value.Split(':'); player.ChatMessage(serverskvp.Key + ": " + string.Format(kvp.Value[ConfigDefaultKeys.apiLink], parts[0])); } } } } player.ChatMessage(_lang("EarnReward")); } [ChatCommand("claim")] private void ClaimChatCommand(BasePlayer player, string command, string[] args) { // Check if the player data is present in the data file CheckIfPlayerDataExists(player); var timeout = 5000; player.ChatMessage(_lang("PleaseWait", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix])); // Loop through the vote sites api list foreach (KeyValuePair> kvp in _config.VoteSitesAPI) { // Loop through all the servers foreach (KeyValuePair> serverskvp in _config.Servers) { // Loop through all the ID's and Keys foreach (KeyValuePair serveridkeys in serverskvp.Value) { // If the key of the api is equal to the server key if (Equals(kvp.Key, serveridkeys.Key)) { string[] idkey = serveridkeys.Value.Split(':'); string url = ""; if (kvp.Value[ConfigDefaultKeys.apiUsername] == "true") { url = string.Format(kvp.Value[ConfigDefaultKeys.apiClaim], idkey[1], player.displayName); } else if (serveridkeys.Key == "Rustservers.gg") { url = string.Format(kvp.Value[ConfigDefaultKeys.apiClaim], idkey[1], player.UserIDString, idkey[0]); } else { url = string.Format(kvp.Value[ConfigDefaultKeys.apiClaim], idkey[1], player.UserIDString); } webrequest.Enqueue(url, null, (code, response) => HandleClaimWebRequestCallback(code, response, player, url, serverskvp.Key, serveridkeys.Key), this, RequestMethod.GET, null, timeout, (code, response, exception) => HandleClaimWebRequestCallback(code, response, player, url, serverskvp.Key, serveridkeys.Key)); } } } } // Wait until all web requests are done and then send a message timer.Once(5f, () => { player.ChatMessage(_lang("ClaimReward", player.UserIDString, _config.PluginSettings[ConfigDefaultKeys.Prefix])); }); } #endregion #region ConsoleHelpers protected void ConsoleLog(object message) { Puts(message?.ToString()); } protected void ConsoleError(string message) { if (Convert.ToBoolean(_config.PluginSettings[ConfigDefaultKeys.LogEnabled])) LogToFile("EasyVote2", $"ERROR: {message}", this); Debug.LogError($"ERROR: " + message); } protected void ConsoleWarn(string message) { if (Convert.ToBoolean(_config.PluginSettings[ConfigDefaultKeys.LogEnabled])) LogToFile("EasyVote2", $"WARNING: {message}", this); Debug.LogWarning($"WARNING: " + message); } protected void _Debug(string message, string arg = null) { if (_config.PluginSettings[ConfigDefaultKeys.DebugEnabled] == "true") { if (Convert.ToBoolean(_config.PluginSettings[ConfigDefaultKeys.LogEnabled])) LogToFile("EasyVote2", $"DEBUG: {message}", this); Puts($"DEBUG: {message}"); if (arg != null) { Puts($"DEBUG ARG: {arg}"); } } } #endregion #region ConsoleCommands [ConsoleCommand("clearvote")] private void ClearPlayerVoteCountConsoleCommand(ConsoleSystem.Arg arg) { // Check if an argument is even passed if (!arg.HasArgs(1)) { ConsoleError("Command clearvote usage: clearvote steamid|username"); return; } // Get the player based off the argument passed BasePlayer player = arg.GetPlayer(0); if (player == null) { ConsoleError($"Failed to find player with ID/Username/IP of: {arg.GetString(0)}"); return; } // Update the player vote count in the data file DataFile[player.UserIDString] = 0; SaveDataFile(DataFile); ConsoleLog($"{player.displayName}/{player.UserIDString} vote count has been reset to 0"); } [ConsoleCommand("checkvote")] private void CheckPlayerVoteCountConsoleCommand(ConsoleSystem.Arg arg) { // Check if an argument is even passed if (!arg.HasArgs(1)) { ConsoleError("Command checkvote usage: checkvote steamid|username"); return; } // Get the player based off the argument passed BasePlayer player = arg.GetPlayer(0); if (player == null) { ConsoleError($"Failed to find player with ID/Username/IP of: {arg.GetString(0)}"); return; } ConsoleLog($"Player {player.displayName}/{player.UserIDString} has {getPlayerVotes(player.UserIDString)} votes total"); } [ConsoleCommand("setvote")] private void SetPlayerVoteCountConsoleCommand(ConsoleSystem.Arg arg) { // Check if an argument is even passed if (!arg.HasArgs(2)) { ConsoleError("Command setvote usage: setvote steamid|username numberOfVotes"); return; } // Get the player based off the argument passed BasePlayer player = arg.GetPlayer(0); if (player == null) { ConsoleError($"Failed to find player with ID/Username/IP of: {arg.GetString(0)}"); return; } DataFile[player.UserIDString] = arg.GetInt(1); SaveDataFile(DataFile); ConsoleLog($"Player {player.displayName}/{player.UserIDString} vote count has been updated to {arg.GetString(1)}"); } [ConsoleCommand("resetvotedata")] private void ResetAllVoteDataConsoleCommand(ConsoleSystem.Arg arg) { if (arg.HasArgs(1)) { ConsoleError("Command resetvotedata usage: This command has no arguments. Run the command again with no arguments"); return; } ResetAllVoteData(); } #endregion #region APIHooks [HookMethod("getPlayerVotes")] public int getPlayerVotes(string steamID) { if (DataFile[steamID] == null) { _Debug("getPlayerVotes(): Player data doesn't exist"); return 0; } return (int) DataFile[steamID]; } #endregion #region Localization string _lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); private void LoadMessages() { lang.RegisterMessages(new Dictionary { ["NoPermission"] = "You do not have permission to use this command!", ["ClaimStatus"] = "{0} {1}\nChecked {2}, Status: {3}", ["ClaimReward"] = "{0} If you voted, and the votes went through, then you just received your vote reward(s). Enjoy!", ["PleaseWait"] = "{0} Checking all the VoteSites API's... Please be patient as this can take some time...", ["VoteList"] = "{0} You can vote for our server at the following links:", ["EarnReward"] = "When you have voted, type /claim to claim your reward(s)!", ["ThankYou"] = "{0} Thank you for voting! You have voted {1} time(s) Here is your reward for: {2}", ["NoRewards"] = "{0} You haven't voted for {1} on {2} yet! Type /vote to get started!", ["RememberClaim"] = "{0} {1} is reporting that you have an unclaimed reward! Use /claim to claim your reward!\n You have to claim your reward within 24h! Otherwise it will be gone!", ["GlobalChatAnnouncements"] = "{0} has voted {1} time(s) and just received their rewards. Find out where you can vote by typing /vote\nTo see a list of available rewards type /rewardlist", ["AlreadyVoted"] = "{0} {1} reports you have already voted! Vote again later.", ["DiscordWebhookMessage"] = "{0} has voted for {1} on {2} and got some rewards! Type /rewardlist in game to find out what you can get when you vote for us!" }, this); } #endregion #region Config private PluginConfig _config; protected override void LoadDefaultConfig() { _config = new PluginConfig(); _config.PluginSettings = new Dictionary { {ConfigDefaultKeys.DebugEnabled, "false"}, {ConfigDefaultKeys.LogEnabled, "true"}, {ConfigDefaultKeys.ClearRewardsOnWipe, "true"}, {ConfigDefaultKeys.RewardIsCumulative, "false"}, {ConfigDefaultKeys.Prefix, "[EasyVote] "}, }; _config.NotificationSettings = new Dictionary { {ConfigDefaultKeys.GlobalChatAnnouncements, "true"}, {ConfigDefaultKeys.OnPlayerSleepEnded, "false"}, {ConfigDefaultKeys.OnPlayerConnected, "true"} }; _config.Discord = new Dictionary { {ConfigDefaultKeys.discordWebhookURL, "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"}, {ConfigDefaultKeys.DiscordEnabled, "false"}, {ConfigDefaultKeys.discordTitle, "A player has just voted for us!"} }; _config.Rewards = new Dictionary> { { "@", new List() { "giveto {playerid} supply.signal 1" } }, { "first", new List() { "giveto {playerid} stones 10000", "sr add {playerid} 10000" } }, { "3", new List() { "addgroup {playerid} vip 7d" } }, { "6", new List() { "grantperm {playerid} plugin.test 1d" } }, { "10", new List() { "zl.lvl {playerid} * 2" } } }; _config.RewardDescriptions = new Dictionary { { "@", "Every Vote: 1 Supply Signal" }, { "first", "First Vote: 10000 Stones, 10000 RP" }, { "3", "3rd Vote: 7 days of VIP rank" }, { "6", "6th Vote: 1 day of plugin.test permission" }, { "10", "10th Vote: 2 zLevels in Every Category" } }; _config.Servers = new Dictionary> { { "ServerName1", new Dictionary() { { "Rust-Servers.net", "ID:KEY" },{ "Rustservers.gg", "ID:KEY" }, { "BestServers.com", "ID:KEY" }, { "top-serveurs.net", "ID:TOKEN" } } }, { "ServerName2", new Dictionary() { { "Rust-Servers.net", "ID:KEY" },{ "Rustservers.gg", "ID:KEY" }, { "BestServers.com", "ID:KEY" }, { "top-serveurs.net", "ID:TOKEN" } } } }; _config.VoteSitesAPI = new Dictionary> { { "Rust-Servers.net", new Dictionary() { { ConfigDefaultKeys.apiClaim, "http://rust-servers.net/api/?action=custom&object=plugin&element=reward&key={0}&steamid={1}" }, { ConfigDefaultKeys.apiStatus, "http://rust-servers.net/api/?object=votes&element=claim&key={0}&steamid={1}" }, { ConfigDefaultKeys.apiLink, "http://rust-servers.net/server/{0}" }, { ConfigDefaultKeys.apiUsername, "false"} } }, { "Rustservers.gg", new Dictionary() { { ConfigDefaultKeys.apiClaim, "https://rustservers.gg/vote-api.php?action=claim&key={0}&server={2}&steamid={1}" }, { ConfigDefaultKeys.apiStatus, "https://rustservers.gg/vote-api.php?action=status&key={0}&server={2}&steamid={1}" }, { ConfigDefaultKeys.apiLink, "https://rustservers.gg/server/{0}" }, { ConfigDefaultKeys.apiUsername, "false"} } }, { "BestServers.com", new Dictionary() { { ConfigDefaultKeys.apiClaim, "https://bestservers.com/api/vote.php?action=claim&key={0}&steamid={1}" }, { ConfigDefaultKeys.apiStatus, "https://bestservers.com/api/vote.php?action=status&key={0}&steamid={1}" }, { ConfigDefaultKeys.apiLink, "https://bestservers.com/server/{0}" }, { ConfigDefaultKeys.apiUsername, "false"} } } }; SaveConfig(); ConsoleWarn("A new configuration file has been generated!"); } protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) { LoadDefaultConfig(); //SaveConfig(); } } catch { ConsoleError("The configuration file is corrupted. Please delete the config file and reload the plugin."); LoadDefaultConfig(); SaveConfig(); } } protected override void SaveConfig() => Config.WriteObject(_config); class ConfigDefaultKeys { // API Stuff public const string apiClaim = "API Claim Reward (GET URL)"; public const string apiStatus = "API Vote status (GET URL)"; public const string apiLink = "Vote link (URL)"; public const string apiUsername = "Site Uses Username Instead of Player Steam ID?"; // Discord Webhook public const string discordTitle = "Discord Title"; public const string discordWebhookURL = "Discord webhook (URL)"; public const string DiscordEnabled = "DiscordMessage Enabled (true / false)"; // Plugin Settings public const string Prefix = "Chat Prefix"; public const string LogEnabled = "Enable logging => logs/EasyVote (true / false)"; public const string DebugEnabled = "Debug Enabled?"; public const string RewardIsCumulative = "Vote rewards cumulative (true / false)"; public const string ClearRewardsOnWipe = "Wipe Rewards Count on Map Wipe?"; // Notification Settings public const string GlobalChatAnnouncements = "Globally announcment in chat when player voted (true / false)"; public const string OnPlayerSleepEnded = "Notify player of rewards when they stop sleeping?"; public const string OnPlayerConnected = "Notify player of rewards when they connect to the server?"; } private class PluginConfig { [JsonProperty(PropertyName = "Plugin Settings")] public Dictionary PluginSettings; [JsonProperty(PropertyName = "Notification Settings")] public Dictionary NotificationSettings; [JsonProperty(PropertyName = "Discord")] public Dictionary Discord; [JsonProperty(PropertyName = "Rewards")] public Dictionary> Rewards; [JsonProperty(PropertyName = "Reward Descriptions")] public Dictionary RewardDescriptions; [JsonProperty(PropertyName = "Server Voting IDs and Keys")] public Dictionary> Servers; [JsonProperty(PropertyName = "Voting Sites API Information")] public Dictionary> VoteSitesAPI; } #endregion #region Data protected internal static DynamicConfigFile DataFile = Interface.Oxide.DataFileSystem.GetDatafile(_PluginName); private void SaveDataFile(DynamicConfigFile data) { data.Save(); _Debug("Data file has been updated."); } #endregion #region DiscordMessages private IEnumerator DiscordSendMessage(string msg) { // Check if the discord webhook is default or null/empty if (_config.Discord[ConfigDefaultKeys.discordWebhookURL] != "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" || !string.IsNullOrEmpty(_config.Discord[ConfigDefaultKeys.discordWebhookURL])) { // Grab the form data WWWForm formData = new WWWForm(); string content = $"{msg}\n"; formData.AddField("content", content); // Define the request using (var request = UnityWebRequest.Post(_config.Discord[ConfigDefaultKeys.discordWebhookURL], formData)) { // Execute the request yield return request.SendWebRequest(); if ((request.isNetworkError || request.isHttpError) && request.error.Contains("Too Many Requests")) { Puts("Discord Webhook Rate Limit Exceeded... Waiting 30 seconds..."); yield return new WaitForSeconds(30f); } } } ServerMgr.Instance.StopCoroutine(coroutine); } #endregion } // Create a new BasePlayer Extension public static class BasePlayerExtensionLite { public static object voteCount(this BasePlayer player) { return EasyVoteLite.DataFile[player.UserIDString]; } } }