From 0f14c8cc3b46f5fe002ee84aea2b0b7cad682063 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Thu, 31 Oct 2024 17:34:13 +0800 Subject: [PATCH 1/3] [OneBot] Better LightApp parse (#666) --- .../Core/Entity/Common/LightApp.cs | 5 +++-- .../Operation/Converters/AutosizeConverter.cs | 1 + .../Operation/Converters/ForwardConverter.cs | 22 +++++++++++++++++++ .../Converters/NullIfEmptyConverter.cs | 20 +++++++++++++++++ .../Message/Entity/LocationSegment.cs | 2 +- 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 Lagrange.OneBot/Core/Operation/Converters/ForwardConverter.cs create mode 100644 Lagrange.OneBot/Core/Operation/Converters/NullIfEmptyConverter.cs diff --git a/Lagrange.OneBot/Core/Entity/Common/LightApp.cs b/Lagrange.OneBot/Core/Entity/Common/LightApp.cs index 0fad7fdc7..f2aa95e25 100644 --- a/Lagrange.OneBot/Core/Entity/Common/LightApp.cs +++ b/Lagrange.OneBot/Core/Entity/Common/LightApp.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using Lagrange.OneBot.Core.Operation.Converters; namespace Lagrange.OneBot.Core.Entity.Common; #pragma warning disable CS8618 @@ -14,9 +15,9 @@ public class LightApp [JsonPropertyName("from")] public long From { get; set; } - [JsonPropertyName("meta")] public Meta Meta { get; set; } + [JsonPropertyName("meta")] [JsonConverter(typeof(NullIfEmptyConverter))] public Meta Meta { get; set; } - [JsonPropertyName("extra")] public Extra? Extra { get; set; } + [JsonPropertyName("extra")] [JsonConverter(typeof(NullIfEmptyConverter))] public Extra? Extra { get; set; } [JsonPropertyName("prompt")] public string Prompt { get; set; } diff --git a/Lagrange.OneBot/Core/Operation/Converters/AutosizeConverter.cs b/Lagrange.OneBot/Core/Operation/Converters/AutosizeConverter.cs index 2bc8620e9..0bbc1512e 100644 --- a/Lagrange.OneBot/Core/Operation/Converters/AutosizeConverter.cs +++ b/Lagrange.OneBot/Core/Operation/Converters/AutosizeConverter.cs @@ -14,6 +14,7 @@ public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSer JsonTokenType.True => true, JsonTokenType.False => false, JsonTokenType.String when Utf8Parser.TryParse(reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan, out bool value, out _) => value, + JsonTokenType.String when Utf8Parser.TryParse(reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan, out int intValue, out _) => intValue != 0, JsonTokenType.Number when reader.TryGetInt32(out int val) => Convert.ToBoolean(val), _ => throw new JsonException() }; diff --git a/Lagrange.OneBot/Core/Operation/Converters/ForwardConverter.cs b/Lagrange.OneBot/Core/Operation/Converters/ForwardConverter.cs new file mode 100644 index 000000000..d3976174e --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Converters/ForwardConverter.cs @@ -0,0 +1,22 @@ +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Operation.Converters; + +public class ForwardConverter : JsonConverter +{ + public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.True => 1, + JsonTokenType.False => 0, + JsonTokenType.Number when reader.TryGetInt32(out int intValue) => intValue, + _ => throw new JsonException() + }; + } + + public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) => writer.WriteNumberValue(value); +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Converters/NullIfEmptyConverter.cs b/Lagrange.OneBot/Core/Operation/Converters/NullIfEmptyConverter.cs new file mode 100644 index 000000000..eae49f967 --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Converters/NullIfEmptyConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Operation.Converters; + +public class NullIfEmptyConverter : JsonConverter where T : class +{ + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.String when reader.ValueSpan.Length == 0 => null, + JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options), + _ => throw new JsonException() + }; + } + + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, options); +} \ No newline at end of file diff --git a/Lagrange.OneBot/Message/Entity/LocationSegment.cs b/Lagrange.OneBot/Message/Entity/LocationSegment.cs index f69b437a1..cad592b6a 100644 --- a/Lagrange.OneBot/Message/Entity/LocationSegment.cs +++ b/Lagrange.OneBot/Message/Entity/LocationSegment.cs @@ -24,7 +24,7 @@ public LocationSegment() : this(0f, 0f) { } [SegmentSubscriber(typeof(LightAppEntity), "location")] public partial class LocationSegment : SegmentBase { - private static readonly JsonSerializerOptions Options = new() { Converters = { new AutosizeConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + private static readonly JsonSerializerOptions Options = new() { Converters = { new AutosizeConverter(), new ForwardConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; public override void Build(MessageBuilder builder, SegmentBase segment) { From ddee04b38207605aaf6ec331f50ec3390e1169e4 Mon Sep 17 00:00:00 2001 From: DarkRRb <177549718+DarkRRb@users.noreply.github.com> Date: Sat, 2 Nov 2024 18:15:25 +0800 Subject: [PATCH 2/3] [OneBot] Supported BotOnline and BotOffline event (#671) --- .../Core/Entity/Notify/OneBotBotOffline.cs | 11 +++++++++++ .../Core/Entity/Notify/OneBotBotOnline.cs | 12 ++++++++++++ Lagrange.OneBot/Core/Notify/NotifyService.cs | 10 ++++++++++ 3 files changed, 33 insertions(+) create mode 100644 Lagrange.OneBot/Core/Entity/Notify/OneBotBotOffline.cs create mode 100644 Lagrange.OneBot/Core/Entity/Notify/OneBotBotOnline.cs diff --git a/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOffline.cs b/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOffline.cs new file mode 100644 index 000000000..951aec052 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOffline.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Entity.Notify; + +[Serializable] +public class OneBotBotOffline(uint selfId, string tag, string message) : OneBotNotify(selfId, "bot_offline") +{ + [JsonPropertyName("tag")] public string Tag { get; } = tag; + + [JsonPropertyName("message")] public string Message { get; } = message; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOnline.cs b/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOnline.cs new file mode 100644 index 000000000..0ffaacd3f --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Notify/OneBotBotOnline.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using static Lagrange.Core.Event.EventArg.BotOnlineEvent; + +namespace Lagrange.OneBot.Core.Entity.Notify; + +[Serializable] +public class OneBotBotOnline(uint selfId, OnlineReason reason) : OneBotNotify(selfId, "bot_online") +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonPropertyName("reason")] + public OnlineReason Reason { get; } = reason; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Notify/NotifyService.cs b/Lagrange.OneBot/Core/Notify/NotifyService.cs index a54ab1695..ea09b137b 100644 --- a/Lagrange.OneBot/Core/Notify/NotifyService.cs +++ b/Lagrange.OneBot/Core/Notify/NotifyService.cs @@ -240,5 +240,15 @@ await service.SendJsonAsync(new OneBotGroupNameChange( @event.Name )); }; + + bot.Invoker.OnBotOnlineEvent += async (bot, @event) => + { + await service.SendJsonAsync(new OneBotBotOnline(bot.BotUin, @event.Reason)); + }; + + bot.Invoker.OnBotOfflineEvent += async (bot, @event) => + { + await service.SendJsonAsync(new OneBotBotOffline(bot.BotUin, @event.Tag, @event.Message)); + }; } } From cdf493e9b32ffd04fe5187f3f83f2a267bf70050 Mon Sep 17 00:00:00 2001 From: Decrabbityyy <99632363+Decrabbityyy@users.noreply.github.com> Date: Sun, 3 Nov 2024 11:45:06 +0800 Subject: [PATCH 3/3] [All] AiRecord (#670) Co-authored-by: DarkRRb <177549718+DarkRRb@users.noreply.github.com> --- Lagrange.Core/Common/Entity/AiCharacter.cs | 32 ++++++++ .../Common/Interface/Api/OperationExt.cs | 29 ++++--- .../Logic/Implementation/OperationLogic.cs | 77 ++++++++++++++++--- .../Event/Action/FetchAiCharacterListEvent.cs | 32 ++++++++ .../Event/Action/GroupAiRecordEvent.cs | 38 +++++++++ .../Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs | 25 ++++++ .../Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs | 12 +++ .../Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs | 12 +++ .../OidbSvcTrpcTcp0x929B_0Response.cs | 16 ++++ .../OidbSvcTrpcTcp0x929C_0Response.cs | 19 +++++ .../OidbSvcTrpcTcp0x929D_0Response.cs | 26 +++++++ .../Action/FetchAiCharacterListService.cs | 51 ++++++++++++ .../Service/Action/GroupAiRecordService.cs | 49 ++++++++++++ .../Entity/Action/OneBotGetAiCharacters.cs | 11 +++ .../Core/Entity/Action/OneBotGetAiRecord.cs | 13 ++++ .../Core/Entity/OnebotAiCharacts.cs | 23 ++++++ .../Core/Operation/Generic/GetAiCharacters.cs | 26 +++++++ .../Operation/Group/GetAiRecordOperation.cs | 24 ++++++ .../Message/SendGroupAiRecordOperation.cs | 34 ++++++++ 19 files changed, 530 insertions(+), 19 deletions(-) create mode 100644 Lagrange.Core/Common/Entity/AiCharacter.cs create mode 100644 Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs create mode 100644 Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs create mode 100644 Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs create mode 100644 Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs create mode 100644 Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs create mode 100644 Lagrange.OneBot/Core/Entity/Action/OneBotGetAiCharacters.cs create mode 100644 Lagrange.OneBot/Core/Entity/Action/OneBotGetAiRecord.cs create mode 100644 Lagrange.OneBot/Core/Entity/OnebotAiCharacts.cs create mode 100644 Lagrange.OneBot/Core/Operation/Generic/GetAiCharacters.cs create mode 100644 Lagrange.OneBot/Core/Operation/Group/GetAiRecordOperation.cs create mode 100644 Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs diff --git a/Lagrange.Core/Common/Entity/AiCharacter.cs b/Lagrange.Core/Common/Entity/AiCharacter.cs new file mode 100644 index 000000000..50e908144 --- /dev/null +++ b/Lagrange.Core/Common/Entity/AiCharacter.cs @@ -0,0 +1,32 @@ +namespace Lagrange.Core.Common.Entity; + +[Serializable] +public class AiCharacter +{ + public string VoiceId { get; set; } + + public string CharacterName { get; set; } + + public string CharacterVoiceUrl { get; set; } + + public AiCharacter(string voiceId, string characterName, string characterVoiceUrl) + { + VoiceId = voiceId; + CharacterName = characterName; + CharacterVoiceUrl = characterVoiceUrl; + } +} + +[Serializable] +public class AiCharacterList +{ + public string Type { get; set; } + + public List Characters { get; set; } + + public AiCharacterList(string type, List characters) + { + Type = type; + Characters = characters; + } +} \ 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 d12d43acc..ff4f29870 100644 --- a/Lagrange.Core/Common/Interface/Api/OperationExt.cs +++ b/Lagrange.Core/Common/Interface/Api/OperationExt.cs @@ -212,7 +212,8 @@ public static Task GroupClockIn(this BotContext bot, uint public static Task MarkAsRead(this BotContext bot, MessageChain targetChain) { uint timestamp = (uint)(targetChain.Time - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - return bot.ContextCollection.Business.OperationLogic.MarkAsRead(targetChain.GroupUin ?? 0, targetChain.Uid, targetChain.Sequence, timestamp); + return bot.ContextCollection.Business.OperationLogic.MarkAsRead(targetChain.GroupUin ?? 0, targetChain.Uid, + targetChain.Sequence, timestamp); } public static Task UploadFriendFile(this BotContext bot, uint targetUin, FileEntity fileEntity) @@ -241,7 +242,7 @@ public static Task FriendShake(this BotContext bot, uint friendUi public static Task?> FetchMarketFaceKey(this BotContext bot, List faceIds) => bot.ContextCollection.Business.OperationLogic.FetchMarketFaceKey(faceIds); - + /// /// Set the avatar of the bot itself /// @@ -252,22 +253,32 @@ public static Task SetAvatar(this BotContext bot, ImageEntity avatar) public static Task FetchSuperFaceId(this BotContext bot, uint id) => bot.ContextCollection.Business.OperationLogic.FetchSuperFaceId(id); - + public static Task FetchFaceEntity(this BotContext bot, uint id) => bot.ContextCollection.Business.OperationLogic.FetchFaceEntity(id); - + public static Task GroupJoinEmojiChain(this BotContext bot, uint groupUin, uint emojiId, uint targetMessageSeq) => bot.ContextCollection.Business.OperationLogic.GroupJoinEmojiChain(groupUin, emojiId, targetMessageSeq); - public static Task FriendJoinEmojiChain(this BotContext bot, uint friendUin, uint emojiId, uint targetMessageSeq) + 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); -} + + public static Task<(int Code, string ErrMsg, string? Url)> GetGroupGenerateAiRecordUrl(this BotContext bot, uint groupUin, string character, string text) + => bot.ContextCollection.Business.OperationLogic.GetGroupGenerateAiRecordUrl(groupUin, character, text); + + public static Task<(int Code, string ErrMsg, RecordEntity? RecordEntity)> GetGroupGenerateAiRecord(this BotContext bot, uint groupUin, string character, string text) + => bot.ContextCollection.Business.OperationLogic.GetGroupGenerateAiRecord(groupUin, character, text); + + public static Task<(int Code, string ErrMsg, List? Result)> GetAiCharacters(this BotContext bot, uint chatType, uint groupUin = 42) + => bot.ContextCollection.Business.OperationLogic.GetAiCharacters(chatType, groupUin); +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs b/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs index 32359d0ca..86f40d126 100644 --- a/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs +++ b/Lagrange.Core/Internal/Context/Logic/Implementation/OperationLogic.cs @@ -212,6 +212,7 @@ public async Task> FetchGroupFSList(uint groupUin, string targ if (((GroupFSListEvent)events[0]).IsEnd) break; startIndex += 20; } + return entries; } @@ -350,7 +351,8 @@ public async Task RecallFriendMessage(MessageChain chain) foreach (var result in resolved) { - var uins = await Task.WhenAll(ResolveUid(result.InvitorMemberUid), ResolveUid(result.TargetMemberUid), ResolveUid(result.OperatorUid)); + var uins = await Task.WhenAll(ResolveUid(result.InvitorMemberUid), ResolveUid(result.TargetMemberUid), + ResolveUid(result.OperatorUid)); uint invitorUin = uins[0]; uint targetUin = uins[1]; uint operatorUin = uins[2]; @@ -664,7 +666,8 @@ public async Task SetAvatar(ImageEntity avatar) var ticket = ((HighwayUrlEvent)highwayUrlResults[0]).SigSession; var md5 = avatar.ImageStream.Value.Md5().UnHex(); - return await Collection.Highway.UploadSrcByStreamAsync(90, avatar.ImageStream.Value, ticket, md5, Array.Empty()); + return await Collection.Highway.UploadSrcByStreamAsync(90, avatar.ImageStream.Value, ticket, md5, + Array.Empty()); } public async Task GroupSetAvatar(uint groupUin, ImageEntity avatar) @@ -697,18 +700,20 @@ public async Task GroupSetAvatar(uint groupUin, ImageEntity avatar) var ret = (FetchGroupAtAllRemainEvent)results[0]; return (ret.RemainAtAllCountForUin, ret.RemainAtAllCountForGroup); } - - public async Task FetchSuperFaceId(uint id) => await Collection.Business.CachingLogic.GetCachedIsSuperFaceId(id); - public async Task FetchFaceEntity(uint id) => await Collection.Business.CachingLogic.GetCachedFaceEntity(id); - + public async Task FetchSuperFaceId(uint id) => + await Collection.Business.CachingLogic.GetCachedIsSuperFaceId(id); + + public async Task FetchFaceEntity(uint id) => + await Collection.Business.CachingLogic.GetCachedFaceEntity(id); + public async Task GroupJoinEmojiChain(uint groupUin, uint emojiId, uint targetMessageSeq) { var groupJoinEmojiChainEvent = GroupJoinEmojiChainEvent.Create(targetMessageSeq, emojiId, groupUin); var results = await Collection.Business.SendEvent(groupJoinEmojiChainEvent); return results.Count != 0 && results[0].ResultCode == 0; } - + public async Task FriendJoinEmojiChain(uint friendUin, uint emojiId, uint targetMessageSeq) { string? friendUid = await Collection.Business.CachingLogic.ResolveUid(null, friendUin); @@ -717,7 +722,59 @@ 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<(int Code, string ErrMsg, string? Url)> GetGroupGenerateAiRecordUrl(uint groupUin, string character, string text) + { + var (code, errMsg, record) = await GetGroupGenerateAiRecord(groupUin, character, text); + if (code != 0) + return (code, errMsg, null); + + var recordGroupDownloadEvent = RecordGroupDownloadEvent.Create(groupUin, record!.MsgInfo!); + var @event = await Collection.Business.SendEvent(recordGroupDownloadEvent); + if (@event.Count == 0) return (-1, "running event missing!", null); + + var finalResult = (RecordGroupDownloadEvent)@event[0]; + return finalResult.ResultCode == 0 + ? (finalResult.ResultCode, string.Empty, finalResult.AudioUrl) + : (finalResult.ResultCode, "Failed to get group ai record", null); + } + + public async Task<(int Code, string ErrMsg, RecordEntity? Record)> GetGroupGenerateAiRecord(uint groupUin, string character, string text) + { + var groupAiRecordEvent = GroupAiRecordEvent.Create(groupUin, character, text); + while (true) + { + var results = await Collection.Business.SendEvent(groupAiRecordEvent); + if (results.Count == 0) return (-1, "running event missing!", null); + var aiRecordResult = (GroupAiRecordEvent)results[0]; + if (aiRecordResult.ResultCode != 0) + return (aiRecordResult.ResultCode, aiRecordResult.ErrorMessage, null); + if (aiRecordResult.RecordInfo is not null) + { + var index = aiRecordResult.RecordInfo!.MsgInfoBody[0].Index; + return (aiRecordResult.ResultCode, string.Empty, new RecordEntity(index.FileUuid, index.Info.FileName, index.Info.FileHash.UnHex()) + { + AudioLength = (int)index.Info.Time, + FileSha1 = index.Info.FileSha1, + MsgInfo = aiRecordResult.RecordInfo + }); + } + } + + } + + public async Task<(int Code, string ErrMsg, List? Result)> GetAiCharacters(uint chatType, uint groupUin) + { + var fetchAiRecordListEvent = FetchAiCharacterListEvent.Create(chatType, groupUin); + + var results = await Collection.Business.SendEvent(fetchAiRecordListEvent); + if (results.Count == 0) return (-1, "Event missing!", null); + + var result = (FetchAiCharacterListEvent)results[0]; + + return (result.ResultCode, result.ErrorMessage, result.AiCharacters); + } + public async Task UploadImage(ImageEntity image) { await Collection.Highway.ManualUploadEntity(image); @@ -728,14 +785,14 @@ public async Task UploadImage(ImageEntity image) 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); diff --git a/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs b/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs new file mode 100644 index 000000000..3c1bdd7a2 --- /dev/null +++ b/Lagrange.Core/Internal/Event/Action/FetchAiCharacterListEvent.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Common.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class FetchAiCharacterListEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + + public uint ChatType { get; set; } + + public List? AiCharacters { get; set; } + + public string ErrorMessage { get; set; }=string.Empty; + + private FetchAiCharacterListEvent(uint chatType, uint groupUin = 42) : base(true) + { + ChatType = chatType; + GroupUin = groupUin; + AiCharacters = new List(); + } + + private FetchAiCharacterListEvent(int resultCode, List? aiCharacters,string errMsg) : base(resultCode) + { + AiCharacters = aiCharacters; + ErrorMessage = errMsg; + } + + public static FetchAiCharacterListEvent Create(uint chatType, uint groupUin) => new(chatType, groupUin); + + public static FetchAiCharacterListEvent Result(int resultCode, List? aiCharacters,string errMsg) => + new(resultCode, aiCharacters, errMsg); +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs b/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs new file mode 100644 index 000000000..d5389204e --- /dev/null +++ b/Lagrange.Core/Internal/Event/Action/GroupAiRecordEvent.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using Lagrange.Core.Message.Entity; + +namespace Lagrange.Core.Internal.Event.Action; + +internal class GroupAiRecordEvent : ProtocolEvent +{ + public uint GroupUin { get; set; } + public string Character { get; set; }= string.Empty; + public string Text { get; set; }= string.Empty; + + public MsgInfo? RecordInfo { get; set; } + public string ErrorMessage { get; set; }= string.Empty; + + private GroupAiRecordEvent(uint groupUin, string character, string text) : base(0) + { + GroupUin = groupUin; + Character = character; + Text = text; + } + + private GroupAiRecordEvent(int resultCode, MsgInfo? msgInfo) : base(resultCode) + { + RecordInfo = msgInfo; + } + + private GroupAiRecordEvent(int resultCode, string errMsg) : base(resultCode) + { + ErrorMessage = errMsg; + } + + public static GroupAiRecordEvent Create(uint groupUin, string character, string text) => + new(groupUin, character, text); + + public static GroupAiRecordEvent Result(int resultCode, MsgInfo? msgInfo) => new(resultCode, msgInfo); + + public static GroupAiRecordEvent Result(int resultCode, string errMessage) => new(resultCode, errMessage); +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs new file mode 100644 index 000000000..cbf2c4f40 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929B_0.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +#pragma warning disable CS8618 +[ProtoContract] +[OidbSvcTrpcTcp(0x929B, 0)] +internal class OidbSvcTrpcTcp0x929B_0 +{ + [ProtoMember(1)] public uint GroupCode { get; set; } + + [ProtoMember(2)] public string VoiceId { get; set; } + + [ProtoMember(3)] public string Text { get; set; } + + [ProtoMember(4)] public uint ChatType { get; set; } = 1; //1 voice,2 song + + [ProtoMember(5)] private OidbSvcTrpcTcp0x929B_0ClientMsgInfo ClientMsgInfo { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929B_0ClientMsgInfo +{ + [ProtoMember(1)] private uint MsgRandom { get; set; } = 233; +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs new file mode 100644 index 000000000..c11af0d3f --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929C_0.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x929C, 0)] +internal class OidbSvcTrpcTcp0x929C_0 +{ + [ProtoMember(1)] public uint Group { get; set; } + + [ProtoMember(2)] public uint ChatType { get; set; } = 1; //1 voice,2 song +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs new file mode 100644 index 000000000..642b14269 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Request/OidbSvcTrpcTcp0x929D_0.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request; + +[ProtoContract] +[OidbSvcTrpcTcp(0x929D, 0)] +internal class OidbSvcTrpcTcp0x929D_0 +{ + [ProtoMember(1)] public uint Group { get; set; } + + [ProtoMember(2)] public uint ChatType { get; set; } //1 voice,2 song +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs new file mode 100644 index 000000000..27792eae3 --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929B_0Response.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Internal.Packets.Service.Oidb.Common; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929B_0Response +{ + [ProtoMember(1)] uint Field1 { get; set; } //1 complete ,2 wait + + [ProtoMember(2)] uint? Field2 { get; set; } //319 + + [ProtoMember(3)] uint Field3 { get; set; } //20 + + [ProtoMember(4)] public MsgInfo? MsgInfo { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs new file mode 100644 index 000000000..7a1f29d4e --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929C_0Response.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 +[ProtoContract] +internal class OidbSvcTrpcTcp0x929C_0Response +{ + [ProtoMember(1)] public List Property { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929C_0ResponseProperty +{ + [ProtoMember(1)] public string CharacterId { get; set; } + + [ProtoMember(2)] public string CharacterName { get; set; } + + [ProtoMember(3)] public string CharacterVoiceUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs new file mode 100644 index 000000000..a43eb06bb --- /dev/null +++ b/Lagrange.Core/Internal/Packets/Service/Oidb/Response/OidbSvcTrpcTcp0x929D_0Response.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response; +#pragma warning disable CS8618 +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0Response +{ + [ProtoMember(1)] public List Property { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0ResponseKey +{ + [ProtoMember(1)] public string Type { get; set; } + [ProtoMember(2)] public List Vulue { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x929D_0ResponseProperty +{ + [ProtoMember(1)] public string CharacterId { get; set; } + + [ProtoMember(2)] public string CharacterName { get; set; } + + [ProtoMember(3)] public string CharacterVoiceUrl { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs b/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs new file mode 100644 index 000000000..91ac8ec79 --- /dev/null +++ b/Lagrange.Core/Internal/Service/Action/FetchAiCharacterListService.cs @@ -0,0 +1,51 @@ +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(FetchAiCharacterListEvent))] +[Service("OidbSvcTrpcTcp.0x929d_0")] +internal class FetchAiCharacterListService : BaseService +{ + protected override bool Build(FetchAiCharacterListEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, + out Span output, out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase(new OidbSvcTrpcTcp0x929D_0() + { + Group = input.GroupUin, + ChatType = input.ChatType, + }); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out FetchAiCharacterListEvent output, out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + extraEvents = null; + if (payload.ErrorCode == 0) + { + + output = FetchAiCharacterListEvent.Result((int)payload.ErrorCode,payload.Body.Property + .Select(x => new AiCharacterList(x.Type, + x.Vulue.Select(x => new AiCharacter(x.CharacterId, x.CharacterName, x.CharacterVoiceUrl)).ToList()) + ).ToList(),string.Empty); + } + else + { + output = FetchAiCharacterListEvent.Result((int)payload.ErrorCode, null,payload.ErrorMsg); + } + + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs b/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs new file mode 100644 index 000000000..faa68b435 --- /dev/null +++ b/Lagrange.Core/Internal/Service/Action/GroupAiRecordService.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Common; +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.Common; +using Lagrange.Core.Internal.Packets.Service.Oidb.Request; +using Lagrange.Core.Internal.Packets.Service.Oidb.Response; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Internal.Service.Action; + +[EventSubscribe(typeof(GroupAiRecordEvent))] +[Service("OidbSvcTrpcTcp.0x929b_0")] +internal class GroupAiRecordService : BaseService +{ + protected override bool Build(GroupAiRecordEvent input, BotKeystore keystore, BotAppInfo appInfo, + BotDeviceInfo device, out Span output, + out List>? extraPackets) + { + var packet = new OidbSvcTrpcTcpBase( + new OidbSvcTrpcTcp0x929B_0 { GroupCode = input.GroupUin, VoiceId = input.Character, Text = input.Text } + ); + output = packet.Serialize(); + extraPackets = null; + return true; + } + + protected override bool Parse(Span input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out GroupAiRecordEvent output, + out List? extraEvents) + { + var payload = Serializer.Deserialize>(input); + + extraEvents = null; + if (payload.ErrorCode != 0) + { + output = GroupAiRecordEvent.Result((int)payload.ErrorCode, payload.ErrorMsg); + } + else + { + output = GroupAiRecordEvent.Result(0, payload.Body.MsgInfo); + } + + + return true; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiCharacters.cs b/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiCharacters.cs new file mode 100644 index 000000000..8867187c3 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiCharacters.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Entity.Action; + +[Serializable] +public class OneBotGetAiCharacters +{ + [JsonPropertyName("group_id")] public uint GroupId { get; set; } = 42; + + [JsonPropertyName("chat_type")] public uint ChatType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiRecord.cs b/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiRecord.cs new file mode 100644 index 000000000..e215b27f7 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Action/OneBotGetAiRecord.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Lagrange.OneBot.Core.Entity.Action; + +[Serializable] +public class OneBotGetAiRecord +{ + [JsonPropertyName("character")] public string Character { get; set; } = string.Empty; + + [JsonPropertyName("group_id")] public uint GroupId { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/OnebotAiCharacts.cs b/Lagrange.OneBot/Core/Entity/OnebotAiCharacts.cs new file mode 100644 index 000000000..116c8c4ae --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/OnebotAiCharacts.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using Lagrange.Core.Common.Entity; + +namespace Lagrange.OneBot.Core.Entity; + +[Serializable] +public class OneBotAiCharacter(AiCharacter aiCharacter) +{ + [JsonPropertyName("character_id")] public string CharacterId { get; set; } = aiCharacter.VoiceId; + + [JsonPropertyName("character_name")] public string CharacterName { get; set; } = aiCharacter.CharacterName; + + [JsonPropertyName("preview_url")] public string PreviewUrl { get; set; } = aiCharacter.CharacterVoiceUrl; +} + +public class OneBotAiCharacters(AiCharacterList aiCharacters) +{ + [JsonPropertyName("type")] public string Type { get; set; } = aiCharacters.Type; + + [JsonPropertyName("characters")] + public List Characters { get; set; } = + aiCharacters.Characters.Select(x => new OneBotAiCharacter(x)).ToList(); +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Generic/GetAiCharacters.cs b/Lagrange.OneBot/Core/Operation/Generic/GetAiCharacters.cs new file mode 100644 index 000000000..7d1fe2678 --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Generic/GetAiCharacters.cs @@ -0,0 +1,26 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.OneBot.Core.Entity; +using Lagrange.OneBot.Core.Entity.Action; +using Lagrange.OneBot.Core.Operation.Converters; + +namespace Lagrange.OneBot.Core.Operation.Generic; + +[Operation("get_ai_characters")] +public class GetAiCharacters : IOperation +{ + public async Task HandleOperation(BotContext context, JsonNode? payload) + { + var message = payload.Deserialize(SerializerOptions.DefaultOptions) + ?? throw new Exception(); + + var (code, errMsg, result) = await context.GetAiCharacters(message.ChatType, message.GroupId); + if (code != 0) return new(null, -1, "failed"); + + return result != null + ? new OneBotResult(result.Select(x => new OneBotAiCharacters(x)), 0, "ok") + : new OneBotResult(null, -1, "failed"); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Group/GetAiRecordOperation.cs b/Lagrange.OneBot/Core/Operation/Group/GetAiRecordOperation.cs new file mode 100644 index 000000000..3860878a5 --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Group/GetAiRecordOperation.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.OneBot.Core.Entity.Action; +using Lagrange.OneBot.Core.Operation.Converters; + +namespace Lagrange.OneBot.Core.Operation.Group; + +[Operation("get_ai_record")] +public class GetAiRecordOperation : IOperation +{ + public async Task HandleOperation(BotContext context, JsonNode? payload) + { + var message = payload.Deserialize(SerializerOptions.DefaultOptions); + if (message != null) + { + var (code, errMsg, url) = await context.GetGroupGenerateAiRecordUrl(message.GroupId, message.Character, message.Text); + return code != 0 ? new OneBotResult(errMsg, code, "failed") : new OneBotResult(url, 0, "ok"); + } + + throw new Exception(); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs b/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs new file mode 100644 index 000000000..c49ca5503 --- /dev/null +++ b/Lagrange.OneBot/Core/Operation/Message/SendGroupAiRecordOperation.cs @@ -0,0 +1,34 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core; +using Lagrange.Core.Common.Interface.Api; +using Lagrange.Core.Message; +using Lagrange.OneBot.Core.Entity.Action; +using Lagrange.OneBot.Core.Entity.Action.Response; +using Lagrange.OneBot.Core.Operation.Converters; +using Lagrange.OneBot.Database; + +namespace Lagrange.OneBot.Core.Operation.Message; + +[Operation("send_group_ai_record")] +public class GetAiRecordOperation : IOperation +{ + public async Task HandleOperation(BotContext context, JsonNode? payload) + { + var message = payload.Deserialize(SerializerOptions.DefaultOptions); + if (message != null) + { + (int code, string errMsg, var recordEntity) = await context.GetGroupGenerateAiRecord(message.GroupId, message.Character, message.Text); + if (code != 0 || recordEntity == null) return new OneBotResult(null, code, "failed"); + + + var chain = MessageBuilder.Group(message.GroupId).Add(recordEntity).Build(); + var result = await context.SendMessage(chain); + int hash = MessageRecord.CalcMessageHash(chain.MessageId, result.Sequence ?? 0); + + return new OneBotResult(new OneBotMessageResponse(hash), (int)result.Result, "ok"); + } + + throw new Exception(); + } +} \ No newline at end of file