using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Libraries;
using UnityEngine;
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedMember.Global
namespace Oxide.Plugins
{
[Info("Ban System", "Bombardir, Moscow.OVH", "0.4.29")]
[Description("Ban System")]
public class BanSystem : RustPlugin
{
private static readonly string SyncPermission = "bansystem.sync";
private static readonly string KickPermission = "bansystem.kick";
private static readonly string BanPermission = "bansystem.ban";
private static readonly string StrikePermission = "bansystem.strike";
private static readonly string UnbanPermission = "bansystem.unban";
private readonly Core.Libraries.Plugins _plugins = GetLibrary<Core.Libraries.Plugins>();
private readonly Permission _permission = GetLibrary<Permission>();
private readonly Lang _lang = GetLibrary<Lang>();
private readonly Covalence _covalence = GetLibrary<Covalence>();
private ExConfig _exConfig;
private WebApi _webApi;
private GameObject _mainObject;
private BansData _bansData;
private static BanSystem _plugin;
private bool _initialized = false;
protected override void LoadDefaultConfig() { } // Совместимость с .cs плагинами, создание пустого файла конфигурации.
public override void HandleRemovedFromManager(PluginManager manager)
{
if (_mainObject != null)
{
UnityEngine.Object.Destroy(_mainObject);
}
base.HandleRemovedFromManager(manager);
}
private static readonly Regex RegexStringTime = new Regex(@"(\d+)([dhms])", RegexOptions.Compiled);
private static bool ConvertToSeconds(string time, out uint seconds)
{
seconds = 0;
if (time == "0" || string.IsNullOrEmpty(time))
{
return true;
}
MatchCollection matches = RegexStringTime.Matches(time);
if (matches.Count == 0)
{
return false;
}
for (var i = 0; i < matches.Count; i++)
{
Match match = matches[i];
switch (match.Groups[2].Value)
{
case "d":
{
seconds += uint.Parse(match.Groups[1].Value) * 24 * 60 * 60;
break;
}
case "h":
{
seconds += uint.Parse(match.Groups[1].Value) * 60 * 60;
break;
}
case "m":
{
seconds += uint.Parse(match.Groups[1].Value) * 60;
break;
}
case "s":
{
seconds += uint.Parse(match.Groups[1].Value);
break;
}
}
}
return true;
}
private static string GetNiceTime(ulong seconds, string day, string hour, string min, string sec)
{
TimeSpan span = TimeSpan.FromSeconds(seconds).Duration();
string formatted = string.Empty;
if (span.Days > 0)
{
formatted += span.Days + day;
}
if (span.Hours > 0)
{
formatted += span.Hours + hour;
}
if (span.Minutes > 0)
{
formatted += span.Minutes + min;
}
if (span.Seconds > 0)
{
formatted += span.Seconds + sec;
}
if (string.IsNullOrEmpty(formatted))
{
formatted = "0 сек";
}
return formatted;
}
private void LogAdmin(string action, ulong steamId, string playerName, string playerSteam)
{
string initiatorStr;
if (steamId == 0)
{
initiatorStr = "через консоль";
}
else
{
string steamStr = steamId.ToString();
string adminName = _covalence.Players.FindPlayerById(steamId.ToString())?.Name;
initiatorStr = "администратором " + (string.IsNullOrEmpty(adminName) ? steamStr : $"{adminName} ({steamStr})");
}
string log = $"[{DateTime.Now.ToString(CultureInfo.InvariantCulture)}] Игрок {playerName} ({playerSteam}) {action} {initiatorStr}\n";
Log.Debug(log);
Log.File(log, "actions");
}
private void SyncStandartBans(string fileName, bool doMove)
{
string filePath = ConVar.Server.GetServerFolder("cfg") + "/" + fileName;
if (!File.Exists(filePath))
{
if (!doMove)
{
Log.Debug("Нет банов для синхронизации");
}
return;
}
string fileText = File.ReadAllText(filePath);
if (string.IsNullOrEmpty(fileText))
{
if (!doMove)
{
Log.Debug("Нет банов для синхронизации");
}
return;
}
var banRegex = new Regex("banid\\s+(\\d+)\\s+\"[^\"]*\"\\s+\"([^\"]*)\"", RegexOptions.Compiled);
List<string> bans = fileText.Split('\n').ToList();
for (int i = bans.Count - 1; i >= 0; i--)
{
Match match = banRegex.Match(bans[i]);
if (!match.Success)
{
continue;
}
string reason = match.Groups[2].Value;
if (reason == "VAC" || reason == "EAC" || reason == "GAMEBAN")
{
if (doMove)
{
bans.RemoveAt(i);
}
continue;
}
ulong steam = ulong.Parse(match.Groups[1].Value);
_bansData.AddBan(steam, reason, 0, 0, "0.0.0.0", 0, 0);
}
if (doMove)
{
try
{
string backupFilePath = filePath + ".backup";
if (!File.Exists(backupFilePath))
{
File.WriteAllLines(backupFilePath, bans.ToArray());
}
else
{
List<string> backupLines = File.ReadAllLines(backupFilePath).ToList();
foreach (string newLine in bans)
{
if (backupLines.Contains(newLine))
{
continue;
}
backupLines.Add(newLine);
}
File.WriteAllLines(backupFilePath, backupLines.ToArray());
}
File.Delete(filePath);
}
catch
{
// ignored
}
}
Log.Debug($"Баны из файла {fileName} синхронизированы локально, идет синхронизация с сайтом");
SyncBanList(_bansData.noSyncBans, (success) =>
{
Log.Debug(success ? "Баны успешно синхронизированы" :
"Во время синхронизации банов произошла ошибка, синхронизация будет повторена позже");
});
}
public static string FormatString(int str, string first, string second, string third)
{
string formatted = str + " ";
if (str > 100)
{
str = str % 100;
}
if (str > 9 && str < 21)
{
formatted += third;
}
else
{
switch (str % 10)
{
case 1:
formatted += first;
break;
case 2:
case 3:
case 4:
formatted += second;
break;
default:
formatted += third;
break;
}
}
return formatted;
}
[HookMethod("Init")]
private void Init()
{
_plugin = this;
try { _bansData = Interface.Oxide.DataFileSystem.ReadObject<BansData>("BanSystem"); }
catch
{
// ignored
}
if (_bansData == null)
{
_bansData = new BansData();
}
_exConfig = ExConfig.Parse(this);
_permission.RegisterPermission(BanPermission, this);
_permission.RegisterPermission(KickPermission, this);
_permission.RegisterPermission(SyncPermission, this);
_permission.RegisterPermission(UnbanPermission, this);
_permission.RegisterPermission(StrikePermission, this);
foreach (string hookName in Hooks.Keys)
{
if (hookName == nameof(OnServerInitialized) || hookName == nameof(OnPluginLoaded))
{
continue;
}
Unsubscribe(hookName);
}
}
private string GetLangMessage(string key, string userId) => _lang.GetMessage(key, this, userId);
private string GetLangMessage(string key, BasePlayer player) => GetLangMessage(key, player.UserIDString);
[HookMethod("OnPluginLoaded")]
private void OnPluginLoaded(Plugin plugin)
{
if (plugin.Name == "RustStore" && _initialized)
{
PostInitialize();
}
}
private void PostInitialize()
{
if (_mainObject != null)
return;
_initialized = true;
Plugin store = _plugins.Find("RustStore");
if (store == null || !store.IsLoaded)
{
Log.Warning("Не найден обязательный плагин RustStore, ожидается его загрузка.");
return;
}
Log.Debug("Синхронизация с магазином...");
string storeId;
string serverId;
string serverKey;
try
{
storeId = store.Config.Get<string>("номер магазина");
serverId = store.Config.Get<string>("номер сервера");
serverKey = store.Config.Get<string>("ключ сервера");
}
catch
{
Log.Error("Ошибка в данных для авторизации (конфиг RustStore).");
Interface.Oxide.UnloadPlugin("BanSystem");
return;
}
_mainObject = new GameObject("BanSystem" + Version);
_webApi = new WebApi(_mainObject.AddComponent<WWWRequests>(), storeId, serverId, serverKey);
_webApi.PostRequest(_webApi.InitData, (r, c) =>
{
ConsoleSystem.Run(ConsoleSystem.Option.Unrestricted, "server.writecfg");
///SyncStandartBans("bans.cfg", true);
});
var comLib = GetLibrary<Command>();
comLib.AddConsoleCommand("bs.ban", this, BanConsoleCmd);
comLib.AddConsoleCommand("bs.unban", this, UnbanConsoleCmd);
comLib.AddConsoleCommand("bs.sync", this, SyncConsoleCmd);
comLib.AddConsoleCommand("bs.kick", this, KickConsoleCmd);
comLib.AddConsoleCommand("bs.strike", this, StrikeConsoleCmd);
comLib.AddChatCommand("kick", this, KickChatCmd);
comLib.AddChatCommand("ban", this, BanChatCmd);
comLib.AddChatCommand("unban", this, UnbanChatCmd);
comLib.AddChatCommand("strike", this, StrikeChatCmd);
foreach (string hookName in Hooks.Keys)
{
Subscribe(hookName);
}
}
[HookMethod("OnServerInitialized")]
private void OnServerInitialized()
{
Interface.Oxide.NextTick(PostInitialize);
}
private void SyncBanList(IEnumerable<BansData.SyncBanData> syncList, Action<bool> onSync = null)
{
BansData.SyncBanData[] syncCopy = syncList.ToArray();
if (syncCopy.Length == 0)
{
return;
}
_webApi.AddBanListData["data"] = JsonConvert.SerializeObject(syncCopy);
_webApi.PostRequest(_webApi.AddBanListData, (success, res) =>
{
if (!success)
{
onSync?.Invoke(false);
return;
}
foreach (BansData.SyncBanData sync in syncCopy)
{
_bansData.SyncBan(sync.steamID, 0, false);
ServerUsers.Remove(sync.steamID);
}
_bansData.Save();
onSync?.Invoke(true);
});
}
[HookMethod("OnServerSave")]
private void OnServerSave()
{
SyncBanList(_bansData.noSyncBans);
}
private void KickConnectionBan(Connection connection, string reason)
{
string steam = connection.userid.ToString();
string rejectMessage = string.IsNullOrEmpty(reason)
? GetLangMessage("BAN.MESSAGE", steam) :
GetLangMessage("BAN.MESSAGE.REASON", steam).Replace("{reason}", reason);
ConnectionAuth.Reject(connection, rejectMessage);
}
[HookMethod("OnPlayerConnected")]
private void OnPlayerConnected(BasePlayer player)
{
Connection connection = player.Connection;
if (connection == null)
{
return;
}
ulong steam = connection.userid;
ulong owner = connection.ownerid;
if (owner == steam)
{
owner = 0;
}
BansData.SyncBanData noSynced = _bansData.noSyncBans.FirstOrDefault(p => p.steamID == steam || p.familyShare == steam ||
owner > 0 && (p.familyShare == owner || p.steamID == owner));
if (noSynced != default(BansData.SyncBanData))
{
KickConnectionBan(connection, noSynced.reason);
return;
}
_webApi.CheckBanData["steamID"] = steam.ToString();
_webApi.CheckBanData["familyShare"] = owner.ToString();
_webApi.CheckBanData["ip"] = connection.ipaddress?.Remove(connection.ipaddress.Length - 6, 6) ?? string.Empty;
_webApi.PostRequest(_webApi.CheckBanData, (success, resp) =>
{
bool isLocalBanned = _bansData.IsPlayerBanned(steam);
if (!success)
{
if (isLocalBanned)
{
KickConnectionBan(connection, string.Empty);
}
return;
}
if (resp.message != "banned")
{
if (isLocalBanned)
{
_bansData.RemoveBan(steam);
}
return;
}
if (!isLocalBanned)
{
_bansData.SyncBan(steam, 0);
}
KickConnectionBan(connection, resp.data.ToString());
});
}
private bool FindPlayer(string pattern, out ulong steamId, out string name)
{
if (ulong.TryParse(pattern, out steamId) && steamId > 76561190000000000 && steamId < 76561199999999999)
{
name = _covalence.Players.FindPlayerById(steamId.ToString())?.Name ?? string.Empty;
return true;
}
List<BasePlayer> players = BasePlayer.activePlayerList.Where(pl => !pl.IsSleeping() && pl.displayName.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) != -1).ToList();
players.AddRange(BasePlayer.sleepingPlayerList.Where(pl => pl.displayName.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) != -1));
if (players.Count == 0)
{
name = "Игрок с таким именем не найден";
return false;
}
if (players.Count > 1)
{
name = $"Найдены совпадения по имени: {string.Join(" ", players.Select(p => p.displayName).ToArray())}";
return false;
}
steamId = players[0].userID;
name = players[0].displayName;
return true;
}
private string KickPlayer(ulong initiator, string pattern, string reason)
{
if (reason == "-")
{
reason = string.Empty;
}
List<BasePlayer> players = BasePlayer.activePlayerList.Where(pl => pl.displayName.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) != -1).ToList();
if (players.Count == 0)
{
return "Игрок с таким именем не найден";
}
if (players.Count > 1)
{
return $"Найдены совпадения по имени: {string.Join(" ", players.Select(p => p.displayName).ToArray())}";
}
BasePlayer ply = players[0];
string name = ply.displayName;
string steamStr = ply.UserIDString;
Interface.Oxide.CallHook("OnBanSystemKick", ply, initiator);
LogAdmin("был кикнут", initiator, name, steamStr);
if (_exConfig.showGlobalKickMessage)
{
foreach (BasePlayer brodPlayer in BasePlayer.activePlayerList)
{
brodPlayer.ChatMessage(
GetLangMessage(string.IsNullOrEmpty(reason) ? "KICK.BROADCAST" : "KICK.BROADCAST.REASON", brodPlayer)
.Replace("{reason}", reason)
.Replace("{name}", name)
.Replace("{steamID}", steamStr));
}
}
ply.Kick(reason);
return $"Игрок {name} {steamStr} кикнут с сервера.";
}
private void BanPlayer(string player, string reason, string banTimeStr, ulong bannedBy, int single, Action<string> onMessage)
{
uint banTime;
if (!ConvertToSeconds(banTimeStr, out banTime))
{
onMessage("Ошибка в формате времени");
return;
}
ulong steam;
string name;
if (!FindPlayer(player, out steam, out name))
{
onMessage(name);
return;
}
if (!string.IsNullOrEmpty(name))
{
name += " ";
}
BanPlayer(name, steam, reason, banTime, bannedBy, single, onMessage);
}
private void BanPlayer(string name, ulong steam, string reason, uint banTime, ulong bannedBy, int single, Action<string> onMessage)
{
if (reason == "-")
{
reason = string.Empty;
}
string steamStr = steam.ToString();
ulong owner = 0;
string banTimeString = banTime == 0 ? "навсегда" : $"на {GetNiceTime(banTime, "д.", "ч.", "мин.", "сек.")}";
var ip = "0.0.0.0";
Connection con = BasePlayer.FindByID(steam)?.net?.connection;
if (con != null)
{
ip = con.ipaddress;
ip = ip.Remove(ip.IndexOf(':'));
owner = con.ownerid;
if (owner == steam)
{
owner = 0;
}
KickConnectionBan(con, reason);
if (owner > 0)
{
Connection ownerCon = BasePlayer.FindByID(owner)?.net?.connection;
if (ownerCon != null)
{
KickConnectionBan(ownerCon, "Family Share Ban");
}
}
}
bool isBannedAlready = _bansData.IsPlayerBanned(steam);
if (!isBannedAlready)
{
_bansData.AddBan(steam, reason, banTime, bannedBy, ip, owner, single);
}
if (_exConfig.showGlobalBanMessage && (con != null || _exConfig.showGlobalBanMessageOffline && !isBannedAlready))
{
foreach (BasePlayer brodPlayer in BasePlayer.activePlayerList)
{
brodPlayer.ChatMessage(
banTime == 0
? GetLangMessage(string.IsNullOrEmpty(reason) ? "BAN.BROADCAST.FOREVER" : "BAN.BROADCAST.FOREVER.REASON", brodPlayer)
.Replace("{reason}", reason)
.Replace("{name}", name)
.Replace("{steamID}", steamStr)
: GetLangMessage(string.IsNullOrEmpty(reason) ? "BAN.BROADCAST" : "BAN.BROADCAST.REASON", brodPlayer)
.Replace("{reason}", reason)
.Replace("{name}", name)
.Replace("{steamID}", steamStr)
.Replace("{time}", GetNiceTime(banTime, GetLangMessage("DAY_SHORT", brodPlayer), GetLangMessage("HOUR_SHORT", brodPlayer),
GetLangMessage("MIN_SHORT", brodPlayer), GetLangMessage("SEC_SHORT", brodPlayer))));
}
}
_webApi.AddBanData["steamID"] = steam.ToString();
_webApi.AddBanData["reason"] = reason;
_webApi.AddBanData["banTime"] = banTime.ToString();
_webApi.AddBanData["bannedBy"] = bannedBy.ToString();
_webApi.AddBanData["ip"] = ip;
_webApi.AddBanData["familyShare"] = owner.ToString();
_webApi.AddBanData["singleBan"] = single.ToString();
_webApi.PostRequest(_webApi.AddBanData, (success, res) =>
{
if (!success)
{
if (isBannedAlready)
{
onMessage($"Игрок {name}{steam} уже забанен");
}
else
{
Interface.Oxide.CallHook("OnBanSystemBan", steam, owner, reason, banTime, bannedBy, single);
LogAdmin($"был забанен {banTimeString}", bannedBy, name, steamStr);
onMessage($"Игрок {name}{steam} успешно забанен {banTimeString} локально, но при синхронизации произошла ошибка, бан будет синхронизирован с сайтом позже");
}
return;
}
_bansData.SyncBan(steam, owner);
if (res.message == "userAlreadyBanned")
{
onMessage($"Игрок {name}{steam} уже забанен");
return;
}
Interface.Oxide.CallHook("OnBanSystemBan", steam, owner, reason, banTime, bannedBy, single);
LogAdmin($"был забанен {banTimeString}" , bannedBy, name, steamStr);
onMessage($"Игрок {name}{steam} успешно забанен {banTimeString}");
});
}
private void StrikePlayer(string player, string reason, ulong initiator, Action<string> onMessage)
{
if (reason == "-")
{
reason = string.Empty;
}
ulong steamId;
string name;
if (!FindPlayer(player, out steamId, out name))
{
onMessage(name);
return;
}
if (!string.IsNullOrEmpty(name))
{
name += " ";
}
List<string> strikes = _bansData.GiveStrike(steamId, reason).strikes;
BasePlayer ply = BasePlayer.FindByID(steamId);
if (ply != null && ply.IsConnected)
{
ply.ChatMessage(
GetLangMessage(string.IsNullOrEmpty(reason) ? "STRIKE.MESSAGE" : "STRIKE.MESSAGE.REASON", ply)
.Replace("{reason}", reason)
.Replace("{strikeCount}", strikes.Count.ToString())
.Replace("{banStrikeCount}", _exConfig.strikeBanCount.ToString()));
}
string steamStr = steamId.ToString();
Interface.Oxide.CallHook("OnBanSystemStrike", steamId, reason, initiator);
LogAdmin("был страйкнут", initiator, name, steamStr);
if (_exConfig.showGlobalStrikeMessage)
{
string cntStr = strikes.Count.ToString();
string maxCntStr = _exConfig.strikeBanCount.ToString();
foreach (BasePlayer brodPlayer in BasePlayer.activePlayerList)
{
brodPlayer.ChatMessage(
GetLangMessage(string.IsNullOrEmpty(reason) ? "STRIKE.BROADCAST" : "STRIKE.BROADCAST.REASON", brodPlayer)
.Replace("{reason}", reason)
.Replace("{name}", name)
.Replace("{steamID}", steamStr)
.Replace("{strikeCount}", cntStr)
.Replace("{banStrikeCount}", maxCntStr));
}
}
if (strikes.Count < _exConfig.strikeBanCount)
{
onMessage($"Игрок {name}{steamId} получил страйк.");
return;
}
uint banTime;
if (!ConvertToSeconds(_exConfig.strikeBanTime, out banTime))
{
onMessage("Ошибка в формате времени");
return;
}
string[] nonEmptyStrikes = strikes.Where(s => !string.IsNullOrEmpty(s)).ToArray();
BanPlayer(name, steamId, GetLangMessage(nonEmptyStrikes.Length == 0 ? "BAN.STRIKE.FORMAT" : "BAN.STRIKE.FORMAT.REASON", steamId.ToString())
.Replace("{STRIKES}", string.Join(", ", nonEmptyStrikes)), banTime, initiator, _exConfig.syncStrikeBans ? 0 : 1, onMessage);
}
private void UnbanPlayer(string player, ulong initiator, Action<string> onMessage)
{
ulong steamId;
string name;
if (!FindPlayer(player, out steamId, out name))
{
onMessage(name);
return;
}
if (!string.IsNullOrEmpty(name))
{
name += " ";
}
_webApi.RemoveBanData["steamID"] = steamId.ToString();
_webApi.RemoveBanData["bannedBy"] = initiator.ToString();
_webApi.PostRequest(_webApi.RemoveBanData, (success, res) =>
{
if (!success)
{
onMessage("Что-то пошло не так в процессе разбана, попробуйте позже");
return;
}
_bansData.RemoveBan(steamId);
ServerUsers.User serverUser = ServerUsers.Get(steamId);
if (serverUser != null && serverUser.group == ServerUsers.UserGroup.Banned)
{
ServerUsers.Remove(steamId);
ConsoleSystem.Run(ConsoleSystem.Option.Unrestricted, "server.writecfg");
} else if (res.message == "userNotBanned")
{
onMessage($"Игрок {name}{steamId} не был забанен");
return;
}
string steamStr = steamId.ToString();
LogAdmin("был разбанен", initiator, name, steamStr);
Interface.Oxide.CallHook("OnBanSystemUnban", steamId, initiator);
onMessage($"Игрок {name}{steamId} успешно разбанен");
if (!_exConfig.showGlobalUnbanMessage)
{
return;
}
foreach (BasePlayer brodPlayer in BasePlayer.activePlayerList)
{
brodPlayer.ChatMessage(
GetLangMessage("UNBAN.BROADCAST", brodPlayer)
.Replace("{name}", name)
.Replace("{steamID}", steamStr));
}
});
}
private void KickChatCmd(BasePlayer player, string command, string[] args)
{
if (player.net.connection.authLevel < 2 &&
!_permission.UserHasPermission(player.UserIDString, KickPermission))
{
player.ChatMessage("Недостаточно прав");
return;
}
if (args.Length == 0)
{
player.ChatMessage("Синтаксис: /kick {имя/стим} {причина}");
return;
}
player.ChatMessage(KickPlayer(player.userID, args[0], args.Length > 1 ? args[1] : _exConfig.defaultKickReason));
}
private void StrikeChatCmd(BasePlayer player, string command, string[] args)
{
if (args.Length > 0 && args[0] == "info")
{
List<string> strikes = _bansData.GetStrikeData(player.userID)?.strikes;
if (strikes == null)
{
player.ChatMessage(GetLangMessage("STRIKE.INFO.EMPTY", player));
return;
}
string[] nonEmptyStrikes = strikes.Where(s => !string.IsNullOrEmpty(s)).ToArray();
player.ChatMessage(
GetLangMessage(nonEmptyStrikes.Length == 0 ? "STRIKE.INFO" : "STRIKE.INFO.REASON", player)
.Replace("{strikeCount}", strikes.Count.ToString())
.Replace("{banStrikeCount}", _exConfig.strikeBanCount.ToString())
.Replace("{STRIKES}", string.Join(", ", nonEmptyStrikes)));
return;
}
if (player.net.connection.authLevel < 2 &&
!_permission.UserHasPermission(player.UserIDString, StrikePermission))
{
player.ChatMessage("Недостаточно прав");
return;
}
if (args.Length == 0)
{
player.ChatMessage("Синтаксис: /strike {имя/стим} {причина}");
return;
}
StrikePlayer(args[0], args.Length > 1 ? args[1] : _exConfig.defaultStrikeReason, player.userID, player.ChatMessage);
}
private void BanChatCmd(BasePlayer player, string command, string[] args)
{
if (player.net.connection.authLevel < 2 &&
!_permission.UserHasPermission(player.UserIDString, BanPermission))
{
player.ChatMessage("Недостаточно прав");
return;
}
if (args.Length == 0)
{
player.ChatMessage("Синтаксис: /ban {имя/стим} {причина} {время}, /ban single {имя/стим} {причина} {время}");
return;
}
int single = args[0] == "single" ? 1 : 0;
if (single == 1 && args.Length == 1)
{
player.ChatMessage("Синтаксис: /ban {имя/стим} {причина} {время}, /ban single {имя/стим} {причина} {время}");
return;
}
string banTime = _exConfig.defaultTime;
string reason = _exConfig.defaultBanReason;
if (args.Length >= 2 + single)
{
reason = args[1 + single];
if (args.Length >= 3 + single)
{
banTime = args[2 + single];
}
}
BanPlayer(args[0 + single], reason, banTime, player.userID, single, player.ChatMessage);
}
private void UnbanChatCmd(BasePlayer player, string command, string[] args)
{
if (player.net.connection.authLevel < 2 &&
!_permission.UserHasPermission(player.UserIDString, UnbanPermission))
{
player.ChatMessage("Недостаточно прав");
return;
}
if (args.Length == 0)
{
player.ChatMessage("Синтаксис: /unban {имя/стим}");
return;
}
UnbanPlayer(args[0], player.userID, player.ChatMessage);
}
private bool BanConsoleCmd(ConsoleSystem.Arg arg)
{
var bannedBy = 0UL;
if (arg.Connection != null)
{
if (arg.Connection.authLevel < 2 &&
!_permission.UserHasPermission(arg.Connection.userid.ToString(), BanPermission))
{
return false;
}
bannedBy = arg.Connection.userid;
}
string name = arg.GetString(0);
if (string.IsNullOrEmpty(name))
{
arg.ReplyWith("Синтаксис: bs.ban {имя/стим} {причина} {время}, bs.ban single {имя/стим} {причина} {время}");
return false;
}
int single = name == "single" ? 1 : 0;
if (string.IsNullOrEmpty(arg.GetString(0 + single)))
{
arg.ReplyWith("Синтаксис: bs.ban {имя/стим} {причина} {время}, bs.ban single {имя/стим} {причина} {время}");
return false;
}
BanPlayer(arg.GetString(0 + single), arg.GetString(1 + single, _exConfig.defaultBanReason), arg.GetString(2 + single, _exConfig.defaultTime), bannedBy, single, Log.Debug);
return true;
}
private bool KickConsoleCmd(ConsoleSystem.Arg arg)
{
if (arg.Connection != null && arg.Connection.authLevel < 2 && !_permission.UserHasPermission(arg.Connection.userid.ToString(), KickPermission))
{
return false;
}
string name = arg.GetString(0);
if (string.IsNullOrEmpty(name))
{
arg.ReplyWith("Синтаксис: bs.kick {имя/стим} {причина}");
return false;
}
ulong initiator = 0;
if (arg.Connection != null)
{
initiator = arg.Connection.userid;
}
arg.ReplyWith(KickPlayer(initiator, name, arg.GetString(1, _exConfig.defaultKickReason)));
return true;
}
private bool StrikeConsoleCmd(ConsoleSystem.Arg arg)
{
var strikedBy = 0UL;
if (arg.Connection != null)
{
if (arg.Connection.authLevel < 2 &&
!_permission.UserHasPermission(arg.Connection.userid.ToString(), StrikePermission))
{
return false;
}
strikedBy = arg.Connection.userid;
}
string name = arg.GetString(0);
if (string.IsNullOrEmpty(name))
{
arg.ReplyWith("Синтаксис: bs.strike {имя/стим} {причина}");
return false;
}
StrikePlayer(name, arg.GetString(1, _exConfig.defaultKickReason), strikedBy, Log.Debug);
return true;
}
private bool SyncConsoleCmd(ConsoleSystem.Arg arg)
{
if (arg.Connection != null && arg.Connection.authLevel < 2 && !_permission.UserHasPermission(arg.Connection.userid.ToString(), SyncPermission))
{
return false;
}
///SyncStandartBans("bans.cfg.backup", false);
return true;
}
private bool UnbanConsoleCmd(ConsoleSystem.Arg arg)
{
var unbannedBy = 0UL;
if (arg.Connection != null)
{
if (arg.Connection.authLevel < 2 &&
!_permission.UserHasPermission(arg.Connection.userid.ToString(), UnbanPermission))
{
return false;
}
unbannedBy = arg.Connection.userid;
}
string name = arg.GetString(0);
if (string.IsNullOrEmpty(name))
{
arg.ReplyWith("Синтаксис: bs.unban {имя/стим}");
return false;
}
UnbanPlayer(name, unbannedBy, Log.Debug);
return true;
}
[HookMethod("OnServerCommand")]
private object OnServerCommand(ConsoleSystem.Arg arg)
{
string command = arg.cmd?.Name;
if (command == null)
{
return null;
}
switch (command)
{
case "ban":
BanConsoleCmd(arg);
return true;
case "banid":
if (arg.Args.Length == 2)
{
arg.Args = new[] { arg.Args[0] };
}
else if (arg.Args.Length >= 3)
{
arg.Args = new[] { arg.Args[0], arg.Args[2] };
}
BanConsoleCmd(arg);
return true;
case "unban":
UnbanConsoleCmd(arg);
return true;
default:
return null;
}
}
public class WWWRequests : MonoBehaviour
{
private readonly HashSet<WWW> activeRequests = new HashSet<WWW>();
public void Request(string url, Dictionary<string, string> data = null, Action<string, string> onRequestComplete = null)
{
StartCoroutine(WaitForRequest(url, data, onRequestComplete));
}
private IEnumerator WaitForRequest(string url, Dictionary<string, string> data = null, Action<string, string> onRequestComplete = null)
{
WWW www;
if (data != null)
{
var form = new WWWForm();
foreach (KeyValuePair<string, string> pair in data)
{
form.AddField(pair.Key, pair.Value);
}
www = new WWW(url, form.data);
}
else
{
www = new WWW(url);
}
www.threadPriority = ThreadPriority.High;
activeRequests.Add(www);
yield return www;
onRequestComplete?.Invoke(www.text, www.error);
activeRequests.Remove(www);
}
private void OnDestroy()
{
foreach (WWW www in activeRequests)
{
try { www.Dispose(); } catch { }
}
}
}
public class WebApi
{
public class JsonResponse
{
public string message = "empty";
public string status = "empty";
public JToken data;
public JsonResponse() { }
public bool IsSuccess() => status == "success";
}
private readonly WWWRequests _requests;
private const string BaseUrl = "https://store-api.moscow.ovh/index.php";
public readonly Dictionary<string, string> InitData = new Dictionary<string, string>()
{
["modules"] = "servers",
["action"] = "checkAuth"
};
public readonly Dictionary<string, string> CheckBanData = new Dictionary<string, string>()
{
["modules"] = "banlist",
["action"] = "checkUserBan",
["steamID"] = "0",
["familyShare"] = "0",
["ip"] = "0.0.0.0"
};
public readonly Dictionary<string, string> AddBanData = new Dictionary<string, string>()
{
["modules"] = "banlist",
["action"] = "addBan",
["steamID"] = "0",
["reason"] = "",
["banTime"] = "0",
["bannedBy"] = "0",
["ip"] = "0.0.0.0",
["familyShare"] = "0",
["singleBan"] = "0"
};
public readonly Dictionary<string, string> AddBanListData = new Dictionary<string, string>()
{
["modules"] = "banlist",
["action"] = "importBans",
["data"] = ""
};
public readonly Dictionary<string, string> RemoveBanData = new Dictionary<string, string>()
{
["modules"] = "banlist",
["action"] = "removeBan",
["steamID"] = "0",
["bannedBy"] = "0"
//["ip"] = "0"
};
public WebApi(WWWRequests www, string storeId, string serverId, string serverKey)
{
_requests = www;
InitData["storeID"] = storeId;
InitData["serverID"] = serverId;
InitData["serverKey"] = serverKey;
CheckBanData["storeID"] = storeId;
CheckBanData["serverID"] = serverId;
CheckBanData["serverKey"] = serverKey;
AddBanData["storeID"] = storeId;
AddBanData["serverID"] = serverId;
AddBanData["serverKey"] = serverKey;
AddBanListData["storeID"] = storeId;
AddBanListData["serverID"] = serverId;
AddBanListData["serverKey"] = serverKey;
RemoveBanData["storeID"] = storeId;
RemoveBanData["serverID"] = serverId;
RemoveBanData["serverKey"] = serverKey;
}
public void PostRequest(Dictionary<string, string> data, Action<bool, JsonResponse> response = null)
{
_requests.Request(BaseUrl, data, (res, error) =>
{
if (!string.IsNullOrEmpty(error) || string.IsNullOrEmpty(res))
{
Log.File($"Request error: {error}", "errors");
response?.Invoke(false, new JsonResponse() { status = "RequestError", message = error });
return;
}
JsonResponse converted = null;
try
{
var settings = new JsonSerializerSettings
{
StringEscapeHandling = StringEscapeHandling.EscapeHtml
};
converted = JsonConvert.DeserializeObject<JsonResponse>(res, settings);
}
catch
{
// ignored
}
if (converted == null)
{
Log.File($"JsonError: {res}", "errors");
response?.Invoke(false, new JsonResponse() { status = "JsonError", message = res });
return;
}
if (!converted.IsSuccess())
{
switch (converted.message)
{
case "unload":
Log.Debug("Плагин отключен в настройках магазина.");
Interface.Oxide.UnloadPlugin("BanSystem");
return;
case "invalidStoreAuth":
case "invalidServerAuth":
Log.Error("Ошибка авторизации.");
Interface.Oxide.UnloadPlugin("BanSystem");
return;
}
response?.Invoke(false, converted);
return;
}
response?.Invoke(true, converted);
});
}
}
public static class Log
{
public static void File(string text, string title = "info")
{
text = $"[{DateTime.Now:HH:mm:ss}] {text}";
_plugin.LogToFile($"{title}-{DateTime.Now:yyyy-MM-dd}.txt", text, _plugin);
}
public static void Debug(string format)
{
UnityEngine.Debug.Log($"[BanSystem] {format}");
}
public static void Error(string format, params object[] args)
{
UnityEngine.Debug.LogError($"[BanSystem] {string.Format(format, args)}");
}
public static void Warning(string format, params object[] args)
{
UnityEngine.Debug.LogWarning($"[BanSystem] {string.Format(format, args)}");
}
}
public class ExConfig
{
private readonly Dictionary<string, string> _messages = new Dictionary<string, string>()
{
["BAN.MESSAGE"] = "Вы забанены на этом сервере",
["BAN.MESSAGE.REASON"] = "Вы забанены на этом сервере, причина: {reason}",
["BAN.STRIKE.FORMAT"] = "Страйк бан",
["BAN.STRIKE.FORMAT.REASON"] = "Страйк бан: {STRIKES}",
["BAN.BROADCAST"] = "Игрок {name} ({steamID}) был забанен на {time}.",
["BAN.BROADCAST.REASON"] = "Игрок {name} ({steamID}) был забанен на {time}.\nПричина: {reason}",
["BAN.BROADCAST.FOREVER"] = "Игрок {name} ({steamID}) был забанен навсегда.",
["BAN.BROADCAST.FOREVER.REASON"] = "Игрок {name} ({steamID}) был забанен навсегда.\nПричина: {reason}",
["STRIKE.INFO"] = "Вы получили {strikeCount}/{banStrikeCount} страйков.",
["STRIKE.INFO.EMPTY"] = "Вы не получали страйков.",
["STRIKE.INFO.REASON"] = "Вы получили {strikeCount}/{banStrikeCount} страйков, причины: {STRIKES}",
["STRIKE.MESSAGE"] = "Вы получили страйк, всего страйков {strikeCount}/{banStrikeCount}",
["STRIKE.MESSAGE.REASON"] = "Вы получили страйк, причина: {reason}, всего страйков {strikeCount}/{banStrikeCount}",
["STRIKE.BROADCAST"] = "Игрок {name} ({steamID}) получил страйк.\nВсего страйков игрока {strikeCount}/{banStrikeCount}",
["STRIKE.BROADCAST.REASON"] = "Игрок {name} ({steamID}) получил страйк.\nПричина: {reason}.\nВсего страйков игрока {strikeCount}/{banStrikeCount}",
["KICK.BROADCAST"] = "Игрок {name} ({steamID}) был кикнут.",
["KICK.BROADCAST.REASON"] = "Игрок {name} ({steamID}) был кикнут.\nПричина: {reason}",
["UNBAN.BROADCAST"] = "Игрок {name} ({steamID}) был разбанен.",
["DAY_SHORT"] = "д ",
["HOUR_SHORT"] = "ч ",
["MIN_SHORT"] = "мин ",
["SEC_SHORT"] = "сек "
};
[JsonProperty("Время бана по умолчанию")]
public string defaultTime = "0";
[JsonProperty("Причина бана по умолчанию")]
public string defaultBanReason = "";
[JsonProperty("Причина кика по умолчанию")]
public string defaultKickReason = "";
[JsonProperty("Причина страйка по умолчанию")]
public string defaultStrikeReason = "";
[JsonProperty("Время бана за страйки")]
public string strikeBanTime = "1d";
[JsonProperty("Количество страйков для бана")]
public ushort strikeBanCount = 3;
[JsonProperty("Синхронизировать баны за страйки с другими серверами")]
public bool syncStrikeBans = false;
[JsonProperty("Оповещение о бане")]
public bool showGlobalBanMessage = true;
[JsonProperty("Оповещение о бане игрока, который не находится на сервере")]
public bool showGlobalBanMessageOffline = false;
[JsonProperty("Оповещение о разбане")]
public bool showGlobalUnbanMessage = false;
[JsonProperty("Оповещение о кике")]
public bool showGlobalKickMessage = true;
[JsonProperty("Оповещение о страйке")]
public bool showGlobalStrikeMessage = true;
private CSPlugin _pluginOwner;
private void Initialize()
{
CSPlugin.GetLibrary<Lang>().RegisterMessages(_messages, _pluginOwner);
}
public static ExConfig Parse(RustPlugin plugin)
{
DynamicConfigFile pluginConfig = plugin.Config;
ExConfig output = null;
// Parsing config
try { output = pluginConfig.ReadObject<ExConfig>(); }
catch
{
// ignored
}
if (output == null)
{
pluginConfig.Save($"{pluginConfig.Filename}.jsonError");
Log.Debug("Lol");
output = new ExConfig();
Log.Error("Файл конфигурации \"{0}\" содержит ошибку и был заменен на стандартный.\n" +
"Ошибочный файл конфигурации сохранен под названием \"{0}.jsonError\"",
GetFileName(pluginConfig.Filename));
}
pluginConfig.WriteObject(output);
output._pluginOwner = plugin;
output.Initialize();
return output;
}
}
public class BansData
{
public HashSet<StrikeData> strikes = new HashSet<StrikeData>();
public HashSet<SyncBanData> noSyncBans = new HashSet<SyncBanData>();
public HashSet<ulong> bans = new HashSet<ulong>();
public class SyncBanData
{
public ulong steamID;
public ulong familyShare;
public ulong bannedBy;
public string reason;
public string ip;
public uint banTime;
public int singleBan;
public SyncBanData() { }
}
public class StrikeData
{
public ulong steamID;
public List<string> strikes;
public StrikeData() { }
}
public void Save()
{
Interface.Oxide.DataFileSystem.WriteObject("BanSystem", this);
}
public StrikeData GetStrikeData(ulong steam)
{
StrikeData strikeData = strikes.FirstOrDefault(sd => sd.steamID == steam);
if (strikeData == default(StrikeData) || strikeData.strikes == null)
{
return null;
}
return strikeData;
}
public StrikeData GiveStrike(ulong steam, string reason)
{
StrikeData strikeData = strikes.FirstOrDefault(sd => sd.steamID == steam);
if (strikeData == default(StrikeData) || strikeData.strikes == null)
{
strikeData = new StrikeData() { steamID = steam, strikes = new List<string> { reason } };
strikes.Add(strikeData);
}
else
{
strikeData.strikes.Add(reason);
}
Save();
return strikeData;
}
public void AddBan(ulong steam, string res, uint time, ulong admin, string _ip, ulong ownerID, int _single)
{
strikes.RemoveWhere(b => b.steamID == steam);
noSyncBans.Add(new SyncBanData()
{
steamID = steam,
reason = res,
banTime = time,
bannedBy = admin,
ip = _ip,
familyShare = ownerID,
singleBan = _single
});
Save();
}
public void RemoveBan(ulong steam)
{
strikes.RemoveWhere(b => b.steamID == steam);
noSyncBans.RemoveWhere(b => b.steamID == steam);
bans.Remove(steam);
Save();
}
public void SyncBan(ulong steam, ulong owner, bool save = true)
{
noSyncBans.RemoveWhere(b => b.steamID == steam);
bans.Add(steam);
if (owner > 0)
{
bans.Add(owner);
}
if (save)
{
Save();
}
}
public bool IsPlayerBanned(ulong steamID)
{
return noSyncBans.Any(b => b.steamID == steamID) || bans.Contains(steamID);
}
}
public static string GetFileName(string path)
{
if (path != null)
{
int length = path.Length;
int index = length;
while (--index >= 0)
{
char ch = path[index];
if ((int)ch == (int)Path.DirectorySeparatorChar || (int)ch == (int)Path.AltDirectorySeparatorChar || (int)ch == (int)Path.VolumeSeparatorChar)
return path.Substring(index + 1, length - index - 1);
}
}
return path;
}
}
}