C#:
/*
########### 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<string> defaultBanReason = new List<string>
{
"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<List<GameBanDatabase>>(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<PlayerSummaries>(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<string, string>() { { "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<Embed> embeds = new List<Embed>();
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<Configuration>();
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<string, string>
{
{"kickReason", "You are auto kicked by GameBanCheck"},
{"banReason", "You are auto banned by GameBanCheck"}
}, this);
}
#endregion
}
}