模组架构修改

This commit is contained in:
milimoe 2025-01-18 16:01:01 +08:00
parent d26a1ccdd7
commit d5327af742
Signed by: milimoe
GPG Key ID: 05D280912DA6C69E
20 changed files with 590 additions and 108 deletions

View File

@ -1,6 +1,6 @@
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
namespace Oshima.FunGame.OshimaModules namespace Oshima.Core.Constant
{ {
public class OshimaGameModuleConstant public class OshimaGameModuleConstant
{ {
@ -16,6 +16,7 @@ namespace Oshima.FunGame.OshimaModules
public const string FastAutoMap = "oshima.fungame.fastauto.map"; public const string FastAutoMap = "oshima.fungame.fastauto.map";
public const string Anonymous = "oshima.fungame.anonymous"; public const string Anonymous = "oshima.fungame.anonymous";
public const string AnonymousMap = "oshima.fungame.anonymous.map"; public const string AnonymousMap = "oshima.fungame.anonymous.map";
public const string Server = "oshima.fungame.server";
private static readonly string[] Maps = [FastAutoMap]; private static readonly string[] Maps = [FastAutoMap];
private static readonly string[] Characters = [Character]; private static readonly string[] Characters = [Character];

View File

@ -1,5 +1,5 @@
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
namespace Oshima.FunGame.OshimaMaps namespace Oshima.FunGame.OshimaMaps
{ {

View File

@ -1,5 +1,5 @@
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
namespace Oshima.FunGame.OshimaMaps namespace Oshima.FunGame.OshimaMaps
{ {

View File

@ -19,7 +19,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OshimaModules\OshimaModules.csproj" /> <ProjectReference Include="..\OshimaCore\OshimaCore.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -5,7 +5,7 @@ using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
namespace Oshima.FunGame.OshimaModes namespace Oshima.FunGame.OshimaModes
{ {

View File

@ -1,5 +1,6 @@
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Characters;
namespace Oshima.FunGame.OshimaModules namespace Oshima.FunGame.OshimaModules

View File

@ -1,5 +1,6 @@
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;
namespace Oshima.FunGame.OshimaModules namespace Oshima.FunGame.OshimaModules

View File

@ -1,5 +1,6 @@
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Effects.ItemEffects; using Oshima.FunGame.OshimaModules.Effects.ItemEffects;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;

View File

@ -22,6 +22,10 @@
<NoWarn>1701;1702;CS8981;IDE1006;IDE0130</NoWarn> <NoWarn>1701;1702;CS8981;IDE1006;IDE0130</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\OshimaCore\OshimaCore.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FunGame.Core"> <Reference Include="FunGame.Core">
<HintPath>..\..\FunGame.Core\bin\Debug\net9.0\FunGame.Core.dll</HintPath> <HintPath>..\..\FunGame.Core\bin\Debug\net9.0\FunGame.Core.dll</HintPath>

View File

@ -5,10 +5,8 @@ using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Oshima.Core;
using Oshima.Core.Configs; using Oshima.Core.Configs;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaServers.Service;
namespace Oshima.FunGame.OshimaServers namespace Oshima.FunGame.OshimaServers
{ {
@ -94,9 +92,6 @@ namespace Oshima.FunGame.OshimaServers
{ {
Controller.NewSQLHelper(); Controller.NewSQLHelper();
Controller.NewMailSender(); Controller.NewMailSender();
OSMCore.InitOSMCore();
FunGameService.InitFunGame();
FunGameSimulation.InitFunGameSimulation();
} }
/// <summary> /// <summary>

View File

@ -7,7 +7,7 @@ using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Skills; using Oshima.FunGame.OshimaModules.Skills;
using Oshima.FunGame.OshimaServers.Service; using Oshima.FunGame.OshimaServers.Service;

View File

@ -0,0 +1,129 @@
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant;
using Oshima.Core;
using Oshima.Core.Configs;
using Oshima.Core.Constant;
using Oshima.FunGame.OshimaServers.Service;
using TaskScheduler = Milimoe.FunGame.Core.Api.Utility.TaskScheduler;
namespace Oshima.FunGame.OshimaServers
{
public class OshimaServer : ServerPlugin
{
public override string Name => OshimaGameModuleConstant.Server;
public override string Description => OshimaGameModuleConstant.Description;
public override string Version => OshimaGameModuleConstant.Version;
public override string Author => OshimaGameModuleConstant.Author;
public override void ProcessInput(string input)
{
if (input.StartsWith("fungametest"))
{
FunGameSimulation.StartSimulationGame(true, true);
}
// OSM指令
if (input.Length >= 4 && input[..4].Equals(".osm", StringComparison.CurrentCultureIgnoreCase))
{
//MasterCommand.Execute(read, GeneralSettings.Master, false, GeneralSettings.Master, false);
Controller.WriteLine("试图使用 .osm 指令:" + input);
}
}
public override void AfterLoad(ServerPluginLoader loader, params object[] objs)
{
Controller.NewSQLHelper();
Controller.NewMailSender();
OSMCore.InitOSMCore();
FunGameService.InitFunGame();
FunGameSimulation.InitFunGameSimulation();
TaskScheduler.Shared.AddTask("重置每日运势", new TimeSpan(0, 0, 0), () =>
{
Controller.WriteLine("已重置所有人的今日运势");
Daily.ClearDaily();
});
TaskScheduler.Shared.AddTask("重置交易冷却1", new TimeSpan(9, 0, 0), () =>
{
Controller.WriteLine("重置物品交易冷却时间");
_ = FunGameService.AllowSellAndTrade();
});
TaskScheduler.Shared.AddTask("重置交易冷却2", new TimeSpan(15, 0, 0), () =>
{
Controller.WriteLine("重置物品交易冷却时间");
_ = FunGameService.AllowSellAndTrade();
});
TaskScheduler.Shared.AddRecurringTask("刷新存档缓存", TimeSpan.FromMinutes(1), () =>
{
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
PluginConfig pc = new("saved", fileName);
pc.LoadConfig();
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
// 将用户存入缓存
FunGameService.UserIdAndUsername[user.Id] = user;
// 任务结算
EntityModuleConfig<Quest> quests = new("quests", user.Id.ToString());
quests.LoadConfig();
if (quests.Count > 0 && FunGameService.SettleQuest(user, quests))
{
quests.SaveConfig();
user.LastTime = DateTime.Now;
pc.Add("user", user);
pc.SaveConfig();
}
}
}
Controller.WriteLine("读取 FunGame 存档缓存", LogLevel.Debug);
}
}, true);
TaskScheduler.Shared.AddTask("刷新每日任务", new TimeSpan(4, 0, 0), () =>
{
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/quests";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
EntityModuleConfig<Quest> quests = new("quests", fileName);
quests.Clear();
FunGameService.CheckQuestList(quests);
quests.SaveConfig();
}
Controller.WriteLine("刷新每日任务");
}
// 刷新签到
directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
PluginConfig pc = new("saved", fileName);
pc.LoadConfig();
pc.Add("signed", false);
pc.SaveConfig();
}
Controller.WriteLine("刷新签到");
}
});
TaskScheduler.Shared.AddRecurringTask("刷新boss", TimeSpan.FromHours(1), () =>
{
FunGameService.GenerateBoss();
Controller.WriteLine("刷新boss");
}, true);
}
}
}

View File

@ -2,7 +2,7 @@ using System.Text;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaModules.Characters; using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Effects.OpenEffects;
using Oshima.FunGame.OshimaModules.Items; using Oshima.FunGame.OshimaModules.Items;

View File

@ -0,0 +1,160 @@
using System.Globalization;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Oshima.FunGame.WebAPI.Models;
using Oshima.FunGame.WebAPI.Services;
using Rebex.Security.Cryptography;
namespace Oshima.FunGame.WebAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class QQBotController(IOptions<BotConfig> botConfig, ILogger<QQBotController> logger, QQBotService service) : ControllerBase
{
private readonly BotConfig _botConfig = botConfig.Value;
private readonly ILogger<QQBotController> _logger = logger;
private readonly QQBotService _service = service;
[HttpPost]
public async Task<IActionResult> Post([FromBody] Payload? payload)
{
if (payload is null)
{
return BadRequest("Payload 格式无效");
}
_logger.LogDebug("收到 Webhook 请求:{payload}", payload);
try
{
if (payload.Op == 13)
{
return HandleValidation(payload);
}
else if (payload.Op == 0)
{
// 处理其他事件
return await HandleEventAsync(payload);
}
else
{
_logger.LogWarning("未处理操作码:{payload.Op}", payload.Op);
return Ok();
}
}
catch (Exception e)
{
_logger.LogError("Error: {e}", e);
return StatusCode(500, "服务器内部错误");
}
}
private IActionResult HandleValidation(Payload payload)
{
ValidationRequest? validationPayload = JsonSerializer.Deserialize<ValidationRequest>(payload.Data.ToString() ?? "");
if (validationPayload is null)
{
_logger.LogError("反序列化验证 Payload 失败");
return BadRequest("无效的验证 Payload 格式");
}
string seed = _botConfig.Secret;
while (seed.Length < 32)
{
seed += seed;
}
seed = seed[..32];
byte[] privateKeyBytes = Encoding.UTF8.GetBytes(seed);
Ed25519 ed25519 = new();
ed25519.FromSeed(privateKeyBytes);
// 将你的消息转换为 byte[]
byte[] message = Encoding.UTF8.GetBytes(validationPayload.EventTs + validationPayload.PlainToken);
// 使用 Sign 方法签名消息
byte[] result = ed25519.SignMessage(message);
string signature = Convert.ToHexString(result).ToLower(CultureInfo.InvariantCulture);
ValidationResponse response = new()
{
PlainToken = validationPayload.PlainToken,
Signature = signature
};
string responseJson = JsonSerializer.Serialize(response);
_logger.LogDebug("验证相应:{responseJson}", responseJson);
return Ok(response);
}
private async Task<IActionResult> HandleEventAsync(Payload payload)
{
_logger.LogDebug("处理事件:{EventType}, 数据:{Data}", payload.EventType, payload.Data);
try
{
switch (payload.EventType)
{
case "C2C_MESSAGE_CREATE":
C2CMessage? c2cMessage = JsonSerializer.Deserialize<C2CMessage>(payload.Data.ToString() ?? "");
if (c2cMessage != null)
{
// TODO
_logger.LogInformation("收到来自用户 {c2cMessage.Author.UserOpenId} 的消息:{c2cMessage.Content}", c2cMessage.Author.UserOpenId, c2cMessage.Content);
// 上传图片
var (fileUuid, fileInfo, ttl, error) = await _service.UploadC2CMediaAsync(c2cMessage.Author.UserOpenId, 1, $"{Request.Scheme}://{Request.Host}{Request.PathBase}/images/zi/dj1.png");
if (string.IsNullOrEmpty(error))
{
// 回复富媒体消息
await _service.SendC2CMessageAsync(c2cMessage.Author.UserOpenId, "", msgType: 7, media: new { file_info = fileInfo });
}
else
{
_logger.LogError("上传图片失败:{error}", error);
}
}
else
{
_logger.LogError("反序列化 C2C 消息数据失败");
return BadRequest("无效的 C2C 消息数据格式");
}
break;
case "GROUP_AT_MESSAGE_CREATE":
GroupAtMessage? groupAtMessage = JsonSerializer.Deserialize<GroupAtMessage>(payload.Data.ToString() ?? "");
if (groupAtMessage != null)
{
// TODO
_logger.LogInformation("收到来自群组 {groupAtMessage.GroupOpenId} 的消息:{groupAtMessage.Content}", groupAtMessage.GroupOpenId, groupAtMessage.Content);
// 回复消息
await _service.SendGroupMessageAsync(groupAtMessage.GroupOpenId, $"你发送的消息是:{groupAtMessage.Content}", msgType: 0);
}
else
{
_logger.LogError("反序列化群聊消息数据失败");
return BadRequest("无效的群聊消息数据格式");
}
break;
default:
_logger.LogWarning("未定义事件:{EventType}", payload.EventType);
break;
}
return Ok();
}
catch (JsonException e)
{
_logger.LogError("反序列化过程遇到错误:{e}", e);
return BadRequest("Invalid JSON format");
}
catch (Exception e)
{
_logger.LogError("Error: {e}", e);
return StatusCode(500, "服务器内部错误");
}
}
}
}

View File

@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Oshima.Core.Configs; using Oshima.Core.Configs;
using Oshima.FunGame.WebAPI.Models; using Oshima.FunGame.WebAPI.Models;
using Oshima.FunGame.WebAPI.Utils; using Oshima.FunGame.WebAPI.Services;
namespace Oshima.FunGame.WebAPI.Controllers namespace Oshima.FunGame.WebAPI.Controllers
{ {
@ -16,13 +16,13 @@ namespace Oshima.FunGame.WebAPI.Controllers
[HttpPost("get/{user_id}", Name = "GetUserDaily")] [HttpPost("get/{user_id}", Name = "GetUserDaily")]
public UserDaily Get(long user_id) public UserDaily Get(long user_id)
{ {
return UserDailyUtil.GetUserDaily(user_id); return UserDailyService.GetUserDaily(user_id);
} }
[HttpGet("view/{user_id}", Name = "ViewUserDaily")] [HttpGet("view/{user_id}", Name = "ViewUserDaily")]
public UserDaily View(long user_id) public UserDaily View(long user_id)
{ {
return UserDailyUtil.ViewUserDaily(user_id); return UserDailyService.ViewUserDaily(user_id);
} }
[HttpPost("open/{open_id}", Name = "GetOpenUserDaily")] [HttpPost("open/{open_id}", Name = "GetOpenUserDaily")]
@ -30,7 +30,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
{ {
if (QQOpenID.QQAndOpenID.TryGetValue(open_id, out long qq) && qq != 0) if (QQOpenID.QQAndOpenID.TryGetValue(open_id, out long qq) && qq != 0)
{ {
return UserDailyUtil.GetUserDaily(qq); return UserDailyService.GetUserDaily(qq);
} }
return new(0, 0, "你似乎没有绑定QQ呢请先发送【绑定+QQ号】绑定123456789再使用哦"); return new(0, 0, "你似乎没有绑定QQ呢请先发送【绑定+QQ号】绑定123456789再使用哦");
} }
@ -38,7 +38,7 @@ namespace Oshima.FunGame.WebAPI.Controllers
[HttpPost("remove/{user_id}", Name = "RemoveUserDaily")] [HttpPost("remove/{user_id}", Name = "RemoveUserDaily")]
public string Remove(long user_id) public string Remove(long user_id)
{ {
return UserDailyUtil.RemoveDaily(user_id); return UserDailyService.RemoveDaily(user_id);
} }
[HttpGet("img/{type}", Name = "GetTypeImage")] [HttpGet("img/{type}", Name = "GetTypeImage")]

View File

@ -0,0 +1,136 @@
using System.Text.Json.Serialization;
namespace Oshima.FunGame.WebAPI.Models
{
public class Payload
{
[JsonPropertyName("id")]
public string Id { get; set; } = "";
[JsonPropertyName("op")]
public int Op { get; set; } = 0;
[JsonPropertyName("d")]
public object Data { get; set; } = new();
[JsonPropertyName("s")]
public int SequenceNumber { get; set; } = 0;
[JsonPropertyName("t")]
public string EventType { get; set; } = "";
}
public class ValidationRequest
{
[JsonPropertyName("plain_token")]
public string PlainToken { get; set; } = "";
[JsonPropertyName("event_ts")]
public string EventTs { get; set; } = "";
}
public class ValidationResponse
{
[JsonPropertyName("plain_token")]
public string PlainToken { get; set; } = "";
[JsonPropertyName("signature")]
public string Signature { get; set; } = "";
}
public class BotConfig
{
public string AppId { get; set; } = "";
public string Secret { get; set; } = "";
}
public class Author
{
[JsonPropertyName("user_openid")]
public string UserOpenId { get; set; } = "";
[JsonPropertyName("member_openid")]
public string MemberOpenId { get; set; } = "";
}
public class Attachment
{
[JsonPropertyName("content_type")]
public string ContentType { get; set; } = "";
[JsonPropertyName("filename")]
public string Filename { get; set; } = "";
[JsonPropertyName("height")]
public int Height { get; set; } = 0;
[JsonPropertyName("width")]
public int Width { get; set; } = 0;
[JsonPropertyName("size")]
public int Size { get; set; } = 0;
[JsonPropertyName("url")]
public string Url { get; set; } = "";
}
public class C2CMessage
{
[JsonPropertyName("id")]
public string Id { get; set; } = "";
[JsonPropertyName("author")]
public Author Author { get; set; } = new();
[JsonPropertyName("content")]
public string Content { get; set; } = "";
[JsonPropertyName("timestamp")]
public string Timestamp { get; set; } = "";
[JsonPropertyName("attachments")]
public Attachment[] Attachments { get; set; } = [];
}
public class GroupAtMessage
{
[JsonPropertyName("id")]
public string Id { get; set; } = "";
[JsonPropertyName("author")]
public Author Author { get; set; } = new();
[JsonPropertyName("content")]
public string Content { get; set; } = "";
[JsonPropertyName("timestamp")]
public string Timestamp { get; set; } = "";
[JsonPropertyName("group_openid")]
public string GroupOpenId { get; set; } = "";
[JsonPropertyName("attachments")]
public Attachment[] Attachments { get; set; } = [];
}
public class MediaResponse
{
[JsonPropertyName("file_uuid")]
public string FileUuid { get; set; } = "";
[JsonPropertyName("file_info")]
public string FileInfo { get; set; } = "";
[JsonPropertyName("ttl")]
public int Ttl { get; set; }
}
public class AccessTokenResponse
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; } = "";
[JsonPropertyName("expires_in")]
public string ExpiresIn { get; set; } = "";
}
}

View File

@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant;
using Oshima.Core.Configs; using Oshima.Core.Configs;
using Oshima.FunGame.OshimaModules; using Oshima.Core.Constant;
using Oshima.FunGame.OshimaServers.Service; using Oshima.FunGame.OshimaServers.Service;
using Oshima.FunGame.WebAPI.Constant; using Oshima.FunGame.WebAPI.Constant;
using TaskScheduler = Milimoe.FunGame.Core.Api.Utility.TaskScheduler; using Oshima.FunGame.WebAPI.Models;
using Oshima.FunGame.WebAPI.Services;
namespace Oshima.FunGame.WebAPI namespace Oshima.FunGame.WebAPI
{ {
@ -34,95 +35,19 @@ namespace Oshima.FunGame.WebAPI
} }
} }
public override void AfterLoad(params object[] objs) public override void AfterLoad(WebAPIPluginLoader loader, params object[] objs)
{ {
Statics.RunningPlugin = this; Statics.RunningPlugin = this;
Controller.NewSQLHelper(); Controller.NewSQLHelper();
Controller.NewMailSender(); Controller.NewMailSender();
if (objs.Length > 0 && objs[0] is WebApplicationBuilder builder)
{
builder.Services.AddMemoryCache();
builder.Services.AddScoped<QQBotService>();
// 使用 Configure<BotConfig> 从配置源绑定
builder.Services.Configure<BotConfig>(builder.Configuration.GetSection("Bot"));
}
WebAPIAuthenticator.WebAPICustomBearerTokenAuthenticator += CustomBearerTokenAuthenticator; WebAPIAuthenticator.WebAPICustomBearerTokenAuthenticator += CustomBearerTokenAuthenticator;
TaskScheduler.Shared.AddTask("重置每日运势", new TimeSpan(0, 0, 0), () =>
{
Controller.WriteLine("已重置所有人的今日运势");
Daily.ClearDaily();
});
TaskScheduler.Shared.AddTask("重置交易冷却1", new TimeSpan(9, 0, 0), () =>
{
Controller.WriteLine("重置物品交易冷却时间");
_ = FunGameService.AllowSellAndTrade();
});
TaskScheduler.Shared.AddTask("重置交易冷却2", new TimeSpan(15, 0, 0), () =>
{
Controller.WriteLine("重置物品交易冷却时间");
_ = FunGameService.AllowSellAndTrade();
});
TaskScheduler.Shared.AddRecurringTask("刷新存档缓存", TimeSpan.FromMinutes(1), () =>
{
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
PluginConfig pc = new("saved", fileName);
pc.LoadConfig();
if (pc.Count > 0)
{
User user = FunGameService.GetUser(pc);
// 将用户存入缓存
FunGameService.UserIdAndUsername[user.Id] = user;
// 任务结算
EntityModuleConfig<Quest> quests = new("quests", user.Id.ToString());
quests.LoadConfig();
if (quests.Count > 0 && FunGameService.SettleQuest(user, quests))
{
quests.SaveConfig();
user.LastTime = DateTime.Now;
pc.Add("user", user);
pc.SaveConfig();
}
}
}
Controller.WriteLine("读取 FunGame 存档缓存", LogLevel.Debug);
}
}, true);
TaskScheduler.Shared.AddTask("刷新每日任务", new TimeSpan(4, 0, 0), () =>
{
string directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/quests";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
EntityModuleConfig<Quest> quests = new("quests", fileName);
quests.Clear();
FunGameService.CheckQuestList(quests);
quests.SaveConfig();
}
Controller.WriteLine("刷新每日任务");
}
// 刷新签到
directoryPath = $@"{AppDomain.CurrentDomain.BaseDirectory}configs/saved";
if (Directory.Exists(directoryPath))
{
string[] filePaths = Directory.GetFiles(directoryPath);
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileNameWithoutExtension(filePath);
PluginConfig pc = new("saved", fileName);
pc.LoadConfig();
pc.Add("signed", false);
pc.SaveConfig();
}
Controller.WriteLine("刷新签到");
}
});
TaskScheduler.Shared.AddRecurringTask("刷新boss", TimeSpan.FromHours(1), () =>
{
FunGameService.GenerateBoss();
Controller.WriteLine("刷新boss");
}, true);
} }
private string CustomBearerTokenAuthenticator(string token) private string CustomBearerTokenAuthenticator(string token)

