mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-08 14:25:33 +08:00
319 lines
10 KiB
C#
319 lines
10 KiB
C#
using Microsoft.Extensions.Hosting;
|
||
using RainOpsMini.Helpers;
|
||
using RainOpsMini.Helpers.RCON;
|
||
using RainOpsMini.Services;
|
||
using RainOpsMini;
|
||
using RainOpsMini.Models;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Text.Json;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace RainOpsMini.Plugins
|
||
{
|
||
public class LotteryConfig
|
||
{
|
||
/// <summary>
|
||
/// 是否启用插件
|
||
/// </summary>
|
||
public bool Enabled { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 抽奖触发关键字
|
||
/// </summary>
|
||
public List<string> Keywords { get; set; } = new List<string> { "抽奖", "cj" };
|
||
|
||
/// <summary>
|
||
/// 积分模式(Fixed=固定值,Random=随机值)
|
||
/// </summary>
|
||
public string PointsMode { get; set; } = "Random"; // "Fixed" or "Random"
|
||
|
||
/// <summary>
|
||
/// 固定积分数值
|
||
/// </summary>
|
||
public int FixedPoints { get; set; } = 100;
|
||
|
||
/// <summary>
|
||
/// 随机积分范围(格式:最小值,最大值)
|
||
/// </summary>
|
||
public string RandomRange { get; set; } = "10,500"; // "Min,Max"
|
||
|
||
/// <summary>
|
||
/// 抽奖冷却时间(分钟)
|
||
/// </summary>
|
||
public int CooldownMinutes { get; set; } = 60;
|
||
|
||
/// <summary>
|
||
/// 冷却模式(Min=按分钟,Day=每日一次)
|
||
/// </summary>
|
||
public string CooldownType { get; set; } = "Min"; // "Min" or "Day"
|
||
|
||
/// <summary>
|
||
/// 通知模式(Warn=警告弹窗,Broadcast=全服广播)
|
||
/// </summary>
|
||
public string NotificationMode { get; set; } = "Warn"; // "Broadcast" or "Warn"
|
||
}
|
||
|
||
public class PlayerLotteryData
|
||
{
|
||
public string SteamId { get; set; } = "";
|
||
public DateTime LastDrawTime { get; set; } = DateTime.MinValue;
|
||
}
|
||
|
||
public class LotteryPlugin : IPlugin, IDisposable
|
||
{
|
||
public string Name => "抽奖插件";
|
||
public string Description => "玩家积分抽奖 (固定/随机模式)";
|
||
public PluginCategory Category => PluginCategory.PointsFunction;
|
||
public bool IsEnabled => _config.Enabled;
|
||
|
||
private LotteryConfig _config = new LotteryConfig();
|
||
private ConcurrentDictionary<string, PlayerLotteryData> _data = new ConcurrentDictionary<string, PlayerLotteryData>();
|
||
private readonly string _dataPath;
|
||
|
||
/// <summary>
|
||
/// 构造函数,初始化数据路径并加载数据
|
||
/// </summary>
|
||
public LotteryPlugin()
|
||
{
|
||
_dataPath = Path.Combine(Environment.CurrentDirectory, "PluginConfig", "LotteryData.json");
|
||
LoadData();
|
||
Program.OnRconMessageReceived += OnRconMessage;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前配置
|
||
/// </summary>
|
||
/// <returns>配置对象</returns>
|
||
public object GetConfig() => _config;
|
||
|
||
/// <summary>
|
||
/// 更新配置
|
||
/// </summary>
|
||
/// <param name="config">JSON配置元素</param>
|
||
public void UpdateConfig(JsonElement config)
|
||
{
|
||
try
|
||
{
|
||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||
_config = JsonSerializer.Deserialize<LotteryConfig>(config.GetRawText(), options) ?? new LotteryConfig();
|
||
// SaveData(); // Not needed
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
RainOpsLog.Log($"[{Name}] UpdateConfig Error: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置配置为默认值
|
||
/// </summary>
|
||
public void ResetConfig()
|
||
{
|
||
_config = new LotteryConfig();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源,取消订阅
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Program.OnRconMessageReceived -= OnRconMessage;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理RCON消息回调
|
||
/// </summary>
|
||
/// <param name="msg">消息内容</param>
|
||
private async void OnRconMessage(string msg)
|
||
{
|
||
if (!_config.Enabled) return;
|
||
|
||
try
|
||
{
|
||
var chatMsg = RconParser.ParseChatMessage(msg);
|
||
if (chatMsg == null) return;
|
||
|
||
if (IsKeywordMatch(chatMsg.Message))
|
||
{
|
||
await HandleDraw(chatMsg);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
RainOpsLog.Log($"[{Name}] OnRconMessage Error: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查消息是否匹配关键字
|
||
/// </summary>
|
||
/// <param name="message">消息内容</param>
|
||
/// <returns>是否匹配</returns>
|
||
private bool IsKeywordMatch(string message)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(message)) return false;
|
||
foreach (var key in _config.Keywords)
|
||
{
|
||
if (string.Equals(message, key, StringComparison.OrdinalIgnoreCase)) return true;
|
||
|
||
string keyPinyin = PinyinHelper.GetInitials(key);
|
||
if (string.Equals(message, keyPinyin, StringComparison.OrdinalIgnoreCase)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理抽奖逻辑
|
||
/// </summary>
|
||
/// <param name="chatMsg">聊天消息对象</param>
|
||
private async Task HandleDraw(ChatMessage chatMsg)
|
||
{
|
||
var data = GetOrAddData(chatMsg.SteamId);
|
||
|
||
// 检查冷却时间
|
||
bool canDraw = false;
|
||
if (_config.CooldownType.Equals("Day", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
if (data.LastDrawTime.Date < DateTime.Now.Date)
|
||
{
|
||
canDraw = true;
|
||
}
|
||
}
|
||
else // Min
|
||
{
|
||
if ((DateTime.Now - data.LastDrawTime).TotalMinutes >= _config.CooldownMinutes)
|
||
{
|
||
canDraw = true;
|
||
}
|
||
}
|
||
|
||
if (!canDraw)
|
||
{
|
||
string waitMsg = _config.CooldownType.Equals("Day", StringComparison.OrdinalIgnoreCase)
|
||
? "今天已抽过奖,请明天再来!"
|
||
: $"抽奖冷却中,请等待 {(_config.CooldownMinutes - (DateTime.Now - data.LastDrawTime).TotalMinutes):F1} 分钟。";
|
||
|
||
// 对于冷却警告,通常只警告用户,不广播失败
|
||
await Program.Client.SendCommandAsync($"AdminWarn \"{chatMsg.SteamId}\" \"{waitMsg}\"");
|
||
return;
|
||
}
|
||
|
||
// 计算积分
|
||
int points = 0;
|
||
if (_config.PointsMode.Equals("Fixed", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
points = _config.FixedPoints;
|
||
}
|
||
else
|
||
{
|
||
points = GetRandomPoints(_config.RandomRange);
|
||
}
|
||
|
||
// 应用积分变更
|
||
int newTotal = await PointsService.AdjustPointsAsync(chatMsg.SteamId, points);
|
||
|
||
// 更新冷却时间
|
||
data.LastDrawTime = DateTime.Now;
|
||
SaveData();
|
||
|
||
// 发送通知
|
||
string successMsg = $"恭喜!抽奖获得 {points} 积分!当前总积分: {newTotal}";
|
||
|
||
if (_config.NotificationMode.Equals("Broadcast", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
await Program.Client.SendCommandAsync($"AdminBroadcast \"玩家 {chatMsg.Name} {successMsg}\"");
|
||
}
|
||
else
|
||
{
|
||
await Program.Client.SendCommandAsync($"AdminWarn \"{chatMsg.SteamId}\" \"{successMsg}\"");
|
||
}
|
||
|
||
RainOpsLog.Log($"[{Name}] Player {chatMsg.Name} ({chatMsg.SteamId}) drew lottery. +{points}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取随机积分
|
||
/// </summary>
|
||
/// <param name="valConfig">积分范围配置字符串</param>
|
||
/// <returns>随机积分值</returns>
|
||
private int GetRandomPoints(string valConfig)
|
||
{
|
||
try
|
||
{
|
||
var parts = valConfig.Split(',');
|
||
if (parts.Length >= 2)
|
||
{
|
||
int min = int.Parse(parts[0]);
|
||
int max = int.Parse(parts[1]);
|
||
if (min > max) (min, max) = (max, min);
|
||
return new Random().Next(min, max + 1);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
RainOpsLog.Log($"[{Name}] Invalid RandomRange config: {valConfig}");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取或添加玩家抽奖数据
|
||
/// </summary>
|
||
/// <param name="steamId">玩家SteamID</param>
|
||
/// <returns>抽奖数据对象</returns>
|
||
private PlayerLotteryData GetOrAddData(string steamId)
|
||
{
|
||
return _data.GetOrAdd(steamId, id => new PlayerLotteryData { SteamId = id });
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从文件加载数据
|
||
/// </summary>
|
||
private void LoadData()
|
||
{
|
||
if (File.Exists(_dataPath))
|
||
{
|
||
try
|
||
{
|
||
string json = File.ReadAllText(_dataPath);
|
||
var list = JsonSerializer.Deserialize<List<PlayerLotteryData>>(json);
|
||
if (list != null)
|
||
{
|
||
foreach (var item in list)
|
||
{
|
||
_data[item.SteamId] = item;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
RainOpsLog.Log($"[{Name}] LoadData Error: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存数据到文件
|
||
/// </summary>
|
||
private void SaveData()
|
||
{
|
||
try
|
||
{
|
||
string dir = Path.GetDirectoryName(_dataPath);
|
||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||
|
||
string json = JsonSerializer.Serialize(_data.Values, new JsonSerializerOptions { WriteIndented = true });
|
||
File.WriteAllText(_dataPath, json);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
RainOpsLog.Log($"[{Name}] SaveData Error: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|