Update Progress for Lagrange.OneBot

This commit is contained in:
Linwenxuan 2023-10-12 12:51:30 +08:00
parent 5ee7d28465
commit dc2589e30b
20 changed files with 246 additions and 219 deletions

View file

@ -5,36 +5,25 @@ using Lagrange.OneBot.Core.Message;
namespace Lagrange.OneBot.Core.Entity.Message;
[Serializable]
public class OneBotGroupMsg : OneBotEntityBase
public class OneBotGroupMsg(uint selfId, uint groupUin, List<OneBotSegment> message, BotGroupMember member) : OneBotEntityBase(selfId, "message")
{
[JsonPropertyName("message_type")] public string MessageType { get; set; }
[JsonPropertyName("sub_type")] public string SubType { get; set; }
[JsonPropertyName("message_id")] public int MessageId { get; set; }
[JsonPropertyName("group_id")] public uint GroupId { get; set; }
[JsonPropertyName("user_id")] public uint UserId { get; set; }
[JsonPropertyName("anonymous")] public object? Anonymous { get; set; }
[JsonPropertyName("message")] public List<ISegment> Message { get; set; }
[JsonPropertyName("raw_message")] public string RawMessage { get; set; }
[JsonPropertyName("font")] public int Font { get; set; }
[JsonPropertyName("sender")] public OneBotGroupSender GroupSender { get; set; }
[JsonPropertyName("message_type")] public string MessageType { get; set; } = "group";
public OneBotGroupMsg(uint selfId, List<ISegment> message, BotGroupMember member) : base(selfId, "message")
{
MessageType = "group";
SubType = "normal";
Anonymous = null;
Message = message;
RawMessage = string.Empty;
Font = 0;
GroupSender = new OneBotGroupSender(member.Uin, member.MemberName, member.MemberCard, (int)member.GroupLevel, member.Permission);
}
[JsonPropertyName("sub_type")] public string SubType { get; set; } = "normal";
[JsonPropertyName("message_id")] public int MessageId { get; set; }
[JsonPropertyName("group_id")] public uint GroupId { get; set; } = groupUin;
[JsonPropertyName("user_id")] public uint UserId { get; set; } = member.Uin;
[JsonPropertyName("anonymous")] public object? Anonymous { get; set; } = null;
[JsonPropertyName("message")] public List<OneBotSegment> Message { get; set; } = message;
[JsonPropertyName("raw_message")] public string RawMessage { get; set; } = string.Empty;
[JsonPropertyName("font")] public int Font { get; set; } = 0;
[JsonPropertyName("sender")] public OneBotGroupSender GroupSender { get; set; } = new(member.Uin, member.MemberName, member.MemberCard, (int)member.GroupLevel, member.Permission);
}

View file

@ -4,42 +4,29 @@ using Lagrange.Core.Common.Entity;
namespace Lagrange.OneBot.Core.Entity.Message;
[Serializable]
public class OneBotGroupSender
public class OneBotGroupSender(uint userId, string nickname, string card, int level, GroupMemberPermission permission)
{
[JsonPropertyName("user_id")] public uint UserId { get; set; }
[JsonPropertyName("nickname")] public string Nickname { get; set; }
[JsonPropertyName("card")] public string Card { get; set; }
[JsonPropertyName("sex")] public string Sex { get; set; }
[JsonPropertyName("age")] public uint Age { get; set; }
[JsonPropertyName("area")] public string Area { get; set; }
[JsonPropertyName("level")] public string Level { get; set; }
[JsonPropertyName("role")] public string Role { get; set; }
[JsonPropertyName("title")] public string Title { get; set; }
public OneBotGroupSender(uint userId, string nickname, string card, int level, GroupMemberPermission permission)
[JsonPropertyName("user_id")] public uint UserId { get; set; } = userId;
[JsonPropertyName("nickname")] public string Nickname { get; set; } = nickname;
[JsonPropertyName("card")] public string Card { get; set; } = card;
[JsonPropertyName("sex")] public string Sex { get; set; } = "unknown";
[JsonPropertyName("age")] public uint Age { get; set; } = 0;
[JsonPropertyName("area")] public string Area { get; set; } = string.Empty;
[JsonPropertyName("level")] public string Level { get; set; } = level.ToString();
[JsonPropertyName("role")] public string Role { get; set; } = permission switch
{
UserId = userId;
Nickname = nickname;
Card = card;
Sex = "unknown";
Age = 0;
Area = string.Empty;
Level = level.ToString();
Role = permission switch
{
GroupMemberPermission.Owner => "owner",
GroupMemberPermission.Admin => "admin",
GroupMemberPermission.Member => "member",
_ => "unknown"
};
Title = string.Empty;
}
GroupMemberPermission.Owner => "owner",
GroupMemberPermission.Admin => "admin",
GroupMemberPermission.Member => "member",
_ => "unknown"
};
[JsonPropertyName("title")] public string Title { get; set; } = string.Empty;
}

