using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using RainOpsMini.Models; namespace RainOpsMini.Helpers { public class VipStorage { private static readonly string FilePath = "vip_members.json"; private static List _members = new List(); private static object _lock = new object(); static VipStorage() { Load(); } private static void Load() { lock (_lock) { if (File.Exists(FilePath)) { try { var json = File.ReadAllText(FilePath); _members = JsonSerializer.Deserialize>(json) ?? new List(); } catch { _members = new List(); } } } } public static void Save() { lock (_lock) { var json = JsonSerializer.Serialize(_members, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(FilePath, json); } } public static List GetAll() { lock (_lock) { // Refresh load in case file changed externally? // For now, assume single process ownership. return _members.OrderByDescending(m => m.CreatedAt).ToList(); } } public static void Add(VipMember member) { lock (_lock) { _members.Add(member); Save(); } } public static void Remove(string steamId) { lock (_lock) { _members.RemoveAll(m => m.SteamId == steamId); Save(); } } public static void Update(VipMember member) { lock (_lock) { var existing = _members.FirstOrDefault(m => m.SteamId == member.SteamId); if (existing != null) { existing.Name = member.Name; existing.Group = member.Group; existing.ExpiryDate = member.ExpiryDate; existing.Remark = member.Remark; Save(); } } } public static VipMember? GetBySteamId(string steamId) { lock (_lock) { return _members.FirstOrDefault(m => m.SteamId == steamId); } } public static void SyncToAdminConfig(string adminConfigPath) { lock (_lock) { List lines = new List(); if (File.Exists(adminConfigPath)) { lines = File.ReadAllLines(adminConfigPath).ToList(); } // 1. Remove existing definitions for our managed groups // We do this to ensure we enforce the permissions requested by the user. lines.RemoveAll(l => l.Trim().StartsWith("Group=VIP_RainOpsMini:")); lines.RemoveAll(l => l.Trim().StartsWith("Group=SVIP_RainOpsMini:")); // 2. Remove existing Admin lines for these groups // We match strict pattern to avoid false positives in comments // Pattern: Admin=...:VIP_RainOpsMini... lines.RemoveAll(l => { var trim = l.Trim(); if (!trim.StartsWith("Admin=")) return false; // Check if the group part matches // Format: Admin=SteamID:GroupId [// comment] // We can check if it contains ":VIP_RainOpsMini" or ":SVIP_RainOpsMini" before any comment start "//" // Simple check: split by // first var content = trim.Split("//")[0]; return content.Contains(":VIP_RainOpsMini") || content.Contains(":SVIP_RainOpsMini"); }); // 3. Add Group Definitions // Find a good place to insert. Usually top or after other groups. // We'll insert at the beginning if no groups, or after the last group. int lastGroupIndex = lines.FindLastIndex(l => l.Trim().StartsWith("Group=")); int insertIndex = lastGroupIndex != -1 ? lastGroupIndex + 1 : 0; // Insert in reverse order so they appear in order lines.Insert(insertIndex, "Group=SVIP_RainOpsMini:reserve,teamchange"); lines.Insert(insertIndex, "Group=VIP_RainOpsMini:reserve"); // 4. Add Valid VIPs (Refresh status first) bool needSave = false; // Use a copy for iteration or index to avoid modification issues if we were removing (we are not removing here, just updating) foreach (var m in _members) { // If managed expiries are present, re-evaluate status if (m.VipExpiryDate.HasValue || m.SvipExpiryDate.HasValue) { bool isSvipActive = m.SvipExpiryDate.HasValue && m.SvipExpiryDate.Value > DateTime.Now; bool isVipActive = m.VipExpiryDate.HasValue && m.VipExpiryDate.Value > DateTime.Now; string newGroup = m.Group; DateTime newExpiry = m.ExpiryDate; if (isSvipActive) { newGroup = "SVIP_RainOpsMini"; newExpiry = m.SvipExpiryDate.Value; } else if (isVipActive) { newGroup = "VIP_RainOpsMini"; newExpiry = m.VipExpiryDate.Value; } else { // Both expired, but we don't change group/expiry to "Expired" state explicitly, // we just let ExpiryDate be the last one? // Or we set ExpiryDate to the latest one that expired? // For consistency, let's set it to the latest expiry date. DateTime svipExp = m.SvipExpiryDate ?? DateTime.MinValue; DateTime vipExp = m.VipExpiryDate ?? DateTime.MinValue; newExpiry = svipExp > vipExp ? svipExp : vipExp; // Keep last group or downgrade? // If SVIP expired last, keep SVIP group but with expired date. // If VIP expired last, keep VIP group. newGroup = svipExp > vipExp ? "SVIP_RainOpsMini" : "VIP_RainOpsMini"; } if (m.Group != newGroup || m.ExpiryDate != newExpiry) { m.Group = newGroup; m.ExpiryDate = newExpiry; needSave = true; } } } if (needSave) { Save(); } var validMembers = _members.Where(m => m.ExpiryDate > DateTime.Now).ToList(); foreach (var m in validMembers) { // Admin=SteamID:Group // Name - Remark string line = $"Admin={m.SteamId}:{m.Group} // {m.Name} - {m.Remark} (Expires: {m.ExpiryDate:yyyy-MM-dd})"; lines.Add(line); } File.WriteAllLines(adminConfigPath, lines); } } } }