diff --git a/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs b/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs new file mode 100644 index 000000000..aa9f9aa5c --- /dev/null +++ b/Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.Core.Common.Entity +{ + [Serializable] + public class ImageOcrResult + { + [JsonPropertyName("texts")] public List Texts { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + public ImageOcrResult(List texts, string language) + { + Texts = texts; + Language = language; + } + } + + [Serializable] + public class TextDetection + { + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("confidence")] public int Confidence { get; set; } + + [JsonPropertyName("coordinates")] public List Coordinates { get; set; } + + public TextDetection(string text, int confidence, List coordinates) + { + Text = text; + Confidence = confidence; + Coordinates = coordinates; + } + } + + [Serializable] + public class Coordinate + { + [JsonPropertyName("x")] public int X { get; set; } + + [JsonPropertyName("y")] public int Y { get; set; } + + public Coordinate(int x, int y) + { + X = x; + Y = y; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Common/Interface/Api/OperationExt.cs b/Lagrange.Core/Common/Interface/Api/OperationExt.cs index a597ca88c..9b14cd765 100644 --- a/Lagrange.Core/Common/Interface/Api/OperationExt.cs +++ b/Lagrange.Core/Common/Interface/Api/OperationExt.cs @@ -269,5 +269,14 @@ public static Task GroupJoinEmojiChain(this BotContext bot, uint groupUin, public static Task FriendJoinEmojiChain(this BotContext bot, uint friendUin, uint emojiId, uint targetMessageSeq) => bot.ContextCollection.Business.OperationLogic.FriendJoinEmojiChain(friendUin, emojiId, targetMessageSeq); + + public static Task UploadImage(this BotContext bot, ImageEntity entity) + => bot.ContextCollection.Business.OperationLogic.UploadImage(entity); + + public static Task OcrImage(this BotContext bot, string url) + => bot.ContextCollection.Business.OperationLogic.ImageOcr(url); + + public static Task OcrImage(this BotContext bot, ImageEntity entity) + => bot.ContextCollection.Business.OperationLogic.ImageOcr(entity); } diff --git a/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs b/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs new file mode 100644 index 000000000..95808da16 --- /dev/null +++ b/Lagrange.Core/Event/EventArg/GroupMemberEnterEvent.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Event.EventArg; + +public class GroupMemberEnterEvent : EventBase +{ + public uint GroupUin { get; } + + public uint GroupMemberUin { get; } + + public uint StyleId { get; } + + internal GroupMemberEnterEvent(uint groupUin, uint groupMemberUin, uint styleId) + { + GroupUin = groupUin; + GroupMemberUin = groupMemberUin; + StyleId = styleId; + + EventMessage = $"[{nameof(GroupMemberEnterEvent)}]: {GroupMemberUin} Enter {GroupUin} | {StyleId}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Event/EventInvoker.Events.cs b/Lagrange.Core/Event/EventInvoker.Events.cs index 30c55da53..f8e73f378 100644 --- a/Lagrange.Core/Event/EventInvoker.Events.cs +++ b/Lagrange.Core/Event/EventInvoker.Events.cs @@ -57,4 +57,6 @@ public partial class EventInvoker public event LagrangeEvent? OnGroupNameChangeEvent; public event LagrangeEvent? OnGroupTodoEvent; + + public event LagrangeEvent? OnGroupMemberEnterEvent; } \ No newline at end of file diff --git a/Lagrange.Core/Event/EventInvoker.cs b/Lagrange.Core/Event/EventInvoker.cs index 98af01f84..273d5de76 100644 --- a/Lagrange.Core/Event/EventInvoker.cs +++ b/Lagrange.Core/Event/EventInvoker.cs @@ -43,6 +43,7 @@ internal EventInvoker(BotContext context) RegisterEvent((GroupReactionEvent e) => OnGroupReactionEvent?.Invoke(context, e)); RegisterEvent((GroupNameChangeEvent e) => OnGroupNameChangeEvent?.Invoke(context, e)); RegisterEvent((GroupTodoEvent e) => OnGroupTodoEvent?.Invoke(context, e)); + RegisterEvent((GroupMemberEnterEvent e) => OnGroupMemberEnterEvent?.Invoke(context, e)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs index c82399ff8..e0cce9231 100644 --- a/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs +++ b/Lagrange.Core/Internal/Context/Logic/Implementation/MessagingLogic.cs @@ -37,6 +37,7 @@ namespace Lagrange.Core.Internal.Context.Logic.Implementation; [EventSubscribe(typeof(GroupSysNameChangeEvent))] [EventSubscribe(typeof(FriendSysRecallEvent))] [EventSubscribe(typeof(FriendSysRequestEvent))] +[EventSubscribe(typeof(GroupSysMemberEnterEvent))] [EventSubscribe(typeof(FriendSysPokeEvent))] [EventSubscribe(typeof(LoginNotifyEvent))] [EventSubscribe(typeof(MultiMsgDownloadEvent))] @@ -159,6 +160,12 @@ public override async Task Incoming(ProtocolEvent e) Collection.Invoker.PostEvent(requestArgs); break; } + case GroupSysMemberEnterEvent info: + { + var @event = new GroupMemberEnterEvent(info.GroupUin, info.GroupMemberUin, info.StyleId); + Collection.Invoker.PostEvent(@event); + break; + } case GroupSysMuteEvent groupMute: { uint? operatorUin = null; diff --git a/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs b/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs index 7d75364be..060d33bd3 100644 --- a/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs +++ b/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs @@ -724,4 +724,28 @@ public async Task FriendJoinEmojiChain(uint friendUin, uint emojiId, uint var results = await Collection.Business.SendEvent(friendJoinEmojiChainEvent); return results.Count != 0 && results[0].ResultCode == 0; } + + public async Task UploadImage(ImageEntity image) + { + await Collection.Highway.ManualUploadEntity(image); + var msgInfo = image.MsgInfo; + if (msgInfo is null) throw new Exception(); + var downloadEvent = ImageDownloadEvent.Create(Collection.Keystore.Uid ?? "", msgInfo); + var result = await Collection.Business.SendEvent(downloadEvent); + var ret = (ImageDownloadEvent)result[0]; + return ret.ImageUrl; + } + + public async Task ImageOcr(string imageUrl) + { + var imageOcrEvent = ImageOcrEvent.Create(imageUrl); + var results = await Collection.Business.SendEvent(imageOcrEvent); + return results.Count != 0 ? ((ImageOcrEvent)results[0]).ImageOcrResult : null; + } + + public async Task ImageOcr(ImageEntity image) + { + var imageUrl = await UploadImage(image); + return await ImageOcr(imageUrl); + } } \ No newline at end of file diff --git a/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs b/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs new file mode 100644 index 000000000..c41c046bd --- /dev/null +++ b/Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs @@ -0,0 +1,25 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class ImageOcrEvent : ProtocolEvent +{ + public string Url { get; } = string.Empty; + + public ImageOcrResult ImageOcrResult { get; } + + private ImageOcrEvent(string url) : base(true) + { + Url = url; + ImageOcrResult = new ImageOcrResult(new List(), ""); + } + + private ImageOcrEvent(int resultCode, ImageOcrResult result) : base(resultCode) + { + ImageOcrResult = result; + } + + public static ImageOcrEvent Create(string url) => new(url); + + public static ImageOcrEvent Result(int resultCode, ImageOcrResult result) => new(resultCode, result); +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs b/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs new file mode 100644 index 000000000..900915169 --- /dev/null +++ b/Lagrange.Core/Internal/Event/Notify/GroupSysMemberEnterEvent.cs @@ -0,0 +1,20 @@ +namespace Lagrange.Core.Internal.Event.Notify; + +internal class GroupSysMemberEnterEvent : ProtocolEvent +{ + public uint GroupUin { get; } + + public uint GroupMemberUin { get; } + + public uint StyleId { get; } + + private GroupSysMemberEnterEvent(uint groupUin, uint groupMemberUin, uint styleId) : base(0) + { + GroupUin = groupUin; + GroupMemberUin = groupMemberUin; + StyleId = styleId; + } + + public static GroupSysMemberEnterEvent Result(uint groupUin, uint groupMemberUin, uint styleId) => + new(groupUin, groupMemberUin, styleId); +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs b/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs new file mode 100644 index 000000000..2e08ae554 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Message/Notify/GroupMemberEnter.cs @@ -0,0 +1,62 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Internal.Packets.Message.Notify; + +[ProtoContract] +internal class GroupMemberEnter +{ + [ProtoMember(1)] public bool Field1 { get; set; } + + [ProtoMember(2)] public GroupMemberEnterContentBody Body { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterContentBody +{ + [ProtoMember(1)] public GroupMemberEnterContentBodyField1 Field1 { get; set; } + + [ProtoMember(2)] public uint GroupId { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public GroupMemberEnterInfo Info { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterContentBodyField1 +{ + [ProtoMember(1)] public uint Field1 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterInfo +{ + [ProtoMember(2)] public uint Field2 { get; set; } + + [ProtoMember(3)] public GroupMemberEnterDetail Detail { get; set; } + + [ProtoMember(4)] public uint Field4 { get; set; } + + [ProtoMember(5)] public uint Field5 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterDetail +{ + [ProtoMember(3)] public uint GroupMemberUin { get; set; } + [ProtoMember(4)] public uint GroupId { get; set; } + [ProtoMember(5)] public uint Field5 { get; set; } + [ProtoMember(6)] public uint Field6 { get; set; } + [ProtoMember(20)] public GroupMemberEnterStyle Style { get; set; } + [ProtoMember(21)] public uint Field21 { get; set; } +} + +[ProtoContract] +internal class GroupMemberEnterStyle +{ + [ProtoMember(1)] public uint StyleId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs new file mode 100644 index 000000000..4fc299c90 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp.0xE07_0.cs @@ -0,0 +1,41 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0xE07, 0)] +internal class OidbSvcTrpcTcp0xE07_0 +{ + [ProtoMember(1)] public uint Version { get; set; } + + [ProtoMember(2)] public uint Client { get; set; } + + [ProtoMember(3)] public uint Entrance { get; set; } + + [ProtoMember(10)] public OcrReqBody OcrReqBody { get; set; } +} + +[ProtoContract] +internal class OcrReqBody +{ + [ProtoMember(1)] public string ImageUrl { get; set; } + + [ProtoMember(2)] public uint LanguageType { get; set; } + + [ProtoMember(3)] public uint Scene { get; set; } + + [ProtoMember(10)] public string OriginMd5 { get; set; } + + [ProtoMember(11)] public string AfterCompressMd5 { get; set; } + + [ProtoMember(12)] public string AfterCompressFileSize { get; set; } + + [ProtoMember(13)] public string AfterCompressWeight { get; set; } + + [ProtoMember(14)] public string AfterCompressHeight { get; set; } + + [ProtoMember(15)] public bool IsCut { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs new file mode 100644 index 000000000..3c7b9fdad --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0xE07_0Response.cs @@ -0,0 +1,72 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE07_0_Response +{ + [ProtoMember(1)] public int RetCode { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } + + [ProtoMember(3)] public string Wording { get; set; } + + [ProtoMember(10)] public OcrRspBody OcrRspBody { get; set; } +} + +[ProtoContract] +internal class OcrRspBody +{ + [ProtoMember(1)] public List TextDetections { get; set; } + + [ProtoMember(2)] public string Language { get; set; } + + [ProtoMember(3)] public string RequestId { get; set; } + + [ProtoMember(101)] public List OcrLanguageList { get; set; } + + [ProtoMember(102)] public List DstTranslateLanguageList { get; set; } + + [ProtoMember(103)] public List LanguageList { get; set; } + + [ProtoMember(111)] public uint AfterCompressWeight { get; set; } + + [ProtoMember(112)] public uint AfterCompressHeight { get; set; } +} + +[ProtoContract] +internal class TextDetection +{ + [ProtoMember(1)] public string DetectedText { get; set; } + + [ProtoMember(2)] public uint Confidence { get; set; } + + [ProtoMember(3)] public Polygon Polygon { get; set; } + + [ProtoMember(4)] public string AdvancedInfo { get; set; } +} + +[ProtoContract] +internal class Polygon +{ + [ProtoMember(1)] public List Coordinates { get; set; } +} + +[ProtoContract] +internal class Coordinate +{ + [ProtoMember(1)] public int X { get; set; } + + [ProtoMember(2)] public int Y { get; set; } +} + +[ProtoContract] +internal class Language +{ + [ProtoMember(1)] public string LanguageCode { get; set; } + + [ProtoMember(2)] public string LanguageDesc { get; set; } +} diff --git a/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs b/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs new file mode 100644 index 000000000..fadf49ab6 --- /dev/null +++ b/Lagrange.Core/Internal/Service/Action/ImageOcrService.cs @@ -0,0 +1,57 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Entity; +using Lagrange.Core.Internal.Event; +using Lagrange.Core.Internal.Event.Action; +using Lagrange.Core.Internal.Packets.Service.Oidb; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(ImageOcrEvent))] +[Service("OidbSvcTrpcTcp.0xe07_0")] +internal class ImageOcrService : BaseService +{ + protected override bool Build(ImageOcrEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0xE07_0 + { + Version = 1, + Client = 0, + Entrance = 1, + OcrReqBody = new OcrReqBody + { + ImageUrl = input.Url, + OriginMd5 = "", + AfterCompressMd5 = "", + AfterCompressFileSize = "", + AfterCompressWeight = "", + AfterCompressHeight = "", + IsCut = false + } + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ImageOcrEvent output, out List? extraEvents) + { + var packet = Serializer.Deserialize>(input); + var response = new ImageOcrResult( + packet?.Body.OcrRspBody.TextDetections.Select(d => new Common.Entity.TextDetection( + d.DetectedText, + (int)d.Confidence, + d.Polygon.Coordinates.Select(c => new Common.Entity.Coordinate(c.X, c.Y)).ToList() + )).ToList() ?? new List(), + packet?.Body.OcrRspBody.Language ?? string.Empty + ); + output = ImageOcrEvent.Result(packet?.Body.RetCode ?? -1, response); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Service/Message/PushMessageService.cs b/Lagrange.Core/Internal/Service/Message/PushMessageService.cs index 71e678101..49b873849 100644 --- a/Lagrange.Core/Internal/Service/Message/PushMessageService.cs +++ b/Lagrange.Core/Internal/Service/Message/PushMessageService.cs @@ -245,11 +245,6 @@ private static void ProcessEvent0x210(Span payload, PushMsg msg, List(content.AsSpan()).Info is { } info) @@ -259,6 +254,21 @@ private static void ProcessEvent0x210(Span payload, PushMsg msg, List(content.AsSpan()); + if (info is { Body.Info.Detail: { Style: { } style } detail }) + { + var @event = GroupSysMemberEnterEvent.Result(detail.GroupId, detail.GroupMemberUin, style.StyleId); + extraEvents.Add(@event); + } + break; + } + case Event0x210SubType.FriendDeleteNotice: + { + // 0A8D010A4008AFB39FF80A1218755F54305768425A6368695A684555496253786F6F63474128AFB39FF80A3218755F54305768425A6368695A684555496253786F6F634741122108900410271827209092D9C10228F2C00330809096AF06609092D9C182808080021A260A0012220A2008001005721A0A18755F597831586B5A4E4E656E4E3141356A53423361576667 + break; + } case Event0x210SubType.FriendRecallNotice when msg.Message.Body?.MsgContent is { } content: { var recall = Serializer.Deserialize(content.AsSpan()); @@ -341,6 +351,7 @@ private enum Event0x2DCSubType16Field13 private enum Event0x210SubType { FriendRequestNotice = 35, + GroupMemberEnterNotice = 38, FriendDeleteNotice = 39, FriendRecallNotice = 138, FriendPokeNotice = 290, diff --git a/Lagrange.Core/Message/Entity/FaceEntity.cs b/Lagrange.Core/Message/Entity/FaceEntity.cs index 941f1b6d8..c3e35120b 100644 --- a/Lagrange.Core/Message/Entity/FaceEntity.cs +++ b/Lagrange.Core/Message/Entity/FaceEntity.cs @@ -11,13 +11,13 @@ public class FaceEntity : IMessageEntity { public ushort FaceId { get; set; } - public bool? IsLargeFace { get; set; } + public bool IsLargeFace { get; set; } public SysFaceEntry? SysFaceEntry { get; set; } public FaceEntity() { } - public FaceEntity(ushort faceId, bool? isLargeFace) + public FaceEntity(ushort faceId, bool isLargeFace) { FaceId = faceId; IsLargeFace = isLargeFace; @@ -25,7 +25,7 @@ public FaceEntity(ushort faceId, bool? isLargeFace) IEnumerable IMessageEntity.PackElement() { - if (IsLargeFace ?? false) + if (IsLargeFace) { var qBigFace = new QBigFaceExtra { @@ -106,5 +106,5 @@ IEnumerable IMessageEntity.PackElement() return null; } - public string ToPreviewString() => $"[Face][{(IsLargeFace ?? false ? "Large" : "Small")}]: {FaceId}"; + public string ToPreviewString() => $"[Face][{(IsLargeFace ? "Large" : "Small")}]: {FaceId}"; } \ No newline at end of file diff --git a/Lagrange.Core/Message/MessageBuilder.cs b/Lagrange.Core/Message/MessageBuilder.cs index 176a5ab82..c81d6cde0 100644 --- a/Lagrange.Core/Message/MessageBuilder.cs +++ b/Lagrange.Core/Message/MessageBuilder.cs @@ -102,7 +102,7 @@ public MessageBuilder Mention(uint target, string? display = null) /// /// The id of emoji /// Is the emoji large - public MessageBuilder Face(ushort id, bool? isLarge = false) + public MessageBuilder Face(ushort id, bool isLarge = false) { var faceEntity = new FaceEntity(id, isLarge); _chain.Add(faceEntity); diff --git a/Lagrange.OneBot/Core/Entity/Action/OneBotOcrImage.cs b/Lagrange.OneBot/Core/Entity/Action/OneBotOcrImage.cs new file mode 100644 index 000000000..0c4cca5a7 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Action/OneBotOcrImage.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Entity.Action; + +[Serializable] +public class OneBotOcrImage +{ + [JsonPropertyName("image")] public string Image { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Ability/UploadImageOperation.cs b/Lagrange.OneBot/Core/Operation/Ability/UploadImageOperation.cs index 78e39a444..45b13ece5 100644 --- a/Lagrange.OneBot/Core/Operation/Ability/UploadImageOperation.cs +++ b/Lagrange.OneBot/Core/Operation/Ability/UploadImageOperation.cs @@ -1,6 +1,6 @@ using System.Text.Json.Nodes; using Lagrange.Core; -using Lagrange.Core.Internal.Event.Message; +using Lagrange.Core.Common.Interface.Api; using Lagrange.Core.Message.Entity; using Lagrange.OneBot.Core.Entity.Action; using Lagrange.OneBot.Message.Entity; @@ -15,15 +15,8 @@ public async Task HandleOperation(BotContext context, JsonNode? pa if (payload?["file"]?.ToString() is { } file && CommonResolver.ResolveStream(file) is { } stream) { var entity = new ImageEntity(stream); - await context.ContextCollection.Highway.ManualUploadEntity(entity); - var msgInfo = entity.MsgInfo; - if (msgInfo is null) throw new Exception(); - - var downloadEvent = ImageDownloadEvent.Create(context.ContextCollection.Keystore.Uid ?? "", msgInfo); - var result = await context.ContextCollection.Business.SendEvent(downloadEvent); - var ret = (ImageDownloadEvent)result[0]; - - return new OneBotResult(ret.ImageUrl, 0, "ok"); + var url = await context.UploadImage(entity); + return new OneBotResult(url, 0, "ok"); } throw new Exception(); diff --git a/Lagrange.OneBot/Core/Operation/Generic/OcrImageOperation.cs b/Lagrange.OneBot/Core/Operation/Generic/OcrImageOperation.cs new file mode 100644 index 000000000..b203c4688 --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Generic/OcrImageOperation.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.OneBot.Core.Entity.Action; +using Lagrange.OneBot.Core.Operation.Converters; +using Lagrange.OneBot.Message.Entity; + +namespace Lagrange.OneBot.Core.Operation.Generic; + + +[Operation(".ocr_image")] +[Operation("ocr_image")] +public class OcrImageOperation() : IOperation +{ + public async Task HandleOperation(BotContext context, JsonNode? payload) + { + if (payload.Deserialize(SerializerOptions.DefaultOptions) is { } data && CommonResolver.ResolveStream(data.Image) is { } stream) + { + var entity = new ImageEntity(stream); + var res = await context.OcrImage(entity); + return new OneBotResult(res, 0, "ok"); + } + throw new Exception(); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/LagrangeAppBuilder.cs b/Lagrange.OneBot/LagrangeAppBuilder.cs index e37aed50b..d0b3987e1 100644 --- a/Lagrange.OneBot/LagrangeAppBuilder.cs +++ b/Lagrange.OneBot/LagrangeAppBuilder.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Lagrange.Core.Common; using Lagrange.Core.Common.Interface; using Lagrange.Core.Utility.Sign; @@ -119,6 +118,12 @@ public LagrangeAppBuilder ConfigureOneBot() BsonMapper.Global.TrimWhitespace = false; BsonMapper.Global.EmptyStringToNull = false; + // Specify ctor for some classes + BsonMapper.Global.RegisterType( + LiteDbUtility.IMessageEntitySerialize, + LiteDbUtility.IMessageEntityDeserialize + ); + string path = Configuration["ConfigPath:Database"] ?? $"lagrange-{Configuration["Account:Uin"]}.db"; bool isFirstCreate = false; diff --git a/Lagrange.OneBot/Message/Entity/FaceSegment.cs b/Lagrange.OneBot/Message/Entity/FaceSegment.cs index 4fa52beea..8d86b3ff0 100644 --- a/Lagrange.OneBot/Message/Entity/FaceSegment.cs +++ b/Lagrange.OneBot/Message/Entity/FaceSegment.cs @@ -22,7 +22,7 @@ public partial class FaceSegment : SegmentBase public override void Build(MessageBuilder builder, SegmentBase segment) { - if (segment is FaceSegment faceSegment) builder.Face(ushort.Parse(faceSegment.Id), faceSegment.IsLarge); + if (segment is FaceSegment faceSegment) builder.Face(ushort.Parse(faceSegment.Id), faceSegment.IsLarge ?? false); } public override SegmentBase? FromEntity(MessageChain chain, IMessageEntity entity) diff --git a/Lagrange.OneBot/Utility/LiteDbUtility.cs b/Lagrange.OneBot/Utility/LiteDbUtility.cs new file mode 100644 index 000000000..006e566b5 --- /dev/null +++ b/Lagrange.OneBot/Utility/LiteDbUtility.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Message; +using Lagrange.Core.Message.Entity; +using LiteDB; + +namespace Lagrange.OneBot.Utility; + +public static class LiteDbUtility +{ + public static BsonValue IMessageEntitySerialize(IMessageEntity entity) + { + var type = entity.GetType(); + var result = BsonMapper.Global.Serialize(type, entity); + result["_type"] = new BsonValue(DefaultTypeNameBinder.Instance.GetName(type)); + return result; + } + + public static IMessageEntity IMessageEntityDeserialize(BsonValue bson) + { + if (!bson.IsDocument) throw new Exception("bson not BsonDocument"); + + var doc = bson.AsDocument; + if (!doc.TryGetValue("_type", out var typeBson) || !typeBson.IsString) + { + throw new Exception("no `_type` or `_type` not string"); + } + + var type = DefaultTypeNameBinder.Instance.GetType(typeBson.AsString); + + if (type == typeof(MarkdownEntity)) return MarkdownEntityDeserialize(doc); + + return (IMessageEntity)BsonMapper.Global.Deserialize(type, bson); + } + + private static MarkdownEntity MarkdownEntityDeserialize(BsonDocument doc) + { + if (!doc.TryGetValue("Data", out var dataBson) || !dataBson.IsDocument) + { + throw new Exception("no `Data` or `Data` not document"); + } + + var dataDocument = dataBson.AsDocument; + if (!dataDocument.TryGetValue("Content", out var contentBson) || !contentBson.IsString) + { + throw new InvalidCastException("no `Data.Content` or `Data.Content` not string"); + } + + return new(new MarkdownData() { Content = contentBson.AsString }); + } +} \ No newline at end of file