From fe4a38a68f4db47a3ad052217b7332df57d9134d Mon Sep 17 00:00:00 2001 From: milimoe Date: Mon, 27 Apr 2026 21:13:58 +0800 Subject: [PATCH] update api --- MilimoeWebAPI/Controllers/APIController.cs | 4 +- MilimoeWebAPI/MilimoeMap.cs | 35 +++ MilimoeWebAPI/MilimoeServer.cs | 176 +++++++++++++ MilimoeWebAPI/MilimoeWebAPI.cs | 9 +- MilimoeWebAPI/MilimoeWebAPI.csproj | 4 - MilimoeWebAPI/Services/WebSocketService.cs | 277 +++++++++++++++++++++ 6 files changed, 493 insertions(+), 12 deletions(-) create mode 100644 MilimoeWebAPI/MilimoeMap.cs create mode 100644 MilimoeWebAPI/MilimoeServer.cs create mode 100644 MilimoeWebAPI/Services/WebSocketService.cs diff --git a/MilimoeWebAPI/Controllers/APIController.cs b/MilimoeWebAPI/Controllers/APIController.cs index 9069649..0fc34c2 100644 --- a/MilimoeWebAPI/Controllers/APIController.cs +++ b/MilimoeWebAPI/Controllers/APIController.cs @@ -134,12 +134,12 @@ namespace Milimoe.FunGame.WebAPI.Controllers } if (!reverse && userHas) { - result += $"你在的圣人点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")}(累计有效:{userTimes} 次),排在 {season}第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + result += $"你的圣人点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")}(累计有效:{userTimes} 次),排在 {season}第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; } if (reverse && userHas) { - result += $"你在的出生点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")}(累计有效:{userTimes} 次),排在 {season}的出生榜第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + result += $"你的出生点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")}(累计有效:{userTimes} 次),排在 {season}的出生榜第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; } } diff --git a/MilimoeWebAPI/MilimoeMap.cs b/MilimoeWebAPI/MilimoeMap.cs new file mode 100644 index 0000000..d81f885 --- /dev/null +++ b/MilimoeWebAPI/MilimoeMap.cs @@ -0,0 +1,35 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Addon; +using Milimoe.FunGame.WebAPI.Constant; + +namespace Milimoe.FunGame.WebAPI +{ + public class MilimoeMap + { + public class AnonymousMap : GameMap + { + public override string Name => Constants.Map; + + public override string Description => Constants.Description; + + public override string Version => Constants.Version; + + public override string Author => Constants.Author; + + public override int Length => 6; + + public override int Width => 6; + + public override int Height => 3; + + public override float Size => 6; + + public override GameMap InitGamingQueue(IGamingQueue queue) + { + GameMap map = new AnonymousMap(); + map.Load(); + return map; + } + } + } +} diff --git a/MilimoeWebAPI/MilimoeServer.cs b/MilimoeWebAPI/MilimoeServer.cs new file mode 100644 index 0000000..2ac68a1 --- /dev/null +++ b/MilimoeWebAPI/MilimoeServer.cs @@ -0,0 +1,176 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Addon; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.WebAPI.Constant; +using Milimoe.FunGame.WebAPI.Service; + +namespace Milimoe.FunGame.WebAPI +{ + public class MilimoeServer : GameModuleServer + { + public override string Name => Constants.Anonymous; + + public override string Description => Constants.Description; + + public override string Version => Constants.Version; + + public override string Author => Constants.Author; + + public override string DefaultMap => Constants.Map; + + public override GameModuleDepend GameModuleDepend => new([], [], [], []); + + public override bool IsAnonymous => true; + + public static HashSet Instances { get; } = []; + + public WebSocketService Service { get; } + + public MilimoeServer() + { + Service = new(this); + } + + /// + /// 向客户端推送事件 + /// + /// + public static async Task PushMessageToClients(string openid, string msg) + { + MilimoeServer[] servers = [.. Instances]; + foreach (MilimoeServer anonymous in servers) + { + try + { + await anonymous.PushMessage(openid, msg); + } + catch (Exception e) + { + anonymous.Controller.Error(e); + } + } + } + + protected HashSet _clientModels = []; + + /// + /// 启动匿名服务器 + /// + /// + /// + public override bool StartAnonymousServer(IServerModel model, Dictionary data) + { + // 可以做验证处理 + string access_token = Controller.JSON.GetObject(data, "access_token") ?? ""; + if (Configs.TokenList.Contains(access_token)) + { + // 添加当前单例 + Instances.Add(this); + Controller.WriteLine($"{model.GetClientName()} 连接至匿名服务器", LogLevel.Info); + // 接收连接匿名服务器的客户端 + _clientModels.Add(model); + return true; + } + else + { + Controller.WriteLine($"{model.GetClientName()} 连接匿名服务器失败,访问令牌不匹配", LogLevel.Warning); + } + return false; + } + + /// + /// 关闭匿名服务器 + /// + /// + public override void CloseAnonymousServer(IServerModel model) + { + // 移除当前单例 + Instances.Remove(this); + // 移除客户端 + _clientModels.Remove(model); + Controller.WriteLine($"{model.GetClientName()} 从匿名服务器断开", LogLevel.Info); + } + + public override void AfterLoad(GameModuleLoader loader, params object[] args) + { + Controller.NewSQLHelper(); + Controller.NewMailSender(); + } + + /// + /// 向客户端推送事件 + /// + /// + public async Task PushMessage(string openid, string msg) + { + Dictionary data = []; + data.Add(nameof(openid), openid); + data.Add(nameof(msg), msg); + Controller.WriteLine("向客户端推送事件", LogLevel.Debug); + List failedModels = await SendAnonymousGameServerMessage(_clientModels, data); + failedModels.ForEach(model => _clientModels.Remove(model)); + } + + /// + /// 接收并处理匿名服务器消息 + /// + /// + /// + /// + public override async Task> AnonymousGameServerHandler(IServerModel model, Dictionary data) + { + Dictionary result = []; + Controller.WriteLine("接收匿名服务器消息", LogLevel.Debug); + + long groupid = Controller.JSON.GetObject(data, "groupid"); + if (groupid > 0) + { + result["groupid"] = groupid; + } + string msg = ""; + if (data.Count > 0) + { + // 根据服务器和客户端的数据传输约定,自行处理 data,并返回。 + string command = Controller.JSON.GetObject(data, "command") ?? ""; + switch (command.Trim().ToLower()) + { + case "scadd": + msg = Service.SCAdd(data); + break; + case "sclist": + msg = Service.SCList(data); + break; + case "sclist_backup": + msg = Service.SCList_Backup(data); + break; + case "screcord": + msg = Service.SCRecord(data); + break; + case "att": + break; + default: + msg = "匿名服务器已经收到消息了"; + break; + } + await Task.Delay(1); + } + if (msg.Trim() != "") + { + result["msg"] = msg; + } + + return result; + } + + public override Task> GamingMessageHandler(IServerModel model, GamingType type, Dictionary data) + { + throw new NotImplementedException(); + } + + public override bool StartServer(GamingObject obj, params object[] args) + { + throw new NotImplementedException(); + } + } +} diff --git a/MilimoeWebAPI/MilimoeWebAPI.cs b/MilimoeWebAPI/MilimoeWebAPI.cs index e0043bd..f25f9df 100644 --- a/MilimoeWebAPI/MilimoeWebAPI.cs +++ b/MilimoeWebAPI/MilimoeWebAPI.cs @@ -1,9 +1,6 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Milimoe.FunGame.WebAPI.Constant; -using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Common.Addon; +using Milimoe.FunGame.WebAPI.Constant; namespace Milimoe.FunGame.WebAPI { @@ -17,11 +14,11 @@ namespace Milimoe.FunGame.WebAPI public override string Author => Constants.Author; - public override void ProcessInput(string input) + public override void ProcessInput(string order, string[] args) { try { - if (input.Trim() != "") + if (order.Trim() != "") { Controller.WriteLine("Console Reply"); } diff --git a/MilimoeWebAPI/MilimoeWebAPI.csproj b/MilimoeWebAPI/MilimoeWebAPI.csproj index d24abb2..753574a 100644 --- a/MilimoeWebAPI/MilimoeWebAPI.csproj +++ b/MilimoeWebAPI/MilimoeWebAPI.csproj @@ -28,8 +28,4 @@ - - - - diff --git a/MilimoeWebAPI/Services/WebSocketService.cs b/MilimoeWebAPI/Services/WebSocketService.cs new file mode 100644 index 0000000..cca4bf8 --- /dev/null +++ b/MilimoeWebAPI/Services/WebSocketService.cs @@ -0,0 +1,277 @@ +using System.Data; +using Milimoe.FunGame.Core.Api.Transmittal; +using Milimoe.FunGame.Core.Controller; +using Milimoe.FunGame.Core.Interface.Addons; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.WebAPI.Service +{ + public class WebSocketService(MilimoeServer server) + { + public MilimoeServer ServerInstance { get; set; } = server; + public ServerAddonController Controller => ServerInstance.Controller; + + public string SCAdd(Dictionary data) + { + string result = ""; + + using SQLHelper? sql = Controller.GetSQLHelper(); + if (sql != null) + { + try + { + long qq = Controller.JSON.GetObject(data, "qq"); + long groupid = Controller.JSON.GetObject(data, "groupid"); + double sc = Controller.JSON.GetObject(data, "sc"); + sql.NewTransaction(); + sql.Script = "select * from saints where qq = @qq and `group` = @group"; + sql.Parameters.Add("qq", qq); + sql.Parameters.Add("group", groupid); + sql.ExecuteDataSet(); + string content = Controller.JSON.GetObject(data, "content") ?? ""; + string record = ""; + if (sql.Success) + { + record = Convert.ToString(sql.DataSet.Tables[0].Rows[0]["record"]) ?? ""; + } + record = $"{DateTime.Now:MM/dd HH:mm}:{content}({(sc < 0 ? "-" : "+") + Math.Abs(sc)})\r\n{record}"; + record = string.Join("\r\n", record.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).Take(10)); + if (sql.Success) + { + sql.Script = "update saints set sc = sc + @sc, record = @record where qq = @qq and `group` = @group"; + } + else + { + sql.Script = "insert into saints(qq, sc, `group`, record) values(@qq, @sc, @group, @record)"; + } + sql.Parameters.Add("sc", sc); + sql.Parameters.Add("qq", qq); + sql.Parameters.Add("group", groupid); + sql.Parameters.Add("record", record); + sql.Execute(); + if (sql.Success) + { + Controller.WriteLine($"用户 {qq} 的圣人点数增加了 {sc}", LogLevel.Debug); + sql.Commit(); + } + else + { + sql.Rollback(); + } + } + catch (Exception e) + { + result = e.ToString(); + sql.Rollback(); + } + } + else result = "无法调用此接口:SQL 服务不可用。"; + + return result; + } + + public string SCList(Dictionary data) + { + string result; + + SQLHelper? sql = Controller.SQLHelper; + if (sql != null) + { + long userQQ = Controller.JSON.GetObject(data, "qq"); + (bool userHas, double userSC, int userTop, string userRemark) = (false, 0, 0, ""); + long groupid = Controller.JSON.GetObject(data, "groupid"); + bool reverse = Controller.JSON.GetObject(data, "reverse"); + if (!reverse) + { + result = $"☆--- OSMTV 圣人排行榜 TOP10 ---☆\r\n统计时间:{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)}\r\n"; + } + else + { + result = $"☆--- OSMTV 出生排行榜 TOP10 ---☆\r\n统计时间:{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)}\r\n"; + } + sql.Script = "select * from saints where `group` = @group order by sc" + (!reverse ? " desc" : ""); + sql.Parameters.Add("group", groupid); + sql.ExecuteDataSet(); + if (sql.Success && sql.DataSet.Tables.Count > 0) + { + int count = 0; + foreach (DataRow dr in sql.DataSet.Tables[0].Rows) + { + count++; + long qq = Convert.ToInt64(dr["qq"]); + double sc = Convert.ToDouble(dr["sc"]); + string remark = Convert.ToString(dr["remark"]) ?? ""; + if (reverse) + { + sc = -sc; + remark = remark.Replace("+", "-"); + } + if (qq == userQQ) + { + userHas = true; + userSC = sc; + userTop = count; + userRemark = remark; + } + if (count > 10) continue; + if (!reverse) + { + result += $"{count}. 用户:{qq},圣人点数:{sc} 分{(remark.Trim() != "" ? $" ({remark})" : "")}\r\n"; + } + else + { + result += $"{count}. 用户:{qq},出生点数:{sc} 分{(remark.Trim() != "" ? $" ({remark})" : "")}\r\n"; + } + } + if (!reverse && userHas) + { + result += $"你的圣人点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")},排在第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; + } + if (reverse && userHas) + { + result += $"你的出生点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")},排在出生榜第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; + } + } + else + { + if (reverse) + { + result = "出生榜目前没有任何数据。"; + } + else + { + result = "圣人榜目前没有任何数据。"; + } + } + } + else result = "无法调用此接口:SQL 服务不可用。"; + + return result.Trim(); + } + + public string SCList_Backup(Dictionary data) + { + string result; + + SQLHelper? sql = Controller.SQLHelper; + if (sql != null) + { + long userQQ = Controller.JSON.GetObject(data, "qq"); + (bool userHas, double userSC, int userTop, string userRemark) = (false, 0, 0, ""); + long groupid = Controller.JSON.GetObject(data, "groupid"); + bool reverse = Controller.JSON.GetObject(data, "reverse"); + if (!reverse) + { + result = $"☆--- OSMTV 圣人排行榜 TOP10 ---☆\r\n该榜单为上赛季封榜记录\r\n"; + } + else + { + result = $"☆--- OSMTV 出生排行榜 TOP10 ---☆\r\n该榜单为上赛季封榜记录\r\n"; + } + sql.Script = "select * from saints_backup where `group` = @group order by sc" + (!reverse ? " desc" : ""); + sql.Parameters.Add("group", groupid); + sql.ExecuteDataSet(); + if (sql.Success && sql.DataSet.Tables.Count > 0) + { + int count = 0; + foreach (DataRow dr in sql.DataSet.Tables[0].Rows) + { + count++; + long qq = Convert.ToInt64(dr["qq"]); + double sc = Convert.ToDouble(dr["sc"]); + string remark = Convert.ToString(dr["remark"]) ?? ""; + if (reverse) + { + sc = -sc; + remark = remark.Replace("+", "-"); + } + if (qq == userQQ) + { + userHas = true; + userSC = sc; + userTop = count; + userRemark = remark; + } + if (count > 10) continue; + if (!reverse) + { + result += $"{count}. 用户:{qq},圣人点数:{sc} 分{(remark.Trim() != "" ? $" ({remark})" : "")}\r\n"; + } + else + { + result += $"{count}. 用户:{qq},出生点数:{sc} 分{(remark.Trim() != "" ? $" ({remark})" : "")}\r\n"; + } + } + if (!reverse && userHas) + { + result += $"你的上赛季圣人点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")},排在第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; + } + if (reverse && userHas) + { + result += $"你的上赛季出生点数为:{userSC} 分{(userRemark.Trim() != "" ? $"({userRemark})" : "")},排在出生榜第 {userTop} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + $"本排行榜仅供娱乐,不代表任何官方立场或真实情况。"; + } + } + else + { + if (reverse) + { + result = "出生榜目前没有任何备份数据。"; + } + else + { + result = "圣人榜目前没有任何备份数据。"; + } + } + } + else result = "无法调用此接口:SQL 服务不可用。"; + + return result.Trim(); + } + + public string SCRecord(Dictionary data) + { + string result = ""; + + SQLHelper? sql = Controller.SQLHelper; + if (sql != null) + { + long userQQ = Controller.JSON.GetObject(data, "qq"); + long groupid = Controller.JSON.GetObject(data, "groupid"); + result = $"☆--- 圣人点数信息 ---☆\r\n统计时间:{DateTime.Now.ToString(General.GeneralDateTimeFormatChinese)}\r\n"; + sql.Script = "select * from saints where `group` = @group order by sc desc"; + sql.Parameters.Add("group", groupid); + sql.Parameters.Add("qq", userQQ); + sql.ExecuteDataSet(); + if (sql.Success && sql.DataSet.Tables.Count > 0) + { + Dictionary dict = sql.DataSet.Tables[0].AsEnumerable().Select((r, i) => new { Index = i + 1, Row = r }).ToDictionary(c => c.Index, c => c.Row); + int index = dict.Where(kv => Convert.ToInt64(kv.Value["qq"]) == userQQ).Select(r => r.Key).FirstOrDefault(); + if (index != 0 && dict.TryGetValue(index, out DataRow? dr) && dr != null) + { + long qq = Convert.ToInt64(dr["qq"]); + double sc = Convert.ToDouble(dr["sc"]); + string remark = Convert.ToString(dr["remark"]) ?? ""; + string record = Convert.ToString(dr["record"]) ?? ""; + result += $"用户:{qq},圣人点数:{sc} 分{(remark.Trim() != "" ? $" ({remark})" : "")},排在圣人榜第 {index} / {sql.DataSet.Tables[0].Rows.Count} 名。\r\n" + + $"{(record != "" ? "显示近期点数变动信息:\r\n" + record + "\r\n" : "")}本系统仅供娱乐,不代表任何官方立场或真实情况。"; + } + else + { + result = "你在这个群没有任何历史记录。"; + } + } + else + { + result = "你在这个群没有任何历史记录。"; + } + } + else result = "无法调用此接口:SQL 服务不可用。"; + + return result.Trim(); + } + } +}