View file

@ -4,31 +4,21 @@ using Lagrange.OneBot.Core.Message;
namespace Lagrange.OneBot.Core.Entity.Message;
[Serializable]
public class OneBotPrivateMsg : OneBotEntityBase
public class OneBotPrivateMsg(uint selfId) : OneBotEntityBase(selfId, "message")
{
[JsonPropertyName("message_type")] public string MessageType { get; set; }
[JsonPropertyName("sub_type")] public string SubType { get; set; }
[JsonPropertyName("message_type")] public string MessageType { get; } = "private";
[JsonPropertyName("sub_type")] public string SubType { get; } = "friend";
[JsonPropertyName("message_id")] public int MessageId { get; set; }
[JsonPropertyName("user_id")] public uint UserId { get; set; }
[JsonPropertyName("message")] public List<ISegment> Message { get; set; }
[JsonPropertyName("raw_message")] public string RawMessage { get; set; }
[JsonPropertyName("font")] public int Font { get; set; }
[JsonPropertyName("sender")] public OneBotSender GroupSender { get; set; }
public OneBotPrivateMsg(uint selfId) : base(selfId, "message")
{
MessageType = "private";
SubType = "friend";
Message = new List<ISegment>();
RawMessage = string.Empty;
Font = 0;
GroupSender = new OneBotSender();
}
[JsonPropertyName("message")] public List<OneBotSegment> Message { get; set; } = new();
[JsonPropertyName("raw_message")] public string RawMessage { get; set; } = string.Empty;
[JsonPropertyName("font")] public int Font { get; set; } = 0;
[JsonPropertyName("sender")] public OneBotSender GroupSender { get; set; } = new();
}

View file

@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
using Lagrange.OneBot.Core.Message;
namespace Lagrange.OneBot.Core.Entity.Message;
public class OneBotSegment(string type, ISegment data)
{
[JsonPropertyName("type")] public string Type { get; set; } = type;
[JsonPropertyName("data")] public object Data { get; set; } = data;
}

View file

@ -3,15 +3,9 @@ using System.Text.Json.Serialization;
namespace Lagrange.OneBot.Core.Entity.Meta;
[Serializable]
public class OneBotHeartBeat : OneBotMeta
public class OneBotHeartBeat(uint selfId, int interval, object status) : OneBotMeta(selfId, "heartbeat")
{
[JsonPropertyName("interval")] public int Interval { get; set; }
[JsonPropertyName("status")] public object Status { get; set; }
public OneBotHeartBeat(uint selfId, int interval, object status) : base(selfId, "heartbeat")
{
Interval = interval;
Status = status;
}
[JsonPropertyName("interval")] public int Interval { get; set; } = interval;
[JsonPropertyName("status")] public object Status { get; set; } = status;
}

View file

@ -3,12 +3,7 @@ using System.Text.Json.Serialization;
namespace Lagrange.OneBot.Core.Entity.Meta;
[Serializable]
public class OneBotLifecycle : OneBotMeta
public class OneBotLifecycle(uint selfId, string subType) : OneBotMeta(selfId, "lifecycle")
{
[JsonPropertyName("sub_type")] public string SubType { get; set; }
public OneBotLifecycle(uint selfId, string subType) : base(selfId, "lifecycle")
{
SubType = subType;
}
[JsonPropertyName("sub_type")] public string SubType { get; set; } = subType;
}

View file

@ -3,12 +3,7 @@ using System.Text.Json.Serialization;
namespace Lagrange.OneBot.Core.Entity.Meta;
[Serializable]
public abstract class OneBotMeta : OneBotEntityBase
public abstract class OneBotMeta(uint selfId, string metaEventType) : OneBotEntityBase(selfId, "meta_event")
{
[JsonPropertyName("meta_event_type")] public string MetaEventType { get; set; }
protected OneBotMeta(uint selfId, string metaEventType) : base(selfId, "meta_event")
{
MetaEventType = metaEventType;
}
[JsonPropertyName("meta_event_type")] public string MetaEventType { get; set; } = metaEventType;
}

