/* ########### README #################################################### !!! DON'T EDIT THIS FILE !!! ########### CHANGES ################################################### 1.0.0 - Plugin release 1.0.1 - Fixed json deserialize settings - Added auto kick or ban - Added localization - Added test command - Added OnPlayerBanned 1.0.2 - Fixed validating webhook in config 1.0.3 - Fixed potential NRE 1.0.4 - API update 1.0.5 - Small fix in API link ####################################################################### */ using System.Collections.Generic; using Oxide.Core; using System; using Newtonsoft.Json; using Oxide.Core.Libraries; namespace Oxide.Plugins { [Info("Game Ban Check", "Paulsimik", "1.0.5")] [Description("Check players game ban")] class GameBanCheck : RustPlugin { #region [Fields] private readonly string urlGameBanAPI = "https://rustbans.com/api/"; private Configuration config; private JsonSerializerSettings serializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; private List defaultBanReason = new List { "cheat detected", "publisherbanned" }; private enum AutoSolution { Ignore, Kick, Ban } #endregion #region [Oxide Hooks] private void OnServerInitialized() => CheckValidConfig(); private void OnPlayerConnected(BasePlayer player) => CheckPlayer(player); private void OnPlayerBanned(string name, ulong id, string address, string reason) { BasePlayer player = BasePlayer.FindByID(id); if (player == null) return; if (!player.IsConnected) return; LiveGameBan(player, reason); } private void OnPlayerBanned(Network.Connection connection, string status) { if (connection == null) return; BasePlayer player = connection.player as BasePlayer; if (player == null) return; if (!player.IsConnected) return; LiveGameBan(player, status); } #endregion #region [Hooks] private void CheckValidConfig() { bool repeat = false; if (string.IsNullOrEmpty(config.steamApi) || config.steamApi.Contains("https://steamcommunity.com/dev/apikey")) { PrintError("Steam API has not been set or is invalid. Check your configuration."); repeat = true; } if (string.IsNullOrEmpty(config.webhookUrl) || !(config.webhookUrl.Contains("https://discordapp.com/api/webhooks/") || config.webhookUrl.Contains("https://discord.com/api/webhooks/"))) { PrintError("Discord webhook has not been set or is invalid. Check your configuration."); repeat = true; } if (repeat) timer.Once(10f, CheckValidConfig); } private void CheckPlayer(BasePlayer player) { if (player == null) return; if (!player.IsConnected) return; try { webrequest.Enqueue(urlGameBanAPI + player.UserIDString, string.Empty, (code, response) => { if (code != 200) { PrintError($"Error {code}: Game Ban service is not available"); return; } if (string.IsNullOrEmpty(response)) { PrintError("Error: Game Ban service response is empty"); return; } var json = JsonConvert.DeserializeObject>(response, serializerSettings); if (json != null) { if (!json[0].isBanned) return; TimeSpan time = TimeSpan.FromMilliseconds(json[0].dateTime); DateTime banDate = new DateTime(1970, 1, 1) + time; DateTime currentDate = DateTime.UtcNow; var diference = currentDate - banDate; if (diference.Days <= config.maxDaysBan) { DiscordMessage message = new DiscordMessage(); DiscordMessage.Embed embed = message.embed; embed.AddTitle(player.displayName); embed.AddDescription($"Player was game [banned]({json[0].tweetLink}) on {banDate}\n{player.UserIDString}\n[Battlemetrics Link](https://www.battlemetrics.com/rcon/players?filter[search]={player.UserIDString})"); embed.AddUrl($"https://steamcommunity.com/profiles/{player.UserIDString}"); embed.AddColor(13452850); embed.AddAuthor("Game Ban Detected", "https://czechrust.eu/img/menu/rust_logo.png"); embed.AddTimestamp(currentDate); GetPlayerAvatar(player, message, embed); Solution(player); } } }, this, RequestMethod.GET); } catch (Exception ex) { PrintError($"Error: {ex.Message}"); } } private void GetPlayerAvatar(BasePlayer player, DiscordMessage message, DiscordMessage.Embed embed) { string avatarUrl = "https://czechrust.eu/img/menu/rust_logo.png"; string urlSummaries = $"http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key={config.steamApi}&steamids={player.UserIDString}"; try { webrequest.Enqueue(urlSummaries, string.Empty, (code, response) => { if (code != 200) { PrintError($"Error {code}: Steam service is not available"); return; } if (string.IsNullOrEmpty(response)) { PrintError("Error: Steam response is empty"); return; } var json = JsonConvert.DeserializeObject(response).content.Players[0]; if (json != null) { avatarUrl = json.avatarMedium; embed.AddThumbnail(avatarUrl); message.AddEmbed(embed); SendDiscordMessage(message); } }, this, RequestMethod.GET); } catch { } } private void SendDiscordMessage(DiscordMessage message) => webrequest.Enqueue(config.webhookUrl, message.ToJson(), Callback, this, RequestMethod.POST, new Dictionary() { { "Content-Type", "application/json" } }); private void Callback(int code, string response) { if (code != 204) { PrintError($"Error send discord message: {code} : {response}"); } } private void Solution(BasePlayer player) { switch (config.autoSolution) { case AutoSolution.Kick: string reason = GetLang("kickReason"); Interface.CallHook("OnUserKicked", player.IPlayer, reason); Interface.CallHook("OnPlayerKicked", player, reason); player.Kick(reason); return; case AutoSolution.Ban: BanPlayer(player); return; default: return; } } private void BanPlayer(BasePlayer player) { var reason = GetLang("banReason"); Unsubscribe(nameof(OnPlayerBanned)); Interface.CallHook("OnUserBanned", player.displayName, player.UserIDString, player.net.connection.ipaddress, reason); Interface.CallHook("OnPlayerBanned", player.displayName, player.userID, player.net.connection.ipaddress, reason); player.Kick(reason); timer.Once(10f, () => Subscribe(nameof(OnPlayerBanned))); } private void LiveGameBan(BasePlayer player, string reason) { if (!defaultBanReason.Exists(x => reason.ToLower().Contains(x))) return; DateTime currentDate = DateTime.UtcNow; DiscordMessage message = new DiscordMessage(); DiscordMessage.Embed embed = message.embed; embed.AddTitle(player.displayName); embed.AddDescription($"Player was just been game banned\n{player.UserIDString}\n[Battlemetrics Link](https://www.battlemetrics.com/rcon/players?filter[search]={player.UserIDString})"); embed.AddUrl($"https://steamcommunity.com/profiles/{player.UserIDString}"); embed.AddColor(13452850); embed.AddAuthor("Game Ban Detected", "https://czechrust.eu/img/menu/rust_logo.png"); embed.AddTimestamp(currentDate); GetPlayerAvatar(player, message, embed); } #endregion #region [Chat Commands] [ChatCommand("gbtest")] private void cmdGameBanCheckTest(BasePlayer player, string command, string[] args) { OnPlayerBanned(player.displayName, player.userID, player.net.connection.ipaddress, "Cheat Detected!"); } #endregion #region [Classes] private class Configuration { [JsonProperty("Steam API")] public string steamApi; [JsonProperty("Detect bans younger than days")] public int maxDaysBan; [JsonProperty("Discord webhook")] public string webhookUrl; [JsonProperty("Auto solution (0 - Ignore, 1 - Kick, 2 - Ban)")] public AutoSolution autoSolution; public VersionNumber version; } private class PlayerSummaries { [JsonProperty("response")] public Content content; public class Content { [JsonProperty("players")] public Player[] Players; public class Player { [JsonProperty("avatarmedium")] public string avatarMedium; } } } private class GameBanDatabase { [JsonProperty("TweetLink")] public string tweetLink; [JsonProperty("BanDateMilliseconds")] public double dateTime; [JsonProperty("Banned")] public bool isBanned; } private class DiscordMessage { [JsonProperty("embeds")] private List embeds = new List(); public Embed embed = new Embed(); public DiscordMessage AddEmbed(Embed embed) { this.embeds.Add(embed); return this; } internal class Embed { [JsonProperty("title")] private string title; [JsonProperty("description")] private string description; [JsonProperty("url")] private string url; [JsonProperty("color")] private int color; [JsonProperty("author")] private Author author; [JsonProperty("timestamp")] private DateTime dateTime; [JsonProperty("thumbnail")] private Thumbnail thumbnail; internal class Author { [JsonProperty("name")] private string name; [JsonProperty("icon_url")] private string iconUrl; public Author(string name, string iconUrl) { this.name = name; this.iconUrl = iconUrl; } } internal class Thumbnail { [JsonProperty("url")] private string url; public Thumbnail(string url) { this.url = url; } } public Embed AddTitle(string title) { this.title = title; return this; } public Embed AddDescription(string description) { this.description = description; return this; } public Embed AddUrl(string url) { this.url = url; return this; } public Embed AddColor(int color) { this.color = color; return this; } public Embed AddAuthor(string name, string iconUrl) { this.author = new Author(name, iconUrl); return this; } public Embed AddTimestamp(DateTime date) { this.dateTime = date; return this; } public Embed AddThumbnail(string url) { this.thumbnail = new Thumbnail(url); return this; } } public string ToJson() => JsonConvert.SerializeObject(this); } #endregion #region [Config] private Configuration GetDefaultConfig() { return new Configuration { steamApi = "https://steamcommunity.com/dev/apikey", maxDaysBan = 365, webhookUrl = "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks", autoSolution = AutoSolution.Ignore, version = Version }; } protected override void LoadDefaultConfig() { config = GetDefaultConfig(); Puts("Generating new configuration file........"); } protected override void SaveConfig() => Config.WriteObject(config); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) LoadDefaultConfig(); if (config.version < Version) UpdateConfig(); } catch { PrintError("######### Configuration file is not valid! #########"); return; } SaveConfig(); } private void UpdateConfig() { Puts("Updating configuration values....."); config.version = Version; Puts("Configuration updated"); } #endregion #region [Localization] private string GetLang(string key) => string.Format(lang.GetMessage(key, this)); protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { {"kickReason", "You are auto kicked by GameBanCheck"}, {"banReason", "You are auto banned by GameBanCheck"} }, this); } #endregion } }