├── .editorconfig ├── .github └── workflows │ ├── milky-build.yaml │ ├── native-api-build.yaml │ ├── nuget-push-lagrange-core.yaml │ ├── nuget-push-lagrange-proto-codegen.yaml │ └── nuget-push-lagrange-proto.yaml ├── .gitignore ├── Lagrange.Codec ├── AssemblyInfo.cs ├── AudioCodec.cs ├── AudioHelper.cs ├── Entities │ └── VideoInfo.cs ├── Exceptions │ └── CodecException.cs ├── Interop │ ├── AudioInterop.cs │ └── VideoInterop.cs ├── Lagrange.Codec.csproj ├── Streams │ ├── NativeCodecStream.cs │ ├── PCMStream.cs │ └── SilkStream.cs └── VideoCodec.cs ├── Lagrange.Core.NativeAPI.Test ├── Lagrange.Core.NativeAPI.Test.csproj ├── NativeModel │ ├── BotConfigStruct.cs │ ├── BotLogEventStruct.cs │ ├── BotQrCodeEventStruct.cs │ ├── ByteArrayNative.cs │ └── ReverseEventCountStruct.cs ├── Program.cs └── Wrapper.cs ├── Lagrange.Core.NativeAPI ├── Context.cs ├── Lagrange.Core.NativeAPI.csproj ├── NativeModel │ ├── Common │ │ ├── BotConfigStruct.cs │ │ ├── BotFriendCategoryStruct.cs │ │ ├── BotKeystoreStruct.cs │ │ ├── ByteArrayDictNative.cs │ │ ├── ByteArrayKVPNative.cs │ │ ├── ByteArrayNative.cs │ │ └── KeyValuePairNative.cs │ ├── Event │ │ ├── BotCaptchaEventStruct.cs │ │ ├── BotLogEventStruct.cs │ │ ├── BotLoginEventStruct.cs │ │ ├── BotMessageEventStruct.cs │ │ ├── BotNewDeviceVerifyEventStruct.cs │ │ ├── BotOnlineEventStruct.cs │ │ ├── BotQrCodeEventStruct.cs │ │ ├── BotQrCodeQueryEventStruct.cs │ │ ├── BotRefreshKeystoreEventStruct.cs │ │ ├── BotSMSEventStruct.cs │ │ ├── IEventStruct.cs │ │ └── ReverseEventCountStruct.cs │ ├── Message │ │ ├── BotFriendStruct.cs │ │ ├── BotGroupMemberStruct.cs │ │ ├── BotGroupStruct.cs │ │ ├── BotMessageStruct.cs │ │ ├── BotStrangerStruct.cs │ │ ├── Entity │ │ │ ├── EntityType.cs │ │ │ ├── IEntityStruct.cs │ │ │ ├── ImageEntityStruct.cs │ │ │ ├── MentionEntityStruct.cs │ │ │ ├── MultiMsgEntityStruct.cs │ │ │ ├── RecordEntityStruct.cs │ │ │ ├── ReplyEntityStruct.cs │ │ │ ├── TextEntityStruct.cs │ │ │ └── VideoEntityStruct.cs │ │ └── TypedEntityStruct.cs │ └── StructExtensions.cs ├── Program.cs ├── ReverseEvent │ ├── Abstract │ │ └── ReverseEventBase.cs │ ├── BotCaptchaReverseEvent.cs │ ├── BotLogReverseEvent.cs │ ├── BotLoginReverseEvent.cs │ ├── BotMessageReverseEvent.cs │ ├── BotNewDeviceVerifyReverseEvent.cs │ ├── BotOnlineReverseEvent.cs │ ├── BotQrCodeQueryReverseEvent.cs │ ├── BotQrCodeReverseEvent.cs │ ├── BotRefreshKeystoreReverseEvent.cs │ ├── BotSMSReverseEvent.cs │ ├── EventEntryPoint.cs │ └── ReverseEventInvoker.cs ├── SendMessageContext.cs ├── SendMessageEntryPoint.cs └── StatusCode.cs ├── Lagrange.Core.Runner ├── InteropSignProvider.cs ├── Lagrange.Core.Runner.csproj ├── Program.cs └── QrCodeHelper.cs ├── Lagrange.Core.Test ├── AssemblyInfo.cs ├── Binary │ └── BinaryPacketTest.cs ├── Cryptography │ ├── AesGcmTest.cs │ ├── AesTest.cs │ ├── EcdhTest.cs │ ├── Sha1Test.cs │ └── TeaTest.cs └── Lagrange.Core.Test.csproj ├── Lagrange.Core.sln ├── Lagrange.Core ├── AssemblyInfo.cs ├── BotContext.cs ├── Common │ ├── BotAppInfo.cs │ ├── BotConfig.cs │ ├── BotGender.cs │ ├── BotInfo.cs │ ├── BotKeystore.cs │ ├── Entity │ │ ├── BotContact.cs │ │ ├── BotFriend.cs │ │ ├── BotFriendCategory.cs │ │ ├── BotGroup.cs │ │ ├── BotGroupMember.cs │ │ └── BotStranger.cs │ ├── IAndroidBotSignProvider.cs │ ├── IBotSignProvider.cs │ ├── Interface │ │ ├── BotExt.cs │ │ ├── BotFactory.cs │ │ ├── MessageExt.cs │ │ └── OperationExt.cs │ └── Response │ │ └── BotQrCodeInfo.cs ├── Events │ ├── EventArgs │ │ ├── BotCaptchaEvent.cs │ │ ├── BotLogEvent.cs │ │ ├── BotLoginEvent.cs │ │ ├── BotMessageEvent.cs │ │ ├── BotNewDeviceVerifyEvent.cs │ │ ├── BotOfflineEvent.cs │ │ ├── BotOnlineEvent.cs │ │ ├── BotQrCodeEvent.cs │ │ ├── BotQrCodeQueryEvent.cs │ │ ├── BotRefreshKeystoreEvent.cs │ │ └── BotSMSEvent.cs │ ├── EventBase.cs │ └── EventInvoker.cs ├── Exceptions │ ├── InvalidTargetException.cs │ ├── LagrangeException.cs │ ├── OperationException.cs │ └── ServiceNotFoundException.cs ├── Internal │ ├── Context │ │ ├── CacheContext.cs │ │ ├── EventContext.cs │ │ ├── HighwayContext.cs │ │ ├── PacketContext.cs │ │ ├── ServiceContext.cs │ │ └── SocketContext.cs │ ├── Events │ │ ├── EventSubscribeAttribute.cs │ │ ├── Login │ │ │ ├── CloseCodeEvent.cs │ │ │ ├── ExchangeEmpEvent.cs │ │ │ ├── KeyExchangeEvent.cs │ │ │ ├── LoginEvent.cs │ │ │ ├── NTLoginEvent.cs │ │ │ ├── TransEmpEvent.cs │ │ │ ├── UinResolveEvent.cs │ │ │ └── VerifyCodeEvent.cs │ │ ├── Message │ │ │ ├── GroupFSEvent.cs │ │ │ ├── GroupFileSendEvent.cs │ │ │ ├── LongMsgRecvEvent.cs │ │ │ ├── LongMsgSendEvent.cs │ │ │ ├── NTV2RichMediaDownloadEvent.cs │ │ │ ├── NTV2RichMediaUploadEvent.cs │ │ │ ├── NudgeEvent.cs │ │ │ ├── PushMessageEvent.cs │ │ │ └── SendMessageEvent.cs │ │ ├── ProtocolEvent.cs │ │ └── System │ │ │ ├── AliveEvent.cs │ │ │ ├── FetchClientKeyEvent.cs │ │ │ ├── FetchCookiesEvent.cs │ │ │ ├── FetchFriendsEvent.cs │ │ │ ├── FetchGroupMembersEvent.cs │ │ │ ├── FetchGroupsEvent.cs │ │ │ ├── FileUploadEvent.cs │ │ │ ├── HighwaySessionEvent.cs │ │ │ ├── InfoSyncEvent.cs │ │ │ ├── InfoSyncPushEvent.cs │ │ │ ├── KickEvent.cs │ │ │ ├── PushParamsEvent.cs │ │ │ ├── SsoHeartBeatEvent.cs │ │ │ └── SsoUnregisterEvent.cs │ ├── Logic │ │ ├── ILogic.cs │ │ ├── MessagingLogic.cs │ │ ├── OperationLogic.cs │ │ ├── PushLogic.cs │ │ └── WtExchangeLogic.cs │ ├── Network │ │ ├── CallbackClientListener.cs │ │ ├── ClientListener.SocketSession.cs │ │ ├── ClientListener.cs │ │ └── IClientListener.cs │ ├── Packets │ │ ├── Login │ │ │ ├── NTEcdh.cs │ │ │ ├── NTLogin.cs │ │ │ ├── NTNewDevice.cs │ │ │ ├── QrLogin.cs │ │ │ ├── QrLoginExtInfo.cs │ │ │ ├── Tlv.cs │ │ │ ├── TlvQrCode.cs │ │ │ └── WtLogin.cs │ │ ├── Message │ │ │ ├── Elem.cs │ │ │ ├── Extra.cs │ │ │ ├── GroupFileCommon.cs │ │ │ ├── LongMsgInterface.cs │ │ │ ├── NTMessageCommon.cs │ │ │ ├── NTPushService.cs │ │ │ └── NTSendMessage.cs │ │ ├── Service │ │ │ ├── FetchGroupMembers.cs │ │ │ ├── FetchGroups.cs │ │ │ ├── FileUploadExt.cs │ │ │ ├── Highway.cs │ │ │ ├── IncPull.cs │ │ │ ├── KickNT.cs │ │ │ ├── LightApp.cs │ │ │ ├── NTV2RichMedia.cs │ │ │ ├── NTV2RichMediaHighwayExt.cs │ │ │ ├── NTV2RichMediaReq.cs │ │ │ ├── NTV2RichMediaResp.cs │ │ │ ├── OfflineFileUpload.cs │ │ │ ├── Oidb.cs │ │ │ ├── Oidb_0x102A.cs │ │ │ ├── Oidb_0x6D6.cs │ │ │ ├── Oidb_0x6D9.cs │ │ │ ├── Oidb_0xED3.cs │ │ │ └── SubCmd0x501.cs │ │ ├── Struct │ │ │ ├── ServicePacker.cs │ │ │ ├── SsoPacker.cs │ │ │ ├── SsoPacket.cs │ │ │ ├── SsoReserveFields.cs │ │ │ └── StructBase.cs │ │ └── System │ │ │ ├── InfoSyncPush.cs │ │ │ ├── NTSsoHeartBeat.cs │ │ │ ├── PushParams.cs │ │ │ ├── SsoInfoSync.cs │ │ │ ├── SsoUnregister.cs │ │ │ └── ThirdPartyLoginResponse.cs │ └── Services │ │ ├── BaseService.cs │ │ ├── IService.cs │ │ ├── Login │ │ ├── EasyLoginService.cs │ │ ├── ExchangeEmpService.cs │ │ ├── KeyExchangeService.cs │ │ ├── LoginService.cs │ │ ├── NTLoginCommon.cs │ │ ├── NewDeviceLoginService.cs │ │ ├── PasswordLoginService.cs │ │ ├── QrLoginService.cs │ │ ├── RefreshA2Service.cs │ │ ├── RefreshTicketService.cs │ │ ├── TransEmpService.cs │ │ ├── UinResolveService.cs │ │ └── UnusualEasyLoginService.cs │ │ ├── Message │ │ ├── GroupFSService.cs │ │ ├── GroupFileSendService.cs │ │ ├── LongMsgRecvService.cs │ │ ├── LongMsgSendService.cs │ │ ├── NTV2RichMediaDownloadService.cs │ │ ├── NTV2RichMediaUploadService.cs │ │ ├── NudgeService.cs │ │ ├── PushMessageService.cs │ │ └── SendMessageService.cs │ │ ├── OidbService.cs │ │ ├── ServiceAttribute.cs │ │ └── System │ │ ├── AliveService.cs │ │ ├── FetchClientKeyService.cs │ │ ├── FetchCookiesService.cs │ │ ├── FetchFriendsService.cs │ │ ├── FetchGroupMembersService.cs │ │ ├── FetchGroupsService.cs │ │ ├── FileUploadService.cs │ │ ├── HighwaySessionService.cs │ │ ├── InfoSyncPushService.cs │ │ ├── InfoSyncService.cs │ │ ├── KickService.cs │ │ ├── PushParamsService.cs │ │ ├── SsoHeartBeatService.cs │ │ └── SsoUnregisterService.cs ├── Lagrange.Core.csproj ├── Message │ ├── BotMessage.Create.cs │ ├── BotMessage.cs │ ├── Entities │ │ ├── IMessageEntity.cs │ │ ├── ImageEntity.cs │ │ ├── MentionEntity.cs │ │ ├── MultiMsgEntity.cs │ │ ├── RecordEntity.cs │ │ ├── ReplyEntity.cs │ │ ├── RichMediaEntityBase.cs │ │ ├── TextEntity.cs │ │ └── VideoEntity.cs │ ├── MessageBuilder.cs │ ├── MessageChain.cs │ ├── MessagePacker.cs │ └── MessageType.cs └── Utility │ ├── AudioHelper.cs │ ├── Binary │ ├── BinaryPacket.cs │ ├── Prefix.cs │ └── SegmentBufferWriter.cs │ ├── BinaryHelper.cs │ ├── Compression │ ├── Common.cs │ └── ZCompression.cs │ ├── Cryptography │ ├── AesGcmProvider.cs │ ├── EcdhProvider.cs │ ├── PowProvider.cs │ ├── Sha1Stream.cs │ ├── TeaProvider.cs │ └── TriSha1Provider.cs │ ├── Extension │ ├── CollectionExt.cs │ ├── ProtocolExt.cs │ ├── ReflectionExt.cs │ ├── SocketExt.cs │ ├── StreamExt.cs │ └── TaskAwaitExt.cs │ ├── ImageHelper.cs │ ├── JsonHelper.cs │ ├── ProtoHelper.cs │ └── ProtocolHelper.cs ├── Lagrange.Milky.Implementation.Api.Generator ├── ApiHandlerInfo.cs ├── DiagnosticDescriptors.cs ├── Extension │ ├── AttributeSyntaxExtension.cs │ └── SymbolExtension.cs ├── Lagrange.Milky.Implementation.Api.Generator.csproj └── MilkyApiHandlerGenerator.cs ├── Lagrange.Milky ├── Constants.cs ├── Core │ ├── Configuration │ │ ├── CoreConfiguration.cs │ │ ├── LoginConfiguration.cs │ │ ├── ServerConfiguration.cs │ │ └── SignerConfiguration.cs │ ├── Extension │ │ ├── HostApplicationBuilderExtension.cs │ │ └── LoggerFilterOptionsExtension.cs │ ├── Service │ │ ├── CoreLoggerService.cs │ │ └── CoreLoginService.cs │ └── Utility │ │ ├── CaptchaResolver │ │ ├── ICaptchaResolver.cs │ │ ├── ManualCaptchaResolver.cs │ │ └── OnlineCaptchaResolver.cs │ │ ├── CoreJsonUtility.cs │ │ ├── QrCodeUtility.cs │ │ └── Signer.cs ├── Extension │ ├── HostApplicationBuilderExtension.cs │ └── ReaderWriterLockSlimExtension.cs ├── Implementation │ ├── Api │ │ ├── ApiAttribute.cs │ │ ├── ApiFailedResult.cs │ │ ├── ApiOkResult.cs │ │ ├── Exception │ │ │ └── ApiException.cs │ │ ├── Handler │ │ │ ├── File │ │ │ │ ├── DeleteGroupFileHandler.cs │ │ │ │ └── GetGroupFileDownloadUrlHandler.cs │ │ │ ├── Friend │ │ │ │ └── SendFriendNudgeHandler.cs │ │ │ ├── Group │ │ │ │ └── SendGroupNudgeHandler.cs │ │ │ ├── Message │ │ │ │ ├── SendGroupMessageHandler.cs │ │ │ │ └── SendPrivateMessageHandler.cs │ │ │ └── System │ │ │ │ ├── GetFriendInfoHandler.cs │ │ │ │ ├── GetFriendListHandler.cs │ │ │ │ ├── GetGroupInfoHandler.cs │ │ │ │ ├── GetGroupListHandler.cs │ │ │ │ ├── GetGroupMemberInfoHandler.cs │ │ │ │ ├── GetGroupMemberListHandler.cs │ │ │ │ ├── GetImplInfoHandler.cs │ │ │ │ └── GetLoginInfoHandler.cs │ │ └── IApiHandler.cs │ ├── Communication │ │ ├── MilkyHttpApiService.cs │ │ ├── MilkyWebHookEventService.cs │ │ └── MilkyWebSocketEventService.cs │ ├── Configuration │ │ ├── MilkyConfiguration.cs │ │ └── WebHookConfiguration.cs │ ├── Entity │ │ ├── Event │ │ │ └── BotOfflineEvent.cs │ │ ├── Friend.cs │ │ ├── FriendCategory.cs │ │ ├── Group.cs │ │ ├── GroupMember.cs │ │ ├── Message │ │ │ └── Incoming │ │ │ │ ├── FriendIncomingMessage.cs │ │ │ │ ├── GroupIncomingMessage.cs │ │ │ │ ├── IncomingMessageBase.cs │ │ │ │ └── TempIncomingMessage.cs │ │ └── Segment │ │ │ ├── Common │ │ │ └── Data │ │ │ │ ├── FaceData.cs │ │ │ │ ├── LightAppData.cs │ │ │ │ ├── MarketFaceData.cs │ │ │ │ ├── MentionAllData.cs │ │ │ │ ├── MentionData.cs │ │ │ │ ├── TextData.cs │ │ │ │ └── XmlData.cs │ │ │ ├── Incoming │ │ │ ├── Data │ │ │ │ ├── ForwardData.cs │ │ │ │ ├── ImageData.cs │ │ │ │ ├── RecordData.cs │ │ │ │ ├── ReplyData.cs │ │ │ │ └── VideoData.cs │ │ │ ├── IIncomingSegment.cs │ │ │ ├── IncomingFaceSegment.cs │ │ │ ├── IncomingForwardSegment.cs │ │ │ ├── IncomingImageSegment.cs │ │ │ ├── IncomingLightAppSegment.cs │ │ │ ├── IncomingMarketFaceSegment.cs │ │ │ ├── IncomingMentionAllSegment.cs │ │ │ ├── IncomingMentionSegment.cs │ │ │ ├── IncomingRecordSegment.cs │ │ │ ├── IncomingReplySegment.cs │ │ │ ├── IncomingSegmentBase.cs │ │ │ ├── IncomingTextSegment.cs │ │ │ ├── IncomingVideoSegment.cs │ │ │ └── IncomingXmlSegment.cs │ │ │ └── Outgoing │ │ │ ├── Data │ │ │ ├── ImageData.cs │ │ │ ├── RecordData.cs │ │ │ ├── ReplyData.cs │ │ │ └── VideoData.cs │ │ │ ├── IOutgoingSegment.cs │ │ │ ├── OutgoingFaceSegment.cs │ │ │ ├── OutgoingForwardSegment.cs │ │ │ ├── OutgoingImageSegment.cs │ │ │ ├── OutgoingMentionAllSegment.cs │ │ │ ├── OutgoingMentionSegment.cs │ │ │ ├── OutgoingRecordSegment.cs │ │ │ ├── OutgoingReplySegment.cs │ │ │ ├── OutgoingSegmentBase.cs │ │ │ ├── OutgoingTextSegment.cs │ │ │ └── OutgoingVideoSegment.cs │ ├── Event │ │ ├── Event.cs │ │ └── EventService.cs │ ├── Extension │ │ ├── HostApplicationBuilderExtension.cs │ │ └── ServiceCollectionExtension.cs │ └── Utility │ │ ├── Converter.Entity.cs │ │ ├── Converter.Event.cs │ │ ├── Converter.Message.cs │ │ ├── Converter.cs │ │ ├── MilkyJsonUtility.cs │ │ └── UriUtility.cs ├── Lagrange.Milky.csproj ├── Program.cs ├── README.md └── Resources │ ├── appsettings.jsonc │ └── appsettings_schema.json ├── Lagrange.Proto.Benchmark ├── AssemblyInfo.cs ├── Lagrange.Proto.Benchmark.csproj └── Program.cs ├── Lagrange.Proto.CodeGen ├── Commands │ ├── DumpCommand.cs │ └── GenerateCommand.cs ├── Format │ ├── ProtoLexer.cs │ ├── ProtoModel.cs │ ├── ProtoParser.cs │ ├── ProtoToken.cs │ └── ProtoWriter.cs ├── Lagrange.Proto.CodeGen.csproj ├── Program.cs └── Utility │ ├── SourceWriter.cs │ └── StringExt.cs ├── Lagrange.Proto.Generator ├── DiagnosticDescriptors.cs ├── Entity │ ├── ProtoFieldInfo.cs │ └── ProtoTypeInfo.cs ├── Lagrange.Proto.Generator.csproj ├── Properties │ └── launchSettings.json ├── ProtoSourceGenerator.Emitter.Measure.cs ├── ProtoSourceGenerator.Emitter.Serialize.cs ├── ProtoSourceGenerator.Emitter.TypeInfo.cs ├── ProtoSourceGenerator.Emitter.cs ├── ProtoSourceGenerator.Parser.cs ├── ProtoSourceGenerator.cs ├── SymbolResolver.cs └── Utility │ ├── Extension │ ├── RoslynExtension.cs │ └── TypeExtension.cs │ ├── ProtoHelper.cs │ ├── Reference │ └── NullableAttribute.cs │ └── SourceWriter.cs ├── Lagrange.Proto.Runner ├── AssemblyInfo.cs ├── Lagrange.Proto.Runner.csproj └── Program.cs ├── Lagrange.Proto.Test ├── CollectionTest.cs ├── EncoderTest.cs ├── Lagrange.Proto.Test.csproj ├── NodeTest.cs ├── ProtoTest.cs ├── ReflectionTest.cs ├── SegmentBufferTest.cs └── UnknownTest.cs ├── Lagrange.Proto ├── AssemblyInfo.cs ├── IProtoSerializable.cs ├── Lagrange.Proto.csproj ├── Nodes │ ├── ProtoArray.IList.cs │ ├── ProtoArray.cs │ ├── ProtoNode.Operators.cs │ ├── ProtoNode.To.cs │ ├── ProtoNode.cs │ ├── ProtoObject.IDictionary.cs │ ├── ProtoObject.cs │ ├── ProtoRawValue.cs │ ├── ProtoValue.CreateOverloads.cs │ └── ProtoValue.cs ├── Primitives │ ├── Lookup.cs │ ├── ProtoReader.cs │ ├── ProtoResolvable.cs │ ├── ProtoWriteHelper.cs │ ├── ProtoWriter.cs │ └── ProtoWriterCache.cs ├── ProtoConstants.cs ├── ProtoMemberAttribute.cs ├── ProtoPackableAttribute.cs ├── ProtoValueMemberAttribute.cs ├── Serialization │ ├── Converter │ │ ├── Collection │ │ │ ├── ProtoArrayConverter.cs │ │ │ ├── ProtoListConverter.cs │ │ │ └── ProtoRepeatedConverter.cs │ │ ├── Generic │ │ │ └── ProtoNullableConverter.cs │ │ ├── Nodes │ │ │ ├── ProtoArrayConverter.cs │ │ │ ├── ProtoNodeConverter.cs │ │ │ ├── ProtoObjectConverter.cs │ │ │ ├── ProtoRawValueConverter.cs │ │ │ └── ProtoValueConverter.cs │ │ ├── Object │ │ │ ├── ProtoErrorConverter.cs │ │ │ ├── ProtoObjectConverter.cs │ │ │ └── ProtoSerializableConverter.cs │ │ └── Value │ │ │ ├── ProtoBooleanConverter.cs │ │ │ ├── ProtoBytesConverter.cs │ │ │ ├── ProtoEnumConverter.cs │ │ │ ├── ProtoMemoryByteConverter.cs │ │ │ ├── ProtoMemoryCharConverter.cs │ │ │ ├── ProtoNumberConverter.cs │ │ │ ├── ProtoReadOnlyMemoryByteConverter.cs │ │ │ ├── ProtoReadOnlyMemoryCharConverter.cs │ │ │ └── ProtoStringConverter.cs │ ├── Metadata │ │ ├── MemberAccessor.cs │ │ ├── ProtoFieldInfo.cs │ │ ├── ProtoObjectInfo.cs │ │ ├── ProtoTypeResolver.Dynamic.cs │ │ ├── ProtoTypeResolver.WellKnownTypes.cs │ │ ├── ProtoTypeResolver.WellKnownTypes.tt │ │ ├── ProtoTypeResolver.cs │ │ ├── ReflectionEmitCachingMemberAccessor.Cache.cs │ │ ├── ReflectionEmitCachingMemberAccessor.cs │ │ ├── ReflectionEmitMemberAccessor.cs │ │ └── ReflectionMemberAccessor.cs │ ├── ProtoConverter.cs │ ├── ProtoNumberHandling.cs │ ├── ProtoSerializer.Deserialize.cs │ ├── ProtoSerializer.Helpers.cs │ ├── ProtoSerializer.Serialize.cs │ └── WireType.cs ├── ThrowHelper.cs └── Utility │ ├── ProtoHelper.cs │ └── SegmentBufferWriter.cs ├── Proto.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # VSCode 2 | /.vscode 3 | 4 | # CSharp 5 | /*/bin 6 | /*/obj 7 | 8 | # VS 9 | /.vs 10 | 11 | # Rider (and other IDEs that use .idea) 12 | /.idea 13 | /global.json -------------------------------------------------------------------------------- /Lagrange.Codec/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Lagrange.Core.Test")] -------------------------------------------------------------------------------- /Lagrange.Codec/Entities/VideoInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Lagrange.Codec.Entities; 5 | 6 | [DebuggerVisualizer("{ToString(),nq}")] 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct VideoInfo 9 | { 10 | public int Width; 11 | public int Height; 12 | public long Duration; 13 | 14 | public override string ToString() => $"Width: {Width}, Height: {Height}, Duration: {Duration}"; 15 | } -------------------------------------------------------------------------------- /Lagrange.Codec/Exceptions/CodecException.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Codec.Exceptions; 2 | 3 | public class CodecException(string message, Exception? innerException = null) : Exception(message, innerException) 4 | { 5 | public CodecException(Exception innerException) : this(innerException.Message, innerException) { } 6 | } -------------------------------------------------------------------------------- /Lagrange.Codec/Interop/AudioInterop.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Codec.Interop; 4 | 5 | internal static partial class AudioInterop 6 | { 7 | public delegate void AudioCodecCallback(IntPtr userData, IntPtr p, int len); 8 | 9 | [LibraryImport("LagrangeCodec", EntryPoint = "audio_to_pcm")] 10 | public static partial int AudioToPCM( 11 | IntPtr audioData, 12 | int dataLen, 13 | AudioCodecCallback callback, 14 | IntPtr userdata); 15 | 16 | [LibraryImport("LagrangeCodec", EntryPoint = "silk_decode")] 17 | public static partial int SilkDecode( 18 | IntPtr silkData, 19 | int dataLen, 20 | AudioCodecCallback callback, 21 | IntPtr userdata); 22 | 23 | [LibraryImport("LagrangeCodec", EntryPoint = "silk_encode")] 24 | public static partial int SilkEncode( 25 | IntPtr pcmData, 26 | int dataLen, 27 | AudioCodecCallback callback, 28 | IntPtr userdata); 29 | } -------------------------------------------------------------------------------- /Lagrange.Codec/Interop/VideoInterop.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Codec.Entities; 3 | 4 | namespace Lagrange.Codec.Interop; 5 | 6 | internal static partial class VideoInterop 7 | { 8 | [LibraryImport("LagrangeCodec", EntryPoint = "video_first_frame")] 9 | public static partial int VideoFirstFrame( 10 | IntPtr videoData, 11 | int dataLen, 12 | ref IntPtr output, 13 | ref int outputLen); 14 | 15 | [LibraryImport("LagrangeCodec", EntryPoint = "video_get_size")] 16 | public static partial int VideoGetSize( 17 | IntPtr videoData, 18 | int dataLen, 19 | ref VideoInfo output); 20 | } -------------------------------------------------------------------------------- /Lagrange.Codec/Lagrange.Codec.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Lagrange.Codec/Streams/PCMStream.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Codec.Interop; 2 | 3 | namespace Lagrange.Codec.Streams; 4 | 5 | public class PCMStream() : NativeCodecStream(AudioInterop.AudioToPCM); -------------------------------------------------------------------------------- /Lagrange.Codec/Streams/SilkStream.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Codec.Interop; 2 | 3 | namespace Lagrange.Codec.Streams; 4 | 5 | public class SilkEncodeStream() : NativeCodecStream(AudioInterop.SilkEncode); 6 | 7 | public class SilkDecodeStream() : NativeCodecStream(AudioInterop.SilkDecode); -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/Lagrange.Core.NativeAPI.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/NativeModel/BotConfigStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.Test.NativeModel 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct BotConfigStruct 7 | { 8 | public BotConfigStruct() 9 | { 10 | } 11 | 12 | public byte Protocol { get; set; } = 0b00000100; 13 | 14 | public bool AutoReconnect { get; set; } = true; 15 | 16 | public bool UseIPv6Network { get; set; } = false; 17 | 18 | public bool GetOptimumServer { get; set; } = true; 19 | 20 | public uint HighwayChunkSize { get; set; } = 1024 * 1024; 21 | 22 | public uint HighwayConcurrent { get; set; } = 4; 23 | 24 | public bool AutoReLogin { get; set; } = true; 25 | } 26 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/NativeModel/BotLogEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.Test.NativeModel 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct BotLogEventStruct 7 | { 8 | public BotLogEventStruct() { } 9 | 10 | public int Level = 0; 11 | public ByteArrayNative Tag = new(); 12 | public ByteArrayNative Message = new(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/NativeModel/BotQrCodeEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.Test.NativeModel 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct BotQrCodeEventStruct 7 | { 8 | public BotQrCodeEventStruct() { } 9 | 10 | public ByteArrayNative Url = new(); 11 | 12 | public ByteArrayNative Image = new(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/NativeModel/ReverseEventCountStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.Test.NativeModel; 4 | 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct ReverseEventCountStruct 7 | { 8 | public ReverseEventCountStruct() { } 9 | 10 | public int BotCaptchaEventCount = 0; 11 | public int BotLoginEventCount = 0; 12 | public int BotLogEventCount = 0; 13 | public int BotMessageEventCount = 0; 14 | public int BotNewDeviceVerifyEventCount = 0; 15 | public int BotOnlineEventCount = 0; 16 | public int BotQrCodeEventCount = 0; 17 | public int BotQrCodeQueryEventCount = 0; 18 | public int BotRefreshKeystoreEventCount = 0; 19 | public int BotSMSEventCount = 0; 20 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI.Test/Wrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.Test; 4 | 5 | public static class Wrapper 6 | { 7 | public const string DLL_NAME = "Lagrange.Core.NativeAPI.dll"; 8 | 9 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 10 | public static extern int Initialize(IntPtr botConfigPtr, IntPtr keystorePtr); 11 | 12 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 13 | public static extern int Start(int index); 14 | 15 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 16 | public static extern int Stop(int index); 17 | 18 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 19 | public static extern int FreeMemory(IntPtr ptr); 20 | 21 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 22 | public static extern IntPtr GetQrCodeEvent(int index); 23 | 24 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 25 | public static extern IntPtr GetEventCount(int index); 26 | 27 | [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] 28 | public static extern IntPtr GetBotLogEvent(int index); 29 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/Context.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.NativeAPI.ReverseEvent; 2 | 3 | namespace Lagrange.Core.NativeAPI 4 | { 5 | public class Context 6 | { 7 | public Context(BotContext botContext) 8 | { 9 | BotContext = botContext; 10 | EventInvoker = new ReverseEventInvoker(BotContext); 11 | SendMessageContext = new SendMessageContext(BotContext); 12 | } 13 | public BotContext BotContext { get; set; } 14 | public ReverseEventInvoker EventInvoker { get; set; } 15 | public SendMessageContext SendMessageContext { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/Lagrange.Core.NativeAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Common/BotFriendCategoryStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Lagrange.Core.Common.Entity; 3 | 4 | namespace Lagrange.Core.NativeAPI.NativeModel.Common 5 | { 6 | public struct BotFriendCategoryStruct 7 | { 8 | public BotFriendCategoryStruct() { } 9 | 10 | public int Id = 0; 11 | 12 | public ByteArrayNative Name = new(); 13 | 14 | public int Count = 0; 15 | 16 | public int SortId = 0; 17 | 18 | public static implicit operator BotFriendCategoryStruct(BotFriendCategory category) 19 | { 20 | return new BotFriendCategoryStruct() 21 | { 22 | Id = category.Id, 23 | Name = Encoding.UTF8.GetBytes(category.Name), 24 | Count = category.Count, 25 | SortId = category.SortId 26 | }; 27 | } 28 | 29 | public static implicit operator BotFriendCategory(BotFriendCategoryStruct category) 30 | { 31 | return new BotFriendCategory( 32 | category.Id, 33 | Encoding.UTF8.GetString(category.Name), 34 | category.Count, 35 | category.SortId 36 | ); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Common/ByteArrayDictNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.NativeModel.Common; 4 | 5 | public struct ByteArrayDictNative 6 | { 7 | public int Length; 8 | public IntPtr Data; 9 | 10 | public static implicit operator ByteArrayDictNative(ByteArrayKVPNative[] dict) 11 | { 12 | int size = Marshal.SizeOf(); 13 | IntPtr ptr = Marshal.AllocHGlobal(size * dict.Length); 14 | for (int i = 0; i < dict.Length; i++) 15 | { 16 | Marshal.StructureToPtr(dict[i], ptr + i * size, false); 17 | } 18 | 19 | return new ByteArrayDictNative { Length = dict.Length, Data = ptr }; 20 | } 21 | 22 | public static implicit operator ByteArrayKVPNative[](ByteArrayDictNative dict) 23 | { 24 | if (dict.Data == IntPtr.Zero || dict.Length == 0) 25 | { 26 | return []; 27 | } 28 | 29 | ByteArrayKVPNative[] result = new ByteArrayKVPNative[dict.Length]; 30 | int size = Marshal.SizeOf(); 31 | for (int i = 0; i < dict.Length; i++) 32 | { 33 | result[i] = Marshal.PtrToStructure(dict.Data + i * size); 34 | } 35 | 36 | Marshal.FreeHGlobal(dict.Data); 37 | 38 | return result; 39 | } 40 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Common/ByteArrayKVPNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | // ReSharper disable InconsistentNaming 3 | 4 | namespace Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | [StructLayout(LayoutKind.Sequential)] 7 | public struct ByteArrayKVPNative 8 | { 9 | public ByteArrayNative Key; 10 | public ByteArrayNative Value; 11 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Common/KeyValuePairNative.cs: -------------------------------------------------------------------------------- 1 | // using System.Runtime.InteropServices; 2 | // 3 | // // 这东西疑似用不了,搁置 4 | // namespace Lagrange.Core.NativeAPI.NativeModel.Common 5 | // { 6 | // [StructLayout(LayoutKind.Sequential)] 7 | // public struct KeyValuePairNative 8 | // { 9 | // public T1 Key; 10 | // public T2 Value; 11 | // } 12 | // } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotCaptchaEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Lagrange.Core.Events.EventArgs; 3 | using Lagrange.Core.NativeAPI.NativeModel.Common; 4 | 5 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 6 | { 7 | public struct BotCaptchaEventStruct : IEventStruct 8 | { 9 | public BotCaptchaEventStruct() { } 10 | 11 | public ByteArrayNative CaptchaUrl = new(); 12 | 13 | public static implicit operator BotCaptchaEventStruct(BotCaptchaEvent e) 14 | { 15 | return new BotCaptchaEventStruct() 16 | { 17 | CaptchaUrl = Encoding.UTF8.GetBytes(e.CaptchaUrl) 18 | }; 19 | } 20 | 21 | public static implicit operator BotCaptchaEvent(BotCaptchaEventStruct e) 22 | { 23 | return new BotCaptchaEvent(Encoding.UTF8.GetString(e.CaptchaUrl)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotLogEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Events.EventArgs; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotLogEventStruct : IEventStruct 10 | { 11 | public BotLogEventStruct() { } 12 | 13 | public int Level = 0; 14 | public ByteArrayNative Tag = new(); 15 | public ByteArrayNative Message = new(); 16 | 17 | public static implicit operator BotLogEventStruct(BotLogEvent e) 18 | { 19 | return new BotLogEventStruct() 20 | { 21 | Level = (int)e.Level, 22 | Tag = Encoding.UTF8.GetBytes(e.Tag), 23 | Message = Encoding.UTF8.GetBytes(e.Message) 24 | }; 25 | } 26 | 27 | public static implicit operator BotLogEvent(BotLogEventStruct e) 28 | { 29 | return new BotLogEvent( 30 | Encoding.UTF8.GetString(e.Tag), 31 | (LogLevel)e.Level, 32 | Encoding.UTF8.GetString(e.Message), 33 | null // TODO: Handle exception if needed 34 | ); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotLoginEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Events.EventArgs; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotLoginEventStruct : IEventStruct 10 | { 11 | public BotLoginEventStruct() { } 12 | public int State = 0; 13 | public ByteArrayNative Tag = new(); 14 | public ByteArrayNative Message = new(); 15 | 16 | public static implicit operator BotLoginEvent(BotLoginEventStruct e) 17 | { 18 | return new BotLoginEvent( 19 | e.State, 20 | (Encoding.UTF8.GetString(e.Tag), Encoding.UTF8.GetString(e.Message)) 21 | ); 22 | } 23 | 24 | public static implicit operator BotLoginEventStruct(BotLoginEvent e) 25 | { 26 | return new BotLoginEventStruct() 27 | { 28 | State = e.State, 29 | Tag = Encoding.UTF8.GetBytes(e.Error?.Tag ?? string.Empty), 30 | Message = Encoding.UTF8.GetBytes(e.Error?.Message ?? string.Empty) 31 | }; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotMessageEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Core.Events.EventArgs; 3 | using Lagrange.Core.NativeAPI.NativeModel.Common; 4 | using Lagrange.Core.NativeAPI.NativeModel.Message; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotMessageEventStruct : IEventStruct 10 | { 11 | public BotMessageEventStruct() { } 12 | 13 | public BotMessageStruct Message = new(); 14 | 15 | // 懒得写了,没意义 16 | // public static BotMessageEvent ToEvent(this BotMessageEventStruct e) 17 | // { 18 | // return new BotMessageEvent( 19 | // e.Message.ToMessage(), 20 | // Encoding.UTF8.GetString(e.RawMessage) 21 | // ); 22 | // } 23 | 24 | public static implicit operator BotMessageEventStruct(BotMessageEvent e) 25 | { 26 | return new BotMessageEventStruct() { Message = e.Message }; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotNewDeviceVerifyEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Events.EventArgs; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotNewDeviceVerifyEventStruct : IEventStruct 10 | { 11 | public BotNewDeviceVerifyEventStruct() { } 12 | 13 | public ByteArrayNative Url = new(); 14 | 15 | public static implicit operator BotNewDeviceVerifyEvent(BotNewDeviceVerifyEventStruct e) 16 | { 17 | return new BotNewDeviceVerifyEvent(Encoding.UTF8.GetString(e.Url)); 18 | } 19 | 20 | public static implicit operator BotNewDeviceVerifyEventStruct(BotNewDeviceVerifyEvent e) 21 | { 22 | return new BotNewDeviceVerifyEventStruct() { Url = Encoding.UTF8.GetBytes(e.Url) }; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotOnlineEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Core.Events.EventArgs; 3 | 4 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public struct BotOnlineEventStruct : IEventStruct 8 | { 9 | public BotOnlineEventStruct() { } 10 | 11 | public int Reason = 0; 12 | 13 | public static implicit operator BotOnlineEvent(BotOnlineEventStruct e) 14 | { 15 | return new BotOnlineEvent((BotOnlineEvent.Reasons)e.Reason); 16 | } 17 | 18 | public static implicit operator BotOnlineEventStruct(BotOnlineEvent e) 19 | { 20 | return new BotOnlineEventStruct() { Reason = (int)e.Reason }; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotQrCodeEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Events.EventArgs; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotQrCodeEventStruct : IEventStruct 10 | { 11 | public BotQrCodeEventStruct() { } 12 | 13 | public ByteArrayNative Url = new(); 14 | 15 | public ByteArrayNative Image = new(); 16 | 17 | public static implicit operator BotQrCodeEvent(BotQrCodeEventStruct e) 18 | { 19 | return new BotQrCodeEvent(Encoding.UTF8.GetString(e.Url), e.Image); 20 | } 21 | 22 | public static implicit operator BotQrCodeEventStruct(BotQrCodeEvent e) 23 | { 24 | return new BotQrCodeEventStruct() 25 | { 26 | Url = Encoding.UTF8.GetBytes(e.Url), 27 | Image = e.Image 28 | }; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotQrCodeQueryEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Core.Events.EventArgs; 3 | 4 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public struct BotQrCodeQueryEventStruct : IEventStruct 8 | { 9 | public BotQrCodeQueryEventStruct() { } 10 | 11 | public byte State = 0; 12 | 13 | public static implicit operator BotQrCodeQueryEvent(BotQrCodeQueryEventStruct e) 14 | { 15 | return new BotQrCodeQueryEvent((BotQrCodeQueryEvent.TransEmpState)e.State); 16 | } 17 | 18 | public static implicit operator BotQrCodeQueryEventStruct(BotQrCodeQueryEvent e) 19 | { 20 | return new BotQrCodeQueryEventStruct { State = (byte)e.State }; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotRefreshKeystoreEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Core.Events.EventArgs; 3 | using Lagrange.Core.NativeAPI.NativeModel.Common; 4 | 5 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 6 | { 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct BotRefreshKeystoreEventStruct : IEventStruct 9 | { 10 | public BotRefreshKeystoreEventStruct() { } 11 | 12 | public BotKeystoreStruct Keystore = new(); 13 | 14 | public static implicit operator BotRefreshKeystoreEvent(BotRefreshKeystoreEventStruct e) 15 | { 16 | return new BotRefreshKeystoreEvent(e.Keystore); 17 | } 18 | 19 | public static implicit operator BotRefreshKeystoreEventStruct(BotRefreshKeystoreEvent e) 20 | { 21 | return new BotRefreshKeystoreEventStruct() { Keystore = e.Keystore }; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/BotSMSEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Events.EventArgs; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotSMSEventStruct : IEventStruct 10 | { 11 | public BotSMSEventStruct() { } 12 | 13 | public ByteArrayNative Url = new(); 14 | 15 | public ByteArrayNative Phone = new(); 16 | 17 | public static implicit operator BotSMSEvent(BotSMSEventStruct e) 18 | { 19 | return new BotSMSEvent( 20 | Encoding.UTF8.GetString(e.Url), 21 | Encoding.UTF8.GetString(e.Phone) 22 | ); 23 | } 24 | 25 | public static implicit operator BotSMSEventStruct(BotSMSEvent e) 26 | { 27 | return new BotSMSEventStruct() 28 | { 29 | Url = Encoding.UTF8.GetBytes(e.Url ?? string.Empty), 30 | Phone = Encoding.UTF8.GetBytes(e.Phone) 31 | }; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/IEventStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 4 | { 5 | public interface IEventStruct 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Event/ReverseEventCountStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.NativeModel.Event 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct ReverseEventCountStruct 7 | { 8 | public ReverseEventCountStruct() { } 9 | 10 | public int BotCaptchaEventCount = 0; 11 | public int BotLoginEventCount = 0; 12 | public int BotLogEventCount = 0; 13 | public int BotMessageEventCount = 0; 14 | public int BotNewDeviceVerifyEventCount = 0; 15 | public int BotOnlineEventCount = 0; 16 | public int BotQrCodeEventCount = 0; 17 | public int BotQrCodeQueryEventCount = 0; 18 | public int BotRefreshKeystoreEventCount = 0; 19 | public int BotSMSEventCount = 0; 20 | } 21 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/BotStrangerStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Common.Entity; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Message 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BotStrangerStruct 10 | { 11 | public BotStrangerStruct() { } 12 | public long Uin = 0; 13 | public ByteArrayNative Nickname = new(); 14 | public ByteArrayNative Uid = new(); 15 | public long Source = 0; 16 | 17 | public static implicit operator BotStranger(BotStrangerStruct stranger) 18 | { 19 | return new BotStranger( 20 | stranger.Uin, 21 | Encoding.UTF8.GetString(stranger.Nickname), 22 | Encoding.UTF8.GetString(stranger.Uid) 23 | ); 24 | } 25 | 26 | public static implicit operator BotStrangerStruct(BotStranger stranger) 27 | { 28 | return new BotStrangerStruct() 29 | { 30 | Uin = stranger.Uin, 31 | Nickname = Encoding.UTF8.GetBytes(stranger.Nickname), 32 | Uid = Encoding.UTF8.GetBytes(stranger.Uid), 33 | Source = stranger.Source 34 | }; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/EntityType.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 2 | { 3 | public enum EntityType 4 | { 5 | ImageEntity = 0, 6 | MentionEntity = 1, 7 | RecordEntity = 2, 8 | ReplyEntity = 3, 9 | VideoEntity = 4, 10 | TextEntity = 5, 11 | MultiMsgEntity = 6, 12 | } 13 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/IEntityStruct.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 2 | { 3 | public interface IEntityStruct; 4 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/MentionEntityStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Message.Entities; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct MentionEntityStruct : IEntityStruct 10 | { 11 | public MentionEntityStruct() { } 12 | 13 | public long Uin = 0; 14 | public ByteArrayNative Display = new(); 15 | 16 | public static implicit operator MentionEntityStruct(MentionEntity entity) 17 | { 18 | return new MentionEntityStruct() 19 | { 20 | Uin = entity.Uin, 21 | Display = Encoding.UTF8.GetBytes(entity.Display ?? string.Empty) 22 | }; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/RecordEntityStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Message.Entities; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct RecordEntityStruct : IEntityStruct 10 | { 11 | public RecordEntityStruct() { } 12 | public ByteArrayNative FileUrl = new(); 13 | 14 | public ByteArrayNative FileName = new(); 15 | 16 | public ByteArrayNative FileSha1 = new(); 17 | 18 | public uint FileSize = 0; 19 | 20 | public ByteArrayNative FileMd5 = new(); 21 | 22 | public static implicit operator RecordEntityStruct(RecordEntity entity) 23 | { 24 | return new RecordEntityStruct() 25 | { 26 | FileUrl = Encoding.UTF8.GetBytes(entity.FileUrl), 27 | FileName = Encoding.UTF8.GetBytes(entity.FileName), 28 | FileSha1 = Encoding.UTF8.GetBytes(entity.FileSha1), 29 | FileSize = entity.FileSize, 30 | FileMd5 = Encoding.UTF8.GetBytes(entity.FileMd5) 31 | }; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/TextEntityStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Message.Entities; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct TextEntityStruct : IEntityStruct 10 | { 11 | public TextEntityStruct() { } 12 | 13 | public ByteArrayNative Text = new(); 14 | 15 | public static implicit operator TextEntityStruct(TextEntity entity) 16 | { 17 | return new TextEntityStruct() { Text = Encoding.UTF8.GetBytes(entity.Text) }; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/Entity/VideoEntityStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | using Lagrange.Core.Message.Entities; 4 | using Lagrange.Core.NativeAPI.NativeModel.Common; 5 | 6 | namespace Lagrange.Core.NativeAPI.NativeModel.Message.Entity 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct VideoEntityStruct : IEntityStruct 10 | { 11 | public VideoEntityStruct() { } 12 | 13 | public ByteArrayNative FileUrl = new(); 14 | 15 | public ByteArrayNative FileName = new(); 16 | 17 | public ByteArrayNative FileSha1 = new(); 18 | 19 | public uint FileSize = 0; 20 | 21 | public ByteArrayNative FileMd5 = new(); 22 | 23 | public static implicit operator VideoEntityStruct(VideoEntity entity) 24 | { 25 | return new VideoEntityStruct() 26 | { 27 | FileUrl = Encoding.UTF8.GetBytes(entity.FileUrl), 28 | FileName = Encoding.UTF8.GetBytes(entity.FileName), 29 | FileSha1 = Encoding.UTF8.GetBytes(entity.FileSha1), 30 | FileSize = entity.FileSize, 31 | FileMd5 = Encoding.UTF8.GetBytes(entity.FileMd5) 32 | }; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/NativeModel/Message/TypedEntityStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lagrange.Core.NativeAPI.NativeModel.Message 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | public class TypedEntityStruct 7 | { 8 | //需要手动释放 9 | public IntPtr Entity = IntPtr.Zero; 10 | public int Type = 0; 11 | } 12 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/Abstract/ReverseEventBase.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.NativeAPI.NativeModel.Event; 2 | 3 | namespace Lagrange.Core.NativeAPI.ReverseEvent.Abstract 4 | { 5 | public abstract class ReverseEventBase 6 | { 7 | public List Events = []; 8 | public virtual void RegisterEventHandler(BotContext context) { } 9 | } 10 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotCaptchaReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotCaptchaReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotCaptchaEventStruct)e) 13 | ); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotLogReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotLogReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => 13 | { 14 | Events.Add((BotLogEventStruct)e); 15 | }); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotLoginReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotLoginReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => 13 | { 14 | Events.Add((BotLoginEventStruct)e); 15 | }); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotMessageReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.NativeModel.Message; 5 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 6 | 7 | namespace Lagrange.Core.NativeAPI.ReverseEvent 8 | { 9 | public class BotMessageReverseEvent : ReverseEventBase 10 | { 11 | public override void RegisterEventHandler(BotContext context) 12 | { 13 | context.EventInvoker.RegisterEvent( 14 | (ctx, e) => Events.Add((BotMessageEventStruct)e) 15 | ); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotNewDeviceVerifyReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotNewDeviceVerifyReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotNewDeviceVerifyEventStruct)e) 13 | ); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotOnlineReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotOnlineReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotOnlineEventStruct)e) 13 | ); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotQrCodeQueryReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotQrCodeQueryReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotQrCodeQueryEventStruct)e) 13 | ); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotQrCodeReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotQrCodeReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotQrCodeEventStruct)e) 13 | ); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotRefreshKeystoreReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotRefreshKeystoreReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotRefreshKeystoreEventStruct)e) 13 | ); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/ReverseEvent/BotSMSReverseEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events.EventArgs; 2 | using Lagrange.Core.NativeAPI.NativeModel; 3 | using Lagrange.Core.NativeAPI.NativeModel.Event; 4 | using Lagrange.Core.NativeAPI.ReverseEvent.Abstract; 5 | 6 | namespace Lagrange.Core.NativeAPI.ReverseEvent 7 | { 8 | public class BotSMSReverseEvent : ReverseEventBase 9 | { 10 | public override void RegisterEventHandler(BotContext context) 11 | { 12 | context.EventInvoker.RegisterEvent((ctx, e) => Events.Add((BotSMSEventStruct)e) 13 | ); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Lagrange.Core.NativeAPI/StatusCode.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.NativeAPI 2 | { 3 | public enum StatusCode 4 | { 5 | Success = 0, 6 | UnInitialized = 1, 7 | AlreadyInitialized = 2, 8 | AlreadyStarted = 3, 9 | InvalidIndex = 4, 10 | } 11 | } -------------------------------------------------------------------------------- /Lagrange.Core.Runner/Lagrange.Core.Runner.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Lagrange.Core.Test/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: Parallelizable(ParallelScope.All)] -------------------------------------------------------------------------------- /Lagrange.Core.Test/Cryptography/AesTest.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Lagrange.Core.Utility.Cryptography; 3 | 4 | namespace Lagrange.Core.Test.Cryptography; 5 | 6 | public class AesTest 7 | { 8 | private byte[] _data; 9 | 10 | private byte[] _key; 11 | 12 | [SetUp] 13 | public void Setup() 14 | { 15 | _data = new byte[0x10000]; 16 | _key = new byte[0x20]; 17 | 18 | RandomNumberGenerator.Fill(_data); 19 | RandomNumberGenerator.Fill(_key); 20 | } 21 | 22 | [Test] 23 | public void TestEncrypt() 24 | { 25 | var cipher = AesGcmProvider.Encrypt(_data, _key); 26 | var plain = AesGcmProvider.Decrypt(cipher, _key); 27 | 28 | Assert.That(plain, Is.EqualTo(_data)); 29 | } 30 | } -------------------------------------------------------------------------------- /Lagrange.Core.Test/Cryptography/Sha1Test.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Lagrange.Core.Utility.Cryptography; 3 | 4 | namespace Lagrange.Core.Test.Cryptography; 5 | 6 | public class Sha1Test 7 | { 8 | private byte[] _data; 9 | 10 | private Sha1Stream _sha1; 11 | 12 | [SetUp] 13 | public void Setup() 14 | { 15 | _data = new byte[1024 * 1024 * 10]; // 10MB 16 | _sha1 = new Sha1Stream(); 17 | 18 | RandomNumberGenerator.Fill(_data.AsSpan()); 19 | } 20 | 21 | [Test] 22 | public void Test() 23 | { 24 | var expected = SHA1.HashData(_data); 25 | var digest = new byte[Sha1Stream.Sha1DigestSize]; 26 | var intermediate = new byte[Sha1Stream.Sha1BlockSize]; 27 | 28 | for (int i = 0; i < _data.Length; i += Sha1Stream.Sha1BlockSize) 29 | { 30 | _sha1.Hash(intermediate.AsSpan(), false); 31 | _sha1.Hash(intermediate.AsSpan(), true); 32 | _sha1.Update(_data.AsSpan(i, Math.Min(Sha1Stream.Sha1BlockSize, _data.Length - i))); 33 | } 34 | 35 | _sha1.Final(digest); 36 | 37 | Assert.That(digest, Is.EqualTo(expected)); 38 | 39 | Assert.Pass(); 40 | } 41 | } -------------------------------------------------------------------------------- /Lagrange.Core.Test/Cryptography/TeaTest.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using Lagrange.Core.Utility.Cryptography; 3 | 4 | namespace Lagrange.Core.Test.Cryptography; 5 | 6 | public class TeaTest 7 | { 8 | private byte[] _data; 9 | 10 | private byte[] _key; 11 | 12 | [SetUp] 13 | public void Setup() 14 | { 15 | _data = new byte[1024 * 1024 * 10]; // 10MB 16 | _key = new byte[16]; 17 | 18 | RandomNumberGenerator.Fill(_key.AsSpan()); 19 | } 20 | 21 | [Test] 22 | public void Test() 23 | { 24 | var encrypted = TeaProvider.Encrypt(_data, _key); 25 | var decrypted = TeaProvider.Decrypt(encrypted, _key); 26 | Assert.Multiple(() => 27 | { 28 | Assert.That(encrypted, Is.Not.Null); 29 | Assert.That(decrypted, Is.Not.Null); 30 | }); 31 | Assert.That(decrypted, Is.EqualTo(_data)); 32 | 33 | Assert.Pass(); 34 | } 35 | } -------------------------------------------------------------------------------- /Lagrange.Core.Test/Lagrange.Core.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | enable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Lagrange.Core/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Lagrange.Core.Test")] 4 | [assembly: InternalsVisibleTo("Lagrange.Core.Runner")] -------------------------------------------------------------------------------- /Lagrange.Core/Common/BotGender.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common; 2 | 3 | public enum BotGender 4 | { 5 | Unset = 0, 6 | Male = 1, 7 | Female = 2, 8 | Unknown = 255 9 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/BotInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Core.Common; 4 | 5 | [Serializable] 6 | public class BotInfo(byte age, byte gender, string name) 7 | { 8 | public byte Age { get; set; } = age; 9 | 10 | public byte Gender { get; set; } = gender; 11 | 12 | public string Name { get; set; } = name; 13 | 14 | public override string ToString() => $"Bot name: {Name} | Gender: {Gender} | Age: {Age}"; 15 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotContact.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public abstract class BotContact 4 | { 5 | public abstract long Uin { get; } 6 | 7 | public abstract string Nickname { get; } 8 | 9 | public abstract string Uid { get; } 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotFriend.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public class BotFriend(long uin, string nickname, string uid, string remarks, string personalSign, string qid, BotFriendCategory category) : BotContact 4 | { 5 | public override long Uin { get; } = uin; 6 | 7 | public override string Nickname { get; } = nickname; 8 | 9 | public override string Uid { get; } = uid; 10 | 11 | public int Age { get; init; } 12 | 13 | public BotGender Gender { get; init; } 14 | 15 | public string Remarks { get; } = remarks; 16 | 17 | public string PersonalSign { get; } = personalSign; 18 | 19 | public string Qid { get; } = qid; 20 | 21 | public BotFriendCategory Category { get; } = category; 22 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotFriendCategory.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public class BotFriendCategory(int id, string name, int count, int sortId) 4 | { 5 | public int Id { get; } = id; 6 | 7 | public string Name { get; } = name; 8 | 9 | public int Count { get; } = count; 10 | 11 | public int SortId { get; } = sortId; 12 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public class BotGroup( 4 | long groupUin, 5 | string groupName, 6 | int memberCount, 7 | int maxMember, 8 | long createTime, 9 | string? description, 10 | string? question, 11 | string? announcement) : BotContact 12 | { 13 | public long GroupUin { get; } = groupUin; 14 | 15 | public string GroupName { get; } = groupName; 16 | 17 | public int MemberCount { get; } = memberCount; 18 | 19 | public int MaxMember { get; } = maxMember; 20 | 21 | public long CreateTime { get; } = createTime; 22 | 23 | public string? Description { get; } = description; 24 | 25 | public string? Question { get; } = question; 26 | 27 | public string? Announcement { get; } = announcement; 28 | 29 | public override long Uin => GroupUin; 30 | 31 | public override string Nickname => GroupName; 32 | 33 | public override string Uid => GroupUin.ToString(); 34 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotGroupMember.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public class BotGroupMember( 4 | BotGroup group, 5 | long uin, 6 | string uid, 7 | string nickname, 8 | GroupMemberPermission permission, 9 | int groupLevel, 10 | string? memberCard, 11 | string? specialTitle, 12 | DateTime joinTime, 13 | DateTime lastMsgTime, 14 | DateTime shutUpTimestamp) : BotContact 15 | { 16 | public BotGroup Group { get; } = group; 17 | 18 | public override long Uin { get; } = uin; 19 | 20 | public override string Uid { get; } = uid; 21 | 22 | public override string Nickname { get; } = nickname; 23 | 24 | public int Age { get; init; } 25 | 26 | public BotGender Gender { get; init; } 27 | 28 | public GroupMemberPermission Permission { get; } = permission; 29 | 30 | public int GroupLevel { get; } = groupLevel; 31 | 32 | public string? MemberCard { get; } = memberCard; 33 | 34 | public string? SpecialTitle { get; } = specialTitle; 35 | 36 | public DateTime JoinTime { get; } = joinTime; 37 | 38 | public DateTime LastMsgTime { get; } = lastMsgTime; 39 | 40 | public DateTime ShutUpTimestamp { get; } = shutUpTimestamp; 41 | } 42 | 43 | public enum GroupMemberPermission 44 | { 45 | Member, 46 | Owner, 47 | Admin, 48 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Entity/BotStranger.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Entity; 2 | 3 | public class BotStranger(long uin, string nickname, string uid) : BotContact 4 | { 5 | public override long Uin { get; } = uin; 6 | 7 | public override string Nickname { get; } = nickname; 8 | 9 | public override string Uid { get; } = uid; 10 | 11 | public long Source { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Interface/BotExt.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Logic; 2 | 3 | namespace Lagrange.Core.Common.Interface; 4 | 5 | public static class BotExt 6 | { 7 | public static Task Login(this BotContext context, long uin, string password, CancellationToken token = default) => 8 | context.EventContext.GetLogic().Login(uin, password, token); 9 | 10 | public static Task Login(this BotContext context, CancellationToken token = default) => 11 | context.EventContext.GetLogic().Login(0, null, token); 12 | 13 | public static Task Logout(this BotContext context) => 14 | context.EventContext.GetLogic().Logout(); 15 | 16 | public static Task ResolveUinByQid(this BotContext context, string qid) => 17 | context.EventContext.GetLogic().ResolveUinByQid(qid); 18 | 19 | public static bool SubmitCaptcha(this BotContext context, string ticket, string randStr) => 20 | context.EventContext.GetLogic().SubmitCaptcha(ticket, randStr); 21 | 22 | public static bool SubmitSMSCode(this BotContext context, string code) => 23 | context.EventContext.GetLogic().SubmitSMSCode(code); 24 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Interface/BotFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Common.Interface; 2 | 3 | public static class BotFactory 4 | { 5 | /// 6 | /// Create new Bot from existing 7 | /// 8 | /// The config for Bot 9 | /// Existing Keystore from deserialization 10 | /// The app info for Bot, if null, will use default app info from 11 | /// Created BotContext Instance 12 | public static BotContext Create(BotConfig config, BotKeystore keystore, BotAppInfo? appInfo = null) => 13 | new(config, keystore, appInfo ?? BotAppInfo.ProtocolToAppInfo[config.Protocol]); 14 | 15 | /// 16 | /// Create new Bot from Empty 17 | /// 18 | /// The config for Bot 19 | /// The app info for Bot, if null, will use default app info from 20 | /// Created BotContext Instance 21 | public static BotContext Create(BotConfig config, BotAppInfo? appInfo = null) => 22 | new(config, BotKeystore.CreateEmpty(), appInfo ?? BotAppInfo.ProtocolToAppInfo[config.Protocol]); 23 | } -------------------------------------------------------------------------------- /Lagrange.Core/Common/Response/BotQrCodeInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Core.Common.Response; 4 | 5 | [Serializable] 6 | public class BotQrCodeInfo(string message, string platform, string location, string? device) 7 | { 8 | [JsonPropertyName("message")] public string Message { get; } = message; 9 | 10 | [JsonPropertyName("platform")] public string Platform { get; } = platform; 11 | 12 | [JsonPropertyName("location")] public string Location { get; } = location; 13 | 14 | [JsonPropertyName("device")] public string? Device { get; } = device; 15 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotCaptchaEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotCaptchaEvent(string captchaUrl) : EventBase 4 | { 5 | public string CaptchaUrl { get; } = captchaUrl; 6 | 7 | public override string ToEventMessage() => $"{nameof(BotCaptchaEvent)}: Captcha required, URL: {CaptchaUrl}"; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotLogEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public enum LogLevel 4 | { 5 | Trace = 0, 6 | Debug = 1, 7 | Information = 2, 8 | Warning = 3, 9 | Error = 4, 10 | Critical = 5 11 | } 12 | 13 | public class BotLogEvent(string tag, LogLevel level, string message, Exception? Exception) : EventBase 14 | { 15 | public string Tag { get; } = tag; 16 | 17 | public LogLevel Level { get; } = level; 18 | 19 | public string Message { get; } = message; 20 | 21 | public Exception? Exception { get; } = Exception; 22 | 23 | public override string ToEventMessage() => $"[{Tag}] [{Level.ToString().ToUpper()}]: {Message}"; 24 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotLoginEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotLoginEvent(int state, (string, string)? error) : EventBase 4 | { 5 | public bool Success => State == 0; 6 | 7 | public int State { get; } = state; 8 | 9 | public (string Tag, string Message)? Error { get; } = error; 10 | 11 | public override string ToEventMessage() => Error == null 12 | ? $"[{nameof(BotLoginEvent)}] State: {State}" 13 | : $"[{nameof(BotLoginEvent)}] State: {State} | Error: {Error?.Tag} | Message: {Error?.Message}"; 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotMessageEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Message; 2 | 3 | namespace Lagrange.Core.Events.EventArgs; 4 | 5 | public class BotMessageEvent(BotMessage message, ReadOnlyMemory rawMessage) : EventBase 6 | { 7 | public BotMessage Message { get; } = message; 8 | 9 | internal ReadOnlyMemory RawMessage { get; } = rawMessage; 10 | 11 | public override string ToEventMessage() => $"{nameof(EventBase)} {Message}"; 12 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotNewDeviceVerifyEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotNewDeviceVerifyEvent(string url) : EventBase 4 | { 5 | public string Url { get; } = url; 6 | 7 | public override string ToEventMessage() => $"[{nameof(BotNewDeviceVerifyEvent)}] URL: {Url}"; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotOfflineEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotOfflineEvent(BotOfflineEvent.Reasons reason, (string, string)? tips) : EventBase 4 | { 5 | public Reasons Reason { get; } = reason; 6 | 7 | public (string Tag, string Message)? Tips { get; } = tips; 8 | 9 | public override string ToEventMessage() => $"{nameof(BotOfflineEvent)}: {Reason} ({Tips?.Tag}, {Tips?.Message})"; 10 | 11 | public enum Reasons 12 | { 13 | Logout, 14 | Kicked, 15 | Disconnected, 16 | } 17 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotOnlineEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotOnlineEvent(BotOnlineEvent.Reasons reason) : EventBase 4 | { 5 | public enum Reasons 6 | { 7 | Login, 8 | Reconnect, 9 | } 10 | 11 | public Reasons Reason { get; } = reason; 12 | 13 | public override string ToEventMessage() => $"{nameof(BotOnlineEvent)}: {Reason}"; 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotQrCodeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotQrCodeEvent(string url, byte[] image) : EventBase 4 | { 5 | public string Url { get; } = url; 6 | 7 | public byte[] Image { get; } = image; 8 | 9 | public override string ToEventMessage() => $"[{nameof(BotQrCodeEvent)}] URL: {Url}"; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotQrCodeQueryEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotQrCodeQueryEvent(BotQrCodeQueryEvent.TransEmpState state) : EventBase 4 | { 5 | public TransEmpState State { get; } = state; 6 | 7 | public override string ToEventMessage() => $"[{nameof(BotQrCodeQueryEvent)}] State: {State}"; 8 | 9 | public enum TransEmpState : byte 10 | { 11 | Confirmed = 0, 12 | CodeExpired = 17, 13 | WaitingForScan = 48, 14 | WaitingForConfirm = 53, 15 | Canceled = 54, 16 | Invalid = 144 17 | } 18 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotRefreshKeystoreEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | 3 | namespace Lagrange.Core.Events.EventArgs; 4 | 5 | public class BotRefreshKeystoreEvent(BotKeystore keystore) : EventBase 6 | { 7 | public BotKeystore Keystore { get; } = keystore; 8 | 9 | public override string ToEventMessage() 10 | { 11 | return $"{nameof(BotRefreshKeystoreEvent)}"; 12 | } 13 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventArgs/BotSMSEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events.EventArgs; 2 | 3 | public class BotSMSEvent(string? url, string phone) : EventBase 4 | { 5 | public string? Url { get; } = url; 6 | 7 | public string Phone { get; } = phone; 8 | 9 | public override string ToEventMessage() => $"{nameof(BotSMSEvent)} {Phone} | URL: {Url}"; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Events/EventBase.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Events; 2 | 3 | public abstract class EventBase : System.EventArgs 4 | { 5 | public DateTime EventTime { get; } 6 | 7 | internal EventBase() => EventTime = DateTime.Now; 8 | 9 | public abstract string ToEventMessage(); 10 | 11 | public override string ToString() => $"[{EventTime:yyyy-MM-dd HH:mm:ss}] {ToEventMessage()}"; 12 | } -------------------------------------------------------------------------------- /Lagrange.Core/Exceptions/InvalidTargetException.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Exceptions; 4 | 5 | /// 6 | /// When specified target is not found or invalid, this exception will be thrown. 7 | /// 8 | public class InvalidTargetException : LagrangeException 9 | { 10 | public long? TargetUin { get; } 11 | 12 | public long? GroupUin { get; } 13 | 14 | public InvalidTargetException(long targetUin) : 15 | base($"Target {targetUin} is invalid or not found.") 16 | { 17 | TargetUin = targetUin; 18 | } 19 | 20 | public InvalidTargetException(long? targetUin, long groupUin) : 21 | base($"Target {targetUin} is invalid in group {groupUin} or not found.") 22 | { 23 | TargetUin = targetUin; 24 | GroupUin = groupUin; 25 | } 26 | } -------------------------------------------------------------------------------- /Lagrange.Core/Exceptions/LagrangeException.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Exceptions; 2 | 3 | /// 4 | /// The Exception class for Lagrange.Core, All exceptions should be derived from this class. 5 | /// 6 | public class LagrangeException : Exception 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | public LagrangeException() { } 12 | 13 | /// 14 | /// Initializes a new instance of the class with a specified error message. 15 | /// 16 | /// The error message that explains the reason for the exception. 17 | public LagrangeException(string message) : base(message) { } 18 | 19 | /// 20 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 21 | /// 22 | /// The error message that explains the reason for the exception. 23 | /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. 24 | public LagrangeException(string? message, Exception innerException) : base(message, innerException) { } 25 | } -------------------------------------------------------------------------------- /Lagrange.Core/Exceptions/OperationException.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Exceptions; 2 | 3 | /// 4 | /// Exception thrown when an operation fails. 5 | /// 6 | public class OperationException(int result, string? errMsg = null) : LagrangeException($"Operation failed with code {result}: {errMsg}") 7 | { 8 | public int Result { get; } = result; 9 | 10 | public string? ErrMsg { get; } = errMsg; 11 | } -------------------------------------------------------------------------------- /Lagrange.Core/Exceptions/ServiceNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Exceptions; 2 | 3 | internal class ServiceNotFoundException(string command) : LagrangeException($"Service not found for command: {command}") 4 | { 5 | public string Command { get; } = command; 6 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/EventSubscribeAttribute.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | 3 | namespace Lagrange.Core.Internal.Events; 4 | 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 6 | internal class EventSubscribeAttribute(Protocols protocol) : EventSubscribeAttribute(typeof(T), protocol) where T : ProtocolEvent; 7 | 8 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 9 | internal class EventSubscribeAttribute(Type type, Protocols protocol) : Attribute 10 | { 11 | public Type EventType { get; } = type; 12 | 13 | public Protocols Protocol { get; set; } = protocol; 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/CloseCodeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class CloseCodeEventReq(byte[] k, bool isApproved) : ProtocolEvent 4 | { 5 | public byte[] K { get; } = k; 6 | 7 | public bool IsApproved { get; } = isApproved; 8 | } 9 | 10 | internal class CloseCodeEventResp(byte state, string message) : ProtocolEvent 11 | { 12 | public byte State { get; } = state; 13 | 14 | public string Message { get; } = message; 15 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/ExchangeEmpEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class ExchangeEmpEventReq(ExchangeEmpEventReq.Command cmd) : ProtocolEvent 4 | { 5 | public enum Command 6 | { 7 | RefreshByA1 = 0xF 8 | } 9 | 10 | public Command Cmd { get; } = cmd; 11 | } 12 | 13 | internal class ExchangeEmpEventResp(byte retCode, Dictionary tlvs) : ProtocolEvent 14 | { 15 | public byte RetCode { get; } = retCode; 16 | 17 | public Dictionary Tlvs { get; set; } = tlvs; 18 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/KeyExchangeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class KeyExchangeEventReq : ProtocolEvent; 4 | 5 | internal class KeyExchangeEventResp(byte[] sessionTicket, byte[] sessionKey) : ProtocolEvent 6 | { 7 | public byte[] SessionTicket { get; } = sessionTicket; 8 | 9 | public byte[] SessionKey { get; } = sessionKey; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/TransEmpEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class TransEmp31EventReq(byte[]? unusualSig) : ProtocolEvent 4 | { 5 | public byte[]? UnusualSig { get; } = unusualSig; 6 | } 7 | 8 | internal class TransEmp31EventResp(string url, byte[] image, byte[] qrSig) : ProtocolEvent 9 | { 10 | public string Url { get; } = url; 11 | 12 | public byte[] Image { get; } = image; 13 | 14 | public byte[] QrSig { get; } = qrSig; 15 | } 16 | 17 | internal class TransEmp12EventReq : ProtocolEvent; 18 | 19 | internal class TransEmp12EventResp(byte state, long uin, (byte[], byte[], byte[])? data) : ProtocolEvent 20 | { 21 | public TransEmpState State { get; } = (TransEmpState)state; 22 | 23 | public long Uin { get; } = uin; 24 | 25 | public (byte[] TgtgtKey, byte[] NoPicSig, byte[] TempPassword)? Data { get; } = data; 26 | 27 | internal enum TransEmpState : byte 28 | { 29 | Confirmed = 0, 30 | CodeExpired = 17, 31 | WaitingForScan = 48, 32 | WaitingForConfirm = 53, 33 | Canceled = 54, 34 | Invalid = 144 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/UinResolveEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class UinResolveEventReq(string qid) : ProtocolEvent 4 | { 5 | public string Qid { get; } = qid; 6 | } 7 | 8 | internal class UinResolveEventResp : ProtocolEvent 9 | { 10 | public byte State { get; } 11 | 12 | public (string, string)? Error { get; } 13 | 14 | public (long, string)? Info { get; } 15 | 16 | public byte[] Tlv104 { get; } = []; 17 | 18 | public UinResolveEventResp(byte retCode, (string, string)? error) 19 | { 20 | State = retCode; 21 | Error = error; 22 | } 23 | 24 | public UinResolveEventResp(byte retCode, (long, string) info, byte[] tlv104) 25 | { 26 | State = retCode; 27 | Info = info; 28 | Tlv104 = tlv104; 29 | } 30 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Login/VerifyCodeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Login; 2 | 3 | internal class VerifyCodeEventReq(byte[] k) : ProtocolEvent 4 | { 5 | public byte[] K { get; } = k; 6 | } 7 | 8 | internal class VerifyCodeEventResp(byte state, string message, string platform, string location, string? device) : ProtocolEvent 9 | { 10 | public byte State { get; } = state; 11 | 12 | public string Message { get; } = message; 13 | 14 | public string Platform { get; } = platform; 15 | 16 | public string Location { get; } = location; 17 | 18 | public string? Device { get; } = device; 19 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/GroupFileSendEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Message; 2 | 3 | internal class GroupFileSendEventReq(long groupUin, string fileId, uint random) : ProtocolEvent 4 | { 5 | public long GroupUin { get; } = groupUin; 6 | 7 | public string FileId { get; } = fileId; 8 | 9 | public uint Random { get; set; } = random; 10 | } 11 | 12 | internal class GroupFileSendEventResp(int retCode, string? retMsg) : ProtocolEvent 13 | { 14 | public int RetCode { get; } = retCode; 15 | 16 | public string? RetMsg { get; } = retMsg; 17 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/LongMsgRecvEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Message; 2 | 3 | namespace Lagrange.Core.Internal.Events.Message; 4 | 5 | internal class LongMsgRecvEventReq(bool isGroup, string resId) : ProtocolEvent 6 | { 7 | public bool IsGroup { get; } = isGroup; 8 | 9 | public string ResId { get; } = resId; 10 | } 11 | 12 | internal class LongMsgRecvEventResp(List messages) : ProtocolEvent 13 | { 14 | public List Messages { get; } = messages; 15 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/LongMsgSendEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | using Lagrange.Core.Message; 3 | 4 | namespace Lagrange.Core.Internal.Events.Message; 5 | 6 | internal class LongMsgSendEventReq(BotContact receiver, List messages) : ProtocolEvent 7 | { 8 | public BotContact Receiver { get; } = receiver; 9 | 10 | public List Messages { get; } = messages; 11 | } 12 | 13 | internal class LongMsgSendEventResp(string resId) : ProtocolEvent 14 | { 15 | public string ResId { get; } = resId; 16 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/NudgeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.Message; 2 | 3 | internal class NudgeEventReq(bool isGroup, long peerUin, long targetUin) : ProtocolEvent 4 | { 5 | public bool IsGroup { get; } = isGroup; 6 | 7 | public long PeerUin { get; } = peerUin; 8 | 9 | public long TargetUin { get; } = targetUin; 10 | } 11 | 12 | internal class NudgeEventResp : ProtocolEvent; -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/PushMessageEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Packets.Message; 2 | 3 | namespace Lagrange.Core.Internal.Events.Message; 4 | 5 | internal class PushMessageEvent(MsgPush msg, ReadOnlyMemory raw) : ProtocolEvent 6 | { 7 | public MsgPush MsgPush { get; } = msg; 8 | 9 | internal ReadOnlyMemory Raw { get; } = raw; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/Message/SendMessageEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | using Lagrange.Core.Internal.Events.System; 3 | using Lagrange.Core.Message; 4 | 5 | namespace Lagrange.Core.Internal.Events.Message; 6 | 7 | internal class SendMessageEventReq(BotMessage message) : ProtocolEvent 8 | { 9 | public BotMessage Message { get; } = message; 10 | } 11 | 12 | internal class SendFriendFileEventReq(BotFriend friend, FileUploadEventReq request, FileUploadEventResp response, int clientSequence, uint sequence) : ProtocolEvent 13 | { 14 | public BotFriend Friend { get; } = friend; 15 | 16 | public FileUploadEventReq Request { get; } = request; 17 | 18 | public FileUploadEventResp Response { get; } = response; 19 | 20 | public int ClientSequence { get; } = clientSequence; 21 | 22 | public uint Sequence { get; } = sequence; 23 | } 24 | 25 | internal class SendMessageEventResp(int result, long sendTime, int sequence) : ProtocolEvent 26 | { 27 | public int Result { get; } = result; 28 | 29 | public long SendTime { get; } = sendTime; 30 | 31 | public int Sequence { get; } = sequence; 32 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/ProtocolEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events; 2 | 3 | internal class ProtocolEvent; -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/AliveEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class AliveEvent : ProtocolEvent; -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FetchClientKeyEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class FetchClientKeyEventReq : ProtocolEvent; 4 | 5 | internal class FetchClientKeyEventResp(string clientKey, uint expiration) : ProtocolEvent 6 | { 7 | public string ClientKey { get; } = clientKey; 8 | 9 | public uint Expiration { get; } = expiration; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FetchCookiesEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class FetchCookiesEventReq(List domain) : ProtocolEvent 4 | { 5 | public List Domain { get; } = domain; 6 | } 7 | 8 | internal class FetchCookiesEventResp(Dictionary cookies) : ProtocolEvent 9 | { 10 | public Dictionary Cookies { get; } = cookies; 11 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FetchFriendsEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Internal.Events.System; 4 | 5 | internal class FetchFriendsEventReq(byte[]? cookie) : ProtocolEvent 6 | { 7 | public byte[]? Cookie { get; set; } = cookie; // for the request of next page 8 | } 9 | 10 | internal class FetchFriendsEventResp(List friends, List category, byte[]? cookie) : ProtocolEvent 11 | { 12 | public List Friends { get; } = friends; 13 | 14 | public List Category { get; } = category; 15 | 16 | public byte[]? Cookie { get; } = cookie; 17 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FetchGroupMembersEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Internal.Events.System; 4 | 5 | internal class FetchGroupMembersEventReq(long groupUin, byte[]? cookie) : ProtocolEvent 6 | { 7 | public long GroupUin { get; } = groupUin; 8 | 9 | public byte[]? Cookie { get; } = cookie; 10 | } 11 | 12 | internal class FetchGroupMembersEventResp(List groupMembers, byte[]? cookie) : ProtocolEvent 13 | { 14 | public List GroupMembers { get; } = groupMembers; 15 | 16 | public byte[]? Cookie { get; } = cookie; 17 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FetchGroupsEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Internal.Events.System; 4 | 5 | internal class FetchGroupsEventReq : ProtocolEvent; 6 | 7 | internal class FetchGroupsEventResp(List groups) : ProtocolEvent 8 | { 9 | public List Groups { get; } = groups; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/FileUploadEvent.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Utility.Extension; 2 | 3 | namespace Lagrange.Core.Internal.Events.System; 4 | 5 | internal class FileUploadEventReq(string targetUid, Stream fileStream, string fileName) : ProtocolEvent 6 | { 7 | public string TargetUid { get; } = targetUid; 8 | 9 | public Stream FileStream { get; } = fileStream; 10 | 11 | public string FileName { get; } = fileName; 12 | 13 | public byte[] FileMd5 { get; } = fileStream.Md5(); 14 | 15 | public byte[] FileSha1 { get; } = fileStream.Sha1(); 16 | } 17 | 18 | internal class FileUploadEventResp(bool isExist, string fileId, byte[] uploadKey, List<(string, uint)> rtpMediaPlatformUploadAddress, string crcMedia) : ProtocolEvent 19 | { 20 | public bool IsExist { get; } = isExist; 21 | 22 | public string FileId { get; } = fileId; 23 | 24 | public byte[] UploadKey { get; } = uploadKey; 25 | 26 | public List<(string, uint)> RtpMediaPlatformUploadAddress { get; } = rtpMediaPlatformUploadAddress; 27 | 28 | public string CrcMedia { get; } = crcMedia; 29 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/HighwaySessionEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class HighwaySessionEventReq : ProtocolEvent; 4 | 5 | internal class HighwaySessionEventResp(Dictionary> highwayUrls, byte[] sigSession) : ProtocolEvent 6 | { 7 | public Dictionary> HighwayUrls { get; } = highwayUrls; 8 | 9 | public byte[] SigSession { get; } = sigSession; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/InfoSyncEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class InfoSyncEventReq : ProtocolEvent; 4 | 5 | internal class InfoSyncEventResp(string message) : ProtocolEvent 6 | { 7 | public string Message { get; } = message; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/InfoSyncPushEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class InfoSyncPushEvent : ProtocolEvent 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/KickEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class KickEvent(string tipsTitle, string tipsInfo) : ProtocolEvent 4 | { 5 | public string TipsTitle { get; } = tipsTitle; 6 | 7 | public string TipsInfo { get; } = tipsInfo; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/PushParamsEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class PushParamsEvent : ProtocolEvent 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/SsoHeartBeatEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class SsoHeartBeatEventReq : ProtocolEvent; 4 | 5 | internal class SsoHeartBeatEventResp(int interval) : ProtocolEvent 6 | { 7 | public int Interval { get; } = interval; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Events/System/SsoUnregisterEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Events.System; 2 | 3 | internal class SsoUnregisterEventReq : ProtocolEvent; 4 | 5 | internal class SsoUnregisterEventResp(string message) : ProtocolEvent 6 | { 7 | public string Message { get; } = message; 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Logic/ILogic.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events; 2 | using Lagrange.Core.Internal.Events; 3 | 4 | namespace Lagrange.Core.Internal.Logic; 5 | 6 | internal interface ILogic 7 | { 8 | public ValueTask Incoming(ProtocolEvent e) => ValueTask.CompletedTask; 9 | 10 | public ValueTask Outgoing(ProtocolEvent e) => ValueTask.CompletedTask; 11 | 12 | public ValueTask Outgoing(EventBase e) => ValueTask.CompletedTask; 13 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Logic/PushLogic.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Events.EventArgs; 3 | using Lagrange.Core.Internal.Events; 4 | using Lagrange.Core.Internal.Events.Message; 5 | 6 | namespace Lagrange.Core.Internal.Logic; 7 | 8 | [EventSubscribe(Protocols.All)] 9 | internal class PushLogic(BotContext context) : ILogic 10 | { 11 | public async ValueTask Incoming(ProtocolEvent e) 12 | { 13 | var messageEvent = (PushMessageEvent)e; 14 | 15 | switch ((Type)messageEvent.MsgPush.CommonMessage.ContentHead.Type) 16 | { 17 | case Type.GroupMessage: 18 | case Type.PrivateMessage: 19 | case Type.TempMessage: 20 | var message = await context.EventContext.GetLogic().Parse(messageEvent.MsgPush.CommonMessage); 21 | context.EventInvoker.PostEvent(new BotMessageEvent(message, messageEvent.Raw)); 22 | break; 23 | } 24 | } 25 | 26 | private enum Type 27 | { 28 | PrivateMessage = 166, 29 | GroupMessage = 82, 30 | TempMessage = 141, 31 | } 32 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Network/CallbackClientListener.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Network; 2 | 3 | internal sealed class CallbackClientListener(IClientListener listener) : ClientListener 4 | { 5 | public override uint HeaderSize => listener.HeaderSize; 6 | 7 | public override uint GetPacketLength(ReadOnlySpan header) => listener.GetPacketLength(header); 8 | 9 | public override void OnDisconnect() => listener.OnDisconnect(); 10 | 11 | public override void OnRecvPacket(ReadOnlySpan packet) => listener.OnRecvPacket(packet); 12 | 13 | public override void OnSocketError(Exception e, ReadOnlyMemory data = default) => listener.OnSocketError(e, data); 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Network/ClientListener.SocketSession.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | 3 | namespace Lagrange.Core.Internal.Network; 4 | 5 | internal abstract partial class ClientListener 6 | { 7 | protected sealed class SocketSession : IDisposable 8 | { 9 | public Socket Socket { get; } 10 | 11 | private CancellationTokenSource? _cts; 12 | 13 | public CancellationToken Token { get; } 14 | 15 | public SocketSession(bool useIPv6 = false) 16 | { 17 | Socket = new Socket(useIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 18 | _cts = new CancellationTokenSource(); 19 | Token = _cts.Token; 20 | } 21 | 22 | public void Dispose() 23 | { 24 | var cts = Interlocked.Exchange(ref _cts, null); 25 | if (cts == null) return; 26 | 27 | cts.Cancel(); 28 | cts.Dispose(); 29 | Socket.Dispose(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Network/IClientListener.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Internal.Network; 2 | 3 | internal interface IClientListener 4 | { 5 | uint HeaderSize { get; } 6 | 7 | /// 8 | /// Dissect a stream 9 | /// 10 | /// 11 | public uint GetPacketLength(ReadOnlySpan header); 12 | 13 | /// 14 | /// On handle a packet 15 | /// 16 | public void OnRecvPacket(ReadOnlySpan packet); 17 | 18 | /// 19 | /// On client disconnect 20 | /// 21 | public void OnDisconnect(); 22 | 23 | /// 24 | /// On socket error 25 | /// 26 | public void OnSocketError(Exception e, ReadOnlyMemory data); 27 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Message/NTPushService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Message; 4 | 5 | #pragma warning disable CS8618 6 | 7 | [ProtoPackable] 8 | internal partial class MsgPush 9 | { 10 | [ProtoMember(1)] public CommonMessage CommonMessage { get; set; } 11 | 12 | [ProtoMember(5)] public bool PushNotifyFlag { get; set; } 13 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Service/KickNT.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Service; 4 | 5 | #pragma warning disable CS8618 6 | 7 | [ProtoPackable] 8 | internal partial class KickNTReq 9 | { 10 | [ProtoMember(1)] public long Uin { get; set; } 11 | 12 | [ProtoMember(2)] public bool IsSameDevice { get; set; } 13 | 14 | [ProtoMember(3)] public string TipsInfo { get; set; } 15 | 16 | [ProtoMember(4)] public string TipsTitle { get; set; } 17 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Service/NTV2RichMediaHighwayExt.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | #pragma warning disable CS8618 4 | 5 | namespace Lagrange.Core.Internal.Packets.Service; 6 | 7 | [ProtoPackable] 8 | internal partial class NTV2RichMediaHighwayExt 9 | { 10 | [ProtoMember(1)] public string FileUuid { get; set; } 11 | 12 | [ProtoMember(2)] public string UKey { get; set; } 13 | 14 | [ProtoMember(5)] public NTHighwayNetwork Network { get; set; } 15 | 16 | [ProtoMember(6)] public List MsgInfoBody { get; set; } 17 | 18 | [ProtoMember(10)] public uint BlockSize { get; set; } 19 | 20 | [ProtoMember(11)] public NTHighwayHash Hash { get; set; } 21 | } 22 | 23 | [ProtoPackable] 24 | internal partial class NTHighwayHash 25 | { 26 | [ProtoMember(1)] public List FileSha1 { get; set; } 27 | } 28 | 29 | [ProtoPackable] 30 | internal partial class NTHighwayNetwork 31 | { 32 | [ProtoMember(1)] public List IPv4s { get; set; } 33 | } 34 | 35 | 36 | [ProtoPackable] 37 | internal partial class NTHighwayIPv4 38 | { 39 | [ProtoMember(1)] public NTHighwayDomain Domain { get; set; } 40 | 41 | [ProtoMember(2)] public uint Port { get; set; } 42 | } 43 | 44 | [ProtoPackable] 45 | internal partial class NTHighwayDomain 46 | { 47 | [ProtoMember(1)] public bool IsEnable { get; set; } // true 48 | 49 | [ProtoMember(2)] public string IP { get; set; } 50 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Service/Oidb.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Service; 4 | 5 | [ProtoPackable] 6 | internal partial class Oidb 7 | { 8 | [ProtoMember(1)] public uint Command { get; set; } 9 | 10 | [ProtoMember(2)] public uint Service { get; set; } 11 | 12 | [ProtoMember(3)] public uint Result { get; set; } 13 | 14 | [ProtoMember(4)] public ReadOnlyMemory Body { get; set; } 15 | 16 | [ProtoMember(5)] public string Message { get; set; } = string.Empty; 17 | 18 | [ProtoMember(12)] public uint Reserved { get; set; } 19 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Service/Oidb_0x102A.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Service; 4 | 5 | #pragma warning disable CS8618 6 | 7 | [ProtoPackable] 8 | internal partial class D102AReqBody 9 | { 10 | [ProtoMember(1)] public List Domain { get; set; } 11 | } 12 | 13 | [ProtoPackable] 14 | internal partial class D102ARspBody 15 | { 16 | [ProtoMember(1)] public Dictionary PsKeys { get; set; } 17 | 18 | [ProtoMember(2)] public int KeyType { get; set; } 19 | 20 | [ProtoMember(3)] public string ClientKey { get; set; } 21 | 22 | [ProtoMember(4)] public uint Expiration { get; set; } 23 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Service/Oidb_0xED3.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Service; 4 | 5 | [ProtoPackable] 6 | internal partial class DED3ReqBody 7 | { 8 | [ProtoMember(1)] public long ToUin { get; set; } // uint64_to_uin 9 | 10 | [ProtoMember(2)] public long GroupCode { get; set; } // uint64_group_code 11 | 12 | [ProtoMember(3)] public uint MsgSeq { get; set; } // uint32_msg_seq 13 | 14 | [ProtoMember(4)] public uint MsgRand { get; set; } // uint32_msg_rand 15 | 16 | [ProtoMember(5)] public long AioUin { get; set; } // uint64_aio_uin 17 | 18 | [ProtoMember(6)] public uint NudgeType { get; set; } // uint32_nudge_type 19 | } 20 | 21 | [ProtoPackable] 22 | internal partial class DED3RspBody; -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Struct/SsoPacket.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks.Sources; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Struct; 4 | 5 | internal class SsoPacket(string command, int sequence, int retCode, string extra) 6 | { 7 | public ReadOnlyMemory Data { get; } 8 | 9 | public string Command { get; } = command; 10 | 11 | public string Extra { get; } = extra; 12 | 13 | public int RetCode { get; } = retCode; 14 | 15 | public int Sequence { get; } = sequence; 16 | 17 | public SsoPacket(string command, ReadOnlyMemory data, int sequence) : this(command, sequence, 0, string.Empty) => Data = data; 18 | } 19 | 20 | internal class SsoPacketValueTaskSource : IValueTaskSource 21 | { 22 | private ManualResetValueTaskSourceCore _core; 23 | 24 | public SsoPacket GetResult(short token) => _core.GetResult(token); 25 | 26 | public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token); 27 | 28 | public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _core.OnCompleted(continuation, state, token, flags); 29 | 30 | public void SetResult(SsoPacket result) => _core.SetResult(result); 31 | 32 | public void SetException(Exception exception) => _core.SetException(exception); 33 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/Struct/StructBase.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | 3 | namespace Lagrange.Core.Internal.Packets.Struct; 4 | 5 | internal abstract class StructBase(BotContext context) 6 | { 7 | protected BotKeystore Keystore => context.Keystore; 8 | 9 | protected BotAppInfo AppInfo => context.AppInfo; 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/System/NTSsoHeartBeat.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.System; 4 | 5 | [ProtoPackable] 6 | internal partial class SsoHeartBeatRequest 7 | { 8 | [ProtoMember(1)] public uint Type { get; set; } 9 | 10 | [ProtoMember(2)] public SilenceState LocalSilence { get; set; } = new(); 11 | 12 | [ProtoMember(3)] public uint BatteryState { get; set; } 13 | 14 | [ProtoMember(4)] public ulong Time { get; set; } 15 | 16 | public void SetBatteryState(byte batteryLevel, bool isCharging) 17 | { 18 | BatteryState = ((uint)batteryLevel & 0x7F) | ((uint)(isCharging ? 1 : 0) << 7); 19 | } 20 | } 21 | 22 | [ProtoPackable] 23 | internal partial class SsoHeartBeatResponse 24 | { 25 | [ProtoMember(3)] public ulong Interval { get; set; } 26 | } 27 | 28 | [ProtoPackable] 29 | internal partial class SilenceState 30 | { 31 | [ProtoMember(1)] public uint LocalSilence { get; set; } 32 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/System/PushParams.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.System; 4 | 5 | [ProtoPackable] 6 | internal partial class PushParams 7 | { 8 | [ProtoMember(4)] public List OnlineDevices { get; set; } = new(); 9 | 10 | [ProtoMember(6)] public GuildParams GuildParams { get; set; } = new(); 11 | 12 | [ProtoMember(7)] public string ErrMsg { get; set; } = string.Empty; 13 | 14 | [ProtoMember(9)] public uint GroupMsgStorageTime { get; set; } 15 | } 16 | 17 | [ProtoPackable] 18 | internal partial class GuildParams 19 | { 20 | [ProtoMember(1)] public uint GuildFlag { get; set; } 21 | 22 | [ProtoMember(2)] public uint GuildSwitchFlag { get; set; } 23 | } 24 | 25 | [ProtoPackable] 26 | internal partial class OnlineDevice 27 | { 28 | [ProtoMember(1)] public uint InstId { get; set; } 29 | 30 | [ProtoMember(2)] public uint ClientType { get; set; } 31 | 32 | [ProtoMember(3)] public uint State { get; set; } 33 | 34 | [ProtoMember(4)] public uint PlatId { get; set; } 35 | 36 | [ProtoMember(5)] public string PlatType { get; set; } = string.Empty; 37 | 38 | [ProtoMember(6)] public uint NewClientType { get; set; } 39 | 40 | [ProtoMember(7)] public string DeviceName { get; set; } = string.Empty; 41 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/System/SsoUnregister.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.System; 4 | 5 | #pragma warning disable CS8618 6 | 7 | [ProtoPackable] 8 | internal partial class SsoUnregister 9 | { 10 | [ProtoMember(1)] public int RegType { get; set; } 11 | 12 | [ProtoMember(2)] public DeviceInfo DeviceInfo { get; set; } 13 | 14 | [ProtoMember(3)] public int UserTrigger { get; set; } 15 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Packets/System/ThirdPartyLoginResponse.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto; 2 | 3 | namespace Lagrange.Core.Internal.Packets.System; 4 | 5 | [ProtoPackable] 6 | internal partial class ThirdPartyLoginResponse 7 | { 8 | [ProtoMember(1)] public ulong Seq { get; set; } 9 | 10 | [ProtoMember(9)] public RespCommonInfo CommonInfo { get; set; } = new(); 11 | } 12 | 13 | [ProtoPackable] 14 | internal partial class RespCommonInfo 15 | { 16 | [ProtoMember(10)] public uint NeedVerifyScenes { get; set; } 17 | 18 | [ProtoMember(11)] public RspNT RspNT { get; set; } = new(); 19 | 20 | [ProtoMember(12)] public uint A1Seq { get; set; } 21 | } 22 | 23 | [ProtoPackable] 24 | internal partial class RspNT 25 | { 26 | [ProtoMember(1)] public string Uid { get; set; } = ""; 27 | 28 | [ProtoMember(2)] public byte[] Ua2 { get; set; } = []; 29 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/BaseService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Events; 2 | 3 | namespace Lagrange.Core.Internal.Services; 4 | 5 | internal abstract class BaseService : IService where TReq : ProtocolEvent where TResp : ProtocolEvent 6 | { 7 | protected virtual ValueTask Parse(ReadOnlyMemory input, BotContext context) => ValueTask.FromResult(null!); 8 | 9 | protected virtual ValueTask> Build(TReq input, BotContext context) => ValueTask.FromResult(ReadOnlyMemory.Empty); 10 | 11 | async ValueTask IService.Parse(ReadOnlyMemory input, BotContext context) => await Parse(input, context); 12 | 13 | ValueTask> IService.Build(ProtocolEvent input, BotContext context) => Build((TReq)input, context); 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/IService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Events; 2 | 3 | namespace Lagrange.Core.Internal.Services; 4 | 5 | internal interface IService 6 | { 7 | public ValueTask Parse(ReadOnlyMemory input, BotContext context); 8 | 9 | public ValueTask> Build(ProtocolEvent input, BotContext context); 10 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/Message/NudgeService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.Message; 4 | using Lagrange.Core.Internal.Packets.Service; 5 | 6 | namespace Lagrange.Core.Internal.Services.Message; 7 | 8 | [EventSubscribe(Protocols.All)] 9 | [Service("OidbSvcTrpcTcp.0xed3_1")] 10 | internal class NudgeService : OidbService 11 | { 12 | private protected override uint Command => 0xed3; 13 | 14 | private protected override uint Service => 1; 15 | 16 | private protected override Task ProcessRequest(NudgeEventReq request, BotContext context) 17 | { 18 | return Task.FromResult(new DED3ReqBody 19 | { 20 | ToUin = request.TargetUin, 21 | GroupCode = request.IsGroup ? request.PeerUin : 0, 22 | AioUin = !request.IsGroup ? request.PeerUin : 0 23 | }); 24 | } 25 | 26 | private protected override Task ProcessResponse(DED3RspBody response, BotContext context) 27 | { 28 | return Task.FromResult(new NudgeEventResp()); 29 | } 30 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/Message/PushMessageService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.Message; 4 | using Lagrange.Core.Internal.Packets.Message; 5 | using Lagrange.Core.Utility; 6 | 7 | namespace Lagrange.Core.Internal.Services.Message; 8 | 9 | [EventSubscribe(Protocols.All)] 10 | [Service("trpc.msg.olpush.OlPushService.MsgPush")] 11 | internal class PushMessageService : BaseService 12 | { 13 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 14 | { 15 | var msg = ProtoHelper.Deserialize(input.Span); 16 | 17 | return new ValueTask(new PushMessageEvent(msg, input)); 18 | } 19 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/ServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Packets.Struct; 2 | 3 | namespace Lagrange.Core.Internal.Services; 4 | 5 | [AttributeUsage(AttributeTargets.Class)] 6 | internal class ServiceAttribute( 7 | string command, 8 | RequestType requestType = RequestType.D2Auth, 9 | EncryptType encryptType = EncryptType.EncryptD2Key) : Attribute 10 | { 11 | public string Command { get; } = command; 12 | 13 | public RequestType RequestType { get; } = requestType; 14 | 15 | public EncryptType EncryptType { get; } = encryptType; 16 | 17 | public bool DisableLog { get; init; } = false; 18 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/AliveService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.Struct; 5 | 6 | namespace Lagrange.Core.Internal.Services.System; 7 | 8 | [EventSubscribe(Protocols.All)] 9 | [Service("Heartbeat.Alive", RequestType.Simple, EncryptType.NoEncrypt, DisableLog = true)] 10 | internal class AliveService : BaseService 11 | { 12 | private static readonly byte[] Buffer = [0x00, 0x00, 0x00, 0x04]; 13 | 14 | protected override ValueTask> Build(AliveEvent input, BotContext context) 15 | { 16 | return new ValueTask>(Buffer); 17 | } 18 | 19 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 20 | { 21 | return new ValueTask(new AliveEvent()); 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/FetchClientKeyService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.Service; 5 | 6 | namespace Lagrange.Core.Internal.Services.System; 7 | 8 | [EventSubscribe(Protocols.All)] 9 | [Service("OidbSvcTrpcTcp.0x102a_1")] 10 | internal class FetchClientKeyService : OidbService 11 | { 12 | private protected override uint Command => 0x102A; 13 | 14 | private protected override uint Service => 1; 15 | 16 | private protected override Task ProcessRequest(FetchClientKeyEventReq request, BotContext context) 17 | { 18 | return Task.FromResult(new D102AReqBody()); 19 | } 20 | 21 | private protected override Task ProcessResponse(D102ARspBody response, BotContext context) 22 | { 23 | return Task.FromResult(new FetchClientKeyEventResp(response.ClientKey, response.Expiration)); 24 | } 25 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/FetchCookiesService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.Service; 5 | 6 | namespace Lagrange.Core.Internal.Services.System; 7 | 8 | [EventSubscribe(Protocols.All)] 9 | [Service("OidbSvcTrpcTcp.0x102a_0")] 10 | internal class FetchCookiesService : OidbService 11 | { 12 | private protected override uint Command => 0x102A; 13 | 14 | private protected override uint Service => 0; 15 | 16 | private protected override Task ProcessRequest(FetchCookiesEventReq request, BotContext context) 17 | { 18 | return Task.FromResult(new D102AReqBody { Domain = request.Domain }); 19 | } 20 | 21 | private protected override Task ProcessResponse(D102ARspBody response, BotContext context) 22 | { 23 | return Task.FromResult(new FetchCookiesEventResp(response.PsKeys)); 24 | } 25 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/InfoSyncPushService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.System; 5 | using Lagrange.Core.Utility; 6 | using Lagrange.Proto.Nodes; 7 | 8 | namespace Lagrange.Core.Internal.Services.System; 9 | 10 | [EventSubscribe(Protocols.All)] 11 | [Service("trpc.msg.register_proxy.RegisterProxy.InfoSyncPush")] 12 | internal class InfoSyncPushService : BaseService 13 | { 14 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 15 | { 16 | var obj = ProtoObject.Parse(input.Span); 17 | var push = ProtoHelper.Deserialize(input.Span); 18 | 19 | return base.Parse(input, context); 20 | } 21 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/KickService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.Service; 5 | using Lagrange.Core.Utility; 6 | 7 | namespace Lagrange.Core.Internal.Services.System; 8 | 9 | [EventSubscribe(Protocols.All)] 10 | [Service("trpc.qq_new_tech.status_svc.StatusService.KickNT")] 11 | internal class KickService : BaseService 12 | { 13 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 14 | { 15 | var payload = ProtoHelper.Deserialize(input.Span); 16 | 17 | return ValueTask.FromResult(new KickEvent(payload.TipsTitle, payload.TipsInfo)); 18 | } 19 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/PushParamsService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.System; 5 | using Lagrange.Core.Utility; 6 | 7 | namespace Lagrange.Core.Internal.Services.System; 8 | 9 | [EventSubscribe(Protocols.All)] 10 | [Service("trpc.msg.register_proxy.RegisterProxy.PushParams")] 11 | internal class PushParamsService : BaseService 12 | { 13 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 14 | { 15 | var @params = ProtoHelper.Deserialize(input.Span); 16 | 17 | return base.Parse(input, context); 18 | } 19 | } -------------------------------------------------------------------------------- /Lagrange.Core/Internal/Services/System/SsoUnregisterService.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common; 2 | using Lagrange.Core.Internal.Events; 3 | using Lagrange.Core.Internal.Events.System; 4 | using Lagrange.Core.Internal.Packets.System; 5 | using Lagrange.Core.Utility; 6 | 7 | namespace Lagrange.Core.Internal.Services.System; 8 | 9 | [EventSubscribe(Protocols.All)] 10 | [Service("trpc.qq_new_tech.status_svc.StatusService.UnRegister")] 11 | internal class SsoUnregisterService : BaseService 12 | { 13 | protected override ValueTask> Build(SsoUnregisterEventReq input, BotContext context) 14 | { 15 | var packet = new SsoUnregister 16 | { 17 | RegType = 1, 18 | DeviceInfo = new DeviceInfo(), 19 | UserTrigger = 1 20 | }; 21 | 22 | return ValueTask.FromResult(ProtoHelper.Serialize(packet)); 23 | } 24 | 25 | protected override ValueTask Parse(ReadOnlyMemory input, BotContext context) 26 | { 27 | var packet = ProtoHelper.Deserialize(input.Span); 28 | 29 | return ValueTask.FromResult(new SsoUnregisterEventResp(packet.Msg)); 30 | } 31 | } -------------------------------------------------------------------------------- /Lagrange.Core/Lagrange.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0;net9.0 4 | enable 5 | enable 6 | true 7 | 13 8 | true 9 | 10 | 2.0.2-beta 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Lagrange.Core/Message/BotMessage.Create.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Message; 4 | 5 | public partial class BotMessage 6 | { 7 | public static BotMessage CreateCustomGroup(long groupUin, long memberUin, string memberCard, MessageChain chain) 8 | { 9 | var dummyGroup = new BotGroup(groupUin, string.Empty, 0, 0, 0, null, null, null); 10 | var dummyMember = new BotGroupMember(dummyGroup, memberUin, string.Empty, memberCard, GroupMemberPermission.Member, 0, memberCard, null, DateTime.Now, DateTime.Now, DateTime.Now); 11 | return new BotMessage(chain, dummyMember, dummyGroup); 12 | } 13 | 14 | public static BotMessage CreateCustomFriend(long senderUin, string senderName, long receiverUin, string receiverName, MessageChain chain) 15 | { 16 | var dummySender = new BotFriend(senderUin, senderName, string.Empty, string.Empty, string.Empty, string.Empty, null!); 17 | var dummyReceiver = new BotFriend(receiverUin, receiverName, string.Empty, string.Empty, string.Empty, string.Empty, null!); 18 | return new BotMessage(chain, dummySender, dummyReceiver); 19 | } 20 | } -------------------------------------------------------------------------------- /Lagrange.Core/Message/BotMessage.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Common.Entity; 2 | 3 | namespace Lagrange.Core.Message; 4 | 5 | public partial class BotMessage 6 | { 7 | internal BotMessage(BotContact contact, BotContact receiver) 8 | { 9 | Contact = contact; 10 | Receiver = receiver; 11 | } 12 | 13 | internal BotMessage(MessageChain chain, BotContact contact, BotContact receiver) 14 | { 15 | Entities = chain; 16 | Contact = contact; 17 | Receiver = receiver; 18 | } 19 | 20 | public BotContact Contact { get; } 21 | 22 | public BotContact Receiver { get; } 23 | 24 | public MessageType Type => Contact switch 25 | { 26 | BotGroupMember _ => MessageType.Group, 27 | BotFriend _ => MessageType.Private, 28 | BotStranger _ => MessageType.Temp, 29 | _ => throw new ArgumentOutOfRangeException(nameof(Contact)) 30 | }; 31 | 32 | public DateTime Time { get; set; } = DateTime.Now; 33 | 34 | public MessageChain Entities { get; } = []; 35 | 36 | internal ulong MessageId { get; set; } 37 | 38 | internal uint Random { get; init; } 39 | 40 | public int Sequence { get; set; } 41 | 42 | public int ClientSequence { get; init; } = new Random().Next(10000, 99999); 43 | } -------------------------------------------------------------------------------- /Lagrange.Core/Message/Entities/IMessageEntity.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Packets.Message; 2 | 3 | namespace Lagrange.Core.Message.Entities; 4 | 5 | public interface IMessageEntity 6 | { 7 | internal Task Preprocess(BotContext context, BotMessage message) => Task.CompletedTask; 8 | 9 | internal Task Postprocess(BotContext context, BotMessage message) => Task.CompletedTask; 10 | 11 | internal Elem[] Build(); 12 | 13 | internal IMessageEntity? Parse(List elements, Elem target); 14 | 15 | internal string ToPreviewString(); 16 | } -------------------------------------------------------------------------------- /Lagrange.Core/Message/Entities/TextEntity.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Internal.Packets.Message; 2 | 3 | namespace Lagrange.Core.Message.Entities; 4 | 5 | public class TextEntity(string text) : IMessageEntity 6 | { 7 | public string Text { get; } = text; 8 | 9 | public TextEntity() : this(string.Empty) { } 10 | 11 | Elem[] IMessageEntity.Build() 12 | { 13 | return 14 | [ 15 | new Elem { Text = new Text { TextMsg = Text } } 16 | ]; 17 | } 18 | 19 | string IMessageEntity.ToPreviewString() => Text; 20 | 21 | IMessageEntity? IMessageEntity.Parse(List elements, Elem target) 22 | { 23 | return target.Text is ({ Attr6Buf: null } or { Attr6Buf.Length: 0 }) and ({ PbReserve.Length: 0 }) 24 | ? new TextEntity(target.Text.TextMsg) 25 | : null; 26 | } 27 | } -------------------------------------------------------------------------------- /Lagrange.Core/Message/MessageChain.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Message.Entities; 2 | 3 | namespace Lagrange.Core.Message; 4 | 5 | public class MessageChain : List; -------------------------------------------------------------------------------- /Lagrange.Core/Message/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Message; 2 | 3 | public enum MessageType 4 | { 5 | Group, 6 | Private, 7 | Temp 8 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Binary/Prefix.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Utility.Binary; 2 | 3 | [Flags] 4 | internal enum Prefix : byte 5 | { 6 | None = 0b0000, 7 | Int8 = 0b0001, 8 | Int16 = 0b0010, 9 | Int32 = 0b0100, 10 | LengthOnly = 0b0000, 11 | WithPrefix = 0b1000, 12 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Compression/Common.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Compression; 2 | 3 | // ReSharper disable MustUseReturnValue 4 | 5 | namespace Lagrange.Core.Utility.Compression; 6 | 7 | internal static class Common 8 | { 9 | public static byte[] Deflate(byte[] data) 10 | { 11 | using var memoryStream = new MemoryStream(); 12 | using var deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress); 13 | deflateStream.Write(data, 0, data.Length); 14 | deflateStream.Close(); 15 | return memoryStream.ToArray(); 16 | } 17 | 18 | public static byte[] Inflate(ReadOnlySpan data) 19 | { 20 | using var ms = new MemoryStream(); 21 | using var ds = new DeflateStream(ms, CompressionMode.Decompress, true); 22 | using var os = new MemoryStream(); 23 | 24 | ms.Write(data); 25 | ms.Position = 0; 26 | 27 | ds.CopyTo(os); 28 | var deflate = new byte[os.Length]; 29 | os.Position = 0; 30 | os.Read(deflate, 0, deflate.Length); 31 | 32 | return deflate; 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Extension/CollectionExt.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Utility.Extension; 2 | 3 | internal static class CollectionExt 4 | { 5 | public static TValue? GetOrNull(this IDictionary dictionary, TKey key) => 6 | dictionary.TryGetValue(key, out var value) ? value : default; 7 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Extension/ProtocolExt.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Lagrange.Core.Common; 3 | using Lagrange.Core.Common.Entity; 4 | using Lagrange.Core.Message; 5 | 6 | namespace Lagrange.Core.Utility.Extension; 7 | 8 | internal static class ProtocolExt 9 | { 10 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 11 | public static bool IsAndroid(this Protocols protocol) => (protocol & ~Protocols.Android) == Protocols.None; 12 | 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public static bool IsPC(this Protocols protocol) => (protocol & ~Protocols.PC) == Protocols.None; 15 | 16 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 17 | public static bool IsGroup(this BotMessage message) => message.Contact is BotGroupMember; 18 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Extension/ReflectionExt.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Core.Utility.Extension; 2 | 3 | internal static class ReflectionExt 4 | { 5 | public static bool HasImplemented(this Type type) 6 | { 7 | if (type is { IsGenericTypeDefinition: false, IsAbstract: false, IsInterface: false }) 8 | { 9 | return type.IsAssignableTo(typeof(TInterface)); 10 | } 11 | 12 | return false; 13 | } 14 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Extension/StreamExt.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Lagrange.Core.Utility.Extension; 4 | 5 | internal static class StreamExt 6 | { 7 | public static byte[] Md5(this Stream stream) 8 | { 9 | using var md5 = MD5.Create(); 10 | var hash = md5.ComputeHash(stream); 11 | 12 | stream.Seek(0, SeekOrigin.Begin); 13 | return hash; 14 | } 15 | 16 | public static byte[] Sha1(this Stream stream) 17 | { 18 | using var sha1 = SHA1.Create(); 19 | var hash = sha1.ComputeHash(stream); 20 | 21 | stream.Seek(0, SeekOrigin.Begin); 22 | return hash; 23 | } 24 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/Extension/TaskAwaitExt.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Lagrange.Core.Utility.Extension; 4 | 5 | internal static class TaskAwaiters 6 | { 7 | /// 8 | /// Returns an awaitable/awaiter that will ensure the continuation is executed 9 | /// asynchronously on the thread pool, even if the task is already completed 10 | /// by the time the await occurs. Effectively, it is equivalent to awaiting 11 | /// with ConfigureAwait(false) and then queuing the continuation with Task.Run, 12 | /// but it avoids the extra hop if the continuation already executed asynchronously. 13 | /// 14 | public static ForceAsyncAwaiter ForceAsync(this Task task) => new(task); 15 | } 16 | 17 | internal readonly struct ForceAsyncAwaiter : ICriticalNotifyCompletion 18 | { 19 | private readonly Task _task; 20 | 21 | internal ForceAsyncAwaiter(Task task) => _task = task; 22 | 23 | public ForceAsyncAwaiter GetAwaiter() => this; 24 | 25 | public bool IsCompleted => false; // the purpose of this type is to always force a continuation 26 | 27 | public void GetResult() => _task.GetAwaiter().GetResult(); 28 | 29 | public void OnCompleted(Action action) => 30 | _task.ConfigureAwait(false).GetAwaiter().OnCompleted(action); 31 | 32 | public void UnsafeOnCompleted(Action action) => 33 | _task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(action); 34 | } -------------------------------------------------------------------------------- /Lagrange.Core/Utility/ProtocolHelper.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Utility.Binary; 2 | 3 | namespace Lagrange.Core.Utility; 4 | 5 | internal static class ProtocolHelper 6 | { 7 | public static Dictionary TlvUnPack(ref BinaryPacket reader) 8 | { 9 | ushort count = reader.Read(); 10 | var tlv = new Dictionary(count); 11 | 12 | for (int i = 0; i < count; i++) 13 | { 14 | ushort tag = reader.Read(); 15 | ushort length = reader.Read(); 16 | 17 | var data = new byte[length]; 18 | reader.ReadBytes(data.AsSpan()); 19 | tlv[tag] = data; 20 | } 21 | 22 | return tlv; 23 | } 24 | 25 | public static string UInt32ToIPV4Addr(uint i) 26 | { 27 | Span ip = stackalloc byte[4]; 28 | 29 | ip[0] = (byte)(i & 0xFF); 30 | ip[1] = (byte)((i >> 8) & 0xFF); 31 | ip[2] = (byte)((i >> 16) & 0xFF); 32 | ip[3] = (byte)((i >> 24) & 0xFF); 33 | 34 | return $"{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}"; 35 | } 36 | } -------------------------------------------------------------------------------- /Lagrange.Milky.Implementation.Api.Generator/ApiHandlerInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Lagrange.Milky.Implementation.Api.Generator; 5 | 6 | public class ApiHandlerInfo(ClassDeclarationSyntax targetNode, INamedTypeSymbol targetSymbol, SemanticModel semanticModel, string apiName, string handlerTypeFullName) 7 | { 8 | public ClassDeclarationSyntax TargetNode { get; } = targetNode; 9 | 10 | public INamedTypeSymbol TargetSymbol { get; } = targetSymbol; 11 | 12 | public SemanticModel SemanticModel { get; } = semanticModel; 13 | 14 | public string ApiName { get; } = apiName; 15 | 16 | public string HandlerTypeFullName { get; } = handlerTypeFullName; 17 | } -------------------------------------------------------------------------------- /Lagrange.Milky.Implementation.Api.Generator/DiagnosticDescriptors.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable RS2008 2 | 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Generator; 6 | 7 | public static class DiagnosticDescriptors 8 | { 9 | public static DiagnosticDescriptor NotImplementIApiHandler = new( 10 | id: "MA001", 11 | title: "{0} does not implement IApiHandler", 12 | messageFormat: "{0} does not implement IApiHandler", 13 | category: "Usage", 14 | defaultSeverity: DiagnosticSeverity.Error, 15 | isEnabledByDefault: true 16 | ); 17 | 18 | public static DiagnosticDescriptor NotUsedJsonSerializable = new( 19 | id: "MA002", 20 | title: "{0} is not used in MilkyJsonContext [JsonSerializable(typeof({0}))]", 21 | messageFormat: "{0} is not used in MilkyJsonContext [JsonSerializable(typeof({0}))]", 22 | category: "Usage", 23 | defaultSeverity: DiagnosticSeverity.Error, 24 | isEnabledByDefault: true 25 | ); 26 | } -------------------------------------------------------------------------------- /Lagrange.Milky.Implementation.Api.Generator/Extension/AttributeSyntaxExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Syntax; 2 | 3 | namespace Lagrange.Milky.Implementation.Api.Generator.Extension; 4 | 5 | public static class AttributeSyntaxExtension 6 | { 7 | public static ClassDeclarationSyntax? GetAnnotatedClass(this AttributeSyntax attribute) 8 | { 9 | var current = attribute.Parent; 10 | while (current != null) 11 | { 12 | if (current is ClassDeclarationSyntax @class) return @class; 13 | 14 | current = current.Parent; 15 | } 16 | return null; 17 | } 18 | } -------------------------------------------------------------------------------- /Lagrange.Milky.Implementation.Api.Generator/Extension/SymbolExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Lagrange.Milky.Implementation.Api.Generator.Extension; 4 | 5 | public static class SymbolExtension 6 | { 7 | public static bool SEquals(this ISymbol? left, ISymbol? right) 8 | { 9 | return SymbolEqualityComparer.Default.Equals(left, right); 10 | } 11 | } -------------------------------------------------------------------------------- /Lagrange.Milky.Implementation.Api.Generator/Lagrange.Milky.Implementation.Api.Generator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | enable 4 | 12.0 5 | netstandard2.0 6 | true 7 | 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Lagrange.Milky/Constants.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Lagrange.Milky; 4 | 5 | internal static class Constants 6 | { 7 | public const string Banner = """ 8 | __ ___ _ __ __ 9 | / |/ / (_) / / / /__ __ __ 10 | / /|_/ / / / / / / //_/ / / / / 11 | / / / / / / / / / ,< / /_/ / 12 | /_/ /_/ /_/ /_/ /_/|_| \__, / 13 | Powered by Lagrange.Core/____/ 14 | """; 15 | 16 | public const string ConfigFileName = "appsettings.jsonc"; 17 | public const string ConfigResourceName = $"Lagrange.Milky.Resources.{ConfigFileName}"; 18 | 19 | public static string ImplementationName = "Lagrange.Milky"; 20 | 21 | public static string ImplementationVersion = typeof(Constants).Assembly 22 | .GetCustomAttribute() 23 | ?.InformationalVersion?[6..] 24 | ?? "Unknown"; 25 | 26 | public static string MilkyVersion = "1.0"; 27 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Configuration/CoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Configuration; 2 | 3 | public class CoreConfiguration 4 | { 5 | public ServerConfiguration Server { get; set; } = new(); 6 | 7 | public SignerConfiguration Signer { get; set; } = new(); 8 | 9 | public LoginConfiguration Login { get; set; } = new(); 10 | } 11 | -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Configuration/LoginConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Configuration; 2 | 3 | public class LoginConfiguration 4 | { 5 | public uint? Uin { get; set; } 6 | 7 | public string? Password { get; set; } 8 | 9 | public string DeviceName { get; set; } = "LGR-Milky"; 10 | 11 | public bool AutoReLogin { get; set; } = true; 12 | 13 | public bool CompatibleQrCode { get; set; } = false; 14 | 15 | public bool UseOnlineCaptchaResolver { get; set; } = true; 16 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Configuration/ServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Configuration; 2 | 3 | public class ServerConfiguration 4 | { 5 | public bool AutoReconnect { get; set; } = true; 6 | 7 | public bool UseIPv6Network { get; set; } = false; 8 | 9 | public bool GetOptimumServer { get; set; } = true; 10 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Configuration/SignerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Configuration; 2 | 3 | public class SignerConfiguration 4 | { 5 | public string? Base { get; set; } 6 | 7 | public string? Version { get; set; } 8 | 9 | public string? ProxyUrl { get; set; } 10 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Extension/LoggerFilterOptionsExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace Lagrange.Milky.Core.Extension; 4 | 5 | public static class LoggerFilterOptionsExtension 6 | { 7 | public static LogLevel GetDefaultLogLevel(this LoggerFilterOptions options) 8 | { 9 | return options.Rules.FirstOrDefault(rule => 10 | { 11 | return rule.ProviderName == null 12 | && rule.CategoryName == null 13 | && rule.Filter == null; 14 | }) 15 | ?.LogLevel 16 | ?? options.MinLevel; 17 | } 18 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Utility/CaptchaResolver/ICaptchaResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Utility.CaptchaResolver; 2 | 3 | public interface ICaptchaResolver 4 | { 5 | Task<(string, string)> ResolveCaptchaAsync(string url, CancellationToken token = default); 6 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Utility/CaptchaResolver/ManualCaptchaResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Core.Utility.CaptchaResolver; 2 | 3 | public class ManualCaptchaResolver : ICaptchaResolver 4 | { 5 | public async Task<(string, string)> ResolveCaptchaAsync(string url, CancellationToken token) 6 | { 7 | // Allow interrupt input 8 | return await Task.Run(() => 9 | { 10 | Console.WriteLine($"Captcha URL: {url}"); 11 | Console.Write("Please enter the ticket: "); 12 | string ticket = Console.ReadLine() ?? string.Empty; 13 | Console.Write("Please enter the randstr: "); 14 | string randstr = Console.ReadLine() ?? string.Empty; 15 | 16 | return (ticket, randstr); 17 | }, token); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Lagrange.Milky/Core/Utility/QrCodeUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using Net.Codecrete.QrCodeGenerator; 4 | 5 | namespace Lagrange.Milky.Core.Utility; 6 | 7 | public static class QrCodeUtility 8 | { 9 | public static string GenerateAscii(string payload, bool compatible) 10 | { 11 | QrCode qrcode = QrCode.EncodeText(payload, QrCode.Ecc.Low); 12 | 13 | StringBuilder result = new(); 14 | for (int y = 0; y < qrcode.Size; y += 2) 15 | { 16 | for (int x = 0; x < qrcode.Size; x++) 17 | { 18 | bool top = qrcode.GetModule(x, y); 19 | bool bottom = qrcode.GetModule(x, y + 1); 20 | 21 | result.Append((top, bottom) switch 22 | { 23 | (true, true) => compatible ? '@' : '█', 24 | (true, false) => compatible ? '^' : '▀', 25 | (false, true) => compatible ? '.' : '▄', 26 | (false, false) => ' ', 27 | }); 28 | } 29 | if (y < qrcode.Size) result.Append('\n'); 30 | } 31 | 32 | return result.ToString(); 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Extension/HostApplicationBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace Lagrange.Milky.Extension; 6 | 7 | public static class HostApplicationBuilderExtension 8 | { 9 | public static HostApplicationBuilder ConfigureConfiguration(this HostApplicationBuilder builder, Action configurer) 10 | { 11 | configurer(builder.Configuration); 12 | return builder; 13 | } 14 | 15 | public static HostApplicationBuilder ConfigureServices(this HostApplicationBuilder builder, Action configurer) 16 | { 17 | configurer(builder.Services); 18 | return builder; 19 | } 20 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/ApiAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Api; 2 | 3 | [AttributeUsage(AttributeTargets.Class)] 4 | public class ApiAttribute(string name) : Attribute 5 | { 6 | public string Name { get; init; } = name; 7 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/ApiFailedResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Api; 4 | 5 | public class ApiFailedResult 6 | { 7 | [JsonPropertyName("status")] 8 | public string Status => "failed"; 9 | 10 | [JsonPropertyName("retcode")] 11 | public required long Retcode { get; init; } 12 | 13 | [JsonPropertyName("message")] 14 | public required string Message { get; init; } 15 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/ApiOkResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Api; 4 | 5 | public class ApiOkResult 6 | { 7 | [JsonPropertyName("status")] 8 | public string Status => "ok"; 9 | 10 | [JsonPropertyName("retcode")] 11 | public long Retcode => 0; 12 | 13 | [JsonPropertyName("data")] 14 | public required object Data { get; init; } 15 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Exception/ApiException.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Api.Exception; 2 | 3 | public class ApiException(long retcode, string error) : System.Exception($"({retcode}) {error}") 4 | { 5 | public long Retcode { get; } = retcode; 6 | 7 | public string Error { get; } = error; 8 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/File/DeleteGroupFileHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Handler.File; 6 | 7 | [Api("delete_group_file")] 8 | public class DeleteGroupFileHandler(BotContext bot) : IApiHandler 9 | { 10 | private readonly BotContext _bot = bot; 11 | 12 | public async Task HandleAsync(DeleteGroupFileParameter parameter, CancellationToken token) 13 | { 14 | await _bot.GroupFSDelete(parameter.GroupId, parameter.FileId); 15 | 16 | return new object(); 17 | } 18 | } 19 | 20 | public class DeleteGroupFileParameter 21 | { 22 | [JsonPropertyName("group_id")] 23 | public required long GroupId { get; init; } 24 | 25 | [JsonPropertyName("file_id")] 26 | public required string FileId { get; init; } 27 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/File/GetGroupFileDownloadUrlHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Handler.File; 6 | 7 | [Api("get_group_file_download_url")] 8 | public class GetGroupFileDownloadUrlHandler(BotContext bot) : IApiHandler 9 | { 10 | private readonly BotContext _bot = bot; 11 | 12 | public async Task HandleAsync(GetGroupFileDownloadUrlParameter parameter, CancellationToken token) 13 | { 14 | return new GetGroupFileDownloadUrlResult 15 | { 16 | DownloadUrl = await _bot.GroupFSDownload(parameter.GroupId, parameter.FileId) 17 | }; 18 | } 19 | } 20 | 21 | public class GetGroupFileDownloadUrlParameter 22 | { 23 | [JsonPropertyName("group_id")] 24 | public required long GroupId { get; init; } 25 | 26 | [JsonPropertyName("file_id")] 27 | public required string FileId { get; init; } 28 | } 29 | 30 | public class GetGroupFileDownloadUrlResult 31 | { 32 | [JsonPropertyName("download_url")] 33 | public required string DownloadUrl { get; init; } 34 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/Friend/SendFriendNudgeHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Handler.Friend; 6 | 7 | [Api("send_friend_nudge")] 8 | public class SendFriendNudgeHandler(BotContext bot) : IApiHandler 9 | { 10 | private readonly BotContext _bot = bot; 11 | 12 | public async Task HandleAsync(SendFriendNudgeParameter parameter, CancellationToken token) 13 | { 14 | await _bot.SendFriendNudge(parameter.UserId, (parameter.IsSelf ?? false) ? _bot.BotUin : parameter.UserId); 15 | 16 | return new object(); 17 | } 18 | } 19 | 20 | public class SendFriendNudgeParameter 21 | { 22 | [JsonPropertyName("user_id")] 23 | public required long UserId { get; init; } 24 | 25 | [JsonPropertyName("is_self")] 26 | public bool? IsSelf { get; init; } // false 27 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/Group/SendGroupNudgeHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Handler.Group; 6 | 7 | [Api("send_group_nudge")] 8 | public class SendGroupNudgeHandler(BotContext bot) : IApiHandler 9 | { 10 | private readonly BotContext _bot = bot; 11 | 12 | public async Task HandleAsync(SendGroupNudgeParameter parameter, CancellationToken token) 13 | { 14 | await _bot.SendGroupNudge(parameter.GroupId, parameter.UserId); 15 | 16 | return new object(); 17 | } 18 | } 19 | 20 | public class SendGroupNudgeParameter 21 | { 22 | [JsonPropertyName("group_id")] 23 | public required long GroupId { get; init; } 24 | 25 | [JsonPropertyName("user_id")] 26 | public required long UserId { get; init; } 27 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/System/GetFriendListHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | using Lagrange.Milky.Implementation.Utility; 5 | 6 | namespace Lagrange.Milky.Implementation.Api.Handler.System; 7 | 8 | [Api("get_friend_list")] 9 | public class GetFriendListHandler(BotContext bot, Converter converter) : IApiHandler 10 | { 11 | private readonly BotContext _bot = bot; 12 | private readonly Converter _converter = converter; 13 | 14 | public async Task HandleAsync(GetFriendListParameter parameter, CancellationToken token) 15 | { 16 | return new GetFriendListResult 17 | { 18 | Friends = (await _bot.FetchFriends(parameter.NoCache ?? false)).Select(_converter.Friend) 19 | }; 20 | } 21 | } 22 | 23 | public class GetFriendListParameter 24 | { 25 | [JsonPropertyName("no_cache")] 26 | public bool? NoCache { get; init; } // false 27 | } 28 | 29 | public class GetFriendListResult 30 | { 31 | [JsonPropertyName("friends")] 32 | public required IEnumerable Friends { get; init; } 33 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/System/GetGroupInfoHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | using Lagrange.Milky.Implementation.Api.Exception; 5 | using Lagrange.Milky.Implementation.Utility; 6 | 7 | namespace Lagrange.Milky.Implementation.Api.Handler.System; 8 | 9 | [Api("get_group_info")] 10 | public class GetGroupInfoHandler(BotContext bot, Converter converter) : IApiHandler 11 | { 12 | private readonly BotContext _bot = bot; 13 | private readonly Converter _converter = converter; 14 | 15 | public async Task HandleAsync(GetGroupInfoParameter parameter, CancellationToken token) 16 | { 17 | var group = (await _bot.FetchGroups(parameter.NoCache ?? false)) 18 | .FirstOrDefault(group => group.Uin == parameter.GroupId) 19 | ?? throw new ApiException(-1, "group not found"); 20 | 21 | return new GetGroupInfoResult { Group = _converter.Group(group) }; 22 | } 23 | } 24 | 25 | public class GetGroupInfoParameter 26 | { 27 | [JsonPropertyName("group_id")] 28 | public required long GroupId { get; init; } 29 | 30 | [JsonPropertyName("no_cache")] 31 | public bool? NoCache { get; init; } // false 32 | } 33 | 34 | public class GetGroupInfoResult 35 | { 36 | [JsonPropertyName("group")] 37 | public required Entity.Group Group { get; init; } 38 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/System/GetGroupListHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Core.Common.Interface; 4 | using Lagrange.Milky.Implementation.Utility; 5 | 6 | namespace Lagrange.Milky.Implementation.Api.Handler.System; 7 | 8 | [Api("get_group_list")] 9 | public class GetGroupListHandler(BotContext bot, Converter converter) : IApiHandler 10 | { 11 | private readonly BotContext _bot = bot; 12 | private readonly Converter _converter = converter; 13 | 14 | public async Task HandleAsync(GetGroupListParameter parameter, CancellationToken token) 15 | { 16 | return new GetGroupListResult 17 | { 18 | Groups = (await _bot.FetchGroups(parameter.NoCache ?? false)).Select(_converter.Group) 19 | }; 20 | } 21 | } 22 | 23 | public class GetGroupListParameter 24 | { 25 | [JsonPropertyName("no_cache")] 26 | public bool? NoCache { get; init; } // false 27 | } 28 | 29 | public class GetGroupListResult 30 | { 31 | [JsonPropertyName("groups")] 32 | public required IEnumerable Groups { get; init; } 33 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/Handler/System/GetLoginInfoHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Core; 3 | using Lagrange.Milky.Implementation.Api.Exception; 4 | 5 | namespace Lagrange.Milky.Implementation.Api.Handler.System; 6 | 7 | [Api("get_login_info")] 8 | public class GetLoginInfoHandler(BotContext bot) : IApiHandler 9 | { 10 | private readonly BotContext _bot = bot; 11 | 12 | public Task HandleAsync(object parameter, CancellationToken token) 13 | { 14 | if (_bot.BotInfo == null) throw new ApiException(-1, "login info is null"); 15 | 16 | return Task.FromResult(new GetLoginInfoResult 17 | { 18 | Uin = _bot.BotUin, 19 | Nickname = _bot.BotInfo.Name, 20 | }); 21 | } 22 | } 23 | 24 | public class GetLoginInfoResult 25 | { 26 | [JsonPropertyName("uin")] 27 | public required long Uin { get; init; } 28 | 29 | [JsonPropertyName("nickname")] 30 | public required string Nickname { get; init; } 31 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Api/IApiHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Api; 2 | 3 | public interface IApiHandler 4 | { 5 | Type ParameterType { get; } 6 | 7 | Task HandleAsync(object parameter, CancellationToken token); 8 | } 9 | 10 | public interface IApiHandler : IApiHandler where TParameter : notnull where TResult : notnull 11 | { 12 | Type IApiHandler.ParameterType => typeof(TParameter); 13 | 14 | async Task IApiHandler.HandleAsync(object parameter, CancellationToken token) 15 | { 16 | return await HandleAsync((TParameter)parameter, token); 17 | } 18 | 19 | Task HandleAsync(TParameter parameter, CancellationToken token); 20 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Configuration/MilkyConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Configuration; 2 | 3 | public class MilkyConfiguration 4 | { 5 | public string? Host { get; set; } 6 | 7 | public ulong? Port { get; set; } 8 | 9 | public string Prefix { get; set; } = "/"; 10 | 11 | public bool UseWebSocket { get; set; } = true; 12 | 13 | public string? AccessToken { get; set; } 14 | 15 | public WebHookConfiguration? WebHook { get; set; } 16 | 17 | public bool IgnoreBotMessage { get; set; } = false; 18 | } 19 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Configuration/WebHookConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Configuration; 2 | 3 | public class WebHookConfiguration 4 | { 5 | public string? Url { get; set; } 6 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Event/BotOfflineEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Event; 4 | 5 | public class BotOfflineEvent 6 | { 7 | [JsonPropertyName("reason")] 8 | public required string Reason { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Friend.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity; 4 | 5 | public class Friend 6 | { 7 | [JsonPropertyName("user_id")] 8 | public required long UserId { get; init; } 9 | 10 | [JsonPropertyName("qid")] 11 | public required string Qid { get; init; } 12 | 13 | [JsonPropertyName("nickname")] 14 | public required string Nickname { get; init; } 15 | 16 | [JsonPropertyName("sex")] 17 | public required string Sex { get; init; } 18 | 19 | [JsonPropertyName("remark")] 20 | public required string Remark { get; init; } 21 | 22 | [JsonPropertyName("category")] 23 | public required FriendCategory Category { get; init; } 24 | } 25 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/FriendCategory.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity; 4 | 5 | public class FriendCategory 6 | { 7 | [JsonPropertyName("category_id")] 8 | public required int CategoryId { get; init; } 9 | 10 | [JsonPropertyName("category_name")] 11 | public required string CategoryName { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Group.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity; 4 | 5 | public class Group 6 | { 7 | 8 | [JsonPropertyName("group_id")] 9 | public required long GroupId { get; init; } 10 | 11 | [JsonPropertyName("name")] 12 | public required string Name { get; init; } 13 | 14 | [JsonPropertyName("member_count")] 15 | public required long MemberCount { get; init; } 16 | 17 | [JsonPropertyName("max_member_count")] 18 | public required long MaxMemberCount { get; init; } 19 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/GroupMember.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity; 4 | 5 | public class GroupMember 6 | { 7 | 8 | [JsonPropertyName("group_id")] 9 | public required long GroupId { get; init; } 10 | 11 | [JsonPropertyName("user_id")] 12 | public required long UserId { get; init; } 13 | 14 | [JsonPropertyName("nickname")] 15 | public required string Nickname { get; init; } 16 | 17 | [JsonPropertyName("card")] 18 | public required string Card { get; init; } 19 | 20 | [JsonPropertyName("title")] 21 | public string? Title { get; init; } 22 | 23 | [JsonPropertyName("sex")] 24 | public required string Sex { get; init; } 25 | 26 | [JsonPropertyName("level")] 27 | public required int Level { get; init; } 28 | 29 | [JsonPropertyName("role")] 30 | public required string Role { get; init; } 31 | 32 | [JsonPropertyName("join_time")] 33 | public required long JoinTime { get; init; } 34 | 35 | [JsonPropertyName("last_sent_time")] 36 | public required long LastSentTime { get; init; } 37 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Message/Incoming/FriendIncomingMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Message.Incoming; 4 | 5 | public class FriendIncomingMessage() : IncomingMessageBase("friend") 6 | { 7 | [JsonPropertyName("friend")] 8 | public required Friend Friend { get; init; } 9 | 10 | [JsonPropertyName("client_seq")] 11 | public required long ClientSeq { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Message/Incoming/GroupIncomingMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Message.Incoming; 4 | 5 | public class GroupIncomingMessage() : IncomingMessageBase("group") 6 | { 7 | [JsonPropertyName("group")] 8 | public required Group Group { get; init; } 9 | 10 | [JsonPropertyName("group_member")] 11 | public required GroupMember GroupMember { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Message/Incoming/IncomingMessageBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming; 3 | 4 | namespace Lagrange.Milky.Implementation.Entity.Message.Incoming; 5 | 6 | public abstract class IncomingMessageBase(string type) 7 | { 8 | [JsonPropertyName("peer_id")] 9 | public required long PeerId { get; init; } 10 | 11 | [JsonPropertyName("message_seq")] 12 | public required long MessageSeq { get; init; } 13 | 14 | [JsonPropertyName("sender_id")] 15 | public required long SenderId { get; init; } 16 | 17 | [JsonPropertyName("time")] 18 | public required long Time { get; init; } 19 | 20 | [JsonPropertyName("segments")] 21 | public required IReadOnlyList Segments { get; init; } 22 | 23 | [JsonPropertyName("message_scene")] 24 | public string MessageScene { get; } = type; 25 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Message/Incoming/TempIncomingMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Message.Incoming; 4 | 5 | public class TempIncomingMessage() : IncomingMessageBase("temp") 6 | { 7 | [JsonPropertyName("group")] 8 | public Group? Group { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/FaceData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class FaceData 6 | { 7 | [JsonPropertyName("face_id")] 8 | public required string FaceId { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/LightAppData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class LightAppData 6 | { 7 | [JsonPropertyName("app_name")] 8 | public required string AppName { get; init; } 9 | 10 | [JsonPropertyName("json_payload")] 11 | public required string JsonPayload { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/MarketFaceData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class MarketFaceData 6 | { 7 | [JsonPropertyName("url")] 8 | public required string Url { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/MentionAllData.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | public class MentionAllData { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/MentionData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class MentionData 6 | { 7 | [JsonPropertyName("user_id")] 8 | public required long UserId { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/TextData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class TextData 6 | { 7 | [JsonPropertyName("text")] 8 | public required string Text { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Common/Data/XmlData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 4 | 5 | public class XmlData 6 | { 7 | [JsonPropertyName("service_id")] 8 | public required int ServiceId { get; init; } 9 | 10 | [JsonPropertyName("xml_payload")] 11 | public required string XmlPayload { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/Data/ForwardData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 4 | 5 | public class IncomingForwardData 6 | { 7 | [JsonPropertyName("forward_id")] 8 | public required string ForwardId { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/Data/ImageData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 4 | 5 | public class IncomingImageData 6 | { 7 | [JsonPropertyName("resource_id")] 8 | public required string ResourceId { get; init; } 9 | 10 | [JsonPropertyName("temp_url")] 11 | public required string TempUrl { get; init; } 12 | 13 | [JsonPropertyName("summary")] 14 | public required string Summary { get; init; } 15 | 16 | [JsonPropertyName("sub_type")] 17 | public required string SubType { get; init; } 18 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/Data/RecordData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 4 | 5 | public class IncomingRecordData 6 | { 7 | [JsonPropertyName("resource_id")] 8 | public required string ResourceId { get; init; } 9 | 10 | [JsonPropertyName("temp_url")] 11 | public required string TempUrl { get; init; } 12 | 13 | [JsonPropertyName("duration")] 14 | public required int Duration { get; init; } 15 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/Data/ReplyData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 4 | 5 | public class IncomingReplyData 6 | { 7 | [JsonPropertyName("message_seq")] 8 | public required long MessageSeq { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/Data/VideoData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 4 | 5 | public class IncomingVideoData 6 | { 7 | [JsonPropertyName("resource_id")] 8 | public required string ResourceId { get; init; } 9 | 10 | [JsonPropertyName("temp_url")] 11 | public required string TempUrl { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IIncomingSegment.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | [JsonDerivedType(typeof(IncomingTextSegment))] 6 | [JsonDerivedType(typeof(IncomingMentionSegment))] 7 | [JsonDerivedType(typeof(IncomingMentionAllSegment))] 8 | [JsonDerivedType(typeof(IncomingFaceSegment))] 9 | [JsonDerivedType(typeof(IncomingReplySegment))] 10 | [JsonDerivedType(typeof(IncomingImageSegment))] 11 | [JsonDerivedType(typeof(IncomingRecordSegment))] 12 | [JsonDerivedType(typeof(IncomingVideoSegment))] 13 | [JsonDerivedType(typeof(IncomingForwardSegment))] 14 | [JsonDerivedType(typeof(IncomingMarketFaceSegment))] 15 | [JsonDerivedType(typeof(IncomingLightAppSegment))] 16 | [JsonDerivedType(typeof(IncomingXmlSegment))] 17 | public interface IIncomingSegment 18 | { 19 | string Type { get; } 20 | object? Data { get; } 21 | } 22 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingFaceSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingFaceSegment() : IncomingSegmentBase("face") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingForwardSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingForwardSegment() : IncomingSegmentBase("forward") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingImageSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingImageSegment() : IncomingSegmentBase("image") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingLightAppSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingLightAppSegment() : IncomingSegmentBase("light_app") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingMarketFaceSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingMarketFaceSegment() : IncomingSegmentBase("market_face") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingMentionAllSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingMentionAllSegment() : IncomingSegmentBase("mention_all") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingMentionSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingMentionSegment() : IncomingSegmentBase("mention") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingRecordSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingRecordSegment() : IncomingSegmentBase("record") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingReplySegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingReplySegment() : IncomingSegmentBase("reply") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingSegmentBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public abstract class IncomingSegmentBase(string type) : IIncomingSegment 6 | { 7 | [JsonPropertyName("type")] 8 | public string Type { get; } = type; 9 | 10 | object? IIncomingSegment.Data => Data; 11 | [JsonPropertyName("data")] 12 | public required TData Data { get; init; } 13 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingTextSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingTextSegment() : IncomingSegmentBase("text") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingVideoSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Incoming.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingVideoSegment() : IncomingSegmentBase("video") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Incoming/IncomingXmlSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Incoming; 4 | 5 | public class IncomingXmlSegment() : IncomingSegmentBase("xml") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/Data/ImageData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 4 | 5 | public class OutgoingImageData 6 | { 7 | [JsonPropertyName("uri")] 8 | public required string Uri { get; init; } 9 | 10 | [JsonPropertyName("summary")] 11 | public string? Summary { get; init; } 12 | 13 | [JsonPropertyName("sub_type")] 14 | public required string SubType { get; init; } 15 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/Data/RecordData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 4 | 5 | public class OutgoingRecordData 6 | { 7 | [JsonPropertyName("uri")] 8 | public required string Uri { get; init; } 9 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/Data/ReplyData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 4 | 5 | public class OutgoingReplyData 6 | { 7 | [JsonPropertyName("message_seq")] 8 | public required long MessageSeq { get; init; } 9 | 10 | [JsonPropertyName("client_seq")] 11 | public long? ClientSeq { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/Data/VideoData.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 4 | 5 | public class OutgoingVideoData 6 | { 7 | [JsonPropertyName("uri")] 8 | public required string Uri { get; init; } 9 | 10 | [JsonPropertyName("thumb_uri")] 11 | public string? ThumbUri { get; init; } 12 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/IOutgoingSegment.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] 6 | 7 | [JsonDerivedType(typeof(OutgoingTextSegment), typeDiscriminator: "text")] 8 | [JsonDerivedType(typeof(OutgoingMentionSegment), typeDiscriminator: "mention")] 9 | [JsonDerivedType(typeof(OutgoingMentionAllSegment), typeDiscriminator: "mention_all")] 10 | [JsonDerivedType(typeof(OutgoingFaceSegment), typeDiscriminator: "face")] 11 | [JsonDerivedType(typeof(OutgoingReplySegment), typeDiscriminator: "reply")] 12 | [JsonDerivedType(typeof(OutgoingImageSegment), typeDiscriminator: "image")] 13 | [JsonDerivedType(typeof(OutgoingRecordSegment), typeDiscriminator: "record")] 14 | [JsonDerivedType(typeof(OutgoingVideoSegment), typeDiscriminator: "video")] 15 | // TODO: [JsonDerivedType(typeof(OutgoingForwardSegment))] 16 | public interface IOutgoingSegment 17 | { 18 | string Type { get; } 19 | object? Data { get; } 20 | } 21 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingFaceSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingFaceSegment() : OutgoingSegmentBase("face") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingForwardSegment.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 2 | 3 | // TODO: 4 | // public class OutgoingForwardSegment() : OutgoingSegmentBase("forward") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingImageSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingImageSegment() : OutgoingSegmentBase("image") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingMentionAllSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingMentionAllSegment() : OutgoingSegmentBase("mention_all") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingMentionSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingMentionSegment() : OutgoingSegmentBase("mention") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingRecordSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingRecordSegment() : OutgoingSegmentBase("record") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingReplySegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingReplySegment() : OutgoingSegmentBase("reply") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingSegmentBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public abstract class OutgoingSegmentBase(string type) : IOutgoingSegment 6 | { 7 | [JsonPropertyName("type")] 8 | public string Type { get; } = type; 9 | 10 | object? IOutgoingSegment.Data => Data; 11 | [JsonPropertyName("data")] 12 | public required TData Data { get; init; } 13 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingTextSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Common.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingTextSegment() : OutgoingSegmentBase("text") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Entity/Segment/Outgoing/OutgoingVideoSegment.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Milky.Implementation.Entity.Segment.Outgoing.Data; 2 | 3 | namespace Lagrange.Milky.Implementation.Entity.Segment.Outgoing; 4 | 5 | public class OutgoingVideoSegment() : OutgoingSegmentBase("video") { } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Event/Event.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Lagrange.Milky.Implementation.Event; 4 | 5 | public class Event 6 | { 7 | [JsonPropertyName("time")] 8 | public required long Time { get; init; } 9 | 10 | [JsonPropertyName("self_id")] 11 | public required long SelfId { get; init; } 12 | 13 | [JsonPropertyName("event_type")] 14 | public required string EventType { get; init; } 15 | 16 | [JsonPropertyName("data")] 17 | public required object Data { get; init; } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Extension/ServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Lagrange.Milky.Implementation.Extension; 4 | 5 | public static partial class ServiceCollectionExtension 6 | { 7 | public static partial TServiceCollection AddApiHandlers(this TServiceCollection services) where TServiceCollection : IServiceCollection; 8 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Utility/Converter.Event.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core.Events; 2 | using Lagrange.Core.Events.EventArgs; 3 | using MilkyBotOfflineEvent = Lagrange.Milky.Implementation.Entity.Event.BotOfflineEvent; 4 | 5 | namespace Lagrange.Milky.Implementation.Utility; 6 | 7 | public partial class Converter 8 | { 9 | private Event.Event CreateEvent(EventBase @event, string type, object data) => new() 10 | { 11 | Time = new DateTimeOffset(@event.EventTime).ToUnixTimeSeconds(), 12 | SelfId = _bot.BotUin, 13 | EventType = type, 14 | Data = data, 15 | }; 16 | 17 | public Event.Event ToBotOfflineEvent(BotOfflineEvent @event) 18 | { 19 | string reason; 20 | if (@event.Tips.HasValue) reason = $"({@event.Tips.Value.Tag}) {@event.Tips.Value.Message}"; 21 | else reason = @event.Reason.ToString(); 22 | 23 | return CreateEvent(@event, "bot_offline", new MilkyBotOfflineEvent { Reason = reason }); 24 | } 25 | 26 | public Event.Event ToMessageReceiveEvent(BotMessageEvent @event) 27 | { 28 | return CreateEvent(@event, "message_receive", ToIncomingMessage(@event.Message)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Utility/Converter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Core; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Lagrange.Milky.Implementation.Utility; 5 | 6 | public partial class Converter(ILogger logger, BotContext bot) 7 | { 8 | private readonly ILogger _logger = logger; 9 | private readonly BotContext _bot = bot; 10 | } -------------------------------------------------------------------------------- /Lagrange.Milky/Implementation/Utility/UriUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Milky.Implementation.Utility; 2 | 3 | public static class UriUtility 4 | { 5 | private static readonly HttpClient Client = new(); 6 | 7 | public static async Task ToMemoryStreamAsync(string uri, CancellationToken token) 8 | { 9 | return uri[..uri.IndexOf("://", StringComparison.Ordinal)] switch 10 | { 11 | "base64" => new MemoryStream(Convert.FromBase64String(uri[9..])), 12 | "file" => new MemoryStream(await File.ReadAllBytesAsync(new Uri(uri).LocalPath, token)), 13 | "http" or "https" => await HttpUriToMemoryStreamAsync(uri, token), 14 | _ => throw new NotSupportedException(), 15 | }; 16 | } 17 | 18 | private static async Task HttpUriToMemoryStreamAsync(string url, CancellationToken token) 19 | { 20 | using var request = new HttpRequestMessage 21 | { 22 | Method = HttpMethod.Get, 23 | RequestUri = new Uri(url), 24 | }; 25 | var response = await Client.SendAsync(request, token); 26 | if (!response.IsSuccessStatusCode) throw new Exception($"Unexpected HTTP status code({response.StatusCode})"); 27 | 28 | var output = new MemoryStream(); 29 | await response.Content.CopyToAsync(output, null, token); 30 | 31 | return output; 32 | } 33 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Benchmark/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: ExcludeFromCodeCoverage] -------------------------------------------------------------------------------- /Lagrange.Proto.Benchmark/Lagrange.Proto.Benchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Lagrange.Proto.CodeGen/Format/ProtoModel.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.CodeGen.Format; 2 | 3 | public class ProtoFile 4 | { 5 | public string Syntax { get; set; } = "proto3"; 6 | public string Package { get; set; } = ""; 7 | public List Messages { get; } = []; 8 | public List Enums { get; } = []; 9 | } 10 | 11 | public class ProtoMessage 12 | { 13 | public string Name { get; set; } = ""; 14 | 15 | public List Fields { get; } = []; 16 | } 17 | 18 | public class ProtoEnum 19 | { 20 | public string Name { get; set; } = ""; 21 | 22 | public Dictionary Values { get; } = new(); 23 | } 24 | 25 | public class ProtoField 26 | { 27 | public string Label { get; set; } = ""; 28 | 29 | public string Type { get; set; } = ""; 30 | 31 | public string Name { get; set; } = ""; 32 | 33 | public int Number { get; set; } 34 | } -------------------------------------------------------------------------------- /Lagrange.Proto.CodeGen/Format/ProtoToken.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.CodeGen.Format; 2 | 3 | public enum TokenType 4 | { 5 | Keyword, 6 | Identifier, 7 | Symbol, 8 | StringLiteral, 9 | IntegerLiteral, 10 | EOF, 11 | } 12 | 13 | public readonly record struct ProtoToken(TokenType Type, string Value); -------------------------------------------------------------------------------- /Lagrange.Proto.CodeGen/Lagrange.Proto.CodeGen.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | full 10 | true 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lagrange.Proto.CodeGen/Utility/StringExt.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.CodeGen.Utility; 2 | 3 | public static class StringExt 4 | { 5 | public static string ToPascalCase(this string str) 6 | { 7 | if (string.IsNullOrEmpty(str)) return str; 8 | if (str.Length == 1) return str.ToUpper(); 9 | return char.ToUpper(str[0]) + str[1..]; 10 | } 11 | 12 | public static string NormailizePackageToNamespace(this string package) 13 | { 14 | if (string.IsNullOrEmpty(package)) return package; 15 | var parts = package.Split('.'); 16 | for (int i = 0; i < parts.Length; i++) 17 | { 18 | parts[i] = parts[i].ToPascalCase(); 19 | } 20 | 21 | return string.Join(".", parts); 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Generator/Entity/ProtoFieldInfo.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Serialization; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Lagrange.Proto.Generator.Entity; 6 | 7 | public class ProtoFieldInfo(ISymbol symbol, ITypeSymbol typeSymbol, WireType wireType, bool isSigned) 8 | { 9 | public ISymbol Symbol { get; } = symbol; 10 | 11 | public ITypeSymbol TypeSymbol { get; } = typeSymbol; 12 | 13 | public WireType WireType { get; } = wireType; 14 | 15 | public bool IsSigned { get; } = isSigned; 16 | 17 | public List ExtraTypeInfo { get; } = []; 18 | } 19 | -------------------------------------------------------------------------------- /Lagrange.Proto.Generator/Entity/ProtoTypeInfo.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Serialization; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Lagrange.Proto.Generator.Entity; 5 | 6 | public class ProtoTypeInfo(ITypeSymbol typeSymbol, WireType wireType, bool isSigned) 7 | { 8 | public ITypeSymbol TypeSymbol { get; } = typeSymbol; 9 | 10 | public WireType WireType { get; } = wireType; 11 | 12 | public bool IsSigned { get; } = isSigned; 13 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Generator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Lagrange.Proto.Generator": { 4 | "commandName": "DebugRoslynComponent", 5 | "targetProject": "..\\Lagrange.Proto.Runner\\Lagrange.Proto.Runner.csproj" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Generator/ProtoSourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Lagrange.Proto.Generator; 5 | 6 | [Generator(LanguageNames.CSharp)] 7 | public partial class ProtoSourceGenerator : IIncrementalGenerator 8 | { 9 | private const string ProtoPackableAttributeName = "Lagrange.Proto.ProtoPackableAttribute"; 10 | 11 | public void Initialize(IncrementalGeneratorInitializationContext context) 12 | { 13 | var valuesProvider = context.SyntaxProvider.ForAttributeWithMetadataName(ProtoPackableAttributeName, 14 | (node, _) => node is ClassDeclarationSyntax, 15 | (ctx, _) => (ContextClass: (ClassDeclarationSyntax)ctx.TargetNode, ctx.SemanticModel)) 16 | .Select((items, token) => 17 | { 18 | var (contextClass, semanticModel) = items; 19 | var parser = new Parser(contextClass, semanticModel); 20 | parser.Parse(token); 21 | return parser; 22 | }); 23 | 24 | context.RegisterSourceOutput(valuesProvider, Emit); 25 | } 26 | 27 | private static void Emit(SourceProductionContext context, Parser parser) 28 | { 29 | foreach (var diagnostic in parser.Diagnostics) context.ReportDiagnostic(diagnostic); 30 | 31 | var emitter = new Emitter(parser); 32 | emitter.Emit(context); 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Generator/Utility/ProtoHelper.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Generator.Utility.Extension; 2 | using Lagrange.Proto.Serialization; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace Lagrange.Proto.Generator.Utility; 6 | 7 | public static class ProtoHelper 8 | { 9 | public static byte[] EncodeVarInt(int value) 10 | { 11 | Span result = stackalloc byte[5]; 12 | int i = 0; 13 | while (value > 127) 14 | { 15 | result[i] = (byte)((value & 0x7F) | 0x80); 16 | value >>= 7; 17 | i++; 18 | } 19 | result[i] = ((byte)value); 20 | 21 | return result.Slice(0, i + 1).ToArray(); 22 | } 23 | 24 | public static WireType GetWireType(ITypeSymbol symbol) 25 | { 26 | if (SymbolResolver.IsRepeatedType(symbol, out var type)) symbol = type; 27 | 28 | if (symbol.IsIntegerType() || symbol.TypeKind == TypeKind.Enum || symbol.SpecialType == SpecialType.System_Boolean) return WireType.VarInt; 29 | if (symbol.SpecialType == SpecialType.System_Single) return WireType.Fixed32; 30 | if (symbol.SpecialType == SpecialType.System_Double) return WireType.Fixed64; 31 | 32 | return WireType.LengthDelimited; 33 | } 34 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Runner/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: ExcludeFromCodeCoverage] -------------------------------------------------------------------------------- /Lagrange.Proto.Runner/Lagrange.Proto.Runner.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Lagrange.Proto.Runner/Program.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Nodes; 2 | 3 | namespace Lagrange.Proto.Runner; 4 | 5 | internal static partial class Program 6 | { 7 | private static void Main(string[] args) 8 | { 9 | var test = new ProtoObject() 10 | { 11 | { 1, new ProtoObject{ { 1, 2 } } }, 12 | { 1, new ProtoObject{ { 1, 2 } } }, 13 | { 3, 4 }, 14 | { 5, 6 } 15 | }; 16 | 17 | var bytes = test.Serialize(); 18 | Console.WriteLine(Convert.ToHexString(bytes)); 19 | var parsed = ProtoObject.Parse(bytes); 20 | 21 | int value = parsed[1][0][1].GetValue(); 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Proto.Test/Lagrange.Proto.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | enable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Lagrange.Proto.Test/SegmentBufferTest.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Utility; 2 | 3 | namespace Lagrange.Proto.Test; 4 | 5 | public class SegmentBufferTest 6 | { 7 | private SegmentBufferWriter _writer; 8 | 9 | private byte[] _raw; 10 | 11 | [SetUp] 12 | public void Setup() 13 | { 14 | _writer = new SegmentBufferWriter(); 15 | 16 | _raw = GC.AllocateUninitializedArray(1024 * 10); 17 | Random.Shared.NextBytes(_raw); 18 | 19 | var span = _writer.GetSpan(_raw.Length); 20 | _writer.Advance(_raw.Length); 21 | 22 | _raw.AsSpan().CopyTo(span); 23 | } 24 | 25 | [Test] 26 | public void TestCreateReadeOnlyMemory() 27 | { 28 | var reader = _writer.CreateReadOnlyMemory(); 29 | Assert.That(reader.Length, Is.EqualTo(_raw.Length)); 30 | Assert.That(reader.Span.ToArray(), Is.EqualTo(_raw)); 31 | } 32 | 33 | [TearDown] 34 | public void TearDown() 35 | { 36 | _writer.Dispose(); 37 | } 38 | } -------------------------------------------------------------------------------- /Lagrange.Proto/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Lagrange.Proto.Test")] -------------------------------------------------------------------------------- /Lagrange.Proto/IProtoSerializable.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Serialization.Metadata; 3 | 4 | namespace Lagrange.Proto; 5 | 6 | public interface IProtoSerializable 7 | { 8 | public static abstract ProtoObjectInfo TypeInfo { get; } 9 | 10 | public static abstract void SerializeHandler(T obj, ProtoWriter writer); 11 | 12 | public static abstract int MeasureHandler(T obj); 13 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Nodes/ProtoNode.To.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using Lagrange.Proto.Primitives; 3 | 4 | namespace Lagrange.Proto.Nodes; 5 | 6 | public partial class ProtoNode 7 | { 8 | public abstract void WriteTo(int field, ProtoWriter writer); 9 | 10 | public abstract int Measure(int field); 11 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Nodes/ProtoRawValue.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Lagrange.Proto.Serialization; 3 | 4 | namespace Lagrange.Proto.Nodes; 5 | 6 | /// 7 | /// The Raw value extracted from the proto source without scheme. 8 | /// 9 | [StructLayout(LayoutKind.Auto)] 10 | internal class ProtoRawValue(WireType wireType, long value) 11 | { 12 | public readonly WireType WireType = wireType; 13 | 14 | public readonly long Value = value; 15 | 16 | public ReadOnlyMemory Bytes = ReadOnlyMemory.Empty; 17 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Primitives/ProtoResolvable.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Lagrange.Proto.Serialization; 3 | using Lagrange.Proto.Serialization.Metadata; 4 | 5 | namespace Lagrange.Proto.Primitives; 6 | 7 | public static class ProtoResolvableExtension 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public static void EncodeResolvable(this ProtoWriter writer, int field, WireType wireType, T value) 11 | { 12 | var converter = ProtoTypeResolver.GetConverter(); 13 | converter.Write(field, wireType, writer, value); 14 | } 15 | 16 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 17 | public static void EncodeResolvable(this ProtoWriter writer, int field, WireType wireType, T value, ProtoNumberHandling numberHandling) 18 | { 19 | var converter = ProtoTypeResolver.GetConverter(); 20 | converter.WriteWithNumberHandling(field, wireType, writer, value, numberHandling); 21 | } 22 | } 23 | 24 | public static class ProtoResolvableUtility 25 | { 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public static int Measure(int field, WireType wireType, T value) 28 | { 29 | var converter = ProtoTypeResolver.GetConverter(); 30 | return converter.Measure(field, wireType, value); 31 | } 32 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Primitives/ProtoWriteHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using System.Diagnostics; 3 | using System.Text.Unicode; 4 | 5 | namespace Lagrange.Proto.Primitives; 6 | 7 | internal static class ProtoWriteHelper 8 | { 9 | internal static OperationStatus ToUtf8(ReadOnlySpan source, Span destination, out int written) 10 | { 11 | var status = Utf8.FromUtf16(source, destination, out int charsRead, out written, replaceInvalidSequences: false, isFinalBlock: true); 12 | Debug.Assert(status is OperationStatus.Done or OperationStatus.DestinationTooSmall or OperationStatus.InvalidData); 13 | Debug.Assert(charsRead == source.Length || status is not OperationStatus.Done); 14 | return status; 15 | } 16 | } -------------------------------------------------------------------------------- /Lagrange.Proto/ProtoConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto; 2 | 3 | internal static class ProtoConstants 4 | { 5 | public const int StackallocByteThreshold = 256; 6 | public const int StackallocCharThreshold = StackallocByteThreshold / 2; 7 | 8 | // In the worst case, a single UTF-16 character could be expanded to 3 UTF-8 bytes. 9 | // Only surrogate pairs expand to 4 UTF-8 bytes but that is a transformation of 2 UTF-16 characters going to 4 UTF-8 bytes (factor of 2). 10 | // All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes. 11 | public const int MaxExpansionFactorWhileTranscoding = 3; 12 | } -------------------------------------------------------------------------------- /Lagrange.Proto/ProtoMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Serialization; 2 | 3 | namespace Lagrange.Proto; 4 | 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class ProtoMemberAttribute(int field) : Attribute 7 | { 8 | public int Field { get; } = field; 9 | 10 | public ProtoNumberHandling NumberHandling { get; init; } = ProtoNumberHandling.Default; 11 | 12 | public WireType NodesWireType { get; init; } 13 | } -------------------------------------------------------------------------------- /Lagrange.Proto/ProtoPackableAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto; 2 | 3 | [AttributeUsage(AttributeTargets.Class)] 4 | public class ProtoPackableAttribute : Attribute 5 | { 6 | /// 7 | /// if true, the fields with default value would be ignored, this would follow the convention of proto3 that 8 | /// default varint 0 is not serialized. 9 | /// 10 | public bool IgnoreDefaultFields { get; init; } 11 | } -------------------------------------------------------------------------------- /Lagrange.Proto/ProtoValueMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Serialization; 2 | 3 | namespace Lagrange.Proto; 4 | 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class ProtoValueMemberAttribute : Attribute 7 | { 8 | public ProtoNumberHandling NumberHandling { get; init; } = ProtoNumberHandling.Default; 9 | 10 | public WireType? NodesWireType { get; init; } 11 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Collection/ProtoArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Lagrange.Proto.Serialization.Converter; 4 | 5 | public class ProtoArrayConverter : ProtoRepeatedConverter 6 | { 7 | private protected override T[] Create() => []; 8 | 9 | private protected override object CreateState() => new List(); 10 | 11 | private protected override void Add(T item, T[] collection, object? state) 12 | { 13 | Debug.Assert(state is List); 14 | ((List)state).Add(item); 15 | } 16 | 17 | private protected override T[] Finalize(T[] collection, object? state) 18 | { 19 | Debug.Assert(state is List); 20 | return ((List)state).ToArray(); 21 | } 22 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Collection/ProtoListConverter.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.Serialization.Converter; 2 | 3 | public class ProtoListConverter : ProtoRepeatedConverter, T> 4 | { 5 | private protected override List Create() => []; 6 | 7 | private protected override void Add(T item, List collection, object? state) => collection.Add(item); 8 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Generic/ProtoNullableConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Serialization.Metadata; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | public class ProtoNullableConverter : ProtoConverter where T : struct 7 | { 8 | private readonly ProtoConverter _converter = ProtoTypeResolver.GetConverter(); 9 | 10 | public override void Write(int field, WireType wireType, ProtoWriter writer, T? value) 11 | { 12 | if (value.HasValue) _converter.Write(0, wireType, writer, value.Value); 13 | } 14 | 15 | public override int Measure(int field, WireType wireType, T? value) 16 | { 17 | return value.HasValue ? _converter.Measure(field, wireType, value.Value) : 0; 18 | } 19 | 20 | public override T? Read(int field, WireType wireType, ref ProtoReader reader) 21 | { 22 | return _converter.Read(field, wireType, ref reader); 23 | } 24 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Nodes/ProtoArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Nodes; 2 | using Lagrange.Proto.Primitives; 3 | using Lagrange.Proto.Serialization.Metadata; 4 | using Lagrange.Proto.Utility; 5 | 6 | namespace Lagrange.Proto.Serialization.Converter; 7 | 8 | public class ProtoArrayConverter : ProtoConverter 9 | { 10 | public override void Write(int field, WireType wireType, ProtoWriter writer, ProtoArray value) 11 | { 12 | value.WriteTo(field, writer); 13 | } 14 | 15 | public override int Measure(int field, WireType wireType, ProtoArray value) 16 | { 17 | return value.Measure(field); 18 | } 19 | 20 | public override ProtoArray Read(int field, WireType wireType, ref ProtoReader reader) 21 | { 22 | var array = new ProtoArray(wireType); 23 | var converter = ProtoTypeResolver.GetConverter(); 24 | 25 | int tag; 26 | while (true) 27 | { 28 | var value = converter.Read(field, wireType, ref reader); 29 | array.Add(new ProtoValue(value, wireType)); 30 | 31 | if ((tag = reader.DecodeVarInt() >> 3) != field) break; 32 | } 33 | 34 | reader.Rewind(-ProtoHelper.GetVarIntLength(tag << 3)); 35 | return array; 36 | } 37 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Nodes/ProtoNodeConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Nodes; 2 | using Lagrange.Proto.Primitives; 3 | using Lagrange.Proto.Serialization.Metadata; 4 | 5 | namespace Lagrange.Proto.Serialization.Converter; 6 | 7 | public class ProtoNodeConverter : ProtoConverter 8 | { 9 | public override void Write(int field, WireType wireType, ProtoWriter writer, ProtoNode value) 10 | { 11 | value.WriteTo(field, writer); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, ProtoNode value) 15 | { 16 | return value.Measure(field); 17 | } 18 | 19 | public override ProtoNode Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | var value = ProtoTypeResolver.GetConverter().Read(field, wireType, ref reader); 22 | return new ProtoValue(value, wireType); 23 | } 24 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Nodes/ProtoObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Nodes; 2 | using Lagrange.Proto.Primitives; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | public class ProtoObjectConverter : ProtoConverter 7 | { 8 | public override void Write(int field, WireType wireType, ProtoWriter writer, ProtoObject value) 9 | { 10 | value.WriteTo(field, writer); 11 | } 12 | 13 | public override int Measure(int field, WireType wireType, ProtoObject value) 14 | { 15 | return value.Measure(field); 16 | } 17 | 18 | public override ProtoObject Read(int field, WireType wireType, ref ProtoReader reader) 19 | { 20 | int length = reader.DecodeVarInt(); 21 | var subSpan = reader.CreateSpan(length); 22 | return ProtoObject.Parse(subSpan); 23 | } 24 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Nodes/ProtoValueConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Nodes; 2 | using Lagrange.Proto.Primitives; 3 | using Lagrange.Proto.Serialization.Metadata; 4 | 5 | namespace Lagrange.Proto.Serialization.Converter; 6 | 7 | public class ProtoValueConverter : ProtoConverter 8 | { 9 | public override void Write(int field, WireType wireType, ProtoWriter writer, ProtoValue value) 10 | { 11 | value.WriteTo(field, writer); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, ProtoValue value) 15 | { 16 | return value.Measure(field); 17 | } 18 | 19 | public override ProtoValue Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | var value = ProtoTypeResolver.GetConverter().Read(field, wireType, ref reader); 22 | return new ProtoValue(value, wireType); 23 | } 24 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Object/ProtoErrorConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | 3 | namespace Lagrange.Proto.Serialization.Converter; 4 | 5 | public class ProtoErrorConverter : ProtoConverter 6 | { 7 | public override void Write(int field, WireType wireType, ProtoWriter writer, T value) 8 | { 9 | ThrowHelper.ThrowInvalidOperationException_FailedDetermineConverter(); 10 | } 11 | 12 | public override int Measure(int field, WireType wireType, T value) 13 | { 14 | ThrowHelper.ThrowInvalidOperationException_FailedDetermineConverter(); 15 | return 0; 16 | } 17 | 18 | public override T Read(int field, WireType wireType, ref ProtoReader reader) 19 | { 20 | ThrowHelper.ThrowInvalidOperationException_FailedDetermineConverter(); 21 | return default!; 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Object/ProtoSerializableConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Serialization.Metadata; 3 | using Lagrange.Proto.Utility; 4 | 5 | namespace Lagrange.Proto.Serialization.Converter; 6 | 7 | public class ProtoSerializableConverter : ProtoConverter where T : IProtoSerializable 8 | { 9 | private readonly ProtoObjectInfo _object = T.TypeInfo; // force the typeInfo to be initialized 10 | 11 | public override void Write(int field, WireType wireType, ProtoWriter writer, T value) 12 | { 13 | int length = T.MeasureHandler(value); 14 | writer.EncodeVarInt(length); 15 | if (length > 0) T.SerializeHandler(value, writer); 16 | } 17 | 18 | public override int Measure(int field, WireType wireType, T value) 19 | { 20 | int length = T.MeasureHandler(value); 21 | return ProtoHelper.GetVarIntLength(length) + length; 22 | } 23 | 24 | public override T Read(int field, WireType wireType, ref ProtoReader reader) 25 | { 26 | int length = reader.DecodeVarInt(); 27 | var span = reader.CreateSpan(length); 28 | return ProtoSerializer.DeserializeProtoPackable(span); 29 | } 30 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Lagrange.Proto.Primitives; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | internal class ProtoBooleanConverter : ProtoConverter 7 | { 8 | public override void Write(int field, WireType wireType, ProtoWriter writer, bool value) 9 | { 10 | writer.WriteRawByte(Unsafe.As(ref value)); 11 | } 12 | 13 | public override int Measure(int field, WireType wireType, bool value) 14 | { 15 | return 1; 16 | } 17 | 18 | public override bool Read(int field, WireType wireType, ref ProtoReader reader) 19 | { 20 | byte b = reader.DecodeVarInt(); 21 | return Unsafe.As(ref b); 22 | } 23 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoBytesConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Utility; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | public class ProtoBytesConverter : ProtoConverter 7 | { 8 | public override void Write(int field, WireType wireType, ProtoWriter writer, byte[] value) 9 | { 10 | writer.EncodeVarInt(value.Length); 11 | writer.WriteRawBytes(value); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, byte[] value) 15 | { 16 | return ProtoHelper.CountBytes(value); 17 | } 18 | 19 | public override byte[] Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | int length = reader.DecodeVarInt(); 22 | if (length == 0) return []; 23 | 24 | var buffer = GC.AllocateUninitializedArray(length); 25 | var span = reader.CreateSpan(length); 26 | span.CopyTo(buffer); 27 | 28 | return buffer; 29 | } 30 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoMemoryByteConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Utility; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | public class ProtoMemoryByteConverter : ProtoConverter> 7 | { 8 | public override void Write(int field, WireType wireType, ProtoWriter writer, Memory value) 9 | { 10 | writer.EncodeVarInt(value.Length); 11 | writer.WriteRawBytes(value.Span); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, Memory value) 15 | { 16 | return ProtoHelper.CountBytes(value.Span); 17 | } 18 | 19 | public override Memory Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | int length = reader.DecodeVarInt(); 22 | if (length == 0) return Memory.Empty; 23 | 24 | var buffer = GC.AllocateUninitializedArray(length); 25 | var span = reader.CreateSpan(length); 26 | span.CopyTo(buffer); 27 | return new Memory(buffer, 0, length); 28 | } 29 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoMemoryCharConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using System.Text.Unicode; 3 | using Lagrange.Proto.Primitives; 4 | using Lagrange.Proto.Utility; 5 | 6 | namespace Lagrange.Proto.Serialization.Converter; 7 | 8 | public class ProtoMemoryCharConverter : ProtoConverter> 9 | { 10 | public override void Write(int field, WireType wireType, ProtoWriter writer, Memory value) 11 | { 12 | writer.EncodeString(value.Span); 13 | } 14 | 15 | public override int Measure(int field, WireType wireType, Memory value) 16 | { 17 | return ProtoHelper.CountString(value.Span); 18 | } 19 | 20 | public override Memory Read(int field, WireType wireType, ref ProtoReader reader) 21 | { 22 | int length = reader.DecodeVarInt(); 23 | if (length == 0) return Memory.Empty; 24 | 25 | var buffer = ArrayPool.Shared.Rent(length); 26 | var utf16 = GC.AllocateUninitializedArray(length); 27 | var span = reader.CreateSpan(length); 28 | span.CopyTo(buffer); 29 | 30 | Utf8.ToUtf16(buffer, utf16, out _, out int charsWritten); 31 | ArrayPool.Shared.Return(buffer); 32 | 33 | return new Memory(utf16, 0, charsWritten); 34 | } 35 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoReadOnlyMemoryByteConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | using Lagrange.Proto.Utility; 3 | 4 | namespace Lagrange.Proto.Serialization.Converter; 5 | 6 | public class ProtoReadOnlyMemoryByteConverter : ProtoConverter> 7 | { 8 | public override void Write(int field, WireType wireType, ProtoWriter writer, ReadOnlyMemory value) 9 | { 10 | writer.EncodeVarInt(value.Length); 11 | writer.WriteRawBytes(value.Span); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, ReadOnlyMemory value) 15 | { 16 | return ProtoHelper.CountBytes(value.Span); 17 | } 18 | 19 | public override ReadOnlyMemory Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | int length = reader.DecodeVarInt(); 22 | if (length == 0) return ReadOnlyMemory.Empty; 23 | 24 | var buffer = GC.AllocateUninitializedArray(length); 25 | var span = reader.CreateSpan(length); 26 | span.CopyTo(buffer); 27 | return new ReadOnlyMemory(buffer, 0, length); 28 | } 29 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoReadOnlyMemoryCharConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using System.Text.Unicode; 3 | using Lagrange.Proto.Primitives; 4 | using Lagrange.Proto.Utility; 5 | 6 | namespace Lagrange.Proto.Serialization.Converter; 7 | 8 | public class ProtoReadOnlyMemoryCharConverter : ProtoConverter> 9 | { 10 | public override void Write(int field, WireType wireType, ProtoWriter writer, ReadOnlyMemory value) 11 | { 12 | writer.EncodeString(value.Span); 13 | } 14 | 15 | public override int Measure(int field, WireType wireType, ReadOnlyMemory value) 16 | { 17 | return ProtoHelper.CountString(value.Span); 18 | } 19 | 20 | public override ReadOnlyMemory Read(int field, WireType wireType, ref ProtoReader reader) 21 | { 22 | int length = reader.DecodeVarInt(); 23 | if (length == 0) return ReadOnlyMemory.Empty; 24 | 25 | var buffer = ArrayPool.Shared.Rent(length); 26 | var utf16 = GC.AllocateUninitializedArray(length); 27 | var span = reader.CreateSpan(length); 28 | span.CopyTo(buffer); 29 | 30 | Utf8.ToUtf16(buffer, utf16, out _, out int charsWritten); 31 | ArrayPool.Shared.Return(buffer); 32 | 33 | return new ReadOnlyMemory(utf16, 0, charsWritten); 34 | } 35 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Converter/Value/ProtoStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Lagrange.Proto.Primitives; 3 | using Lagrange.Proto.Utility; 4 | 5 | namespace Lagrange.Proto.Serialization.Converter; 6 | 7 | internal class ProtoStringConverter : ProtoConverter 8 | { 9 | public override void Write(int field, WireType wireType, ProtoWriter writer, string value) 10 | { 11 | writer.EncodeString(value); 12 | } 13 | 14 | public override int Measure(int field, WireType wireType, string value) 15 | { 16 | return ProtoHelper.CountString(value); 17 | } 18 | 19 | public override string Read(int field, WireType wireType, ref ProtoReader reader) 20 | { 21 | int length = reader.DecodeVarInt(); 22 | var span = reader.CreateSpan(length); 23 | return span.IsEmpty ? string.Empty : Encoding.UTF8.GetString(span); 24 | } 25 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Metadata/MemberAccessor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Lagrange.Proto.Serialization.Metadata; 4 | 5 | internal abstract class MemberAccessor 6 | { 7 | public abstract Func? CreateParameterlessConstructor(ConstructorInfo? constructorInfo); 8 | 9 | public abstract Func CreatePropertyGetter(PropertyInfo propertyInfo); 10 | 11 | public abstract Action CreatePropertySetter(PropertyInfo propertyInfo); 12 | 13 | public abstract Func CreateFieldGetter(FieldInfo fieldInfo); 14 | 15 | public abstract Action CreateFieldSetter(FieldInfo fieldInfo); 16 | 17 | public virtual void Clear() { } 18 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/Metadata/ProtoObjectInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Lagrange.Proto.Serialization.Metadata; 4 | 5 | [DebuggerDisplay("Fields = {Fields.Count}")] 6 | public class ProtoObjectInfo 7 | { 8 | public Dictionary Fields { get; init; } = new(); 9 | 10 | public Func? ObjectCreator { get; init; } 11 | 12 | public bool IgnoreDefaultFields { get; init; } 13 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/ProtoConverter.cs: -------------------------------------------------------------------------------- 1 | using Lagrange.Proto.Primitives; 2 | 3 | namespace Lagrange.Proto.Serialization; 4 | 5 | public abstract class ProtoConverter 6 | { 7 | } 8 | 9 | public abstract class ProtoConverter : ProtoConverter 10 | { 11 | public virtual bool ShouldSerialize(T value, bool ignoreDefaultValue) => value != null; 12 | 13 | public abstract void Write(int field, WireType wireType, ProtoWriter writer, T value); 14 | 15 | public virtual void WriteWithNumberHandling(int field, WireType wireType, ProtoWriter writer, T value, ProtoNumberHandling numberHandling) => Write(field, wireType, writer, value); 16 | 17 | public abstract int Measure(int field, WireType wireType, T value); 18 | 19 | public abstract T Read(int field, WireType wireType, ref ProtoReader reader); 20 | 21 | public virtual T ReadWithNumberHandling(int field, WireType wireType, ref ProtoReader reader, ProtoNumberHandling numberHandling) => Read(field, wireType, ref reader); 22 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/ProtoNumberHandling.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.Serialization; 2 | 3 | [Flags] 4 | public enum ProtoNumberHandling : byte 5 | { 6 | /// 7 | /// Variable size and unsigned 8 | /// 9 | Default = 0b0000000, 10 | 11 | /// 12 | /// Fixed32 Size 13 | /// 14 | Fixed32 = 0b0000001, 15 | 16 | /// 17 | /// Fixed64 Size 18 | /// 19 | Fixed64 = 0b0000010, 20 | 21 | /// 22 | /// Signed VarInt 23 | /// 24 | Signed = 0b0000100 25 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/ProtoSerializer.Helpers.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.Serialization; 2 | 3 | public static partial class ProtoSerializer 4 | { 5 | internal const string SerializationUnreferencedCodeMessage = "Proto serialization and deserialization might require types that cannot be statically analyzed. Use the SerializePackable that takes a IProtoSerializable to ensure generated code is used, or make sure all of the required types are preserved."; 6 | internal const string SerializationRequiresDynamicCodeMessage = "Proto serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use Lagrange.Proto source generation for native AOT applications."; 7 | } -------------------------------------------------------------------------------- /Lagrange.Proto/Serialization/WireType.cs: -------------------------------------------------------------------------------- 1 | namespace Lagrange.Proto.Serialization; 2 | 3 | public enum WireType : byte 4 | { 5 | VarInt = 0, 6 | Fixed64 = 1, 7 | LengthDelimited = 2, 8 | Fixed32 = 5, 9 | Unknown = 255 10 | } --------------------------------------------------------------------------------