View file

@ -3,24 +3,15 @@ using System.Text.Json.Serialization;
namespace Lagrange.OneBot.Core.Entity.Meta;
[Serializable]
public class OneBotStatus
public class OneBotStatus(bool online, bool good)
{
[JsonPropertyName("app_initialized")] public bool AppInitialized { get; set; }
[JsonPropertyName("app_enabled")] public bool AppEnabled { get; set; }
[JsonPropertyName("app_good")] public bool AppGood { get; set; }
[JsonPropertyName("online")] public bool Online { get; set; }
[JsonPropertyName("good")] public bool Good { get; set; }
public OneBotStatus(bool online, bool good)
{
AppInitialized = true;
AppEnabled = true;
AppGood = true;
Online = online;
Good = good;
}
[JsonPropertyName("app_initialized")] public bool AppInitialized { get; set; } = true;
[JsonPropertyName("app_enabled")] public bool AppEnabled { get; set; } = true;
[JsonPropertyName("app_good")] public bool AppGood { get; set; } = true;
[JsonPropertyName("online")] public bool Online { get; set; } = online;
[JsonPropertyName("good")] public bool Good { get; set; } = good;
}

View file

@ -3,18 +3,11 @@ using System.Text.Json.Serialization;
namespace Lagrange.OneBot.Core.Entity;
[Serializable]
public abstract class OneBotEntityBase
public abstract class OneBotEntityBase(uint selfId, string postType)
{
[JsonPropertyName("time")] public long Time { get; set; }
[JsonPropertyName("self_id")] public uint SelfId { get; set; }
[JsonPropertyName("post_type")] public string PostType { get; set; }
protected OneBotEntityBase(uint selfId, string postType)
{
Time = DateTimeOffset.Now.ToUnixTimeSeconds();
SelfId = selfId;
PostType = postType;
}
[JsonPropertyName("time")] public long Time { get; set; } = DateTimeOffset.Now.ToUnixTimeSeconds();
[JsonPropertyName("self_id")] public uint SelfId { get; set; } = selfId;
[JsonPropertyName("post_type")] public string PostType { get; set; } = postType;
}

View file

