mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-06 13:26:25 +08:00
208 lines
7.9 KiB
C#
208 lines
7.9 KiB
C#
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<VipMember> _members = new List<VipMember>();
|
|
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<List<VipMember>>(json) ?? new List<VipMember>();
|
|
}
|
|
catch
|
|
{
|
|
_members = new List<VipMember>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Save()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
var json = JsonSerializer.Serialize(_members, new JsonSerializerOptions { WriteIndented = true });
|
|
File.WriteAllText(FilePath, json);
|
|
}
|
|
}
|
|
|
|
public static List<VipMember> 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<string> lines = new List<string>();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|