using Newtonsoft.Json; using Oxide.Core.Libraries.Covalence; using Oxide.Core; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text.RegularExpressions; using System; namespace Oxide.Plugins { [Info("Timed Permissions", "LaserHydra", "1.6.0")] [Description("Allows you to grant permissions or groups for a specific time")] class TimedPermissions : CovalencePlugin { private const string AdminPermission = "timedpermissions.use"; private const string AdvancedAdminPermission = "timedpermissions.advanced"; private static TimedPermissions _plugin; private static List _playerInformationCollection = new List(); private Regex _timeSpanPattern = new Regex(@"(?:(?\d{1,3})d)?(?:(?\d{1,3})h)?(?:(?\d{1,3})m)?", RegexOptions.Compiled | RegexOptions.IgnoreCase); private Configuration _config; #region Hooks & Loading private void Loaded() { _plugin = this; LoadData(ref _playerInformationCollection); if (_playerInformationCollection == null) { _playerInformationCollection = new List(); SaveData(_playerInformationCollection); } _plugin.timer.Repeat(60, 0, () => { for (int i = _playerInformationCollection.Count - 1; i >= 0; i--) { PlayerInformation playerInformation = _playerInformationCollection[i]; playerInformation.Update(); } }); } private void OnUserConnected(IPlayer player) { PlayerInformation.Get(player.Id)?.EnsureAllAccess(); } private void OnNewSave(string filename) { LoadConfig(); // Ensure config is loaded at this point if (_config.WipeDataOnNewSave) { string backupFileName; ResetAllAccess(out backupFileName); PrintWarning($"New save file detected: all groups and permissions revoked and data cleared. Backup created at {backupFileName}.json"); } } #endregion #region Commands [Command("pinfo")] private void CmdPlayerInfo(IPlayer player, string cmd, string[] args) { IPlayer target; if (args.Length == 0 || !player.HasPermission(AdminPermission)) target = player; else target = FindPlayer(args[0], player); if (target == null) return; var information = PlayerInformation.Get(target.Id); if (information == null) { player.Reply(GetMessage("Player Has No Info", player.Id)); } else { string msg = GetMessage("Player Info", player.Id); msg = msg.Replace("{player}", $"{information.Name} ({information.Id})"); msg = msg.Replace("{groups}", string.Join(", ", (from g in information.Groups select $"{g.Value} until {g.ExpireDate.ToLongDateString() + " " + g.ExpireDate.ToShortTimeString()} UTC").ToArray())); msg = msg.Replace("{permissions}", string.Join(", ", (from p in information.Permissions select $"{p.Value} until {p.ExpireDate.ToLongDateString() + " " + p.ExpireDate.ToShortTimeString()} UTC").ToArray())); player.Reply(msg); } } [Command("grantperm"), Permission(AdminPermission)] private void CmdGrantPerm(IPlayer player, string cmd, string[] args) { if (args.Length != 3) { player.Reply(GetMessage("Syntax : grantperm", player.Id)); return; } IPlayer target = FindPlayer(args[0], player); TimeSpan duration; if (target == null) return; if (!TryParseTimeSpan(args[2], out duration)) { player.Reply(GetMessage("Invalid Time Format", player.Id)); return; } PlayerInformation.GetOrCreate(target).AddPermission(args[1].ToLower(), DateTime.UtcNow + duration); } [Command("revokeperm"), Permission(AdminPermission)] private void CmdRevokePerm(IPlayer player, string cmd, string[] args) { if (args.Length != 2) { player.Reply(GetMessage("Syntax : revokeperm", player.Id)); return; } IPlayer target = FindPlayer(args[0], player); if (target == null) return; PlayerInformation information = PlayerInformation.Get(target.Id); if (information == null || !information.Permissions.Any(p => p.Value == args[1].ToLower())) { player.Reply(GetMessage("User Doesn't Have Permission", player.Id).Replace("{target}", target.Name).Replace("{permission}", args[1].ToLower())); return; } information.RemovePermission(args[1].ToLower()); } [Command("addgroup"), Permission(AdminPermission)] private void CmdAddGroup(IPlayer player, string cmd, string[] args) { if (args.Length != 3) { player.Reply(GetMessage("Syntax : addgroup", player.Id)); return; } IPlayer target = FindPlayer(args[0], player); TimeSpan duration; if (target == null) return; if (!TryParseTimeSpan(args[2], out duration)) { player.Reply(GetMessage("Invalid Time Format", player.Id)); return; } PlayerInformation.GetOrCreate(target).AddGroup(args[1], DateTime.UtcNow + duration); } [Command("removegroup"), Permission(AdminPermission)] private void CmdRemoveGroup(IPlayer player, string cmd, string[] args) { if (args.Length != 2) { player.Reply(GetMessage("Syntax : removegroup", player.Id)); return; } IPlayer target = FindPlayer(args[0], player); if (target == null) return; PlayerInformation information = PlayerInformation.Get(target.Id); if (information == null || !information.Groups.Any(p => p.Value == args[1].ToLower())) { player.Reply(GetMessage("User Isn't In Group", player.Id).Replace("{target}", target.Name).Replace("{group}", args[1].ToLower())); return; } information.RemoveGroup(args[1].ToLower()); } [Command("timedpermissions_resetaccess"), Permission(AdvancedAdminPermission)] private void CmdResetAccess(IPlayer player, string cmd, string[] args) { if (args.Length != 1 || !args[0].Equals("yes", StringComparison.InvariantCultureIgnoreCase)) { player.Reply(GetMessage("Syntax : resetaccess", player.Id)); player.Reply(GetMessage("Reset Access Warning", player.Id)); return; } string backupFileName; ResetAllAccess(out backupFileName); player.Reply(GetMessage("Access Reset Successfully", player.Id).Replace("{filename}", backupFileName)); } #endregion #region Helper Methods private void ResetAllAccess(out string backupFileName) { backupFileName = $"{nameof(TimedPermissions)}_Backups/{DateTime.UtcNow.Date:yyyy-MM-dd}_{DateTime.UtcNow:T}"; SaveData(_playerInformationCollection, backupFileName); // create backup of current data foreach (PlayerInformation playerInformation in _playerInformationCollection) playerInformation.RemoveAllAccess(); _playerInformationCollection = new List(); SaveData(_playerInformationCollection); } #region Time Helper private bool TryParseTimeSpan(string source, out TimeSpan date) { var match = _timeSpanPattern.Match(source); if (!match.Success) { date = default(TimeSpan); return false; } if (!match.Groups[0].Value.Equals(source)) { date = default(TimeSpan); return false; } Group daysGroup = match.Groups["days"]; Group hoursGroup = match.Groups["hours"]; Group minutesGroup = match.Groups["minutes"]; int days = daysGroup.Success ? int.Parse(daysGroup.Value) : 0; int hours = hoursGroup.Success ? int.Parse(hoursGroup.Value) : 0; int minutes = minutesGroup.Success ? int.Parse(minutesGroup.Value) : 0; if (days + hours + minutes == 0) { date = default(TimeSpan); return false; } date = new TimeSpan(days, hours, minutes, 0); return true; } #endregion #region Finding Helper private IPlayer FindPlayer(string nameOrId, IPlayer player) { if (IsConvertibleTo(nameOrId) && nameOrId.StartsWith("7656119") && nameOrId.Length == 17) { IPlayer result = players.All.ToList().Find(p => p.Id == nameOrId); if (result == null) player.Reply($"Could not find player with ID '{nameOrId}'"); return result; } List foundPlayers = new List(); foreach (IPlayer current in players.Connected) { if (string.Equals(current.Name, nameOrId, StringComparison.CurrentCultureIgnoreCase)) return current; if (current.Name.ToLower().Contains(nameOrId.ToLower())) foundPlayers.Add(current); } switch (foundPlayers.Count) { case 0: player.Reply($"Could not find player with name '{nameOrId}'"); break; case 1: return foundPlayers[0]; default: string[] names = (from current in foundPlayers select current.Name).ToArray(); player.Reply("Multiple matching players found: \n" + string.Join(", ", names)); break; } return null; } #endregion #region Conversion Helper private static bool IsConvertibleTo(object obj) { try { var temp = (T)Convert.ChangeType(obj, typeof(T)); return true; } catch { return false; } } #endregion #region Data Helper private static void LoadData(ref T data, string filename = null) => data = Interface.Oxide.DataFileSystem.ReadObject(filename ?? nameof(TimedPermissions)); private static void SaveData(T data, string filename = null) => Interface.Oxide.DataFileSystem.WriteObject(filename ?? nameof(TimedPermissions), data); #endregion #region Message Wrapper public static string GetMessage(string key, string id) => _plugin.lang.GetMessage(key, _plugin, id); #endregion #endregion #region Localization protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["No Permission"] = "You don't have permission to use this command.", ["Invalid Time Format"] = "Invalid Time Format: Ex: 1d12h30m | d = days, h = hours, m = minutes", ["Player Has No Info"] = "There is no info about this player.", ["Player Info"] = "Information for {player}:" + Environment.NewLine + "Groups: {groups}" + Environment.NewLine + "Permissions: {permissions}", ["User Doesn't Have Permission"] = "{target} does not have permission '{permission}'.", ["User Isn't In Group"] = "{target} isn't in group '{group}'.", ["Reset Access Warning"] = "This command will reset all access data and create a backup. Please confirm by calling the command with 'yes' as parameter", ["Access Reset Successfully"] = "All groups and permissions revoked and data cleared. Backup created at {filename}.json", ["Syntax : revokeperm"] = "Syntax: revokeperm ", ["Syntax : grantperm"] = "Syntax: removegroup ", ["Syntax : removegroup"] = "Syntax: removegroup ", ["Syntax : addgroup"] = "Syntax: addgroup