mirror of
https://github.com/oshima-studios/OshimaGameModule.git
synced 2026-06-04 19:42:13 +00:00
293 lines
13 KiB
C#
293 lines
13 KiB
C#
using System.Globalization;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using Oshima.FunGame.OshimaServers.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, RainBOTService fungameService) : ControllerBase
|
||
{
|
||
private BotConfig BotConfig { get; set; } = botConfig.Value;
|
||
private ILogger<QQBotController> Logger { get; set; } = logger;
|
||
private QQBotService Service { get; set; } = service;
|
||
private RainBOTService FungameService { get; set; } = fungameService;
|
||
|
||
[HttpPost]
|
||
public IActionResult Post([FromBody] Payload? payload)
|
||
{
|
||
if (payload is null)
|
||
{
|
||
return BadRequest("Payload 格式无效");
|
||
}
|
||
|
||
if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("收到 Webhook 请求:{payload.Op}", payload.Op);
|
||
|
||
try
|
||
{
|
||
if (payload.Op == 13)
|
||
{
|
||
return HandleValidation(payload);
|
||
}
|
||
else if (payload.Op == 0)
|
||
{
|
||
// 处理其他事件
|
||
return HandleEventAsync(payload);
|
||
}
|
||
else
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Warning)) Logger.LogWarning("未处理操作码:{payload.Op}", payload.Op);
|
||
}
|
||
return Ok();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) 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)
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) 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);
|
||
if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("验证相应:{responseJson}", responseJson);
|
||
return Ok(response);
|
||
}
|
||
|
||
private IActionResult HandleEventAsync(Payload payload)
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("处理事件:{EventType}, 数据:{Data}", payload.EventType, payload.Data);
|
||
|
||
try
|
||
{
|
||
OtherData data = new()
|
||
{
|
||
RequestUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}"
|
||
};
|
||
|
||
switch (payload.EventType)
|
||
{
|
||
case "C2C_MESSAGE_CREATE":
|
||
C2CMessage? c2cMessage = JsonSerializer.Deserialize<C2CMessage>(payload.Data.ToString() ?? "");
|
||
if (c2cMessage != null)
|
||
{
|
||
c2cMessage.Detail = c2cMessage.Detail.Trim();
|
||
if (c2cMessage.Detail.StartsWith('/'))
|
||
{
|
||
c2cMessage.Detail = c2cMessage.Detail[1..];
|
||
}
|
||
// TODO
|
||
if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformation("收到来自用户 {c2cMessage.Author.UserOpenId} 的消息:{c2cMessage.Content}", c2cMessage.Author.UserOpenId, c2cMessage.Content);
|
||
// 上传图片
|
||
//string url = $"{RequestUrl}/images/zi/dj1.png";
|
||
//UploadMediaResult uploadMediaResult = await Service.UploadC2CMediaAsync(c2cMessage.Author.UserOpenId, 1, url);
|
||
//if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("发送的图片地址:{url}", url);
|
||
//if (string.IsNullOrEmpty(uploadMediaResult.Error))
|
||
//{
|
||
// // 回复消息
|
||
// await Service.SendC2CMessageAsync(c2cMessage.Author.UserOpenId, $"你发送的消息是:{c2cMessage.Content}", msgId: c2cMessage.Id);
|
||
// // 回复富媒体消息
|
||
// await Service.SendC2CMessageAsync(c2cMessage.Author.UserOpenId, "", msgType: 7, media: new { file_info = uploadMediaResult.FileInfo }, msgId: c2cMessage.Id);
|
||
//}
|
||
//else
|
||
//{
|
||
// if (Logger.IsEnabled(LogLevel.Error)) Logger.LogError("上传图片失败:{error}", uploadMediaResult.Error);
|
||
//}
|
||
// 发 md 示例
|
||
//// 1. 带蓝字链接
|
||
//MarkdownMessage mdMsg = new()
|
||
//{
|
||
// Content = "请选择:\n<qqbot-cmd-enter text=\"/签到\"/>"
|
||
//};
|
||
//// 2. 带按钮
|
||
//KeyboardMessage kbMsg = new()
|
||
//{
|
||
// Content = new()
|
||
// {
|
||
// Rows = [
|
||
// new()
|
||
// {
|
||
// Buttons = [
|
||
// new()
|
||
// {
|
||
// Id = "btn1",
|
||
// RenderData = new RenderData { Label = "同意", VisitedLabel = "已同意", Style = 1 },
|
||
// Action = new Models.Action
|
||
// {
|
||
// Type = 2,
|
||
// Data = "我同意服务条款",
|
||
// Enter = true,
|
||
// Reply = false,
|
||
// Permission = new Permission { Type = 2 }
|
||
// }
|
||
// }
|
||
// ]
|
||
// }
|
||
// ]
|
||
// }
|
||
//};
|
||
//await Service.SendC2CMarkdownAsync(c2cMessage.AuthorOpenId, mdMsg, kbMsg, c2cMessage.Id);
|
||
Task.Run(async () => await FungameService.Handler(c2cMessage, data));
|
||
}
|
||
else
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) Logger.LogError("反序列化 C2C 消息数据失败");
|
||
return BadRequest("无效的 C2C 消息数据格式");
|
||
}
|
||
break;
|
||
case "GROUP_AT_MESSAGE_CREATE":
|
||
GroupAtMessage? groupAtMessage = JsonSerializer.Deserialize<GroupAtMessage>(payload.Data.ToString() ?? "");
|
||
if (groupAtMessage != null)
|
||
{
|
||
groupAtMessage.Detail = groupAtMessage.Detail.Trim();
|
||
if (groupAtMessage.Detail.StartsWith('/'))
|
||
{
|
||
groupAtMessage.Detail = groupAtMessage.Detail[1..];
|
||
}
|
||
// TODO
|
||
if (Logger.IsEnabled(LogLevel.Information)) Logger.LogInformation("收到来自群组 {groupAtMessage.GroupOpenId} 的消息:{groupAtMessage.Content}", groupAtMessage.GroupOpenId, groupAtMessage.Content);
|
||
// 回复消息
|
||
//await _service.SendGroupMessageAsync(groupAtMessage.GroupOpenId, $"你发送的消息是:{groupAtMessage.Content}", msgId: groupAtMessage.Id);
|
||
Task.Run(async () => await FungameService.Handler(groupAtMessage, data));
|
||
}
|
||
else
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) Logger.LogError("反序列化群聊消息数据失败");
|
||
return BadRequest("无效的群聊消息数据格式");
|
||
}
|
||
break;
|
||
case "INTERACTION_CREATE":
|
||
InteractionEvent? interaction = JsonSerializer.Deserialize<InteractionEvent>(payload.Data.ToString() ?? "");
|
||
if (interaction != null)
|
||
{
|
||
// 提取按钮ID和数据
|
||
string buttonId = interaction.Data?.Resolved?.ButtonId ?? "";
|
||
string buttonData = interaction.Data?.Resolved?.ButtonData ?? "";
|
||
|
||
if (Logger.IsEnabled(LogLevel.Information))
|
||
{
|
||
Logger.LogInformation("收到按钮点击:ButtonId={buttonId}, Data={buttonData}, User={user}", buttonId, buttonData, interaction.UserOpenId);
|
||
}
|
||
|
||
// TODO
|
||
//if (interaction.ChatType == 1)
|
||
//{
|
||
// Task.Run(async () => await Service.SendGroupMessageAsync(interaction.GroupOpenId, $"你点击了按钮:{buttonData}"));
|
||
//}
|
||
//else if (interaction.ChatType == 2)
|
||
//{
|
||
// Task.Run(async () => await Service.SendC2CMessageAsync(interaction.UserOpenId, $"你点击了按钮:{buttonData}"));
|
||
//}
|
||
}
|
||
break;
|
||
default:
|
||
if (Logger.IsEnabled(LogLevel.Warning)) Logger.LogWarning("未定义事件:{EventType}", payload.EventType);
|
||
break;
|
||
}
|
||
return Ok();
|
||
}
|
||
catch (JsonException e)
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) Logger.LogError("反序列化过程遇到错误:{e}", e);
|
||
return BadRequest("无效的 JSON 格式");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (Logger.IsEnabled(LogLevel.Error)) Logger.LogError("Error: {e}", e);
|
||
return StatusCode(500, "服务器内部错误");
|
||
}
|
||
}
|
||
|
||
[Authorize(AuthenticationSchemes = "CustomBearer")]
|
||
[HttpPost("thirdparty")]
|
||
public async Task<IActionResult> ThirdParty([FromBody] ThirdPartyMessage? msg = null)
|
||
{
|
||
if (msg is null) return Ok("");
|
||
|
||
OtherData data = new()
|
||
{
|
||
RequestUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}"
|
||
};
|
||
|
||
bool result = await FungameService.Handler(msg, data);
|
||
|
||
if (!result || msg.IsCompleted)
|
||
{
|
||
return Ok(msg.Result);
|
||
}
|
||
|
||
return Ok("");
|
||
}
|
||
|
||
[Authorize(AuthenticationSchemes = "CustomBearer")]
|
||
[HttpPost("thirdpartytest")]
|
||
public async Task<IActionResult> ThirdPartyTest(string msg, string openID)
|
||
{
|
||
if (msg.Trim() == "") return Ok("");
|
||
|
||
ThirdPartyMessage message = new()
|
||
{
|
||
IsGroup = false,
|
||
AuthorOpenId = openID,
|
||
OpenId = openID,
|
||
Detail = msg,
|
||
Id = openID,
|
||
Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
||
};
|
||
|
||
OtherData data = new()
|
||
{
|
||
RequestUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}"
|
||
};
|
||
|
||
bool result = await FungameService.Handler(message, data);
|
||
|
||
if (!result || message.IsCompleted)
|
||
{
|
||
return Ok(message.Result);
|
||
}
|
||
|
||
return Ok("");
|
||
}
|
||
}
|
||
}
|