C#:
using Newtonsoft.Json.Linq;
using Rust;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Oxide.Core;
using ConVar;
using Oxide.Core.Libraries.Covalence;
using System.Text;
using System.Linq;
using Oxide.Core.Database;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries;
using System;
using Object = System.Object;
using UnityEngine;
namespace Oxide.Plugins {
[Info("IQBanSystem", "Mercury", "1.12.22")]
[Description("IQBanSystem")]
public class IQBanSystem : RustPlugin {
private Dictionary<UInt64, Double> GetTeamsBannedPlayer(UInt64 bannedPlayer) {
Dictionary<UInt64, Double> playerTeamMemory = new Dictionary<UInt64, Double>();
if (teamLocalMemory.TryGetValue(bannedPlayer, out Dictionary<UInt64, TeamLocalMemory> teamsPlayers)) {
foreach (KeyValuePair<UInt64, TeamLocalMemory> teammate in teamsPlayers) {
Double duration = (teammate.Value.lastRemoved == 0 ? CurrentTime : teammate.Value.lastRemoved) - teammate.Value.firstJoin;
playerTeamMemory.Add(teammate.Key, duration);
// PrintToChat(teammate.Key.ToString() + " " + teammate.Value.firstJoin + " " + teammate.Value.lastRemoved);
}
}
//PrintToChat(playerTeamMemory.Count.ToString());
return playerTeamMemory;
}
private List<Int32> appIds = new() {
252490, 480
}
;
private void InserOrUpdateDatabase(String idOrIP, TypeAction typeBanned, IPlayer mbPlayer = null) {
if (sqlConnection == null) return;
PlayerInfo playerInfo = PlayerInfo.Get(idOrIP, typeBanned);
if (playerInfo == null) return;
String ipAddress = typeBanned == TypeAction.BannedIP ? idOrIP : mbPlayer?.Address ?? "None";
String userID = typeBanned == TypeAction.BannedSteamID ? idOrIP : mbPlayer?.Id ?? "None";
String jsonNameHistory = JsonConvert.SerializeObject(playerInfo.nameHistory, Formatting.None);
String jsonIpHistory = JsonConvert.SerializeObject(playerInfo.ipHistory, Formatting.None);
String jsonSteamHistory = JsonConvert.SerializeObject(playerInfo.steamIdHistory, Formatting.None);
String sqlQueryUpdate = SQL_Query_UpdateUser(typeBanned);
Sql updateCommand = Oxide.Core.Database.Sql.Builder.Append(sqlQueryUpdate, userID, ipAddress, playerInfo.permanent, playerInfo.timeUnbanned, playerInfo.reason, playerInfo.serverName, playerInfo.serverAdress, playerInfo.owner, jsonNameHistory, jsonIpHistory, jsonSteamHistory);
sqlLibrary.Update(updateCommand, sqlConnection, rowsAffected => {
if (rowsAffected <= 0) {
String sqlQueryInsert = SQL_Query_InsertUser();
Sql insertCommand = Oxide.Core.Database.Sql.Builder.Append(sqlQueryInsert, userID, ipAddress, playerInfo.permanent, playerInfo.timeUnbanned, playerInfo.reason, playerInfo.serverName, playerInfo.serverAdress, playerInfo.owner, jsonNameHistory, jsonIpHistory, jsonSteamHistory);
sqlLibrary.Insert(insertCommand, sqlConnection, rowsAffecteds => {
Puts(LanguageEn ? $"New user added to the database: {userID}/{ipAddress}" : $"В базу данных добавлен новый пользователь : {userID}/{ipAddress}");
}
);
}
else Puts(LanguageEn ? $"User data updated in the database: {userID}/{ipAddress}" : $"В базе данных обновлены данные о пользователе : {userID}/{ipAddress}");
}
);
}
void ReadData() {
ipListGods = Oxide.Core.Interface.Oxide.DataFileSystem.ReadObject<List<String>>("IQSystem/IQBanSystem/ListGodIps");
teamLocalMemory = Oxide.Core.Interface.Oxide.DataFileSystem.ReadObject<Dictionary<UInt64, Dictionary<UInt64, TeamLocalMemory>>>("IQSystem/IQBanSystem/TeamsMemory");
Puts(LanguageEn ? $"Database initialized with {ipListGods.Count} VPN-verified IPs" : $"Инициализирована база данных {ipListGods.Count} проверенных на VPN IP");
}
[ConsoleCommand("ban")]
private void ConsoleBanCommand(ConsoleSystem.Arg arg) => BanCommand(arg.Player(), arg.Args);
private class TeamLocalMemory {
public Double firstJoin;
public Double lastRemoved;
}
void HandlerProxyCheckIo(Int32 code, String response, String adress) {
ProcessCheckVPN processChecked = ipListChecked[adress];
if (code != 200 || string.IsNullOrEmpty(response)) {
ResponseError("ProxyCheckIO", $"{code}");
processChecked.isCompleteProxyCheck = true;
return;
}
JObject jsonResponse;
try {
jsonResponse = JObject.Parse(response);
}
catch (JsonReaderException e) {
ResponseError("ProxyCheckIO", e.Message);
processChecked.isCompleteProxyCheck = true;
return;
}
String status = jsonResponse["status"]?.ToString();
if (!status.Equals("ok")) {
String message = jsonResponse["message"]?.ToString();
ResponseError("ProxyCheckIO", $"{status} : {message}");
processChecked.isCompleteProxyCheck = true;
return;
}
foreach (JProperty ipInfoProperty in jsonResponse.Properties()) {
if (ipInfoProperty.Name.Equals("status", StringComparison.OrdinalIgnoreCase)) continue;
JToken ipInfo = ipInfoProperty.Value;
if (ipInfo["proxy"] != null && ipInfo["type"] != null) {
String proxy = ipInfo["proxy"].ToString();
String type = ipInfo["type"].ToString();
if (proxy.Equals("yes", StringComparison.OrdinalIgnoreCase) || type.Equals("VPN", StringComparison.OrdinalIgnoreCase)) {
//PrintError("DETECT ");
processChecked.detectedVpn++;
}
}
}
processChecked.isCompleteProxyCheck = true;
}
private void SendInfo(BasePlayer player, String message) {
if (player != null) SendChat(message, player);
else Puts(message);
}
private void IsAvailabilityConnection(IPlayer player) {
if (player == null) return;
if (permission.UserHasPermission(player.Id, permissionIgnoreBan)) return;
String ipAddress = player.Address;
if (config.mysqlConnected.useMySQL) {
GetUserData(player, ipAddress);
return;
}
String userID = player.Id;
PlayerInfo playerDataSteam = PlayerInfo.Get(userID, TypeAction.BannedSteamID);
PlayerInfo playerDataIps = PlayerInfo.Get(ipAddress, TypeAction.BannedIP);
if (playerDataSteam == null && playerDataIps == null) return;
ProcessBanData(player, playerDataSteam, TypeAction.BannedSteamID, userID, ipAddress);
ProcessBanData(player, playerDataIps, TypeAction.BannedIP, userID, ipAddress);
}
void OnTeamDisbanded(RelationshipManager.PlayerTeam team) {
foreach (UInt64 teamMember in team.members) LeaveTeamPlayer(teamMember);
}
private class ProcessCheckVPN {
public Boolean isCompleteProxyCheck;
public Int32 detectedVpn;
public Boolean isCompleteVpnApi;
public Boolean isCompleteIPHub;
}
private readonly Dictionary<String, ProcessCheckVPN> ipListChecked = new Dictionary<String, ProcessCheckVPN>();
private const String VendingPrefab = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
[ChatCommand("ban")]
private void ChatBanCommand(BasePlayer ownerCommand, String cmd, String[] arg) => BanCommand(ownerCommand, arg);
private enum TypeAction {
BannedIP, BannedSteamID,
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) LoadDefaultConfig();
}
catch {
PrintWarning(LanguageEn ? $"Error reading #54327 configuration 'oxide/config/{Name}', creating a new configuration!!" : $"Ошибка чтения #54327 конфигурации 'oxide/config/{Name}', создаём новую конфигурацию!!");
LoadDefaultConfig();
}
NextTick(SaveConfig);
}
private List<String> ipListGods = new List<String>();
private void ProcessStorageContainer(BaseEntity entityPlayer, Action<StorageContainer> action) {
if (entityPlayer is not StorageContainer) return;
StorageContainer storage = entityPlayer as StorageContainer;
if (storage.inventory != null && storage.inventory.itemList.Count != 0) action(storage);
}
private String IsAvailabilityPreConnection(IPlayer player, String id, String ip) {
if (permission.UserHasPermission(id, permissionIgnoreBan)) return null;
if (config.mysqlConnected.useMySQL) return null;
PlayerInfo playerDataSteam = PlayerInfo.Get(id, TypeAction.BannedSteamID);
PlayerInfo playerDataIps = PlayerInfo.Get(ip, TypeAction.BannedIP);
if (playerDataSteam == null && playerDataIps == null) return null;
String bannedReasonID = ProcessBanData(player, playerDataSteam, TypeAction.BannedSteamID, id, ip);
String bannedReasonIP = ProcessBanData(player, playerDataIps, TypeAction.BannedIP, id, ip);
if (!String.IsNullOrEmpty(bannedReasonID)) return bannedReasonID;
if (!String.IsNullOrEmpty(bannedReasonIP)) return bannedReasonIP;
return null;
}
private String SQL_Query_GetUserData() => $"SELECT `steamid`, `ipAdress`, `permanent`, `timeUnbanned`, `reason`, `serverName`, `serverAdress`, `owner`, `nameHistory`, `ipHistory`, `steamIdHistory` FROM {config.mysqlConnected.dbTableName} WHERE `steamid` = @0 OR `ipAdress` = @1";
public String FormatTime(Double Second, String UserID = null) {
TimeSpan time = TimeSpan.FromSeconds(Second);
String Result = String.Empty;
String Days = GetLang("TITLE_FORMAT_DAYS", UserID);
String Hourse = GetLang("TITLE_FORMAT_HOURSE", UserID);
String Minutes = GetLang("TITLE_FORMAT_MINUTES", UserID);
String Seconds = GetLang("TITLE_FORMAT_SECONDS", UserID);
if (time.Seconds != 0) Result = $"{Format(time.Seconds, Seconds, Seconds, Seconds)}";
if (time.Minutes != 0) Result = $"{Format(time.Minutes, Minutes, Minutes, Minutes)}";
if (time.Hours != 0) Result = $"{Format(time.Hours, Hourse, Hourse, Hourse)}";
if (time.Days != 0) Result = $"{Format(time.Days, Days, Days, Days)}";
return Result;
}
private static StringBuilder sb = new StringBuilder();
void OnPlayerConnected(BasePlayer player) {
if (config.generalSetting.discordSetting.alertBanned.teamsAlert.saveInfoTeams || config.generalSetting.additionalSetting.useAlertBlockTeam || config.bannedSetting.teamBannedSetting.useBlockTeam) PullOutTeamsInfo(player);
Configuration.BannedSetting.DestroyedAfterBannedSetting configDestroyed = config.bannedSetting.destroyedAfterBannedSetting;
Boolean isMarkerUse = configDestroyed.mapMarkerDestroyedObjectSetting.useMapMarkerDestroyedObjectPlayer && config.generalSetting.additionalSetting.useAlertAllPlayers && configDestroyed.useAlertGridHomes && configDestroyed.useDestroyObjects;
if (isMarkerUse) {
foreach (MarkerRepository markerRepository in mapMarkers) {
if(!markerRepository.vending.IsDestroyed) markerRepository.vending.SendNetworkUpdate();
if(!markerRepository.marker.IsDestroyed) markerRepository.marker.SendNetworkUpdate();
}
}
}
private const String permissionIgnoreBan = "iqbansystem.ignoreban";
private void SetTirifyUnBan(String steamIdString, String reason = "") {
if (!TirifyGamePluginRust) return;
if (!config.bannedSetting.tirifyBannedReplace) return;
TirifyGamePluginRust.Call("SetTirifyUnBan", steamIdString, reason);
Puts(LanguageEn ? $"The player's hardware has been unblocked {steamIdString} (Tirify)" : $"Выдана разблокировка по железу игроку {steamIdString} (Tirify)");
}
public class Authors {
public String name {
get;
set;
}
public String url {
get;
set;
}
public String icon_url {
get;
set;
}
public String proxy_icon_url {
get;
set;
}
public Authors(String name, String url, String icon_url, String proxy_icon_url) {
this.name = name;
this.url = url;
this.icon_url = icon_url;
this.proxy_icon_url = proxy_icon_url;
}
}
public class Fields {
public String name {
get;
set;
}
public String value {
get;
set;
}
public bool inline {
get;
set;
}
public Fields(String name, String value, bool inline) {
this.name = name;
this.value = value;
this.inline = inline;
}
}
private void MarkerUpdate() {
for (Int32 i = 0; i < mapMarkers.Count; i++) {
MarkerRepository marker = mapMarkers[i];
TimeSpan timeSpan = DateTime.Now - marker.timeSetMarker;
Double totalSeconds = timeSpan.TotalSeconds;
if (!TryParseTimeSpan(config.bannedSetting.destroyedAfterBannedSetting.mapMarkerDestroyedObjectSetting.markerLifeTime, out Double markerUpdate)) {
PrintError(LanguageEn ? "Incorrect marker update time format. The marker has been deleted." : "Некорректный формат времени обновления маркера. Маркер был удален.");
if(!marker.vending.IsDestroyed) marker.vending.Kill();
if(!marker.marker.IsDestroyed) marker.marker.Kill();
mapMarkers.Remove(marker);
}
if(totalSeconds < markerUpdate) continue;
if(!marker.vending.IsDestroyed) marker.vending.Kill();
if(!marker.marker.IsDestroyed) marker.marker.Kill();
mapMarkers.Remove(marker);
}
}
private String SQL_Query_DeletedDatabase(TypeAction typeUnBanned) => $"DELETE FROM `{config.mysqlConnected.dbTableName}` WHERE " + $"{(typeUnBanned == TypeAction.BannedSteamID ? "`steamid` = @0" : "`ipAdress` = @0")}";
private void ResponseError(String nameService, String message) => PrintWarning(LanguageEn ? $"[{nameService}] The service is temporarily unavailable or your key is incorrect, it returned a negative code, the request was not sent.\nInformation (Code or messages from the service): {message}" : $"[{nameService}] Сервис временно недоступен или ваш ключ некорректен, он вернул отрицательный код, запрос не был отправлен.\nИнформация (Код или сообщения от сервиса) : : {message}");
private Boolean TryParseTimeSpan(String source, out Double timeSpan) {
Int32 seconds = 0, minutes = 0, hours = 0, days = 0;
MatchCollection matches = Regex.Matches(source, @"(\d+)([smhd])", RegexOptions.IgnoreCase);
foreach (Match match in matches) {
Int32 value = int.Parse(match.Groups[1].Value);
Char unit = match.Groups[2].Value.ToLower()[0];
switch (unit) {
case 's' : seconds += value;
break;
case 'm' : minutes += value;
break;
case 'h' : hours += value;
break;
case 'd' : days += value;
break;
}
}
source = Regex.Replace(source, @"(\d+)[smhd]", String.Empty, RegexOptions.IgnoreCase);
if (!String.IsNullOrEmpty(source) || (seconds == 0 && minutes == 0 && hours == 0 && days == 0)) {
timeSpan = 0;
return false;
}
timeSpan = new TimeSpan(days, hours, minutes, seconds).TotalSeconds;
return true;
}
private void OnServerShutdown() => Unload();
object OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) {
LeaveTeamPlayer(player.userID);
return null;
}
private class Configuration {
[JsonProperty(LanguageEn ? "Server data configuration" : "Настройка данных сервера")]
public GeneralSetting generalSetting = new GeneralSetting();
[JsonProperty(LanguageEn ? "Setting up MySQL connection" : "Настройка подключения MySQL")]
public MySQLSetting mysqlConnected = new MySQLSetting();
[JsonProperty(LanguageEn ? "Setting up interaction with Steam" : "Настройка взаимодействия со Steam")]
public SteamSetting steamSetting = new SteamSetting();
[JsonProperty(LanguageEn ? "Setting up VPN protection" : "Настройка защиты от VPN")]
public VPNSetting vpnSetting = new VPNSetting();
[JsonProperty(LanguageEn ? "Setting up the blocking system" : "Настройка системы блокировки")]
public BannedSetting bannedSetting = new BannedSetting();
internal class GeneralSetting {
[JsonProperty(LanguageEn ? "Server name" : "Название сервера")]
public String serverName;
[JsonProperty(LanguageEn ? "IP:PORT server" : "IP:Port сервера")]
public String serverAdress;
[JsonProperty(LanguageEn ? "Additional configuration" : "Дополнительная настройка")]
public AdditionalSetting additionalSetting = new AdditionalSetting();
[JsonProperty(LanguageEn ? "Setting IQChat" : "Настройка IQChat")]
public IQChatSetting iqchatSetting = new IQChatSetting();
[JsonProperty(LanguageEn ? "Setting Discord" : "Настройка Discord")]
public DiscordSetting discordSetting = new DiscordSetting();
internal class AdditionalSetting {
[JsonProperty(LanguageEn ? "Sound effect for all players to notify when a player is blocked ('Notify all players' should be enabled) (leave it blank - if you don't need it)" : "Звуковой эффект для всех игроков для уведомления о блокировке игрока (должно быть включено 'Уведомлять всех игроков') (оставьте пустым - если вам не нужно это)")]
public String effectAlertAllPlayers;
[JsonProperty(LanguageEn ? "Notify all players when a player is blocked" : "Уведомлять всех игроков о блокировке игрока")]
public Boolean useAlertAllPlayers;
[JsonProperty(LanguageEn ? "Notify all players about player's kick" : "Уведомлять всех игроков о кике игрока")]
public Boolean useAlertKickPlayer;
[JsonProperty(LanguageEn ? "Add information about blocked teammates to the blocking notification" : "Добавлять в уведомление о блокировке - информацию о заблокированных тиммейтах")]
public Boolean useAlertBlockTeam;
}
internal class IQChatSetting {
[JsonProperty(LanguageEn ? "IQChat: Chat Prefix" : "IQChat : Префикс в чате")]
public String customPrefix;
[JsonProperty(LanguageEn ? "IQChat: Chat Avatar (Use Steam64ID)" : "IQChat : Аватарка в чате (Используйте Steam64ID)")]
public String customAvatar;
}
internal class DiscordSetting {
[JsonProperty(LanguageEn ? "Discord notification setting for player ban" : "Настройка уведомления в Discord о блокировке игрока")]
public AlertBanned alertBanned = new AlertBanned();
internal class AlertBanned {
[JsonProperty(LanguageEn ? "Webhooks: For banned notifications" : "Webhooks : Для уведомлений о блокировке")]
public String webHookBanned;
[JsonProperty(LanguageEn ? "Include home information of the banned player in the notification" : "Добавлять в уведомление дома где прописан заблокированный игрок")]
public Boolean cupboardAuthInfo;
[JsonProperty(LanguageEn ? "Additional notification setting for players who played with the banned player" : "Настройка дополнительного уведомления о тех с кем играл заблокированный")]
public TeamsAlert teamsAlert = new TeamsAlert();
internal class TeamsAlert {
[JsonProperty(LanguageEn ? "Include players who played with the banned player in the notification" : "Добавлять в уведомление игроков с которыми играл забаненный")]
public Boolean saveInfoTeams;
[JsonProperty(LanguageEn ? "Time spent together with the banned player to be included in the notification (Format: 1s/1m/1h/1d)" : "Сколько времени должны провести вместе забаненный с игроком для его отображения в уведомлении (В формате 1s/1m/1h/1d)")]
public String formatTimePlaying;
}
}
[JsonProperty(LanguageEn ? "Webhooks : For unbanned notifications" : "Webhooks : Для уведомлений о разблокировке")]
public String webHookUnBanned;
[JsonProperty(LanguageEn ? "Webhooks : For kicked notifications" : "Webhooks : Для уведомлений о киках")]
public String webHookKicked;
[JsonProperty(LanguageEn ? "Webhooks : For notifications about a blocked player's login attempt" : "Webhooks : Для уведомлений о попытке входа заблокированного игрока")]
public String webHookConnectedBanned;
[JsonProperty(LanguageEn ? "Link to the image in Discord" : "Ссылка на изображение в Discord")]
public String imageLink;
}
}
internal class SteamSetting {
[JsonProperty(LanguageEn ? "Please provide the Steam API Key for the operation of these functions (Obtain it here - https://steamcommunity.com/dev/apikey)" : "Укажите SteamApiKey для работы данных функций (Взять тут - https://steamcommunity.com/dev/apikey)")]
public String steamApiKey;
[JsonProperty(LanguageEn ? "The minimum amount of time since Steam account registration for logging into the server. Format: 1m/1h/1d (leave empty if you don't need this function)" : "Минимальное количество времени с регистрации аккаунта Steam для входа на сервер. Формат 1m/1h/1d (оставьте пустым - если вам не нужна эта функция)")]
public String minSteamRegisterTime;
[JsonProperty(LanguageEn ? "The minimum amount of playtime in RUST required for logging into the server. Format: 1m/1h/1d (leave empty if you don't need this function)" : "Минимальное количество отыгранного времени в RUST для входа на сервер. Формат 1m/1h/1d (оставьте пустым - если вам не нужна эта функция)")]
public String minPlayGameTime;
}
internal class MySQLSetting {
[JsonProperty(LanguageEn ? "Use MySQL database (true - yes/false - no)" : "Использовать базу-данных MySQL (true - да/false - нет)")]
public Boolean useMySQL;
[JsonProperty(LanguageEn ? "Host (IP-Address)" : "Хост (IP-Address)")]
public String dbIP;
[JsonProperty(LanguageEn ? "Port (default 3306)" : "Порт (стандартно 3306)")]
public String dbPort;
[JsonProperty(LanguageEn ? "Database name" : "Имя базы данных")]
public String dbName;
[JsonProperty(LanguageEn ? "Username" : "Имя пользователя")]
public String dbUser;
[JsonProperty(LanguageEn ? "Password" : "Пароль")]
public String dbPassword;
[JsonProperty(LanguageEn ? "Table name" : "Название таблицы")]
public String dbTableName;
}
internal class VPNSetting {
[JsonProperty(LanguageEn ? "Saving 'Good IPs' to avoid reusing requests on the player" : "Сохранять `Хорошие IP`, чтобы не тратить запросы повторно на игрока")]
public Boolean useSaveGodIps;
[JsonProperty(LanguageEn ? "The number of detects for kicking a player for VPN (if you use more than 1 service for VPN checking)" : "Количество детектов для кика игрока за VPN (Если вы испольузете более 1 сервиса на проверку VPN)")]
public Int32 countDetectedToKickInVPN;
[JsonProperty(LanguageEn ? "Token https://iphub.info/" : "Токен от https://iphub.info/")]
public String iPHubToken;
[JsonProperty(LanguageEn ? "Token https://proxycheck.io/" : "Токен от https://proxycheck.io/")]
public String proxyCheckIo;
[JsonProperty(LanguageEn ? "Token https://vpnapi.io/" : "Токен от https://vpnapi.io/")]
public String vpnApiIoToken;
[JsonProperty(LanguageEn ? "IP Whitelist for ignoring VPN" : "Белый список IP для игнорирования VPN")]
public List<String> vpnWhiteList = new List<String>();
}
internal class BannedSetting {
[JsonProperty(LanguageEn ? "TirifyGamePluginRust : Block the player by hardware using Tirify if the lock is issued forever (true - yes/false - no)" : "TirifyGamePluginRust : Блокировать игрока по железу с помощью Tirify если блокировка выдана навсегда (true - да/false - нет)")]
public Boolean tirifyBannedReplace;
[JsonProperty(LanguageEn ? "Kill player after being banned on the server" : "Убивать игрока после блокировки на сервере")]
public Boolean useKilledPlayer;
[JsonProperty(LanguageEn ? "Setting for banning teammates for playing with the offender" : "Настройка блокировки тиммейтов за игру с нарушителем")]
public TeamBannedSetting teamBannedSetting = new TeamBannedSetting();
[JsonProperty(LanguageEn ? "Setting for destroying player's objects after being banned" : "Настройка уничтожения объектов игрока после блокировки")]
public DestroyedAfterBannedSetting destroyedAfterBannedSetting = new DestroyedAfterBannedSetting();
internal class TeamBannedSetting {
[JsonProperty(LanguageEn ? "Specify the time played with the offender for issuing a ban. Format 1s/1m/1h/1d" : "Укажите время игры с нарушителем для выдачи блокировки. Формат 1s/1m/1h/1d")]
public String timeDeteckGamingTeamMemory;
[JsonProperty(LanguageEn ? "Ban teammates of the banned player" : "Блокировать тиммейтов заблокированного игрока")]
public Boolean useBlockTeam;
[JsonProperty(LanguageEn ? "Specify the ban time for teammates for playing with the offender. Format 1s/1m/1h/1d (Leave empty for issuing a permanent ban)" : "Укажите время блокировки тиммейтов за игру с нарушителем. Формат 1s/1m/1h/1d (Оставьте пустым - для выдачи блокировки навсегда)")]
public String timeBlockTeam;
}
internal class DestroyedAfterBannedSetting {
[JsonProperty(LanguageEn ? "Use the function to delete player's objects after being banned" : "Использовать функцию удаления объектов игрока после блокировки")]
public Boolean useDestroyObjects;
[JsonProperty(LanguageEn ? "Type of object destruction: 0 - All objects, 1 - All objects and dropping items from containers on the ground, 2 - Only locks, 3 - Only boxes, 4 - Only boxes with dropping items from them" : "Тип уничтожения объектов : 0 - Все объекты, 1 - Все объекты и выбрасывание предметов из контейнеров на пол, 2 - Только замки, 3 - Только ящики, 4 - Только ящики с выбрасыванием предметов из них")]
public TypeDestroy typeDestroyed;
[JsonProperty(LanguageEn ? "Notify in chat about squares of destroyed player's objects" : "Уведомлять в чате о квадратах разрушенных объектах игрока")]
public Boolean useAlertGridHomes;
[JsonProperty(LanguageEn ? "Setting for displaying markers on the G-Map" : "Настройка отображения маркеров на G-Map")]
public MapMarkerDestroyedObject mapMarkerDestroyedObjectSetting = new();
internal class MapMarkerDestroyedObject {
[JsonProperty(LanguageEn ? "Display points with destroyed objects on the map" : "Отображать на карте точки с разрушенными объектами")]
public Boolean useMapMarkerDestroyedObjectPlayer;
[JsonProperty(LanguageEn ? "Main marker color" : "Основной цвет маркера")]
public String mainColorMarker;
[JsonProperty(LanguageEn ? "Outline marker color" : "Цвет обводки маркера")]
public String additionalColorMarker;
[JsonProperty(LanguageEn ? "Marker radius on the map" : "Радиус маркера на карте")]
public Single radiusMarker;
[JsonProperty(LanguageEn ? "How long the marker will be displayed. Format 1s/1m/1h/1d" : "Сколько будет отображаться маркер. Формат 1s/1m/1h/1d")]
public String markerLifeTime;
}
}
}
public static Configuration GetNewConfiguration() {
return new Configuration {
generalSetting = new GeneralSetting {
serverName = "MY SERVER", serverAdress = "127.0.0.1", additionalSetting = new GeneralSetting.AdditionalSetting {
effectAlertAllPlayers = "assets/bundled/prefabs/fx/item_unlock.prefab", useAlertAllPlayers = true, useAlertKickPlayer = false, useAlertBlockTeam = false,
}
, iqchatSetting = new GeneralSetting.IQChatSetting {
customPrefix = "<color=#1F6BA0>[IQBanSystem]</color>", customAvatar = "0"
}
, discordSetting = new GeneralSetting.DiscordSetting {
alertBanned = new GeneralSetting.DiscordSetting.AlertBanned {
webHookBanned = String.Empty, cupboardAuthInfo = false, teamsAlert = new GeneralSetting.DiscordSetting.AlertBanned.TeamsAlert {
saveInfoTeams = false, formatTimePlaying = "12h",
}
}
, webHookUnBanned = String.Empty, webHookKicked = String.Empty, webHookConnectedBanned = String.Empty, imageLink = "https://i.postimg.cc/MGLfp4dR/IQBan-System.png",
}
,
}
, mysqlConnected = new MySQLSetting {
useMySQL = false, dbIP = "", dbPort = "3306", dbName = "", dbUser = "", dbPassword = "", dbTableName = "IQBanSystem_Db"
}
, steamSetting = new SteamSetting {
steamApiKey = "", minSteamRegisterTime = "14d", minPlayGameTime = "3d",
}
, vpnSetting = new VPNSetting {
useSaveGodIps = true, countDetectedToKickInVPN = 1, iPHubToken = "", proxyCheckIo = "", vpnApiIoToken = "", vpnWhiteList = new List<String> {
"127.0.0.1"
}
}
, bannedSetting = new BannedSetting {
tirifyBannedReplace = false, useKilledPlayer = true, teamBannedSetting = new BannedSetting.TeamBannedSetting {
useBlockTeam = false, timeDeteckGamingTeamMemory = "3h", timeBlockTeam = "7d"
}
, destroyedAfterBannedSetting = new BannedSetting.DestroyedAfterBannedSetting {
useDestroyObjects = false, typeDestroyed = TypeDestroy.AllObjects, useAlertGridHomes = false, mapMarkerDestroyedObjectSetting = new BannedSetting.DestroyedAfterBannedSetting.MapMarkerDestroyedObject {
useMapMarkerDestroyedObjectPlayer = false, mainColorMarker = "#CD412B", additionalColorMarker = "#1E2020", radiusMarker = 0.25f, markerLifeTime = "10m",
}
}
,
}
}
;
}
}
private static Configuration config = new Configuration();
private enum TypeAlertDiscord {
Banned, Unbanned, Kicked, ConnectionBanned
}
private String SQL_Query_InsertUser() {
return $"INSERT INTO {config.mysqlConnected.dbTableName} " + "(`steamid`, `ipAdress`, `permanent`, `timeUnbanned`, `reason`, `serverName`, `serverAdress`, `owner`, `nameHistory`, `ipHistory`, `steamIdHistory`) " + "VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10)";
}
private const String patternIP = @"^\b(?:\d{1,3}\.){3}\d{1,3}\b$";
private void BanIp(String IpAdress, IPlayer iPlayer = null, String reason = default, Double banTime = 0, BasePlayer ownerCommand = null, Boolean skipTeam = false) {
String displayName = iPlayer?.Name ?? String.Empty;
String idPlayer = iPlayer?.Id ?? String.Empty;
BanUserOrIp(idPlayer, displayName, IpAdress, reason, banTime, TypeAction.BannedIP, ownerCommand, skipTeam);
}
private List<String> DestroyObjectBanned(IPlayer bannedPlayer, String bannedDisplayName) {
Vector3 lastObjectPos = Vector3.zero;
List<String> homeCords = new ();
List<BaseEntity> entitesPlayer = Facepunch.Pool.Get<List<BaseEntity>>();
Configuration.BannedSetting.DestroyedAfterBannedSetting configDestroyed = config.bannedSetting.destroyedAfterBannedSetting;
Func<BaseNetworkable, Boolean> baseFilter = e => e != null && e is BaseEntity && (e as BaseEntity).OwnerID == UInt64.Parse(bannedPlayer.Id);
Func<BaseNetworkable, Boolean> filter = baseFilter;
baseFilter = configDestroyed.typeDestroyed switch {
TypeDestroy.OnlyLocks => e => filter(e) && e is BaseLock, TypeDestroy.OnlyStorageAndDropInStorage or TypeDestroy.OnlyStorage => e => filter(e) && e is StorageContainer, _ => baseFilter
}
;
Boolean isBuildingAccess = false;
foreach (BaseEntity entityPlayer in BaseNetworkable.serverEntities.entityList.Get().Values.Where(baseFilter).OrderByDescending(x => x is StorageContainer)) {
if (entityPlayer.IsDestroyed) continue;
if (!isBuildingAccess) {
BuildingPrivlidge buildingPrivilage = entityPlayer.GetBuildingPrivilege();
if (buildingPrivilage == null) continue;
isBuildingAccess = true;
}
switch (configDestroyed.typeDestroyed) {
case TypeDestroy.AllObjectsAndDropInStorage or TypeDestroy.OnlyStorageAndDropInStorage : ProcessStorageContainer(entityPlayer, storage => {
storage.DropItems();
}
);
break;
case TypeDestroy.AllObjects or TypeDestroy.OnlyStorage : ProcessStorageContainer(entityPlayer, storage => {
storage.inventory.Clear();
}
);
break;
}
if (config.generalSetting.additionalSetting.useAlertAllPlayers && configDestroyed.useAlertGridHomes) {
if (lastObjectPos != Vector3.zero) if (Vector3.Distance(lastObjectPos, entityPlayer.transform.position) >= 100) {
String cordHomeOnMap = MapHelper.PositionToString(entityPlayer.transform.position);
if (!homeCords.Contains(cordHomeOnMap)) {
homeCords.Add(cordHomeOnMap);
CreateMapMarker(entityPlayer.transform.position, bannedDisplayName);
}
lastObjectPos = entityPlayer.transform.position;
continue;
}
lastObjectPos = entityPlayer.transform.position;
if (homeCords.Count == 0) {
homeCords.Add(MapHelper.PositionToString(entityPlayer.transform.position));
CreateMapMarker(entityPlayer.transform.position, bannedDisplayName);
}
}
entityPlayer.Kill();
}
Facepunch.Pool.FreeUnmanaged(ref entitesPlayer);
return homeCords;
}
private void Request(String url, String payload, Action<Int32> callback = null) {
Dictionary<String, String> header = new Dictionary<String, String>();
header.Add("Content-Type", "application/json");
webrequest.Enqueue(url, payload, (code, response) => {
if (code != 200 && code != 204) {
if (response != null) {
try {
JObject json = JObject.Parse(response);
if (code == 429) {
Single seconds = Single.Parse(Math.Ceiling((Double)(Int32)json["retry_after"] / 1000).ToString());
}
else {
PrintWarning($" Discord rejected that payload! Responded with \"{json["message"].ToString()}\" Code: {code}");
}
}
catch {
PrintWarning($"Failed to get a valid response from discord! Error: \"{response}\" Code: {code}");
}
}
else {
PrintWarning($"Discord didn't respond (down?) Code: {code}");
}
}
try {
callback?.Invoke(code);
}
catch (Exception ex) {
}
}
, this, RequestMethod.POST, header, timeout : 10f);
}
private String GetKickReason(PlayerInfo playerData, String userID) {
if (!string.IsNullOrWhiteSpace(playerData.reason)) {
return playerData.permanent ? GetLang("BANNED_ALERT_PERMANENT", userID, playerData.reason) : GetLang("BANNED_ALERT_TIME", userID, playerData.reason, FormatTime(playerData.GetUnbanTime, userID));
}
else {
return playerData.permanent ? GetLang("BANNED_DEFAULT_REASON_PERMANENT", userID) : GetLang("BANNED_DEFAULT_REASON", userID, FormatTime(playerData.GetUnbanTime, userID));
}
}
private String ProcessBanData(IPlayer player, PlayerInfo playerData, TypeAction typeAction, String userID, String ipAddress) {
if (playerData == null) return String.Empty;
playerData.UpdateHistory(TypeHistoryInfo.Name, player?.Name ?? "Unknown");
playerData.UpdateHistory(TypeHistoryInfo.IP, ipAddress);
playerData.UpdateHistory(TypeHistoryInfo.SteamID, userID);
if (playerData.IsBanned()) {
String kickReason = GetKickReason(playerData, userID);
player?.Kick(kickReason);
if (!String.IsNullOrWhiteSpace(config.generalSetting.discordSetting.webHookConnectedBanned)) {
List<Fields> fieldsKick = new List<Fields> {
new Fields(LanguageEn ? "Player" : "Игрок", $"{player?.Name ?? "Unknown"}({userID})", false), new Fields("IP", ipAddress, false), new Fields(LanguageEn ? "Banned reason" : "Причина блокировки", playerData.reason, false), new Fields(LanguageEn ? "Unban time" : "Время разблокировки", playerData.permanent ? (LanguageEn ? "never" : "никогда") : FormatTime(playerData.GetUnbanTime), false),
}
;
SendDiscord(fieldsKick, TypeAlertDiscord.ConnectionBanned, config.generalSetting.discordSetting.webHookConnectedBanned);
}
return kickReason;
}
else {
PlayerInfo.Remove(userID, typeAction);
return String.Empty;
}
}
object OnTeamAcceptInvite(RelationshipManager.PlayerTeam team, BasePlayer player) {
TeamAdded(team);
return null;
}
private void CheckSteamController(IPlayer player) {
if (player == null) return;
if (String.IsNullOrWhiteSpace(config.steamSetting.steamApiKey)) return;
webrequest.Enqueue($"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key={config.steamSetting.steamApiKey}&steamids={player.Id}", String.Empty, (code, response) => HandlerSteam(player, code, response), this, timeout : 10f);
}
private Dictionary<UInt64, Dictionary<UInt64, TeamLocalMemory>> teamLocalMemory = new();
/// <summary>
/// Returned unbanTime. -1 = permanent
/// </summary>
/// <param name="idOrIP"></param>
/// <returns></returns>
private Double GetUnbanTime(String idOrIP) {
TypeAction typeAction = TypeAction.BannedSteamID;
if (IsIpAdress(idOrIP)) typeAction = TypeAction.BannedIP;
PlayerInfo pInfo = PlayerInfo.Get(idOrIP, typeAction);
if (pInfo == null) return 0;
if (pInfo.permanent) return -1;
return pInfo.GetUnbanTime;
}
private Boolean IsOpenMySQL() => config.mysqlConnected.useMySQL && sqlConnection != null;
private void CreateMapMarker(Vector3 position, String namePlayer) {
Configuration.BannedSetting.DestroyedAfterBannedSetting configDestroyed = config.bannedSetting.destroyedAfterBannedSetting;
if (!configDestroyed.mapMarkerDestroyedObjectSetting.useMapMarkerDestroyedObjectPlayer) return;
VendingMachineMapMarker vending = GameManager.server.CreateEntity(VendingPrefab, position, Quaternion.identity, true) as VendingMachineMapMarker;
vending.markerShopName = namePlayer;
vending.enableSaving = false;
vending.EnableGlobalBroadcast(true);
vending.Spawn();
MapMarkerGenericRadius genericMarker = GameManager.server.CreateEntity(GenericPrefab, new Vector3(), Quaternion.identity, true) as MapMarkerGenericRadius;
ColorUtility.TryParseHtmlString(configDestroyed.mapMarkerDestroyedObjectSetting.mainColorMarker, out Color color1);
ColorUtility.TryParseHtmlString(configDestroyed.mapMarkerDestroyedObjectSetting.additionalColorMarker, out Color color2);
genericMarker.color1 = color1;
genericMarker.color2 = color2;
genericMarker.radius = configDestroyed.mapMarkerDestroyedObjectSetting.radiusMarker;
genericMarker.alpha = 1f;
genericMarker.SetParent(vending);
genericMarker.Spawn();
genericMarker.SendUpdate();
genericMarker.EnableGlobalBroadcast(true);
MarkerRepository markerInfo = new MarkerRepository() {
vending = vending, marker = genericMarker, timeSetMarker = DateTime.Now
}
;
mapMarkers.Add(markerInfo);
}
private void LoadDataFiles(TypeAction type) {
String[] players = PlayerInfo.GetFiles(type);
foreach (String player in players) PlayerInfo.Load(player, type);
String Message = type == TypeAction.BannedSteamID ? (LanguageEn ? $"Database initialized with {players.Length} banned players" : $"Инициализирована база данных {players.Length} забаненных игроков") : (LanguageEn ? $"Database initialized with {players.Length} banned IPs" : $"Инициализирована база данных {players.Length} забаненных IP-адресов");
Puts(Message);
}
private void RustApp_OnPaidAnnounceBan(String steamID, List<String> targets, String reason) {
if (!UInt64.TryParse(steamID, out UInt64 userID)) return;
IPlayer iPlayer = GetIPlayer(steamID);
BanUser(userID, iPlayer, reason : reason);
}
private Boolean IsIpAdress(String mbIP) => Regex.IsMatch(mbIP, patternIP);
private void Init() {
_ = this;
if (config.mysqlConnected.useMySQL) {
SQL_OpenConnection();
Unsubscribe(nameof(CanUserLogin));
}
else {
LoadDataFiles(TypeAction.BannedIP);
LoadDataFiles(TypeAction.BannedSteamID);
foreach (BasePlayer player in BasePlayer.activePlayerList) OnPlayerConnected(player);
}
if(config.vpnSetting.useSaveGodIps) ReadData();
if (!config.generalSetting.discordSetting.alertBanned.teamsAlert.saveInfoTeams && !config.generalSetting.additionalSetting.useAlertBlockTeam && !config.bannedSetting.teamBannedSetting.useBlockTeam) {
Unsubscribe(nameof(OnTeamCreated));
Unsubscribe(nameof(OnTeamDisbanded));
Unsubscribe(nameof(OnTeamKick));
Unsubscribe(nameof(OnTeamLeave));
Unsubscribe(nameof(OnTeamAcceptInvite));
}
}
void WriteData() {
Oxide.Core.Interface.Oxide.DataFileSystem.WriteObject("IQSystem/IQBanSystem/ListGodIps", ipListGods);
Oxide.Core.Interface.Oxide.DataFileSystem.WriteObject("IQSystem/IQBanSystem/TeamsMemory", teamLocalMemory);
}
private const Boolean LanguageEn = false;
private void BanCommand(BasePlayer ownerCommand, String[] arg) {
if (ownerCommand != null) if (!permission.UserHasPermission(ownerCommand.UserIDString, permissionsBan)) return;
if (arg == null) {
// ... [Code preview truncated due to length. Full source in attached file] ...