C#:
using System.Text.RegularExpressions;
using Oxide.Core.Libraries.Covalence;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Linq;
using System;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
[Info("Timed Permissions", "LaserHydra", "1.4.0", ResourceId = 1926)]
[Description("Allows you to grant permissions or groups for a specific time")]
internal class TimedPermissions : CovalencePlugin
{
private const string AdminPermission = "timedpermissions.use";
private const string AdvancedAdminPermission = "timedpermissions.advanced";
private static TimedPermissions _instance;
private static List<Player> _players = new List<Player>();
private Configuration _config;
#region Hooks & Loading
private void Loaded()
{
_instance = this;
MigrateData();
LoadData(ref _players);
if (_players == null)
{
_players = new List<Player>();
SaveData(_players);
}
}
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}");
}
}
private void MigrateData()
{
List<JObject> data = new List<JObject>();
LoadData(ref data);
if (data == null)
return;
foreach (JObject playerData in data)
{
if (playerData["permissions"] != null)
{
JArray permissions = (JArray) playerData["permissions"];
foreach (JObject obj in permissions)
{
if (obj["permission"] != null)
{
obj["Value"] = obj["permission"];
obj.Remove("permission");
}
if (obj["_expireDate"] != null)
{
string expireDate = obj["_expireDate"].Value<string>();
int[] date = (from val in expireDate.Split('/') select Convert.ToInt32(val)).ToArray();
obj["ExpireDate"] = new DateTime(date[4], date[3], date[2], date[1], date[0], 0);
obj.Remove("_expireDate");
}
}
playerData["Permissions"] = permissions;
playerData.Remove("permissions");
}
if (playerData["groups"] != null)
{
JArray permissions = (JArray)playerData["groups"];
foreach (JObject obj in permissions)
{
if (obj["group"] != null)
{
obj["Value"] = obj["group"];
obj.Remove("group");
}
if (obj["_expireDate"] != null)
{
string expireDate = obj["_expireDate"].Value<string>();
int[] date = (from val in expireDate.Split('/') select Convert.ToInt32(val)).ToArray();
obj["ExpireDate"] = new DateTime(date[4], date[3], date[2], date[1], date[0], 0);
obj.Remove("_expireDate");
}
}
playerData["Groups"] = permissions;
playerData.Remove("groups");
}
if (playerData["steamID"] != null)
{
playerData["Id"] = playerData["steamID"];
playerData.Remove("steamID");
}
if (playerData["name"] != null)
{
playerData["Name"] = playerData["name"];
playerData.Remove("name");
}
}
SaveData(data);
}
#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 pl = Player.Get(target.Id);
if (pl == null)
player.Reply(GetMessage("Player Has No Info", player.Id));
else
{
string msg = GetMessage("Player Info", player.Id);
msg = msg.Replace("{player}", $"{pl.Name} ({pl.Id})");
msg = msg.Replace("{groups}", string.Join(", ", (from g in pl.Groups select $"{g.Value} until {g.ExpireDate.ToLongDateString() + " " + g.ExpireDate.ToShortTimeString()} UTC").ToArray()));
msg = msg.Replace("{permissions}", string.Join(", ", (from p in pl.Permissions select $"{p.Value} until {p.ExpireDate.ToLongDateString() + " " + p.ExpireDate.ToShortTimeString()} UTC").ToArray()));
player.Reply(msg);
}
}
[Command("revokeperm"), Permission(AdminPermission)]
private void CmdRevokePerm(IPlayer player, string cmd, string[] args)
{
if (args.Length != 2)
{
player.Reply($"Syntax: {(player.LastCommand == CommandType.Console ? string.Empty : "/")}revokeperm <player|steamid> <permission>");
return;
}
IPlayer target = FindPlayer(args[0], player);
if (target == null)
return;
Player pl = Player.Get(target.Id);
if (pl == null || !pl.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;
}
pl.RemovePermission(args[1].ToLower());
}
[Command("grantperm"), Permission(AdminPermission)]
private void CmdGrantPerm(IPlayer player, string cmd, string[] args)
{
if (args.Length != 3)
{
player.Reply($"Syntax: {(player.LastCommand == CommandType.Console ? string.Empty : "/")}grantperm <player|steamid> <permission> <time Ex: 1d12h30m>");
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;
}
Player.GetOrCreate(target).AddPermission(args[1].ToLower(), DateTime.UtcNow + duration);
}
[Command("removegroup"), Permission(AdminPermission)]
private void CmdRemoveGroup(IPlayer player, string cmd, string[] args)
{
if (args.Length != 2)
{
player.Reply($"Syntax: {(player.LastCommand == CommandType.Console ? string.Empty : "/")}removegroup <player|steamid> <group>");
return;
}
IPlayer target = FindPlayer(args[0], player);
if (target == null)
return;
Player pl = Player.Get(target.Id);
if (pl == null || !pl.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;
}
pl.RemoveGroup(args[1].ToLower());
}
[Command("addgroup"), Permission(AdminPermission)]
private void CmdAddGroup(IPlayer player, string cmd, string[] args)
{
if (args.Length != 3)
{
player.Reply($"Syntax: {(player.LastCommand == CommandType.Console ? string.Empty : "/")}addgroup <player|steamid> <group> <time Ex: 1d12h30m>");
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;
}
Player.GetOrCreate(target).AddGroup(args[1], DateTime.UtcNow + duration);
}
[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("Syntax: timedpermissions_resetaccess [yes]");
player.Reply("This command will reset all access data and create a backup.Please confirm by calling the command with 'yes' as parameter");
return;
}
string backupFileName;
ResetAllAccess(out backupFileName);
player.Reply($"All groups and permissions revoked and data cleared.Backup created at {backupFileName}");
}
[Command("timedpermissions_ensureaccess"), Permission(AdvancedAdminPermission)]
private void CmdEnsureAccess(IPlayer player, string cmd, string[] args)
{
if (args.Length != 1 || !args[0].Equals("yes", StringComparison.InvariantCultureIgnoreCase))
{
player.Reply("Syntax: timedpermissions_ensureaccess [yes]");
player.Reply("This command will ensure every player has their permissions and groups assigned.Please confirm by calling the command with 'yes' as parameter");
return;
}
foreach (Player playerInformation in _players)
playerInformation.EnsureAllAccess();
player.Reply("All players were ensured to have their permissions and groups assigned.");
}
#endregion
#region Helper Methods
private void ResetAllAccess(out string backupFileName)
{
backupFileName = $"{nameof(TimedPermissions)}_Backups/{DateTime.UtcNow.Date:yyyy-MM-dd}_{DateTime.UtcNow:T}";
SaveData(_players, backupFileName); // create backup of current data
foreach (Player playerInformation in _players)
playerInformation.RemoveAllAccess();
_players = new List<Player>();
SaveData(_players);
}
#region Time Helper
private bool TryParseTimeSpan(string source, out TimeSpan date)
{
int minutes = 0;
int hours = 0;
int days = 0;
Match m = new Regex(@"(\d+?)m", RegexOptions.IgnoreCase).Match(source);
Match h = new Regex(@"(\d+?)h", RegexOptions.IgnoreCase).Match(source);
Match d = new Regex(@"(\d+?)d", RegexOptions.IgnoreCase).Match(source);
if (m.Success)
minutes = Convert.ToInt32(m.Groups[1].ToString());
if (h.Success)
hours = Convert.ToInt32(h.Groups[1].ToString());
if (d.Success)
days = Convert.ToInt32(d.Groups[1].ToString());
source = source.Replace(minutes + "m", string.Empty);
source = source.Replace(hours + "h", string.Empty);
source = source.Replace(days + "d", string.Empty);
if (!string.IsNullOrEmpty(source) || (!m.Success && !h.Success && !d.Success))
{
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<ulong>(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<IPlayer> foundPlayers = new List<IPlayer>();
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<T>(object s)
{
try
{
var parsed = (T)Convert.ChangeType(s, typeof(T));
return true;
}
catch
{
return false;
}
}
#endregion
#region Data Helper
private static void LoadData<T>(ref T data, string filename = null) =>
data = Core.Interface.Oxide.DataFileSystem.ReadObject<T>(filename ?? nameof(TimedPermissions));
private static void SaveData<T>(T data, string filename = null) =>
Core.Interface.Oxide.DataFileSystem.WriteObject(filename ?? nameof(TimedPermissions), data);
#endregion
#region Message Wrapper
public static string GetMessage(string key, string id) => _instance.lang.GetMessage(key, _instance, id);
#endregion
#endregion
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
{"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", $"Info about <color=#C4FF00>{{player}}</color>:{Environment.NewLine}<color=#C4FF00>Groups</color>: {{groups}}{Environment.NewLine}<color=#C4FF00>Permissions</color>: {{permissions}}"},
{"User Doesn't Have Permission", "{target} does not have permission '{permission}'."},
{"User Isn't In Group", "{target} isn't in group '{group}'."},
}, this);
}
#endregion
#region Configuration
protected override void LoadConfig()
{
base.LoadConfig();
_config = Config.ReadObject<Configuration>();
SaveConfig();
}
protected override void LoadDefaultConfig() => _config = new Configuration();
protected override void SaveConfig() => Config.WriteObject(_config);
private class Configuration
{
[JsonProperty("Wipe Data on New Save (Limited to Certain Games)")]
public bool WipeDataOnNewSave { get; private set; } = false;
}
#endregion
#region Data Structures
// TODO: do general refactoring & improvements
private class Player
{
public readonly List<TimedAccessValue> Permissions = new List<TimedAccessValue>();
public readonly List<TimedAccessValue> Groups = new List<TimedAccessValue>();
public string Name = "unknown";
public string Id = "0";
internal static Player Get(string steamId) => _players.Find(p => p.Id == steamId);
internal static Player GetOrCreate(IPlayer player)
{
Player pl = Get(player.Id);
if (pl == null)
{
pl = new Player(player);
_players.Add(pl);
SaveData(_players);
}
return pl;
}
public TimedAccessValue GetTimedPermission(string permission) => Permissions.Find(p => p.Value == permission);
public TimedAccessValue GetTimedGroup(string group) => Groups.Find(g => g.Value == group);
public void AddPermission(string permission, DateTime expireDate)
{
TimedAccessValue existingPermission = GetTimedPermission(permission);
if (existingPermission != null)
{
existingPermission.ExpireDate += expireDate - DateTime.UtcNow;
_instance.Puts($"----> {Name} ({Id}) - Permission Extended: {permission} to {existingPermission.ExpireDate - DateTime.UtcNow}" + Environment.NewLine);
}
else
{
Permissions.Add(new TimedAccessValue(permission, expireDate));
_instance.permission.GrantUserPermission(Id, permission, null);
_instance.Puts($"----> {Name} ({Id}) - Permission Granted: {permission} for {expireDate - DateTime.UtcNow}" + Environment.NewLine);
}
SaveData(_players);
}
internal void AddGroup(string group, DateTime expireDate)
{
TimedAccessValue existingGroup = GetTimedGroup(group);
if (existingGroup != null)
{
existingGroup.ExpireDate += expireDate - DateTime.UtcNow;
_instance.Puts($"----> {Name} ({Id}) - Group Time Extended: {group} to {existingGroup.ExpireDate - DateTime.UtcNow}" + Environment.NewLine);
}
else
{
Groups.Add(new TimedAccessValue(group, expireDate));
_instance.permission.AddUserGroup(Id, group);
_instance.Puts($"----> {Name} ({Id}) - Added to Group: {group} for {expireDate - DateTime.UtcNow}" + Environment.NewLine);
}
SaveData(_players);
}
internal void RemovePermission(string permission)
{
Permissions.Remove(GetTimedPermission(permission));
_instance.permission.RevokeUserPermission(Id, permission);
_instance.Puts($"----> {Name} ({Id}) - Permission Expired: {permission}" + Environment.NewLine);
if (Groups.Count == 0 && Permissions.Count == 0)
_players.Remove(this);
SaveData(_players);
}
internal void RemoveGroup(string group)
{
Groups.Remove(GetTimedGroup(group));
_instance.permission.RemoveUserGroup(Id, group);
_instance.Puts($"----> {Name} ({Id}) - Group Expired: {group}" + Environment.NewLine);
if (Groups.Count == 0 && Permissions.Count == 0)
_players.Remove(this);
SaveData(_players);
}
public void RemoveAllAccess()
{
foreach (TimedAccessValue group in Groups)
_instance.permission.RemoveUserGroup(Id, group.Value);
Groups.Clear();
foreach (TimedAccessValue permission in Permissions)
_instance.permission.RevokeUserPermission(Id, permission.Value);
Permissions.Clear();
}
public void EnsureAllAccess()
{
foreach (TimedAccessValue group in Groups)
_instance.permission.AddUserGroup(Id, group.Value);
foreach (TimedAccessValue permission in Permissions)
_instance.permission.GrantUserPermission(Id, permission.Value, null);
}
private void Update()
{
foreach (TimedAccessValue perm in Permissions.ToList())
if (perm.Expired)
RemovePermission(perm.Value);
foreach (TimedAccessValue group in Groups.ToList())
if (group.Expired)
RemoveGroup(group.Value);
}
public override int GetHashCode() => Id.GetHashCode();
private Player(IPlayer player)
{
Id = player.Id;
Name = player.Name;
_instance.timer.Repeat(60, 0, Update);
}
public Player()
{
_instance.timer.Repeat(60, 0, Update);
}
}
// TODO: do general refactoring & improvements
private class TimedAccessValue
{
public string Value = string.Empty;
public DateTime ExpireDate;
internal bool Expired => DateTime.Compare(DateTime.UtcNow, ExpireDate) > 0;
public override int GetHashCode() => Value.GetHashCode();
internal TimedAccessValue(string value, DateTime expireDate)
{
Value = value;
ExpireDate = expireDate;
}
public TimedAccessValue()
{
}
}
#endregion
}
}