@ -5,20 +5,19 @@ using Lagrange.Core.Message.Entity;
namespace Lagrange.OneBot.Core.Message.Entity;
[Serializable]
public partial class AtSegment
public partial class AtSegment(uint at)
{
public AtSegment(uint at) => At = at.ToString();
[JsonPropertyName("qq")] public string At { get; set; }
public AtSegment() : this(0) { }
[JsonPropertyName("qq")] public string At { get; set; } = at.ToString();
}
[SegmentSubscriber(typeof(MentionEntity), "at")]
public partial class AtSegment : ISegment
{
string ISegment.Type => "at";
public IMessageEntity ToEntity() => new MentionEntity("", uint.Parse(At));
public ISegment FromMessageEntity(IMessageEntity entity)
public ISegment FromEntity(IMessageEntity entity)
{
if (entity is not MentionEntity mentionEntity) throw new ArgumentException("Invalid entity type.");

View file

@ -5,20 +5,19 @@ using Lagrange.Core.Message.Entity;
namespace Lagrange.OneBot.Core.Message.Entity;
[Serializable]
public partial class FaceSegment
public partial class FaceSegment(int id)
{
public FaceSegment(int id) => Id = id.ToString();
[JsonPropertyName("id")] public string Id { get; set; }
public FaceSegment() : this(0) { }
[JsonPropertyName("id")] public string Id { get; set; } = id.ToString();
}
[SegmentSubscriber(typeof(FaceSegment), "face")]
public partial class FaceSegment : ISegment
{
string ISegment.Type => "face";
public IMessageEntity ToEntity() => new FaceEntity(ushort.Parse(Id), false);
public ISegment FromMessageEntity(IMessageEntity entity)
public ISegment FromEntity(IMessageEntity entity)
{
if (entity is not FaceEntity faceEntity) throw new ArgumentException("Invalid entity type.");

View file

@ -0,0 +1,26 @@
using System.Text.Json.Serialization;
using Lagrange.Core.Message;
using Lagrange.Core.Message.Entity;
namespace Lagrange.OneBot.Core.Message.Entity;
[Serializable]
public partial class ImageSegment(string url)
{
public ImageSegment() : this("") { }
[JsonPropertyName("file")] public string Url { get; set; } = url;
}
[SegmentSubscriber(typeof(ImageEntity), "image")]
public partial class ImageSegment : ISegment
{
public IMessageEntity ToEntity() => new ImageEntity(url);
public ISegment FromEntity(IMessageEntity entity)
{
if (entity is not ImageEntity imageEntity) throw new ArgumentException("Invalid entity type.");
return new ImageSegment(imageEntity.ImageUrl);
}
}

View file

@ -5,20 +5,19 @@ using Lagrange.Core.Message.Entity;
namespace Lagrange.OneBot.Core.Message.Entity;
[Serializable]
public partial class TextSegment
public partial class TextSegment(string text)
{
public TextSegment(string text) => Text = text;
public TextSegment() : this("") { }
[JsonPropertyName("text")] public string Text { get; set; }
[JsonPropertyName("text")] public string Text { get; set; } = text;
}
[SegmentSubscriber(typeof(TextEntity), "text")]
public partial class TextSegment : ISegment
{
string ISegment.Type => "text";
public IMessageEntity ToEntity() => new TextEntity(Text);
public ISegment FromMessageEntity(IMessageEntity entity)
public ISegment FromEntity(IMessageEntity entity)
{
if (entity is not TextEntity textEntity) throw new ArgumentException("Invalid entity type.");

View file

@ -1,12 +1,11 @@
using System.Text.Json.Serialization;
using Lagrange.Core.Message;
namespace Lagrange.OneBot.Core.Message;
public interface ISegment
{
internal string Type { get; }
public IMessageEntity ToEntity();
public ISegment FromMessageEntity(IMessageEntity entity);
public ISegment FromEntity(IMessageEntity entity);
}

View file

@ -1,45 +0,0 @@
using System.Reflection;
using Lagrange.Core;
using Lagrange.Core.Internal.Event.EventArg;
namespace Lagrange.OneBot.Core.Message;
/// <summary>
/// The class that converts the OneBot message to/from MessageEntity of Lagrange.Core
/// </summary>
public sealed class MessageConverter
{
public Dictionary<Type, Type> EntityToSegment { get; set; }
public MessageConverter(BotContext bot, ILagrangeWebService service)
{
var invoker = bot.Invoker;
invoker.OnFriendMessageReceived += OnFriendMessageReceived;
invoker.OnGroupMessageReceived += OnGroupMessageReceived;
invoker.OnTempMessageReceived += OnTempMessageReceived;
EntityToSegment = new Dictionary<Type, Type>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
if (type.IsSubclassOf(typeof(ISegment)))
{
}
}
}
private void OnFriendMessageReceived(BotContext bot, FriendMessageEvent e)
{
}
private void OnGroupMessageReceived(BotContext bot, GroupMessageEvent e)
{
}
private void OnTempMessageReceived(BotContext bot, TempMessageEvent e)
{
}
}

View file

@ -0,0 +1,82 @@
using System.Reflection;
using Lagrange.Core;
using Lagrange.Core.Common.Interface.Api;
using Lagrange.Core.Internal.Event.EventArg;
using Lagrange.Core.Message;
using Lagrange.Core.Utility.Extension;
using Lagrange.OneBot.Core.Entity.Message;
namespace Lagrange.OneBot.Core.Message;
/// <summary>
/// The class that converts the OneBot message to/from MessageEntity of Lagrange.Core
/// </summary>
public sealed class MessageService
{
private readonly ILagrangeWebService _service;
public Dictionary<Type, (string, ISegment)> EntityToSegment { get; set; }
public MessageService(BotContext bot, ILagrangeWebService service)
{
_service = service;
var invoker = bot.Invoker;
invoker.OnFriendMessageReceived += OnFriendMessageReceived;
invoker.OnGroupMessageReceived += OnGroupMessageReceived;
invoker.OnTempMessageReceived += OnTempMessageReceived;
EntityToSegment = new Dictionary<Type, (string, ISegment)>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var attribute = type.GetCustomAttribute<SegmentSubscriberAttribute>();
if (attribute != null)
{
EntityToSegment[attribute.Entity] = (attribute.Type, (ISegment)type.CreateInstance(false));
}
}
}
private void OnFriendMessageReceived(BotContext bot, FriendMessageEvent e)
{
var request = new OneBotPrivateMsg(bot.UpdateKeystore().Uin)
{
MessageId = 0,
UserId = e.Chain.FriendUin,
GroupSender = new OneBotSender
{
},
Message = Convert(e.Chain)
};
_service.SendJsonAsync(request);
}
private void OnGroupMessageReceived(BotContext bot, GroupMessageEvent e)
{
var request = new OneBotGroupMsg(bot.UpdateKeystore().Uin, e.Chain.GroupUin ?? 0,Convert(e.Chain),
e.Chain.GroupMemberInfo ?? throw new Exception("Group member not found"));
_service.SendJsonAsync(request);
}
private void OnTempMessageReceived(BotContext bot, TempMessageEvent e)
{
// TODO: Implement temp msg
}
private List<OneBotSegment> Convert(IEnumerable<IMessageEntity> entities)
{
var result = new List<OneBotSegment>();
foreach (var entity in entities)
{
if (EntityToSegment.TryGetValue(entity.GetType(), out var instance))
{
result.Add(new OneBotSegment(instance.Item1, instance.Item2.FromEntity(entity)));
}
}
return result;
}
}

View file

@ -0,0 +1,9 @@
namespace Lagrange.OneBot.Core.Message;
[AttributeUsage(AttributeTargets.Class)]
public class SegmentSubscriberAttribute(Type entity, string type) : Attribute
{
public Type Entity { get; } = entity;
public string Type { get; } = type;
}

View file

@ -1,4 +1,5 @@
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using Lagrange.OneBot.Core.Entity.Meta;
using Microsoft.Extensions.Configuration;
@ -30,10 +31,13 @@ public sealed class ReverseWSService : ILagrangeWebService
{
var socket = new ClientWebSocket();
SetRequestHeader(socket, new Dictionary<string, string>
{
{ "X-Client-Role", "Universal" },
{ "X-Self-ID", _config.GetValue<uint>("Account:Uin").ToString() },
{ "User-Agent", Constant.OneBotImpl }
});
socket.Options.KeepAliveInterval = TimeSpan.FromSeconds(5);
socket.Options.SetRequestHeader("X-Client-Role", "Universal");
socket.Options.SetRequestHeader("X-Self-ID", _config.GetValue<uint>("Account:Uin").ToString());
socket.Options.SetRequestHeader("User-Agent", Constant.OneBotImpl);
if (_config["AccessToken"] != null) socket.Options.SetRequestHeader("Authorization", $"Bearer {_config["AccessToken"]}");
return socket;
@ -61,14 +65,23 @@ public sealed class ReverseWSService : ILagrangeWebService
public Task SendJsonAsync<T>(T json, CancellationToken cancellationToken = default)
{
var payload = JsonSerializer.SerializeToUtf8Bytes(json);
Console.WriteLine(Encoding.UTF8.GetString(payload));
return _socket.SendInstant(payload);
}
private void OnHeartbeat(object? sender)
{
var status = new OneBotStatus(true, true);
var heartBeat = new OneBotHeartBeat(_config.GetValue<uint>("Account:Uin"), _config.GetValue<int>("Implementation:ReverseWebSocket:HeartBeatInterval"), status);
var heartBeat = new OneBotHeartBeat(
_config.GetValue<uint>("Account:Uin"),
_config.GetValue<int>("Implementation:ReverseWebSocket:HeartBeatInterval"),
status);
SendJsonAsync(heartBeat);
}
private static void SetRequestHeader(ClientWebSocket webSocket, Dictionary<string, string> headers)
{
foreach (var (key, value) in headers) webSocket.Options.SetRequestHeader(key, value);
}
}

View file

@ -2,7 +2,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View file

@ -26,14 +26,14 @@ public class LagrangeApp : IHost
public ILagrangeWebService WebService => Services.GetRequiredService<ILagrangeWebService>();
public MessageConverter Converter { get; set; }
public MessageService Service { get; set; }
internal LagrangeApp(IHost host)
{
_hostApp = host;
Logger = Services.GetRequiredService<ILogger<LagrangeApp>>();
Converter = new MessageConverter(Instance, WebService);
Service = new MessageService(Instance, WebService);
}
public async Task StartAsync(CancellationToken cancellationToken = new())