View File

@ -24,6 +24,10 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Rebex.Elliptic.Ed25519" Version="1.2.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OshimaCore\OshimaCore.csproj" /> <ProjectReference Include="..\OshimaCore\OshimaCore.csproj" />
<ProjectReference Include="..\OshimaModules\OshimaModules.csproj" /> <ProjectReference Include="..\OshimaModules\OshimaModules.csproj" />

View File

@ -0,0 +1,125 @@
using System.Net.Http.Headers;
using System.Text.Json;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Oshima.FunGame.WebAPI.Constant;
using Oshima.FunGame.WebAPI.Models;
namespace Oshima.FunGame.WebAPI.Services
{
public class QQBotService(IOptions<BotConfig> botConfig, IHttpClientFactory httpClientFactory, IMemoryCache memoryCache)
{
private readonly BotConfig _botConfig = botConfig.Value;
private readonly HttpClient _httpClient = httpClientFactory.CreateClient();
private readonly IMemoryCache _memoryCache = memoryCache;
private const string AccessTokenCacheKey = "QQBotAccessToken";
public async Task SendC2CMessageAsync(string openid, string content, int msgType = 0, object? media = null, string? msgId = null, int? msgSeq = null)
{
await SendMessageAsync($"/v2/users/{openid}/messages", content, msgType, media, msgId, msgSeq);
}
public async Task SendGroupMessageAsync(string groupOpenid, string content, int msgType = 0, object? media = null, string? msgId = null, int? msgSeq = null)
{
await SendMessageAsync($"/v2/groups/{groupOpenid}/messages", content, msgType, media, msgId, msgSeq);
}
private async Task SendMessageAsync(string url, string content, int msgType = 0, object? media = null, string? msgId = null, int? msgSeq = null)
{
string accessToken = await GetAccessTokenAsync();
HttpRequestMessage request = new(HttpMethod.Post, $"https://api.sgroup.qq.com{url}");
request.Headers.Authorization = new AuthenticationHeaderValue("QQBot", accessToken);
Statics.RunningPlugin?.Controller.WriteLine($"使用的 Access Token{accessToken}", Milimoe.FunGame.Core.Library.Constant.LogLevel.Debug);
Dictionary<string, object> requestBody = new()
{
{ "content", content },
{ "msg_type", msgType }
};
if (media != null)
{
requestBody.Add("media", media);
}
if (!string.IsNullOrEmpty(msgId))
{
requestBody.Add("msg_id", msgId);
}
if (msgSeq.HasValue)
{
requestBody.Add("msg_seq", msgSeq.Value);
}
request.Content = new StringContent(JsonSerializer.Serialize(requestBody), System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
public async Task<(string? fileUuid, string? fileInfo, int ttl, string? error)> UploadC2CMediaAsync(string openid, int fileType, string url)
{
return await UploadMediaAsync($"/v2/users/{openid}/files", fileType, url);
}
public async Task<(string? fileUuid, string? fileInfo, int ttl, string? error)> UploadGroupMediaAsync(string groupOpenid, int fileType, string url)
{
return await UploadMediaAsync($"/v2/groups/{groupOpenid}/files", fileType, url);
}
private async Task<(string? fileUuid, string? fileInfo, int ttl, string? error)> UploadMediaAsync(string url, int fileType, string fileUrl)
{
string accessToken = await GetAccessTokenAsync();
HttpRequestMessage request = new(HttpMethod.Post, $"https://api.sgroup.qq.com{url}");
request.Headers.Authorization = new AuthenticationHeaderValue("QQBot", accessToken);
Statics.RunningPlugin?.Controller.WriteLine($"使用的 Access Token{accessToken}", Milimoe.FunGame.Core.Library.Constant.LogLevel.Debug);
Dictionary<string, object> requestBody = new()
{
{ "file_type", fileType },
{ "url", fileUrl },
{ "srv_send_msg", false }
};
request.Content = new StringContent(JsonSerializer.Serialize(requestBody), System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
string errorBody = await response.Content.ReadAsStringAsync();
return (null, null, 0, $"状态码:{response.StatusCode},错误信息:{errorBody}");
}
string responseBody = await response.Content.ReadAsStringAsync();
MediaResponse? mediaResponse = JsonSerializer.Deserialize<MediaResponse>(responseBody);
if (mediaResponse == null)
{
return (null, null, 0, "反序列化富媒体消息失败。");
}
Statics.RunningPlugin?.Controller.WriteLine($"接收到的富媒体消息:{mediaResponse.FileInfo}", Milimoe.FunGame.Core.Library.Constant.LogLevel.Debug);
return (mediaResponse.FileUuid, mediaResponse.FileInfo, mediaResponse.Ttl, null);
}
public async Task<string> GetAccessTokenAsync()
{
if (_memoryCache.TryGetValue(AccessTokenCacheKey, out string? accessToken) && !string.IsNullOrEmpty(accessToken))
{
return accessToken;
}
return await RefreshTokenAsync();
}
public async Task<string> RefreshTokenAsync()
{
HttpRequestMessage request = new(HttpMethod.Post, "https://bots.qq.com/app/getAppAccessToken")
{
Content = new StringContent(JsonSerializer.Serialize(new { appId = _botConfig.AppId, clientSecret = _botConfig.Secret }), System.Text.Encoding.UTF8, "application/json")
};
HttpResponseMessage response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
AccessTokenResponse? tokenResponse = JsonSerializer.Deserialize<AccessTokenResponse>(responseBody);
if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.AccessToken) || !int.TryParse(tokenResponse.ExpiresIn, out int expiresIn))
{
throw new Exception("获取 Access Token 失败!");
}
_memoryCache.Set(AccessTokenCacheKey, tokenResponse.AccessToken, TimeSpan.FromSeconds(expiresIn - 60));
Statics.RunningPlugin?.Controller.WriteLine($"获取到 Access Token{tokenResponse.AccessToken}", Milimoe.FunGame.Core.Library.Constant.LogLevel.Debug);
return tokenResponse.AccessToken;
}
}
}

View File

@ -3,9 +3,9 @@ using Oshima.Core.Configs;
using Oshima.Core.Constant; using Oshima.Core.Constant;
using Oshima.FunGame.WebAPI.Models; using Oshima.FunGame.WebAPI.Models;
namespace Oshima.FunGame.WebAPI.Utils namespace Oshima.FunGame.WebAPI.Services
{ {
public class UserDailyUtil public class UserDailyService
{ {
public static UserDaily GetUserDaily(long user_id) public static UserDaily GetUserDaily(long user_id)
{ {