mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-04 20:46:33 +08:00
243 lines
8.5 KiB
C#
243 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using RainOpsMini.Helpers;
|
|
using RainOpsMini.Models;
|
|
using SqlSugar;
|
|
|
|
namespace RainOpsMini.Services
|
|
{
|
|
public class BanService
|
|
{
|
|
private readonly string _bansCfgPath;
|
|
|
|
public BanService()
|
|
{
|
|
_bansCfgPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SquadGame", "ServerConfig", "Bans.cfg");
|
|
}
|
|
|
|
public async Task<int> BanPlayerAsync(string steamId, string reason, string durationStr, string operatorName)
|
|
{
|
|
long durationSeconds = 0;
|
|
long expiryTimestamp = 0; // 0 means permanent
|
|
|
|
// Parse Duration
|
|
string dur = durationStr?.ToLower() ?? "perm";
|
|
DateTime now = DateTime.UtcNow;
|
|
|
|
if (dur == "perm" || dur == "0")
|
|
{
|
|
expiryTimestamp = 0;
|
|
}
|
|
else
|
|
{
|
|
// Try parsing seconds directly first
|
|
if (long.TryParse(dur, out long seconds))
|
|
{
|
|
durationSeconds = seconds;
|
|
}
|
|
else if (dur.EndsWith("d") && int.TryParse(dur.Replace("d", ""), out int days))
|
|
{
|
|
durationSeconds = days * 86400;
|
|
}
|
|
else if (dur.EndsWith("w") && int.TryParse(dur.Replace("w", ""), out int weeks))
|
|
{
|
|
durationSeconds = weeks * 7 * 86400;
|
|
}
|
|
else if (dur.EndsWith("m") && int.TryParse(dur.Replace("m", ""), out int months))
|
|
{
|
|
durationSeconds = months * 30 * 86400; // Approx
|
|
}
|
|
else if (dur.EndsWith("y") && int.TryParse(dur.Replace("y", ""), out int years))
|
|
{
|
|
durationSeconds = years * 365 * 86400; // Approx
|
|
}
|
|
else if (dur.EndsWith("h") && int.TryParse(dur.Replace("h", ""), out int hours))
|
|
{
|
|
durationSeconds = hours * 3600;
|
|
}
|
|
|
|
if (durationSeconds > 0)
|
|
{
|
|
expiryTimestamp = ((DateTimeOffset)now).ToUnixTimeSeconds() + durationSeconds;
|
|
}
|
|
}
|
|
|
|
var banRecord = new BanRecord
|
|
{
|
|
SteamId = steamId,
|
|
Reason = reason,
|
|
DurationString = durationStr,
|
|
Operator = operatorName,
|
|
BanTime = now,
|
|
BanTimestamp = ((DateTimeOffset)now).ToUnixTimeSeconds(),
|
|
ExpiryTimestamp = expiryTimestamp,
|
|
Status = 0 // Active
|
|
};
|
|
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var result = await db.Insertable(banRecord).ExecuteCommandAsync();
|
|
|
|
// Sync to file and reload config
|
|
await SyncToConfigFileAsync();
|
|
await ReloadServerConfigAsync();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public async Task<bool> UnbanAsync(string steamId, string operatorName)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var record = await db.Queryable<BanRecord>()
|
|
.Where(x => x.SteamId == steamId && x.Status == 0)
|
|
.OrderByDescending(x => x.Id)
|
|
.FirstAsync();
|
|
|
|
if (record == null) return false;
|
|
|
|
record.Status = 2; // Unbanned manually
|
|
record.UnbanTime = DateTime.UtcNow;
|
|
record.UnbanOperator = operatorName;
|
|
|
|
await db.Updateable(record).ExecuteCommandAsync();
|
|
|
|
// Sync to file and reload config
|
|
await SyncToConfigFileAsync();
|
|
await ReloadServerConfigAsync();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private async Task ReloadServerConfigAsync()
|
|
{
|
|
if (Program.Client != null && Program.Client.IsConnected)
|
|
{
|
|
try
|
|
{
|
|
await Program.Client.SendCommandAsync("AdminReloadServerConfig");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RainOpsLog.Log($"[BanService] Failed to reload server config: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task SyncToConfigFileAsync()
|
|
{
|
|
// Ensure directory exists
|
|
var directory = Path.GetDirectoryName(_bansCfgPath);
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
if (!File.Exists(_bansCfgPath))
|
|
{
|
|
File.WriteAllText(_bansCfgPath, "");
|
|
}
|
|
|
|
// Get active bans from DB
|
|
List<BanRecord> activeBans;
|
|
long nowTs = ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeSeconds();
|
|
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
// Update expired bans status first
|
|
// Status 0 (Active) but ExpiryTimestamp > 0 and < Now
|
|
await db.Updateable<BanRecord>()
|
|
.SetColumns(it => new BanRecord { Status = 1 }) // Expired
|
|
.Where(it => it.Status == 0 && it.ExpiryTimestamp > 0 && it.ExpiryTimestamp < nowTs)
|
|
.ExecuteCommandAsync();
|
|
|
|
// Get Active Bans
|
|
activeBans = await db.Queryable<BanRecord>()
|
|
.Where(it => it.Status == 0)
|
|
.ToListAsync();
|
|
}
|
|
|
|
// Format for Bans.cfg: SteamID:Timestamp // [Op: Admin] [Time: 2023-01-01 12:00:00] Reason
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("//RainOps Mini AutoBan Info START");
|
|
foreach (var ban in activeBans)
|
|
{
|
|
// If timestamp is 0 (perm), keep it 0.
|
|
string banTimeStr = ban.BanTime.ToString("yyyy-MM-dd HH:mm:ss");
|
|
string opStr = string.IsNullOrWhiteSpace(ban.Operator) ? "System" : ban.Operator;
|
|
|
|
// Construct the comment part
|
|
// Requirement: 注释操作人和操作时间以及封禁理由
|
|
// Example: // [Op: Admin] [Time: 2023-01-01 12:00:00] Cheating
|
|
string comment = $"[Op: {opStr}] [Time: {banTimeStr}] {ban.Reason}";
|
|
|
|
sb.AppendLine($"{ban.SteamId}:{ban.ExpiryTimestamp} // {comment}");
|
|
}
|
|
sb.AppendLine("//RainOps Mini AutoBan Info END");
|
|
|
|
string newBlock = sb.ToString();
|
|
|
|
// Read existing file
|
|
string[] lines = await File.ReadAllLinesAsync(_bansCfgPath);
|
|
var newFileContent = new List<string>();
|
|
bool inBlock = false;
|
|
bool blockFound = false;
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
if (line.Trim() == "//RainOps Mini AutoBan Info START")
|
|
{
|
|
inBlock = true;
|
|
blockFound = true;
|
|
// We will insert our new block here
|
|
newFileContent.Add(newBlock.TrimEnd());
|
|
continue;
|
|
}
|
|
|
|
if (line.Trim() == "//RainOps Mini AutoBan Info END")
|
|
{
|
|
inBlock = false;
|
|
continue;
|
|
}
|
|
|
|
if (!inBlock)
|
|
{
|
|
newFileContent.Add(line);
|
|
}
|
|
}
|
|
|
|
if (!blockFound)
|
|
{
|
|
// If block not found, insert at the beginning (as requested: "第一行显示")
|
|
newFileContent.Insert(0, newBlock.TrimEnd());
|
|
}
|
|
|
|
await File.WriteAllLinesAsync(_bansCfgPath, newFileContent);
|
|
}
|
|
|
|
public async Task<object> GetBansAsync(int page, int pageSize, string search)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var query = db.Queryable<BanRecord>();
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
query = query.Where(x => x.SteamId.Contains(search) || x.Reason.Contains(search));
|
|
}
|
|
|
|
RefAsync<int> total = 0;
|
|
var list = await query.OrderByDescending(x => x.BanTime).ToPageListAsync(page, pageSize, total);
|
|
|
|
return new { total = total.Value, list };
|
|
}
|
|
}
|
|
}
|
|
}
|