From 47065e0b69c2e04e8cde2509f67d69ff11345abd Mon Sep 17 00:00:00 2001 From: Linwenxuan05 Date: Wed, 28 Jun 2023 11:24:41 +0800 Subject: [PATCH] Initial commit for Public repository --- .gitignore | 5 + Lagrange.Core.Test/Lagrange.Core.Test.csproj | 14 + Lagrange.Core.Test/Program.cs | 19 ++ Lagrange.Core.Test/Protobuf.cs | 24 ++ Lagrange.Core.Test/Tests/BinaryTest.cs | 32 ++ Lagrange.Core.Test/Tests/NTLoginTest.cs | 43 +++ Lagrange.Core.Test/Tests/WtLoginTest.cs | 78 +++++ Lagrange.Core.Test/Utility/ProtoGen.cs | 61 ++++ Lagrange.Core.Test/Utility/Tlv.cs | 35 ++ Lagrange.Core.sln | 34 ++ Lagrange.Core/AssemblyInfo.cs | 4 + Lagrange.Core/BotContext.cs | 41 +++ Lagrange.Core/Common/BotAppInfo.cs | 111 +++++++ Lagrange.Core/Common/BotConfig.cs | 38 +++ Lagrange.Core/Common/BotDeviceInfo.cs | 28 ++ Lagrange.Core/Common/BotKeystore.cs | 110 +++++++ Lagrange.Core/Common/BotScheduler.cs | 86 +++++ Lagrange.Core/Common/Interface/Api/BotExt.cs | 22 ++ Lagrange.Core/Common/Interface/BotFactory.cs | 7 + .../Attributes/BusinessLogicAttribute.cs | 15 + Lagrange.Core/Core/Context/BusinessContext.cs | 135 ++++++++ Lagrange.Core/Core/Context/ContextBase.cs | 22 ++ .../Core/Context/ContextCollection.cs | 38 +++ Lagrange.Core/Core/Context/LogContext.cs | 34 ++ .../Logic/Implementation/MessagingLogic.cs | 28 ++ .../Logic/Implementation/WtExchangeLogic.cs | 239 ++++++++++++++ Lagrange.Core/Core/Context/Logic/LogicBase.cs | 12 + Lagrange.Core/Core/Context/PacketContext.cs | 104 ++++++ .../ServiceContext.SequenceProvider.cs | 28 ++ Lagrange.Core/Core/Context/ServiceContext.cs | 115 +++++++ Lagrange.Core/Core/Context/SocketContext.cs | 134 ++++++++ Lagrange.Core/Core/Core.md | 7 + .../Core/Event/EventArg/BotLogEvent.cs | 30 ++ .../Core/Event/EventArg/BotOfflineEvent.cs | 6 + .../Core/Event/EventArg/BotOnlineEvent.cs | 6 + Lagrange.Core/Core/Event/EventBase.cs | 22 ++ .../Core/Event/EventInvoker.Events.cs | 12 + Lagrange.Core/Core/Event/EventInvoker.cs | 41 +++ .../Event/Protocol/Login/EasyLoginEvent.cs | 26 ++ .../Protocol/Login/Ecdh/KeyExchangeEvent.cs | 23 ++ .../Core/Event/Protocol/Login/LoginEvent.cs | 40 +++ .../Protocol/Login/PasswordLoginEvent.cs | 26 ++ .../Event/Protocol/Login/TransEmpEvent.cs | 69 ++++ .../Protocol/Message/PushMessageEvent.cs | 14 + .../Protocol/Message/SendMessageEvent.cs | 12 + .../Core/Event/Protocol/ProtocolEvent.cs | 12 + .../Core/Event/Protocol/System/AliveEvent.cs | 12 + .../Event/Protocol/System/CorrectTimeEvent.cs | 16 + .../Core/Event/Protocol/System/KickNTEvent.cs | 18 ++ .../Event/Protocol/System/SsoAliveEvent.cs | 12 + .../Protocol/System/StatusRegisterEvent.cs | 16 + .../Network/Tcp/CallbackClientListener.cs | 18 ++ .../Tcp/ClientListener.SocketSession.cs | 32 ++ .../Core/Network/Tcp/ClientListener.cs | 175 ++++++++++ .../Core/Network/Tcp/IClientListener.cs | 27 ++ .../Core/Packets/Action/FirstViewReq.cs | 17 + .../Core/Packets/Action/GetGroupMsgReq.cs | 25 ++ .../Core/Packets/Action/GetGroupMsgResp.cs | 21 ++ .../Core/Packets/Action/HttpConn/HttpConn.cs | 30 ++ .../Action/HttpConn/HttpConn0x6ff_501.cs | 11 + .../Action/Pb/PbGetOneDayRoamMsgReq.cs | 15 + .../Action/Pb/PbGetOneDayRoamMsgResp.cs | 23 ++ .../Core/Packets/Action/Pb/PbMultiMsgItem.cs | 13 + .../Core/Packets/Action/Pb/PbMultiMsgNew.cs | 11 + .../Packets/Action/Pb/PbMultiMsgTransmit.cs | 13 + .../Core/Packets/Action/Pb/PbPushMsg.cs | 21 ++ .../Core/Packets/Action/PushMessagePacket.cs | 20 ++ .../Core/Packets/Action/SendMessageRequest.cs | 37 +++ .../Packets/Action/SendMessageResponse.cs | 13 + .../Core/Packets/Action/SyncCookie.cs | 25 ++ .../Ecdh/Plain/SsoKeyExchangeDecrypted.cs | 15 + .../Login/Ecdh/Plain/SsoKeyExchangePlain.cs | 11 + .../Login/Ecdh/Plain/SsoKeyExchangePlain2.cs | 22 ++ .../Core/Packets/Login/Ecdh/SsoKeyExchange.cs | 22 ++ .../Login/Ecdh/SsoKeyExchangeResponse.cs | 18 ++ .../Login/NTLogin/NTLoginHttpRequest.cs | 15 + .../Login/NTLogin/NTLoginHttpResponse.cs | 21 ++ .../NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs | 11 + .../Plain/Body/SsoNTLoginPasswordLogin.cs | 49 +++ .../Login/NTLogin/Plain/SsoNTLoginBase.cs | 18 ++ .../Login/NTLogin/Plain/SsoNTLoginHeader.cs | 16 + .../Login/NTLogin/Plain/SsoNTLoginResponse.cs | 19 ++ .../Plain/Universal/SsoNTLoginCredentials.cs | 18 ++ .../Plain/Universal/SsoNTLoginSystem.cs | 17 + .../NTLogin/Plain/Universal/SsoNTLoginUid.cs | 12 + .../NTLogin/Plain/Universal/SsoNTLoginUin.cs | 11 + .../Plain/Universal/SsoNTLoginUnusual.cs | 11 + .../Plain/Universal/SsoNTLoginVersion.cs | 15 + .../Login/NTLogin/SsoNTLoginEncryptedData.cs | 17 + .../Packets/Login/WtLogin/Entity/Login.cs | 68 ++++ .../Packets/Login/WtLogin/Entity/TransEmp.cs | 67 ++++ .../Login/WtLogin/Entity/TransEmp12.cs | 49 +++ .../Login/WtLogin/Entity/TransEmp31.cs | 40 +++ .../Core/Packets/Login/WtLogin/WtLoginBase.cs | 78 +++++ Lagrange.Core/Core/Packets/Message/C2C/C2C.cs | 19 ++ .../Core/Packets/Message/C2C/C2CMsgInfo.cs | 33 ++ .../Packets/Message/C2C/C2CMsgWithDrawReq.cs | 18 ++ .../Packets/Message/C2C/C2CMsgWithDrawResp.cs | 14 + .../Packets/Message/C2C/C2CTempMessageHead.cs | 32 ++ .../Core/Packets/Message/Component/Attr.cs | 29 ++ .../Message/Component/Extra/FileExtra.cs | 9 + .../Packets/Message/Component/GroupInfo.cs | 23 ++ .../Message/Component/MutilTransHead.cs | 11 + .../Message/Component/NotOnlineFile.cs | 49 +++ .../Packets/Message/Component/PcSupportDef.cs | 21 ++ .../Core/Packets/Message/Component/Ptt.cs | 57 ++++ .../Packets/Message/Component/RichText.cs | 18 ++ .../Core/Packets/Message/ContentHead.cs | 25 ++ .../Core/Packets/Message/Element/Elem.cs | 48 +++ .../Implementation/AnonymousGroupMessage.cs | 23 ++ .../Element/Implementation/CommonElem.cs | 15 + .../Element/Implementation/CustomElem.cs | 19 ++ .../Element/Implementation/CustomFace.cs | 78 +++++ .../Implementation/ElemFlags2.Instance.cs | 14 + .../Element/Implementation/ElemFlags2.cs | 38 +++ .../Element/Implementation/Extra/FaceExtra.cs | 12 + .../Implementation/Extra/ImageExtra.cs | 13 + .../Implementation/Extra/MentionExtra.cs | 18 ++ .../Implementation/Extra/QFaceExtra.cs | 26 ++ .../Element/Implementation/ExtraInfo.cs | 34 ++ .../Message/Element/Implementation/Face.cs | 13 + .../Element/Implementation/GeneralFlags.cs | 47 +++ .../Element/Implementation/GroupFile.cs | 30 ++ .../Element/Implementation/LightAppElem.cs | 13 + .../Element/Implementation/MarketFace.cs | 36 +++ .../NotOnlineImage.PbReserve.cs | 19 ++ .../Element/Implementation/NotOnlineImage.cs | 62 ++++ .../Element/Implementation/OnlineImage.cs | 16 + .../QQWalletMsg.QQWalletAioBody.cs | 55 ++++ .../QQWalletMsg.QQWalletAioElem.cs | 55 ++++ .../Element/Implementation/QQWalletMsg.cs | 12 + .../Element/Implementation/RedBagInfo.cs | 9 + .../Message/Element/Implementation/RichMsg.cs | 20 ++ .../Implementation/SourceMsg.PbPreserve.cs | 18 ++ .../Message/Element/Implementation/SrcMsg.cs | 29 ++ .../Message/Element/Implementation/Text.cs | 19 ++ .../Element/Implementation/TransElem.cs | 14 + .../Element/Implementation/VideoFile.cs | 57 ++++ Lagrange.Core/Core/Packets/Message/Message.cs | 38 +++ .../Core/Packets/Message/MessageBody.cs | 16 + .../Core/Packets/Message/MessageControl.cs | 9 + Lagrange.Core/Core/Packets/Message/PushMsg.cs | 21 ++ .../Core/Packets/Message/ResponseHead.cs | 22 ++ .../Core/Packets/Message/Routing/Grp.cs | 9 + .../Core/Packets/Message/Routing/GrpTmp.cs | 11 + .../Packets/Message/Routing/ResponseGrp.cs | 15 + .../Packets/Message/Routing/Trans0X211.cs | 13 + .../Core/Packets/Message/Routing/WPATmp.cs | 14 + .../Core/Packets/Message/RoutingHead.cs | 18 ++ .../Service/Oidb/Generics/OidbLafter.cs | 15 + .../Service/Oidb/Generics/OidbProperty.cs | 13 + .../Oidb/Internal/OidbSvcTrpcTcp0x1092_0.cs | 13 + .../Oidb/Internal/OidbSvcTrpcTcp0x10C0_1.cs | 14 + .../Oidb/Internal/OidbSvcTrpcTcp0x10C0_2.cs | 14 + .../Oidb/Internal/OidbSvcTrpcTcp0x116D_1.cs | 21 ++ .../Oidb/Internal/OidbSvcTrpcTcp0x5CF_11.cs | 29 ++ .../Oidb/Internal/OidbSvcTrpcTcp0x644_1.cs | 25 ++ .../Oidb/Internal/OidbSvcTrpcTcp0x758_1.cs | 16 + .../Oidb/Internal/OidbSvcTrpcTcp0x773_0.cs | 12 + .../Oidb/Internal/OidbSvcTrpcTcp0xCDE_2.cs | 19 ++ .../Oidb/Internal/OidbSvcTrpcTcp0xE37_1700.cs | 43 +++ .../Oidb/Internal/OidbSvcTrpcTcp0xE37_800.cs | 33 ++ .../Oidb/Internal/OidbSvcTrpcTcp0xFE1_2.cs | 21 ++ .../Service/Oidb/OidbSvcTrpcTcpAttribute.cs | 18 ++ .../Service/Oidb/OidbSvcTrpcTcpBase.cs | 53 ++++ .../Service/Oidb/OidbSvcTrpcTcpResponse.cs | 22 ++ Lagrange.Core/Core/Packets/ServicePacker.cs | 67 ++++ Lagrange.Core/Core/Packets/SsoPacker.cs | 64 ++++ Lagrange.Core/Core/Packets/SsoPacket.cs | 22 ++ .../Core/Packets/System/NTDeviceSign.cs | 16 + Lagrange.Core/Core/Packets/System/NTOS.cs | 14 + .../Core/Packets/System/NTSsoHeartBeat.cs | 14 + .../Core/Packets/System/NTSysEvent.cs | 16 + .../Core/Packets/System/NTSysEventSub.cs | 22 ++ .../Core/Packets/System/OnlineOsInfo.cs | 20 ++ .../Packets/System/ServiceKickNTResponse.cs | 22 ++ .../Core/Packets/System/ServiceRegister.cs | 28 ++ .../Packets/System/ServiceRegisterResponse.cs | 15 + Lagrange.Core/Core/Packets/System/Sign.cs | 17 + Lagrange.Core/Core/Packets/System/Software.cs | 11 + Lagrange.Core/Core/Packets/Tlv/Tlv100.cs | 30 ++ Lagrange.Core/Core/Packets/Tlv/Tlv103.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv106.cs | 30 ++ Lagrange.Core/Core/Packets/Tlv/Tlv107.cs | 17 + Lagrange.Core/Core/Packets/Tlv/Tlv108.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv10A.cs | 13 + Lagrange.Core/Core/Packets/Tlv/Tlv10C.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv10D.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv10E.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv11.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv114.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv116.cs | 26 ++ Lagrange.Core/Core/Packets/Tlv/Tlv118.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv119.cs | 16 + Lagrange.Core/Core/Packets/Tlv/Tlv11A.cs | 19 ++ Lagrange.Core/Core/Packets/Tlv/Tlv11F.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv12.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv124.cs | 11 + Lagrange.Core/Core/Packets/Tlv/Tlv128.cs | 32 ++ Lagrange.Core/Core/Packets/Tlv/Tlv130.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv133.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv134.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv138.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv141.cs | 13 + Lagrange.Core/Core/Packets/Tlv/Tlv142.cs | 16 + Lagrange.Core/Core/Packets/Tlv/Tlv143.cs | 13 + Lagrange.Core/Core/Packets/Tlv/Tlv144.cs | 30 ++ Lagrange.Core/Core/Packets/Tlv/Tlv145.cs | 14 + Lagrange.Core/Core/Packets/Tlv/Tlv146.cs | 19 ++ Lagrange.Core/Core/Packets/Tlv/Tlv147.cs | 23 ++ Lagrange.Core/Core/Packets/Tlv/Tlv161.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv163.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv166.cs | 11 + Lagrange.Core/Core/Packets/Tlv/Tlv167.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv16A.cs | 28 ++ Lagrange.Core/Core/Packets/Tlv/Tlv16D.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv16E.cs | 14 + Lagrange.Core/Core/Packets/Tlv/Tlv177.cs | 18 ++ Lagrange.Core/Core/Packets/Tlv/Tlv18.cs | 28 ++ Lagrange.Core/Core/Packets/Tlv/Tlv191.cs | 11 + Lagrange.Core/Core/Packets/Tlv/Tlv305.cs | 13 + Lagrange.Core/Core/Packets/Tlv/Tlv318.cs | 11 + Lagrange.Core/Core/Packets/Tlv/Tlv508.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv510.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv521.cs | 13 + Lagrange.Core/Core/Packets/Tlv/Tlv523.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv528.cs | 10 + Lagrange.Core/Core/Packets/Tlv/Tlv543.cs | 26 ++ Lagrange.Core/Core/Packets/Tlv/Tlv550.cs | 10 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode11.cs | 17 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode15.cs | 10 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode16.cs | 34 ++ Lagrange.Core/Core/Packets/Tlv/TlvQrCode17.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode18.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode19.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode1B.cs | 25 ++ Lagrange.Core/Core/Packets/Tlv/TlvQrCode1C.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode1D.cs | 23 ++ Lagrange.Core/Core/Packets/Tlv/TlvQrCode1E.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode2.cs | 10 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode33.cs | 14 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode35.cs | 14 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode4.cs | 13 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode65.cs | 10 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode66.cs | 14 + Lagrange.Core/Core/Packets/Tlv/TlvQrCode7.cs | 10 + Lagrange.Core/Core/Packets/Tlv/TlvQrCodeCE.cs | 12 + Lagrange.Core/Core/Packets/Tlv/TlvQrCodeD1.cs | 37 +++ .../Core/Service/Abstraction/BaseService.cs | 35 ++ .../Core/Service/Abstraction/IService.cs | 15 + .../Core/Service/EventSubscribeAttribute.cs | 12 + .../Core/Service/Login/EasyLoginService.cs | 112 +++++++ .../Service/Login/Ecdh/KeyExchangeService.cs | 86 +++++ .../Core/Service/Login/LoginService.cs | 64 ++++ .../Service/Login/PasswordLoginService.cs | 125 ++++++++ .../Core/Service/Login/TransEmpService.cs | 65 ++++ .../Service/Message/PushMessageService.cs | 49 +++ .../Service/Message/SendMessageService.cs | 17 + .../Core/Service/ServiceAttribute.cs | 15 + .../Core/Service/System/AliveService.cs | 19 ++ .../Core/Service/System/CorrectTimeService.cs | 19 ++ .../Core/Service/System/KickNTService.cs | 28 ++ .../Core/Service/System/SsoAliveService.cs | 35 ++ .../Service/System/StatusRegisterService.cs | 57 ++++ Lagrange.Core/Lagrange.Core.csproj | 26 ++ Lagrange.Core/Message/Entity/FaceEntity.cs | 88 ++++++ Lagrange.Core/Message/Entity/FileEntity.cs | 73 +++++ Lagrange.Core/Message/Entity/ForwardEntity.cs | 76 +++++ Lagrange.Core/Message/Entity/ImageEntity.cs | 41 +++ Lagrange.Core/Message/Entity/JsonEntity.cs | 68 ++++ Lagrange.Core/Message/Entity/MentionEntity.cs | 84 +++++ Lagrange.Core/Message/Entity/PacketEntity.cs | 24 ++ Lagrange.Core/Message/Entity/TextEntity.cs | 30 ++ Lagrange.Core/Message/Entity/XmlEntity.cs | 50 +++ Lagrange.Core/Message/IMessageEntity.cs | 24 ++ Lagrange.Core/Message/MessageBuilder.cs | 19 ++ Lagrange.Core/Message/MessageChain.cs | 42 +++ .../Message/MessageElementAttribute.cs | 9 + Lagrange.Core/Message/MessagePacker.cs | 157 +++++++++ .../Utility/Binary/BInaryPacket.Prefix.cs | 15 + Lagrange.Core/Utility/Binary/BinaryPacket.cs | 292 +++++++++++++++++ .../Utility/Binary/BinaryPropertyAttribute.cs | 19 ++ .../Utility/Binary/BinarySerializer.cs | 123 +++++++ Lagrange.Core/Utility/Binary/BitConverter.cs | 93 ++++++ .../Utility/Binary/Compression/Common.cs | 23 ++ .../Binary/Compression/ZCompression.cs | 59 ++++ .../Utility/Binary/JceStruct/JceReader.cs | 105 ++++++ .../Utility/Binary/JceStruct/JceStruct.cs | 14 + .../Utility/Binary/JceStruct/JceType.cs | 22 ++ .../Utility/Binary/JceStruct/JceWriter.cs | 104 ++++++ .../Utility/Binary/JceStruct/UniPacket.cs | 63 ++++ .../Binary/Tlv/Attributes/TlvAttribute.cs | 21 ++ .../Tlv/Attributes/TlvEncryptAttribute.cs | 15 + .../Tlv/Attributes/TlvQrCodeAttribute.cs | 21 ++ Lagrange.Core/Utility/Binary/Tlv/TlvBody.cs | 8 + Lagrange.Core/Utility/Binary/Tlv/TlvPacker.cs | 143 +++++++++ Lagrange.Core/Utility/Binary/Tlv/TlvPacket.cs | 55 ++++ Lagrange.Core/Utility/Crypto/AesGcmImpl.cs | 39 +++ .../Utility/Crypto/EcdhImpl.Constants.cs | 62 ++++ Lagrange.Core/Utility/Crypto/EcdhImpl.cs | 49 +++ Lagrange.Core/Utility/Crypto/ICryptoImpl.cs | 12 + .../Crypto/Provider/Ecdh/EcdhProvider.cs | 256 +++++++++++++++ .../Crypto/Provider/Ecdh/EllipticCurve.cs | 100 ++++++ .../Crypto/Provider/Ecdh/EllipticPoint.cs | 35 ++ .../Crypto/Provider/Tea/TeaProvider.cs | 140 ++++++++ Lagrange.Core/Utility/Crypto/TeaImpl.cs | 24 ++ .../Utility/Extension/ByteExtension.cs | 59 ++++ .../Utility/Extension/ExpressionExt.cs | 69 ++++ .../Utility/Extension/ReflectionExt.cs | 54 ++++ Lagrange.Core/Utility/Extension/SocketExt.cs | 40 +++ .../Utility/Extension/StringExtension.cs | 24 ++ .../Utility/Extension/TaskAwaitExt.cs | 34 ++ .../Utility/Extension/TypeExtension.cs | 28 ++ Lagrange.Core/Utility/Generator/ByteGen.cs | 14 + Lagrange.Core/Utility/Generator/StringGen.cs | 36 +++ Lagrange.Core/Utility/Network/Http.cs | 45 +++ Lagrange.Core/Utility/Network/Icmp.cs | 13 + Lagrange.Core/Utility/ServiceInjector.cs | 101 ++++++ Lagrange.Core/Utility/Signature.cs | 46 +++ Lagrange.Core/Utility/TaskScheduler.cs | 299 ++++++++++++++++++ Lagrange.OneBot/Constant.cs | 10 + .../Core/Entity/Generic/OneBotBot.cs | 19 ++ .../Core/Entity/Generic/OneBotRequest.cs | 28 ++ .../Core/Entity/Generic/OneBotSelf.cs | 15 + .../Core/Entity/Generic/OneBotVersion.cs | 15 + .../Core/Entity/MetaEvent/Connect.cs | 18 ++ .../Core/Entity/MetaEvent/HeartBeat.cs | 18 ++ .../Core/Entity/MetaEvent/StatusUpdate.cs | 17 + Lagrange.OneBot/Core/Network/WebSocket.cs | 56 ++++ Lagrange.OneBot/Lagrange.OneBot.csproj | 21 ++ Lagrange.OneBot/LagrangeApp.cs | 38 +++ Lagrange.OneBot/LagrangeAppBuilder.cs | 31 ++ Lagrange.OneBot/Program.cs | 21 ++ Lagrange.OneBot/Utility/QrCodeHelper.cs | 56 ++++ Lagrange.Signature/Lagrange.Signature.csproj | 9 + Lagrange.Signature/Program.cs | 6 + .../Properties/launchSettings.json | 37 +++ .../appsettings.Development.json | 8 + Lagrange.Signature/appsettings.json | 9 + 339 files changed, 11241 insertions(+) create mode 100644 .gitignore create mode 100644 Lagrange.Core.Test/Lagrange.Core.Test.csproj create mode 100644 Lagrange.Core.Test/Program.cs create mode 100644 Lagrange.Core.Test/Protobuf.cs create mode 100644 Lagrange.Core.Test/Tests/BinaryTest.cs create mode 100644 Lagrange.Core.Test/Tests/NTLoginTest.cs create mode 100644 Lagrange.Core.Test/Tests/WtLoginTest.cs create mode 100644 Lagrange.Core.Test/Utility/ProtoGen.cs create mode 100644 Lagrange.Core.Test/Utility/Tlv.cs create mode 100644 Lagrange.Core.sln create mode 100644 Lagrange.Core/AssemblyInfo.cs create mode 100644 Lagrange.Core/BotContext.cs create mode 100644 Lagrange.Core/Common/BotAppInfo.cs create mode 100644 Lagrange.Core/Common/BotConfig.cs create mode 100644 Lagrange.Core/Common/BotDeviceInfo.cs create mode 100644 Lagrange.Core/Common/BotKeystore.cs create mode 100644 Lagrange.Core/Common/BotScheduler.cs create mode 100644 Lagrange.Core/Common/Interface/Api/BotExt.cs create mode 100644 Lagrange.Core/Common/Interface/BotFactory.cs create mode 100644 Lagrange.Core/Core/Context/Attributes/BusinessLogicAttribute.cs create mode 100644 Lagrange.Core/Core/Context/BusinessContext.cs create mode 100644 Lagrange.Core/Core/Context/ContextBase.cs create mode 100644 Lagrange.Core/Core/Context/ContextCollection.cs create mode 100644 Lagrange.Core/Core/Context/LogContext.cs create mode 100644 Lagrange.Core/Core/Context/Logic/Implementation/MessagingLogic.cs create mode 100644 Lagrange.Core/Core/Context/Logic/Implementation/WtExchangeLogic.cs create mode 100644 Lagrange.Core/Core/Context/Logic/LogicBase.cs create mode 100644 Lagrange.Core/Core/Context/PacketContext.cs create mode 100644 Lagrange.Core/Core/Context/ServiceContext.SequenceProvider.cs create mode 100644 Lagrange.Core/Core/Context/ServiceContext.cs create mode 100644 Lagrange.Core/Core/Context/SocketContext.cs create mode 100644 Lagrange.Core/Core/Core.md create mode 100644 Lagrange.Core/Core/Event/EventArg/BotLogEvent.cs create mode 100644 Lagrange.Core/Core/Event/EventArg/BotOfflineEvent.cs create mode 100644 Lagrange.Core/Core/Event/EventArg/BotOnlineEvent.cs create mode 100644 Lagrange.Core/Core/Event/EventBase.cs create mode 100644 Lagrange.Core/Core/Event/EventInvoker.Events.cs create mode 100644 Lagrange.Core/Core/Event/EventInvoker.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Login/EasyLoginEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Login/Ecdh/KeyExchangeEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Login/LoginEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Login/PasswordLoginEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Login/TransEmpEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Message/PushMessageEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/Message/SendMessageEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/ProtocolEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/System/AliveEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/System/CorrectTimeEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/System/KickNTEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/System/SsoAliveEvent.cs create mode 100644 Lagrange.Core/Core/Event/Protocol/System/StatusRegisterEvent.cs create mode 100644 Lagrange.Core/Core/Network/Tcp/CallbackClientListener.cs create mode 100644 Lagrange.Core/Core/Network/Tcp/ClientListener.SocketSession.cs create mode 100644 Lagrange.Core/Core/Network/Tcp/ClientListener.cs create mode 100644 Lagrange.Core/Core/Network/Tcp/IClientListener.cs create mode 100644 Lagrange.Core/Core/Packets/Action/FirstViewReq.cs create mode 100644 Lagrange.Core/Core/Packets/Action/GetGroupMsgReq.cs create mode 100644 Lagrange.Core/Core/Packets/Action/GetGroupMsgResp.cs create mode 100644 Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn.cs create mode 100644 Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn0x6ff_501.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgReq.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgResp.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgItem.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgNew.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgTransmit.cs create mode 100644 Lagrange.Core/Core/Packets/Action/Pb/PbPushMsg.cs create mode 100644 Lagrange.Core/Core/Packets/Action/PushMessagePacket.cs create mode 100644 Lagrange.Core/Core/Packets/Action/SendMessageRequest.cs create mode 100644 Lagrange.Core/Core/Packets/Action/SendMessageResponse.cs create mode 100644 Lagrange.Core/Core/Packets/Action/SyncCookie.cs create mode 100644 Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs create mode 100644 Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs create mode 100644 Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs create mode 100644 Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchange.cs create mode 100644 Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpRequest.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpResponse.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs create mode 100644 Lagrange.Core/Core/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs create mode 100644 Lagrange.Core/Core/Packets/Login/WtLogin/Entity/Login.cs create mode 100644 Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp.cs create mode 100644 Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp12.cs create mode 100644 Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp31.cs create mode 100644 Lagrange.Core/Core/Packets/Login/WtLogin/WtLoginBase.cs create mode 100644 Lagrange.Core/Core/Packets/Message/C2C/C2C.cs create mode 100644 Lagrange.Core/Core/Packets/Message/C2C/C2CMsgInfo.cs create mode 100644 Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawReq.cs create mode 100644 Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawResp.cs create mode 100644 Lagrange.Core/Core/Packets/Message/C2C/C2CTempMessageHead.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/Attr.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/Extra/FileExtra.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/GroupInfo.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/MutilTransHead.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/NotOnlineFile.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/PcSupportDef.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/Ptt.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Component/RichText.cs create mode 100644 Lagrange.Core/Core/Packets/Message/ContentHead.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Elem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/CommonElem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomElem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomFace.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/FaceExtra.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/ImageExtra.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/MentionExtra.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/QFaceExtra.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/ExtraInfo.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Face.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/GeneralFlags.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/GroupFile.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/LightAppElem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/MarketFace.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.PbReserve.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/OnlineImage.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/RedBagInfo.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/RichMsg.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/SrcMsg.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/Text.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/TransElem.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Element/Implementation/VideoFile.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Message.cs create mode 100644 Lagrange.Core/Core/Packets/Message/MessageBody.cs create mode 100644 Lagrange.Core/Core/Packets/Message/MessageControl.cs create mode 100644 Lagrange.Core/Core/Packets/Message/PushMsg.cs create mode 100644 Lagrange.Core/Core/Packets/Message/ResponseHead.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Routing/Grp.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Routing/GrpTmp.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Routing/ResponseGrp.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Routing/Trans0X211.cs create mode 100644 Lagrange.Core/Core/Packets/Message/Routing/WPATmp.cs create mode 100644 Lagrange.Core/Core/Packets/Message/RoutingHead.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbLafter.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbProperty.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x1092_0.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_1.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_2.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x116D_1.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x5CF_11.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x644_1.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x758_1.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x773_0.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xCDE_2.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_1700.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_800.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xFE1_2.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs create mode 100644 Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpResponse.cs create mode 100644 Lagrange.Core/Core/Packets/ServicePacker.cs create mode 100644 Lagrange.Core/Core/Packets/SsoPacker.cs create mode 100644 Lagrange.Core/Core/Packets/SsoPacket.cs create mode 100644 Lagrange.Core/Core/Packets/System/NTDeviceSign.cs create mode 100644 Lagrange.Core/Core/Packets/System/NTOS.cs create mode 100644 Lagrange.Core/Core/Packets/System/NTSsoHeartBeat.cs create mode 100644 Lagrange.Core/Core/Packets/System/NTSysEvent.cs create mode 100644 Lagrange.Core/Core/Packets/System/NTSysEventSub.cs create mode 100644 Lagrange.Core/Core/Packets/System/OnlineOsInfo.cs create mode 100644 Lagrange.Core/Core/Packets/System/ServiceKickNTResponse.cs create mode 100644 Lagrange.Core/Core/Packets/System/ServiceRegister.cs create mode 100644 Lagrange.Core/Core/Packets/System/ServiceRegisterResponse.cs create mode 100644 Lagrange.Core/Core/Packets/System/Sign.cs create mode 100644 Lagrange.Core/Core/Packets/System/Software.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv100.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv103.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv106.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv107.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv108.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv10A.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv10C.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv10D.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv10E.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv11.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv114.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv116.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv118.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv119.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv11A.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv11F.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv12.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv124.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv128.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv130.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv133.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv134.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv138.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv141.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv142.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv143.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv144.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv145.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv146.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv147.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv161.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv163.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv166.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv167.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv16A.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv16D.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv16E.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv177.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv18.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv191.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv305.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv318.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv508.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv510.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv521.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv523.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv528.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv543.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/Tlv550.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode11.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode15.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode16.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode17.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode18.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode19.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode1B.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode1C.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode1D.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode1E.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode2.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode33.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode35.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode4.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode65.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode66.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCode7.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCodeCE.cs create mode 100644 Lagrange.Core/Core/Packets/Tlv/TlvQrCodeD1.cs create mode 100644 Lagrange.Core/Core/Service/Abstraction/BaseService.cs create mode 100644 Lagrange.Core/Core/Service/Abstraction/IService.cs create mode 100644 Lagrange.Core/Core/Service/EventSubscribeAttribute.cs create mode 100644 Lagrange.Core/Core/Service/Login/EasyLoginService.cs create mode 100644 Lagrange.Core/Core/Service/Login/Ecdh/KeyExchangeService.cs create mode 100644 Lagrange.Core/Core/Service/Login/LoginService.cs create mode 100644 Lagrange.Core/Core/Service/Login/PasswordLoginService.cs create mode 100644 Lagrange.Core/Core/Service/Login/TransEmpService.cs create mode 100644 Lagrange.Core/Core/Service/Message/PushMessageService.cs create mode 100644 Lagrange.Core/Core/Service/Message/SendMessageService.cs create mode 100644 Lagrange.Core/Core/Service/ServiceAttribute.cs create mode 100644 Lagrange.Core/Core/Service/System/AliveService.cs create mode 100644 Lagrange.Core/Core/Service/System/CorrectTimeService.cs create mode 100644 Lagrange.Core/Core/Service/System/KickNTService.cs create mode 100644 Lagrange.Core/Core/Service/System/SsoAliveService.cs create mode 100644 Lagrange.Core/Core/Service/System/StatusRegisterService.cs create mode 100644 Lagrange.Core/Lagrange.Core.csproj create mode 100644 Lagrange.Core/Message/Entity/FaceEntity.cs create mode 100644 Lagrange.Core/Message/Entity/FileEntity.cs create mode 100644 Lagrange.Core/Message/Entity/ForwardEntity.cs create mode 100644 Lagrange.Core/Message/Entity/ImageEntity.cs create mode 100644 Lagrange.Core/Message/Entity/JsonEntity.cs create mode 100644 Lagrange.Core/Message/Entity/MentionEntity.cs create mode 100644 Lagrange.Core/Message/Entity/PacketEntity.cs create mode 100644 Lagrange.Core/Message/Entity/TextEntity.cs create mode 100644 Lagrange.Core/Message/Entity/XmlEntity.cs create mode 100644 Lagrange.Core/Message/IMessageEntity.cs create mode 100644 Lagrange.Core/Message/MessageBuilder.cs create mode 100644 Lagrange.Core/Message/MessageChain.cs create mode 100644 Lagrange.Core/Message/MessageElementAttribute.cs create mode 100644 Lagrange.Core/Message/MessagePacker.cs create mode 100644 Lagrange.Core/Utility/Binary/BInaryPacket.Prefix.cs create mode 100644 Lagrange.Core/Utility/Binary/BinaryPacket.cs create mode 100644 Lagrange.Core/Utility/Binary/BinaryPropertyAttribute.cs create mode 100644 Lagrange.Core/Utility/Binary/BinarySerializer.cs create mode 100644 Lagrange.Core/Utility/Binary/BitConverter.cs create mode 100644 Lagrange.Core/Utility/Binary/Compression/Common.cs create mode 100644 Lagrange.Core/Utility/Binary/Compression/ZCompression.cs create mode 100644 Lagrange.Core/Utility/Binary/JceStruct/JceReader.cs create mode 100644 Lagrange.Core/Utility/Binary/JceStruct/JceStruct.cs create mode 100644 Lagrange.Core/Utility/Binary/JceStruct/JceType.cs create mode 100644 Lagrange.Core/Utility/Binary/JceStruct/JceWriter.cs create mode 100644 Lagrange.Core/Utility/Binary/JceStruct/UniPacket.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvAttribute.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvEncryptAttribute.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvQrCodeAttribute.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/TlvBody.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/TlvPacker.cs create mode 100644 Lagrange.Core/Utility/Binary/Tlv/TlvPacket.cs create mode 100644 Lagrange.Core/Utility/Crypto/AesGcmImpl.cs create mode 100644 Lagrange.Core/Utility/Crypto/EcdhImpl.Constants.cs create mode 100644 Lagrange.Core/Utility/Crypto/EcdhImpl.cs create mode 100644 Lagrange.Core/Utility/Crypto/ICryptoImpl.cs create mode 100644 Lagrange.Core/Utility/Crypto/Provider/Ecdh/EcdhProvider.cs create mode 100644 Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticCurve.cs create mode 100644 Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticPoint.cs create mode 100644 Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs create mode 100644 Lagrange.Core/Utility/Crypto/TeaImpl.cs create mode 100644 Lagrange.Core/Utility/Extension/ByteExtension.cs create mode 100644 Lagrange.Core/Utility/Extension/ExpressionExt.cs create mode 100644 Lagrange.Core/Utility/Extension/ReflectionExt.cs create mode 100644 Lagrange.Core/Utility/Extension/SocketExt.cs create mode 100644 Lagrange.Core/Utility/Extension/StringExtension.cs create mode 100644 Lagrange.Core/Utility/Extension/TaskAwaitExt.cs create mode 100644 Lagrange.Core/Utility/Extension/TypeExtension.cs create mode 100644 Lagrange.Core/Utility/Generator/ByteGen.cs create mode 100644 Lagrange.Core/Utility/Generator/StringGen.cs create mode 100644 Lagrange.Core/Utility/Network/Http.cs create mode 100644 Lagrange.Core/Utility/Network/Icmp.cs create mode 100644 Lagrange.Core/Utility/ServiceInjector.cs create mode 100644 Lagrange.Core/Utility/Signature.cs create mode 100644 Lagrange.Core/Utility/TaskScheduler.cs create mode 100644 Lagrange.OneBot/Constant.cs create mode 100644 Lagrange.OneBot/Core/Entity/Generic/OneBotBot.cs create mode 100644 Lagrange.OneBot/Core/Entity/Generic/OneBotRequest.cs create mode 100644 Lagrange.OneBot/Core/Entity/Generic/OneBotSelf.cs create mode 100644 Lagrange.OneBot/Core/Entity/Generic/OneBotVersion.cs create mode 100644 Lagrange.OneBot/Core/Entity/MetaEvent/Connect.cs create mode 100644 Lagrange.OneBot/Core/Entity/MetaEvent/HeartBeat.cs create mode 100644 Lagrange.OneBot/Core/Entity/MetaEvent/StatusUpdate.cs create mode 100644 Lagrange.OneBot/Core/Network/WebSocket.cs create mode 100644 Lagrange.OneBot/Lagrange.OneBot.csproj create mode 100644 Lagrange.OneBot/LagrangeApp.cs create mode 100644 Lagrange.OneBot/LagrangeAppBuilder.cs create mode 100644 Lagrange.OneBot/Program.cs create mode 100644 Lagrange.OneBot/Utility/QrCodeHelper.cs create mode 100644 Lagrange.Signature/Lagrange.Signature.csproj create mode 100644 Lagrange.Signature/Program.cs create mode 100644 Lagrange.Signature/Properties/launchSettings.json create mode 100644 Lagrange.Signature/appsettings.Development.json create mode 100644 Lagrange.Signature/appsettings.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/Lagrange.Core.Test/Lagrange.Core.Test.csproj b/Lagrange.Core.Test/Lagrange.Core.Test.csproj new file mode 100644 index 0000000..29a01ac --- /dev/null +++ b/Lagrange.Core.Test/Lagrange.Core.Test.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/Lagrange.Core.Test/Program.cs b/Lagrange.Core.Test/Program.cs new file mode 100644 index 0000000..cc688e6 --- /dev/null +++ b/Lagrange.Core.Test/Program.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using Lagrange.Core.Test.Tests; +using Lagrange.Core.Utility.Binary.JceStruct; +using Lagrange.Core.Utility.Extension; + + +var jce = "10022C3C4C56235151536572766963652E436F6E666967507573685376632E4D61696E53657276616E746607507573685265717D000104050800010607507573685265711800010612436F6E666967507573682E507573685265711D000103DD0A10012D000103CB19000A0A160D33392E3135362E3132352E32302136B030014C5C600870018602746A9602636DAC0B0A160D33362E3135352E3136332E32352101BB30014C5C60087001860273689602636DAC0B0A160E33392E3135362E3134342E313133211F9030014C5C600870018602746A9602636DAC0B0A160E3132302E3233332E31372E313437205030014C5C600870018602737A9602636DAC0B0A160E33362E3135352E3230372E3233302136B030014C5C60087001860273689602636DAC0B0A160E33392E3135362E3132362E3137382101BB30014C5C600870018602746A9602636DAC0B0A160E3132302E3233322E33312E323530205030014C5C600870018602737A9602636DAC0B0A160D33362E3135352E3234352E3136205030014C5C60087001860273689602636DAC0B0A16116D7366776966692E33672E71712E636F6D211F9030014C5C60087C86066F746865727396066F7468657273AC0B0A160E33392E3135362E3132362E313738205030014C5C600870018602746A9602636DAC0B39000A0A160D33392E3135362E3132352E32302136B030014C5C600870018602746A9602636DAC0B0A160D33362E3135352E3136332E32352101BB30014C5C60087001860273689602636DAC0B0A160E33392E3135362E3134342E313133211F9030014C5C600870018602746A9602636DAC0B0A160E3132302E3233332E31372E313437205030014C5C600870018602737A9602636DAC0B0A160E33362E3135352E3230372E3233302136B030014C5C60087001860273689602636DAC0B0A160E33392E3135362E3132362E3137382101BB30014C5C600870018602746A9602636DAC0B0A160E3132302E3233322E33312E323530205030014C5C600870018602737A9602636DAC0B0A160D33362E3135352E3234352E3136205030014C5C60087001860273689602636DAC0B0A16116D7366776966692E33672E71712E636F6D211F9030014C5C60087C86066F746865727396066F7468657273AC0B0A160E33392E3135362E3132362E313738205030014C5C600870018602746A9602636DAC0B4C5C6C70018900020A160E33362E3135352E3231332E313832205030014C500360087C860273689602636DAC0B0A160F3132302E3233322E3133302E313032205030014C500360087C8602737A9602636DAC0B9900020A160E33362E3135352E3231332E313832205030014C500360087C860273689602636DAC0B0A160F3132302E3233322E3133302E313032205030014C500360087C8602737A9602636DAC0BA90CB90CC90CD90CE002FC0FF61014514449414F5F504F4C4943595F44454641554C54F01101330000000A8D41253D0B8C980CA80C".UnHex(); +var decoded = new UniPacket(jce); +var data = decoded.Buffer; + +File.WriteAllText("JceStruct.json", JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true })); + +await new NTLoginTest().LoginByPassword(); +// await new WtLoginTest().FetchQrCode(); + +public class Test +{ + public string Name { get; set; } = ""; +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Protobuf.cs b/Lagrange.Core.Test/Protobuf.cs new file mode 100644 index 0000000..7a0e57f --- /dev/null +++ b/Lagrange.Core.Test/Protobuf.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Core.Packets.Login.Ecdh; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Test; + +public class Protobuf +{ + public void Test() + { + + var test = new SsoKeyExchange() + { + PubKey = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + GcmCalc2 = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + GcmCalc1 = new byte[] { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }, + Timestamp = 23456789, + Type = 1 + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, test); + Console.WriteLine(stream.ToArray().Hex(false, true)); + } +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Tests/BinaryTest.cs b/Lagrange.Core.Test/Tests/BinaryTest.cs new file mode 100644 index 0000000..337a4a2 --- /dev/null +++ b/Lagrange.Core.Test/Tests/BinaryTest.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Test.Tests; + +public class BinaryTest +{ + public class Binary + { + [BinaryProperty] public uint Value { get; set; } = 114514; + + [BinaryProperty] public ulong Value2 { get; set; } = 1919810; + } + + public void Test() + { + var binary = new Binary(); + var bytes = BinarySerializer.Serialize(binary); + Console.WriteLine(bytes.ToArray().Hex()); + Console.WriteLine(bytes.Length); + + var binary2 = new BinaryPacket(); + binary2.WriteUint(114514, false); + binary2.WriteUlong(1919810, false); + Console.WriteLine(binary2.ToArray().Hex()); + + var newPacket = new BinaryPacket(bytes.ToArray()); + var binary3 = newPacket.Deserialize(); + Console.WriteLine(binary3.Value); + Console.WriteLine(binary3.Value2); + } +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Tests/NTLoginTest.cs b/Lagrange.Core.Test/Tests/NTLoginTest.cs new file mode 100644 index 0000000..96dafcb --- /dev/null +++ b/Lagrange.Core.Test/Tests/NTLoginTest.cs @@ -0,0 +1,43 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Common.Interface; +using Lagrange.Core.Common.Interface.Api; + +namespace Lagrange.Core.Test.Tests; + +// ReSharper disable once InconsistentNaming + +public class NTLoginTest +{ + public async Task LoginByPassword() + { + var deviceInfo = WtLoginTest.GetDeviceInfo(); + var keyStore = WtLoginTest.LoadKeystore(); + + if (keyStore == null) + { + Console.WriteLine("Please login by QrCode first"); + return; + } + + var bot = BotFactory.Create(new BotConfig() + { + UseIPv6Network = false, + GetOptimumServer = true, + AutoReconnect = true, + Protocol = Protocols.Linux + }, deviceInfo, keyStore); + + bot.Invoker.OnBotLogEvent += (context, @event) => + { + Console.WriteLine(@event.ToString()); + }; + + bot.Invoker.OnBotOnlineEvent += (context, @event) => + { + Console.WriteLine(@event.ToString()); + WtLoginTest.SaveKeystore(bot.UpdateKeystore()); + }; + + await bot.LoginByPassword(); + } +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Tests/WtLoginTest.cs b/Lagrange.Core.Test/Tests/WtLoginTest.cs new file mode 100644 index 0000000..b947e9f --- /dev/null +++ b/Lagrange.Core.Test/Tests/WtLoginTest.cs @@ -0,0 +1,78 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Lagrange.Core.Common; +using Lagrange.Core.Common.Interface; +using Lagrange.Core.Common.Interface.Api; + +namespace Lagrange.Core.Test.Tests; + +public class WtLoginTest +{ + public async Task FetchQrCode() + { + var deviceInfo = GetDeviceInfo(); + var keyStore = LoadKeystore() ?? throw new Exception(); + + var bot = BotFactory.Create(new BotConfig + { + UseIPv6Network = false, + GetOptimumServer = true, + AutoReconnect = true, + Protocol = Protocols.Linux + }, deviceInfo, keyStore); + + bot.Invoker.OnBotLogEvent += (context, @event) => + { + Console.WriteLine(@event.ToString()); + }; + + bot.Invoker.OnBotOnlineEvent += (context, @event) => + { + Console.WriteLine(@event.ToString()); + SaveKeystore(bot.UpdateKeystore()); + }; + + var qrCode = await bot.FetchQrCode(); + if (qrCode != null) + { + await File.WriteAllBytesAsync("qr.png", qrCode); + await bot.LoginByQrCode(); + } + } + + public static BotDeviceInfo GetDeviceInfo() + { + if (File.Exists("Test/DeviceInfo.json")) + { + var info = JsonSerializer.Deserialize(File.ReadAllText("Test/DeviceInfo.json")); + if (info != null) return info; + + info = BotDeviceInfo.GenerateInfo(); + File.WriteAllText("Test/DeviceInfo.json", JsonSerializer.Serialize(info)); + return info; + } + + var deviceInfo = BotDeviceInfo.GenerateInfo(); + File.WriteAllText("Test/DeviceInfo.json", JsonSerializer.Serialize(deviceInfo)); + return deviceInfo; + } + + public static void SaveKeystore(BotKeystore keystore) => + File.WriteAllText("Test/Keystore.json", JsonSerializer.Serialize(keystore)); + + public static BotKeystore? LoadKeystore() + { + try + { + var text = File.ReadAllText("Test/Keystore.json"); + return JsonSerializer.Deserialize(text, new JsonSerializerOptions() + { + ReferenceHandler = ReferenceHandler.Preserve + }); + } + catch + { + return null; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Utility/ProtoGen.cs b/Lagrange.Core.Test/Utility/ProtoGen.cs new file mode 100644 index 0000000..105e938 --- /dev/null +++ b/Lagrange.Core.Test/Utility/ProtoGen.cs @@ -0,0 +1,61 @@ +using System.Reflection; +using System.Text; +using ProtoBuf; + +namespace Lagrange.Core.Test.Utility; + +internal static class ProtoGen +{ + public static void GenerateProtoFiles() + { + var assembly = typeof(Lagrange.Core.Utility.ServiceInjector).Assembly; + var types = assembly.GetTypes(); + var sb = new StringBuilder(); + + sb.AppendLine("syntax = \"proto3\";"); + sb.AppendLine(); + sb.AppendLine("package Lagrange.Core;"); + + foreach (var type in types) + { + if (type.Namespace?.StartsWith("Lagrange.Core.Packets") != true) continue; + + sb.AppendLine($"message {type.Name} {{"); + var properties = type.GetProperties(); + foreach (var property in properties) + { + string typeString = ParseType(property.PropertyType); + sb.AppendLine($" {GetLastToken(typeString, '.')} {property.Name} = {property.GetCustomAttribute()?.Tag};"); + } + sb.AppendLine("}"); + sb.AppendLine(); + } + + string proto = sb.ToString(); + + File.WriteAllText("packets.proto", proto); + } + + private static string ParseType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + { + return $"repeated {ParseType(type.GetGenericArguments()[0])}"; + } + + return type.ToString() switch + { + "System.UInt64" => "varint", + "System.UInt32" => "varint", + "System.UInt16" => "varint", + "System.Int64" => "varint", + "System.Int32" => "varint", + "System.String" => "string", + "System.Boolean" => "bool", + "System.Byte[]" => "bytes", + _ => type.ToString() + }; + } + + private static string GetLastToken(string str, char separator) => str.Split(separator)[^1]; +} \ No newline at end of file diff --git a/Lagrange.Core.Test/Utility/Tlv.cs b/Lagrange.Core.Test/Utility/Tlv.cs new file mode 100644 index 0000000..00d5d0f --- /dev/null +++ b/Lagrange.Core.Test/Utility/Tlv.cs @@ -0,0 +1,35 @@ +using System.Text; +using Lagrange.Core.Utility.Extension; +using static Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Test.Utility; + +public static class Tlv +{ + public static Dictionary GetTlvDictionary(byte[] tlvs, bool isCommand = true) + { + var result = new Dictionary(); + + using var reader = new BinaryReader(new MemoryStream(tlvs)); + + ushort command; + if (isCommand) + { + command = ToUInt16(reader.ReadBytes(2), false); + } + + ushort tlvCount = ToUInt16(reader.ReadBytes(2), false); + + for (int i = 0; i < tlvCount; i++) + { + ushort tlvTag = ToUInt16(reader.ReadBytes(2), false); + ushort tlvLength = ToUInt16(reader.ReadBytes(2), false); + byte[] tlvValue = reader.ReadBytes(tlvLength); + + result.Add($"0x{tlvTag:X} {tlvLength}", tlvValue.Hex()); + result.Add($"0x{tlvTag:X} UTF8 {tlvLength}", Encoding.UTF8.GetString(tlvValue)); + } + + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core.sln b/Lagrange.Core.sln new file mode 100644 index 0000000..78d2298 --- /dev/null +++ b/Lagrange.Core.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lagrange.Core", "Lagrange.Core\Lagrange.Core.csproj", "{909C99CC-0CB7-4A34-8C75-AD25E6AEA535}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lagrange.Core.Test", "Lagrange.Core.Test\Lagrange.Core.Test.csproj", "{D64B6BAB-CD20-4660-8A6E-BCC936652204}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lagrange.OneBot", "Lagrange.OneBot\Lagrange.OneBot.csproj", "{37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lagrange.Signature", "Lagrange.Signature\Lagrange.Signature.csproj", "{FE894A94-407C-4C8D-AE50-1758EBAB6E8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Debug|Any CPU.Build.0 = Debug|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Release|Any CPU.ActiveCfg = Release|Any CPU + {909C99CC-0CB7-4A34-8C75-AD25E6AEA535}.Release|Any CPU.Build.0 = Release|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64B6BAB-CD20-4660-8A6E-BCC936652204}.Release|Any CPU.Build.0 = Release|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37AEDD3B-9B9F-4782-ADD5-BA2436FB2507}.Release|Any CPU.Build.0 = Release|Any CPU + {FE894A94-407C-4C8D-AE50-1758EBAB6E8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE894A94-407C-4C8D-AE50-1758EBAB6E8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE894A94-407C-4C8D-AE50-1758EBAB6E8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE894A94-407C-4C8D-AE50-1758EBAB6E8C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Lagrange.Core/AssemblyInfo.cs b/Lagrange.Core/AssemblyInfo.cs new file mode 100644 index 0000000..6c8cabd --- /dev/null +++ b/Lagrange.Core/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Lagrange.Core.Test")] // Allow unit test to access internal members +[assembly: InternalsVisibleTo("Lagrange.OneBot")] // OneBot Implementation diff --git a/Lagrange.Core/BotContext.cs b/Lagrange.Core/BotContext.cs new file mode 100644 index 0000000..bcbc367 --- /dev/null +++ b/Lagrange.Core/BotContext.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Context; +using Lagrange.Core.Core.Event; + +namespace Lagrange.Core; + +public class BotContext : IDisposable +{ + public readonly EventInvoker Invoker; + + internal readonly Utility.TaskScheduler Scheduler; + + internal readonly ContextCollection ContextCollection; + + private readonly BotAppInfo _appInfo; + + private readonly BotConfig _config; + + private readonly BotDeviceInfo _deviceInfo; + + private readonly BotKeystore _keystore; + + internal BotContext(BotConfig config, BotDeviceInfo deviceInfo, BotKeystore keystore) + { + Invoker = new EventInvoker(this); + Scheduler = new Utility.TaskScheduler(); + + _config = config; + _appInfo = BotAppInfo.ProtocolToAppInfo[config.Protocol]; + _deviceInfo = deviceInfo; + _keystore = keystore; + + ContextCollection = new ContextCollection(keystore, _appInfo, deviceInfo, Invoker, Scheduler); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + Invoker.Dispose(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Common/BotAppInfo.cs b/Lagrange.Core/Common/BotAppInfo.cs new file mode 100644 index 0000000..73dc78d --- /dev/null +++ b/Lagrange.Core/Common/BotAppInfo.cs @@ -0,0 +1,111 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Common; + +public class BotAppInfo +{ + public string Os { get; private set; } + + public string BaseVersion { get; private set; } + + public string CurrentVersion { get; private set; } + + public int BuildVersion { get; private set; } + + public int MiscBitmap { get; private set; } + + public string PtVersion { get; private set; } + + public int PtOsVersion { get; private set; } + + public string PackageName { get; private set; } + + public string WtLoginSdk { get; private set; } + + /// Or Known as QUA + public string PackageSign { get; private set; } + + public int UnknownIdentifier { get; private set; } + + public int AppId { get; private set; } + + /// Or known as pubId in tencent log + public int SubAppId { get; private set; } + + public int AppIdQrCode { get; private set; } + + public ushort AppClientVersion { get; private set; } + + public uint MainSigMap { get; private set; } + + public ushort SubSigMap { get; private set; } + + private static readonly BotAppInfo Linux = new() + { + Os = "Linux", + + BaseVersion = "3.1.1-11223", + CurrentVersion = "3.1.2-13107", + BuildVersion = 13107, + MiscBitmap = 32764, + PtVersion = "2.0.0", + PtOsVersion = 19, + PackageName = "com.tencent.qq", + WtLoginSdk = "nt.wtlogin.0.0.1", + PackageSign = "V1_LNX_NQ_3.1.2-13107_RDM_B", + + UnknownIdentifier = 70644224, + AppId = 1600001615, + SubAppId = 537146866, + AppIdQrCode = 13697054, + AppClientVersion = 13172, + + MainSigMap = 169742560, + SubSigMap = 0 + }; + + private static readonly BotAppInfo MacOs = new() + { + Os = "MacOS", + + BaseVersion = "6.9.17-12118", + CurrentVersion = "6.9.17-12118", + BuildVersion = 12118, + PtVersion = "2.0.0", + MiscBitmap = 32764, + PtOsVersion = 23, + PackageName = "com.tencent.qqdesktop", + WtLoginSdk = "nt.wtlogin.0.0.1", + PackageSign = "V1_MAC_NQ_6.9.17-12118_RDM_B", + + AppId = 1600001602, + AppIdQrCode = 13697054, + AppClientVersion = 13172 + }; + + private static readonly BotAppInfo Windows = new() + { + Os = "Windows", + + BaseVersion = "9.8.3-13183", + CurrentVersion = "9.8.3-13183", + BuildVersion = 13183, + PtVersion = "2.0.0", + MiscBitmap = 32764, + PtOsVersion = 23, + PackageName = "com.tencent.qq", + WtLoginSdk = "nt.wtlogin.0.0.1", + PackageSign = "V1_WIN_NQ_9.8.3-13183_RDM_B", + + AppId = 1600001604, + AppIdQrCode = 13697054, + AppClientVersion = 13172 + }; + + public static readonly Dictionary ProtocolToAppInfo = new() + { + { Protocols.Windows, Windows }, + { Protocols.Linux, Linux }, + { Protocols.MacOs, MacOs }, + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Common/BotConfig.cs b/Lagrange.Core/Common/BotConfig.cs new file mode 100644 index 0000000..9b38ee4 --- /dev/null +++ b/Lagrange.Core/Common/BotConfig.cs @@ -0,0 +1,38 @@ +namespace Lagrange.Core.Common; + +/// +/// Configuration for The bot client +/// +[Serializable] +public class BotConfig +{ + /// + /// The protocol for the client, default is Linux + /// + public Protocols Protocol { get; set; } = Protocols.Linux; + + /// + /// Auto reconnect to server when disconnected + /// + public bool AutoReconnect { get; set; } = true; + + /// + /// Use the IPv6 to connect to server, only if your network support IPv6 + /// + public bool UseIPv6Network { get; set; } = false; + + /// + /// Get optimum server from Tencent MSF server, set to false to use hardcode server + /// + public bool GetOptimumServer { get; set; } = true; +} + +/// +/// The Protocol for the client +/// +public enum Protocols +{ + Windows = 0, + MacOs = 1, + Linux = 2 +} \ No newline at end of file diff --git a/Lagrange.Core/Common/BotDeviceInfo.cs b/Lagrange.Core/Common/BotDeviceInfo.cs new file mode 100644 index 0000000..7d5703c --- /dev/null +++ b/Lagrange.Core/Common/BotDeviceInfo.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Utility.Generator; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Common; + +[Serializable] +public class BotDeviceInfo +{ + public Guid Guid { get; set; } + + public byte[] MacAddress { get; set; } + + public string DeviceName { get; set; } + + public string SystemKernel { get; set; } + + public string KernelVersion { get; set; } + + public static BotDeviceInfo GenerateInfo() => new() + { + Guid = Guid.NewGuid(), + MacAddress = ByteGen.GenRandomBytes(6), + DeviceName = $"Lagrange-{StringGen.GenerateHex(6).ToUpper()}", + SystemKernel = "Windows 10.0.19042", + KernelVersion = "10.0.19042.0" + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Common/BotKeystore.cs b/Lagrange.Core/Common/BotKeystore.cs new file mode 100644 index 0000000..833b91d --- /dev/null +++ b/Lagrange.Core/Common/BotKeystore.cs @@ -0,0 +1,110 @@ +using System.Text; +using System.Text.Json.Serialization; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; + + +namespace Lagrange.Core.Common; + +public class BotKeystore // TODO: 你自己不恶心吗,穿件衣服吧你 +{ + [JsonConstructor] +#pragma warning disable CS8618 + public BotKeystore() +#pragma warning restore CS8618 + { + SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + PrimeImpl = new EcdhImpl(EcdhImpl.CryptMethod.Prime256V1, false); + TeaImpl = new TeaImpl(); + + Stub = new KeyCollection(); + + var tempPwd = Session?.TempPassword; + Session = tempPwd != null ? new WtLoginSession { TempPassword = tempPwd } : new WtLoginSession(); + } + + /// + /// Create the Bot keystore + /// + /// Set this field 0 to use QrCode Login + /// Password Raw + internal BotKeystore(uint uin, string password) + { + Uin = uin; + PasswordMd5 = Encoding.UTF8.GetBytes(password).Md5(); + + SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + PrimeImpl = new EcdhImpl(EcdhImpl.CryptMethod.Prime256V1, false); + TeaImpl = new TeaImpl(); + Session = new WtLoginSession(); + + Stub = new KeyCollection(); + } + + public uint Uin { get; set; } + + public string? Uid { get; set; } + + public string PasswordMd5 { get; set; } + + internal EcdhImpl SecpImpl { get; set; } + internal EcdhImpl PrimeImpl { get; set; } + internal TeaImpl TeaImpl { get; set; } + + internal KeyCollection Stub { get; } + + public WtLoginSession Session { get; set; } + + public BotInfo? Info { get; internal set; } + + [Serializable] + public class KeyCollection + { + public byte[] RandomKey { get; set; } = ByteGen.GenRandomBytes(16); + public byte[] TgtgtKey { get; set; } = new byte[16]; + } + + [Serializable] + public class WtLoginSession + { + internal byte[] D2Key { get; set; } = new byte[16]; + internal byte[] D2 { get; set; } = Array.Empty(); + internal byte[] Tgt { get; set; } = Array.Empty(); + + internal byte[]? QrSign { get; set; } // size: 24 + internal byte[]? KeySign { get; set; } + internal byte[]? UnusualSign { get; set; } + internal string? QrString { get; set; } + + internal byte[]? ExchangeKey { get; set; } + + public byte[]? TempPassword { get; set; } + internal byte[]? NoPicSig { get; set; } // size: 16, may be from Tlv19, for Tlv16A + + private ushort _sequence; + internal ushort Sequence + { + get => _sequence++; + set => _sequence = value; + } + } + + public class BotInfo + { + internal BotInfo(byte age, byte gender, string name) + { + Age = age; + Gender = gender; + Name = name; + } + + public byte Age { get; } + + public byte Gender { get; } + + public string Name { get; } + + public override string ToString() => $"Bot name: {Name} | Gender: {Gender} | Age: {Age}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Common/BotScheduler.cs b/Lagrange.Core/Common/BotScheduler.cs new file mode 100644 index 0000000..36176bf --- /dev/null +++ b/Lagrange.Core/Common/BotScheduler.cs @@ -0,0 +1,86 @@ +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable UnusedAutoPropertyAccessor.Local + +namespace Lagrange.Core.Common; + +/// +/// Task Scheduler +/// +public class Scheduler +{ + public const int Infinity = int.MaxValue; + + private BotContext Bot { get; } + + private Utility.TaskScheduler Instance { get; } + + public string Name { get; } + + public Action Action { get; } + + internal Scheduler(BotContext bot, string name, Action action) + { + Bot = bot; + Name = name; + Action = action; + Instance = bot.Scheduler; + } + + ~Scheduler() => Cancel(); + + /// + /// Create a task scheduler + /// + /// [In] Bot instance + /// [In] Task identity name + /// [In] Task callback action + /// + public static Scheduler Create(BotContext bot, string name, Action action) => new(bot, name, action); + + /// + /// Execute the task with a specific interval + /// + /// [In] Interval in milliseconds + /// [In] Execute times + /// + /// + public void Interval(int interval, int times) => Instance.Interval(Name, interval, times, () => Action(Bot)); + + /// + /// Execute the task with a specific interval infinity + /// + /// [In] Interval in milliseconds + /// + /// + public void Interval(int interval) => Instance.Interval(Name, interval, Infinity, () => Action(Bot)); + + /// + /// Execute the task once + /// + /// [In] Delay time in milliseconds + /// + /// + public void RunOnce(int delay) => Instance.RunOnce(Name, delay, () => Action(Bot)); + + /// + /// Execute the task once + /// + /// [In] Execute date + /// + /// + public void RunOnce(DateTime date) => Instance.RunOnce(Name, date, () => Action(Bot)); + + /// + /// Trigger a task to run + /// + public void Trigger() => Instance.Trigger(Name); + + /// + /// Cancel the task + /// + /// + public void Cancel() => Instance.Cancel(Name); +} \ No newline at end of file diff --git a/Lagrange.Core/Common/Interface/Api/BotExt.cs b/Lagrange.Core/Common/Interface/Api/BotExt.cs new file mode 100644 index 0000000..e151af1 --- /dev/null +++ b/Lagrange.Core/Common/Interface/Api/BotExt.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Common.Interface.Api; + +public static class BotExt +{ + public static async Task FetchQrCode(this BotContext bot) + => await bot.ContextCollection.Business.WtExchangeLogic.FetchQrCode(); + + /// + /// Use this method to login by QrCode, you should call first + /// + public static async Task LoginByQrCode(this BotContext bot) + => await bot.ContextCollection.Business.WtExchangeLogic.LoginByQrCode(); + + /// + /// Use this method to login by password, EasyLogin may be preformed if there is sig in + /// + public static async Task LoginByPassword(this BotContext bot) + => await bot.ContextCollection.Business.WtExchangeLogic.LoginByPassword(); + + public static BotKeystore UpdateKeystore(this BotContext bot) + => bot.ContextCollection.Keystore; +} \ No newline at end of file diff --git a/Lagrange.Core/Common/Interface/BotFactory.cs b/Lagrange.Core/Common/Interface/BotFactory.cs new file mode 100644 index 0000000..8300883 --- /dev/null +++ b/Lagrange.Core/Common/Interface/BotFactory.cs @@ -0,0 +1,7 @@ +namespace Lagrange.Core.Common.Interface; + +public static class BotFactory +{ + public static BotContext Create(BotConfig config, BotDeviceInfo deviceInfo, BotKeystore keystore) => + new(config, deviceInfo, keystore); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/Attributes/BusinessLogicAttribute.cs b/Lagrange.Core/Core/Context/Attributes/BusinessLogicAttribute.cs new file mode 100644 index 0000000..5eb426e --- /dev/null +++ b/Lagrange.Core/Core/Context/Attributes/BusinessLogicAttribute.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Core.Context.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +internal class BusinessLogicAttribute : Attribute +{ + public string Name { get; } + + public string Description { get; } + + public BusinessLogicAttribute(string name, string description) + { + Name = name; + Description = description; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/BusinessContext.cs b/Lagrange.Core/Core/Context/BusinessContext.cs new file mode 100644 index 0000000..8803a9a --- /dev/null +++ b/Lagrange.Core/Core/Context/BusinessContext.cs @@ -0,0 +1,135 @@ +using System.Reflection; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Context.Attributes; +using Lagrange.Core.Core.Context.Logic; +using Lagrange.Core.Core.Context.Logic.Implementation; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Service; +using Lagrange.Core.Utility.Extension; +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Context; + +internal class BusinessContext : ContextBase +{ + private const string Tag = nameof(BusinessContext); + + private readonly Dictionary> _businessLogics; + + #region Business Logics + + internal MessagingLogic MessagingLogic { get; private set; } + + internal WtExchangeLogic WtExchangeLogic { get; private set; } + + #endregion + + public BusinessContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _businessLogics = new Dictionary>(); + + RegisterLogics(); + } + + private void RegisterLogics() + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var logic in assembly.GetTypeByAttributes(out _)) + { + var constructor = logic.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance); + var instance = (LogicBase)constructor[0].Invoke(new object[] { Collection }); + + foreach (var @event in logic.GetCustomAttributes()) + { + if (!_businessLogics.TryGetValue(@event.EventType, out var list)) + { + list = new List(); + _businessLogics.Add(@event.EventType, list); + } + list.Add(instance); // Append logics + } + + switch (instance) + { + case WtExchangeLogic wtExchangeLogic: + WtExchangeLogic = wtExchangeLogic; + break; + case MessagingLogic messagingLogic: + MessagingLogic = messagingLogic; + break; + } + } + } + + public async Task PushEvent(ProtocolEvent @event) + { + try + { + var packets = Collection.Service.ResolvePacketByEvent(@event); + foreach (var packet in packets) await Collection.Packet.PostPacket(packet); + } + catch + { + return false; + } + + return true; + } + + /// + /// Send Event to the Server, goes through the given context + /// + public async Task> SendEvent(ProtocolEvent @event) + { + var result = new List(); + + try + { + var packets = Collection.Service.ResolvePacketByEvent(@event); + foreach (var packet in packets) + { + var returnVal = await Collection.Packet.SendPacket(packet); + var resolved = Collection.Service.ResolveEventByPacket(returnVal); + foreach (var protocol in resolved) + { + await HandleIncomingEvent(protocol); + result.Add(protocol); + } + } + } + catch(Exception e) + { + Collection.Log.LogWarning(Tag, $"Error when processing the event: {@event}"); + Collection.Log.LogWarning(Tag, e.Message); + if (e.StackTrace != null) Collection.Log.LogWarning(Tag, e.StackTrace); + } + + return result; + } + + public async Task HandleIncomingEvent(ProtocolEvent @event) + { + _businessLogics.TryGetValue(typeof(ProtocolEvent), out var baseLogics); + _businessLogics.TryGetValue(@event.GetType(), out var normalLogics); + + var logics = new List(); + if (baseLogics != null) logics.AddRange(baseLogics); + if (normalLogics != null) logics.AddRange(normalLogics); + + foreach (var logic in logics) + { + try + { + await logic.Incoming(@event); + } + catch (Exception e) + { + Collection.Log.LogFatal(Tag, $"Error occurred while handling event {@event.GetType().Name}"); + Collection.Log.LogFatal(Tag, e.Message); + } + } + + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/ContextBase.cs b/Lagrange.Core/Core/Context/ContextBase.cs new file mode 100644 index 0000000..e9ae171 --- /dev/null +++ b/Lagrange.Core/Core/Context/ContextBase.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Common; + +namespace Lagrange.Core.Core.Context; + +internal abstract class ContextBase +{ + protected readonly ContextCollection Collection; + + protected readonly BotKeystore Keystore; + + protected readonly BotAppInfo AppInfo; + + protected readonly BotDeviceInfo DeviceInfo; + + protected ContextBase(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + { + Collection = collection; + Keystore = keystore; + AppInfo = appInfo; + DeviceInfo = device; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/ContextCollection.cs b/Lagrange.Core/Core/Context/ContextCollection.cs new file mode 100644 index 0000000..ec12689 --- /dev/null +++ b/Lagrange.Core/Core/Context/ContextCollection.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event; +using TaskScheduler = Lagrange.Core.Utility.TaskScheduler; + +namespace Lagrange.Core.Core.Context; + +internal class ContextCollection +{ + public PacketContext Packet { get; } + public SocketContext Socket { get; } + public ServiceContext Service { get; } + public BusinessContext Business { get; } + public LogContext Log { get; } + + public BotKeystore Keystore { get; } + public BotAppInfo AppInfo { get; } + public BotDeviceInfo Device { get; } + + public TaskScheduler Scheduler { get; } + public EventInvoker Invoker { get; } + + public ContextCollection(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, EventInvoker invoker, + TaskScheduler scheduler) + { + Packet = new PacketContext(this, keystore, appInfo, device); + Socket = new SocketContext(this, keystore, appInfo, device); + Service = new ServiceContext(this, keystore, appInfo, device); + Business = new BusinessContext(this, keystore, appInfo, device); + Log = new LogContext(this, keystore, appInfo, device, invoker); + + Keystore = keystore; + AppInfo = appInfo; + Device = device; + + Scheduler = scheduler; + Invoker = invoker; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/LogContext.cs b/Lagrange.Core/Core/Context/LogContext.cs new file mode 100644 index 0000000..dffd4fd --- /dev/null +++ b/Lagrange.Core/Core/Context/LogContext.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event; +using Lagrange.Core.Core.Event.EventArg; + +namespace Lagrange.Core.Core.Context; + +/// +/// Log context, all the logs will be dispatched to this context and then to the . +/// +internal class LogContext : ContextBase +{ + private readonly EventInvoker _invoker; + + public LogContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, EventInvoker invoker) + : base(collection, keystore, appInfo, device) => _invoker = invoker; + + public void LogDebug(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Debug, message)); + + public void LogVerbose(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Verbose, message)); + + public void LogInfo(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Information, message)); + + public void LogWarning(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Warning, message)); + + public void LogFatal(string tag, string message) => + _invoker.PostEvent(new BotLogEvent(tag, LogLevel.Fatal, message)); + + public void Log(string tag, LogLevel level, string message) => + _invoker.PostEvent(new BotLogEvent(tag, level, message)); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/Logic/Implementation/MessagingLogic.cs b/Lagrange.Core/Core/Context/Logic/Implementation/MessagingLogic.cs new file mode 100644 index 0000000..c84ab28 --- /dev/null +++ b/Lagrange.Core/Core/Context/Logic/Implementation/MessagingLogic.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Core.Context.Attributes; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Message; +using Lagrange.Core.Core.Service; + +namespace Lagrange.Core.Core.Context.Logic.Implementation; + +[EventSubscribe(typeof(PushMessageEvent))] +[EventSubscribe(typeof(SendMessageEvent))] +[BusinessLogic("MessagingLogic", "Manage the receiving and sending of messages")] +internal class MessagingLogic : LogicBase +{ + private const string Tag = nameof(MessagingLogic); + + internal MessagingLogic(ContextCollection collection) : base(collection) { } + + public override async Task Incoming(ProtocolEvent e) + { + switch (e) + { + case PushMessageEvent push: + Collection.Log.LogInfo(Tag, "Message Received, Detail to be implemented"); + break; + case SendMessageEvent send: + break; + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/Logic/Implementation/WtExchangeLogic.cs b/Lagrange.Core/Core/Context/Logic/Implementation/WtExchangeLogic.cs new file mode 100644 index 0000000..3b9cf32 --- /dev/null +++ b/Lagrange.Core/Core/Context/Logic/Implementation/WtExchangeLogic.cs @@ -0,0 +1,239 @@ +using System.Text.Json; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Context.Attributes; +using Lagrange.Core.Core.Event.EventArg; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login; +using Lagrange.Core.Core.Event.Protocol.Login.Ecdh; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Packets.Login.NTLogin; +using Lagrange.Core.Core.Packets.Login.WtLogin.Entity; +using Lagrange.Core.Core.Service; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Network; + +// ReSharper disable AsyncVoidLambda + +namespace Lagrange.Core.Core.Context.Logic.Implementation; + +[EventSubscribe(typeof(TransEmpEvent))] +[EventSubscribe(typeof(LoginEvent))] +[EventSubscribe(typeof(KickNTEvent))] +[BusinessLogic("WtExchangeLogic", "Manage the online task of the Bot")] +internal class WtExchangeLogic : LogicBase +{ + private const string Tag = nameof(WtExchangeLogic); + + private const string Interface = "https://ntlogin.qq.com/qr/getFace"; + + private const string QueryEvent = "wtlogin.trans_emp CMD0x12"; + + internal WtExchangeLogic(ContextCollection collection) : base(collection) { } + + public override async Task Incoming(ProtocolEvent e) + { + switch (e) + { + case KickNTEvent kick: + Collection.Log.LogFatal(Tag, $"KickNTEvent: {kick.Tag}: {kick.Message}"); + Collection.Log.LogFatal(Tag, "Bot will be offline in 5 seconds..."); + await Task.Delay(5000); + + Collection.Invoker.PostEvent(new BotOfflineEvent()); // TODO: Fill in the reason of offline + Collection.Scheduler.Dispose(); + break; + } + } + + /// + /// 1. resolve wtlogin.trans_emp CMD0x31 packet + /// 2. Schedule wtlogin.trans_emp CMD0x12 Task + /// + public async Task FetchQrCode() + { + Collection.Log.LogInfo(Tag, "Connecting Servers..."); + if (!await Collection.Socket.Connect()) return null; + Collection.Scheduler.Interval("Heartbeat.Alive", 10 * 1000, async () => await Collection.Business.PushEvent(AliveEvent.Create())); + + var transEmp = TransEmpEvent.Create(TransEmpEvent.State.FetchQrCode); + var result = await Collection.Business.SendEvent(transEmp); + + if (result.Count != 0) + { + var @event = (TransEmpEvent)result[0]; + Collection.Keystore.Session.QrString = @event.QrSig; + Collection.Keystore.Session.QrSign = @event.Signature; + + Collection.Log.LogInfo(Tag, $"QrCode Fetched, Expiration: {@event.Expiration} seconds"); + return @event.QrCode; + } + return null; + } + + public async Task LoginByQrCode() => + await Task.Run(() => Collection.Scheduler.Interval(QueryEvent, 2 * 1000, async () => await QueryQrCodeState())); + + public async Task LoginByPassword() + { + Collection.Log.LogInfo(Tag, "Trying to Login by Keystore and Password..."); + + if (!Collection.Socket.Connected) // if socket not connected, try to connect + { + if (!await Collection.Socket.Connect()) return false; + Collection.Scheduler.Interval("Heartbeat.Alive", 10 * 1000, async () => await Collection.Business.PushEvent(AliveEvent.Create())); + } + + var keyExchangeEvent = KeyExchangeEvent.Create(); + var exchangeResult = await Collection.Business.SendEvent(keyExchangeEvent); + if (exchangeResult.Count != 0) + { + Collection.Log.LogInfo(Tag, "Key Exchange successfully!"); + + if (Collection.Keystore.Session.TempPassword != null) // try EasyLogin + { + Collection.Log.LogInfo(Tag, "Trying to Login by EasyLogin..."); + var easyLoginEvent = EasyLoginEvent.Create(); + var easyLoginResult = await Collection.Business.SendEvent(easyLoginEvent); + + if (easyLoginResult.Count != 0) + { + var @event = (EasyLoginEvent)easyLoginResult[0]; + if (@event is { Success: true, UnusualVerify: false }) + { + Collection.Log.LogInfo(Tag, "Login Success"); + + var onlineEvent = new BotOnlineEvent(); + Collection.Invoker.PostEvent(onlineEvent); + + var registerEvent = StatusRegisterEvent.Create(); + var registerResponse = await Collection.Business.SendEvent(registerEvent); + Collection.Log.LogInfo(Tag, $"Register Status: {((StatusRegisterEvent)registerResponse[0]).Message}"); + Collection.Scheduler.Interval("trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat", + 5 * 60 * 1000, async () => await Collection.Business.PushEvent(SsoAliveEvent.Create())); + + return true; + } + + if (@event is { Success: true, UnusualVerify: true }) + { + throw new NotImplementedException(); // TODO: UnusualVerify + } + } + } + else + { + throw new NotImplementedException(); // TODO: Login by Password + } + } + + return false; + } + + private async Task DoWtLogin() + { + Collection.Log.LogInfo(Tag, "Doing Login..."); + Collection.Keystore.Session.Sequence = 0; + + Collection.Keystore.SecpImpl = new EcdhImpl(EcdhImpl.CryptMethod.Secp192K1); + var loginEvent = LoginEvent.Create(); + var result = await Collection.Business.SendEvent(loginEvent); + + if (result.Count != 0) + { + var @event = (LoginEvent)result[0]; + if (@event.ResultCode == 0) + { + Collection.Log.LogInfo(Tag, "Login Success"); + Collection.Keystore.Info = new BotKeystore.BotInfo(@event.Age, @event.Sex, @event.Name); + Collection.Log.LogInfo(Tag, Collection.Keystore.Info.ToString()); + + var registerEvent = StatusRegisterEvent.Create(); + var registerResponse = await Collection.Business.SendEvent(registerEvent); + Collection.Log.LogInfo(Tag, $"Register Status: {((StatusRegisterEvent)registerResponse[0]).Message}"); + Collection.Scheduler.Interval("trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat", 5 * 60 * 1000, + async () => await Collection.Business.PushEvent(SsoAliveEvent.Create())); + + var onlineEvent = new BotOnlineEvent(); + Collection.Invoker.PostEvent(onlineEvent); + + return true; + } + + Collection.Log.LogFatal(Tag, $"Login failed: {@event.ResultCode}"); + Collection.Log.LogFatal(Tag, $"Tag: {@event.Tag}\nState: {@event.Message}"); + } + + return false; + } + + private async Task QueryQrCodeState() + { + if (Collection.Keystore.Session.QrString == null) + { + Collection.Log.LogFatal(Tag, "QrString is null, Please Fetch QrCode First"); + return false; + } + + var request = new NTLoginHttpRequest + { + Appid = Collection.AppInfo.AppId, + Qrsig = Collection.Keystore.Session.QrString, + FaceUpdateTime = 0 + }; + var payload = JsonSerializer.SerializeToUtf8Bytes(request); + var response = await Http.PostAsync(Interface, payload, "application/json"); + var info = JsonSerializer.Deserialize(response); + if (info != null) Collection.Keystore.Uin = info.Uin; + + var transEmp = TransEmpEvent.Create(TransEmpEvent.State.QueryResult); + var result = await Collection.Business.SendEvent(transEmp); + + if (result.Count != 0) + { + var @event = (TransEmpEvent)result[0]; + var state = (TransEmp12.State)@event.ResultCode; + Collection.Log.LogInfo(Tag, $"QrCode State Queried: {state} Uin: {Collection.Keystore.Uin}"); + + switch (state) + { + case TransEmp12.State.Confirmed: + { + Collection.Log.LogInfo(Tag, "QrCode Confirmed, Logging in with A1 sig..."); + Collection.Scheduler.Cancel(QueryEvent); // cancel query task + + if (@event.TgtgtKey != null) + { + Collection.Keystore.Stub.TgtgtKey = @event.TgtgtKey; + Collection.Keystore.Session.TempPassword = @event.TempPassword; + Collection.Keystore.Session.NoPicSig = @event.NoPicSig; + + return await DoWtLogin(); + } + break; + } + case TransEmp12.State.CodeExpired: + { + Collection.Log.LogWarning(Tag, "QrCode Expired, Please Fetch QrCode Again"); + Collection.Scheduler.Cancel(QueryEvent); + Collection.Scheduler.Dispose(); + + return false; + } + case TransEmp12.State.Canceled: + { + Collection.Log.LogWarning(Tag, "QrCode Canceled, Please Fetch QrCode Again"); + Collection.Scheduler.Cancel(QueryEvent); + Collection.Scheduler.Dispose(); + + return false; + } + case TransEmp12.State.WaitingForConfirm: + case TransEmp12.State.WaitingForScan: + default: + break; + } + } + + return false; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/Logic/LogicBase.cs b/Lagrange.Core/Core/Context/Logic/LogicBase.cs new file mode 100644 index 0000000..25e721d --- /dev/null +++ b/Lagrange.Core/Core/Context/Logic/LogicBase.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Core.Event.Protocol; + +namespace Lagrange.Core.Core.Context.Logic; + +internal abstract class LogicBase +{ + protected readonly ContextCollection Collection; + + protected LogicBase(ContextCollection collection) => Collection = collection; + + public virtual Task Incoming(ProtocolEvent e) => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/PacketContext.cs b/Lagrange.Core/Core/Context/PacketContext.cs new file mode 100644 index 0000000..8ee3144 --- /dev/null +++ b/Lagrange.Core/Core/Context/PacketContext.cs @@ -0,0 +1,104 @@ +using System.Collections.Concurrent; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Utility.Binary; +#pragma warning disable CS4014 + +namespace Lagrange.Core.Core.Context; + +/// +/// Translate the protocol event into SSOPacket and further ServiceMessage +/// And Dispatch the packet from by managing the sequence from Tencent's server +/// Every Packet should be send and received from this context instead of being directly send to +/// +internal class PacketContext : ContextBase +{ + private readonly ConcurrentDictionary> _pendingTasks; + + public PacketContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _pendingTasks = new ConcurrentDictionary>(); + } + + /// + /// Send the packet and wait for the corresponding response by the packet's sequence number. + /// + public Task SendPacket(SsoPacket packet) + { + var task = new TaskCompletionSource(); + _pendingTasks.TryAdd(packet.Sequence, task); + + switch (packet.PacketType) + { + case 12: + { + var sso = SsoPacker.Build(packet, AppInfo, DeviceInfo, Keystore); + var service = ServicePacker.Build(sso, Keystore); + bool _ = Collection.Socket.Send(service.ToArray()).Result; + break; + } + case 13: + { + var service = ServicePacker.BuildProtocol13(packet.Payload, packet.Command, packet.Sequence); + bool _ = Collection.Socket.Send(service.ToArray()).Result; + break; + } + } + + return task.Task; + } + + /// + /// Send the packet and don't wait for the corresponding response by the packet's sequence number. + /// + public async Task PostPacket(SsoPacket packet) + { + switch (packet.PacketType) + { + case 12: + { + var sso = SsoPacker.Build(packet, AppInfo, DeviceInfo, Keystore); + var service = ServicePacker.Build(sso, Keystore); + return await Collection.Socket.Send(service.ToArray()); + } + case 13: + { + var service = ServicePacker.BuildProtocol13(packet.Payload, packet.Command, packet.Sequence); + return await Collection.Socket.Send(service.ToArray()); + } + default: + return false; + } + } + + /// + /// Handle the incoming packet with new sequence number. + /// + public async Task HandleServerPacket(SsoPacket packet) + { + bool success = false; + + var events = Collection.Service.ResolveEventByPacket(packet); + foreach (var @event in events) + { + var isSuccessful = await Collection.Business.HandleIncomingEvent(@event); + if (!isSuccessful) break; + + success = true; + } + + return success; + } + + public void DispatchPacket(BinaryPacket packet) + { + var service = ServicePacker.Parse(packet, Keystore); + if (service.Length == 0) return; + + var sso = SsoPacker.Parse(service); + + if (_pendingTasks.TryRemove(sso.Sequence, out var task)) task.SetResult(sso); + else HandleServerPacket(sso); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/ServiceContext.SequenceProvider.cs b/Lagrange.Core/Core/Context/ServiceContext.SequenceProvider.cs new file mode 100644 index 0000000..2e8ef0e --- /dev/null +++ b/Lagrange.Core/Core/Context/ServiceContext.SequenceProvider.cs @@ -0,0 +1,28 @@ +using System.Collections.Concurrent; + +namespace Lagrange.Core.Core.Context; + +internal partial class ServiceContext +{ + private class SequenceProvider + { + private readonly ConcurrentDictionary _sessionSequence; + + private int _sequence; + + public SequenceProvider() + { + _sessionSequence = new ConcurrentDictionary(); + _sequence = Random.Shared.Next(5000000, 9900000); + } + + public int GetNewSequence() + { + Interlocked.CompareExchange(ref _sequence, 5000000, 9900000); + return Interlocked.Increment(ref _sequence); + } + + public int RegisterSession(string sessionId) => + _sessionSequence.GetOrAdd(sessionId, _ => GetNewSequence()); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/ServiceContext.cs b/Lagrange.Core/Core/Context/ServiceContext.cs new file mode 100644 index 0000000..445c834 --- /dev/null +++ b/Lagrange.Core/Core/Context/ServiceContext.cs @@ -0,0 +1,115 @@ +using System.Reflection; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Service; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + +namespace Lagrange.Core.Core.Context; + +/// +/// Manage the service and packet translation of the Bot +/// Instantiate the Service by and store such +/// Translate the event into , you may manually dispatch the packet to +/// +internal partial class ServiceContext : ContextBase +{ + private const string Tag = nameof(ServiceContext); + + private readonly SequenceProvider _sequenceProvider; + private readonly Dictionary _services; + private readonly Dictionary> _servicesEventType; + + public ServiceContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _sequenceProvider = new SequenceProvider(); + _services = new Dictionary(); + _servicesEventType = new Dictionary>(); + + RegisterServices(); + } + + private void RegisterServices() + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var type in assembly.GetDerivedTypes()) + { + _servicesEventType[type] = new List<(ServiceAttribute, IService)>(); + } + + foreach (var type in assembly.GetTypeByAttributes(out _)) + { + var serviceAttribute = type.GetCustomAttribute(); + + if (serviceAttribute != null) + { + var service = (IService)type.CreateInstance(); + _services[serviceAttribute.Command] = service; + + foreach (var attribute in type.GetCustomAttributes()) + { + _servicesEventType[attribute.EventType].Add((serviceAttribute, service)); + } + } + } + } + + /// + /// Resolve the outgoing packet by the event + /// + public List ResolvePacketByEvent(ProtocolEvent protocolEvent) + { + var result = new List(); + if (!_servicesEventType.TryGetValue(protocolEvent.GetType(), out var serviceList)) return result; // 没找到 滚蛋吧 + + foreach (var (attribute, instance) in serviceList) + { + bool success = instance.Build(protocolEvent, Keystore, AppInfo, DeviceInfo, out var binary, out var extraPackets); + + if (success && binary != null) + { + result.Add(new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), binary)); + + if (extraPackets != null) + { + result.AddRange(extraPackets.Select(extra => new SsoPacket(attribute.PacketType, attribute.Command, (uint)_sequenceProvider.GetNewSequence(), extra))); + } + + Collection.Log.LogVerbose(Tag, $"Outgoing SSOFrame: {attribute.Command}"); + } + } + + return result; + } + + /// + /// Resolve the incoming event by the packet + /// + public List ResolveEventByPacket(SsoPacket packet) + { + var result = new List(); + if (!_services.TryGetValue(packet.Command, out var service)) + { + Collection.Log.LogWarning(Tag, $"Unsupported SSOFrame Received: {packet.Command}"); + var payload = packet.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + Collection.Log.LogDebug(Tag, $"Unsuccessful SSOFrame Payload: {payload.Hex()}"); + return result; // 没找到 滚蛋吧 + } + + bool success = service.Parse(packet, Keystore, AppInfo, DeviceInfo, out var @event, out var extraEvents); + + if (success) + { + result.Add(@event); + if (extraEvents != null) result.AddRange(extraEvents); + + Collection.Log.LogVerbose(Tag, $"Incoming SSOFrame: {packet.Command}"); + } + + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Context/SocketContext.cs b/Lagrange.Core/Core/Context/SocketContext.cs new file mode 100644 index 0000000..6a43802 --- /dev/null +++ b/Lagrange.Core/Core/Context/SocketContext.cs @@ -0,0 +1,134 @@ +using System.Buffers.Binary; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Network.Tcp; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Network; + +namespace Lagrange.Core.Core.Context; + +/// +/// Provide Low-Allocation Tcp Client which connects to the Tencent's SSO Server +/// Internal Implementation, Packet Received would be dispatched to for decryption and unpack +/// MSF Service is also implemented here +/// +internal class SocketContext : ContextBase, IClientListener +{ + private const string Tag = nameof(SocketContext); + + private readonly ClientListener _tcpClient; + + private Uri? ServerUri { get; set; } + + public uint HeaderSize => 4; + + public bool Connected => _tcpClient.Connected; + + public SocketContext(ContextCollection collection, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(collection, keystore, appInfo, device) + { + _tcpClient = new CallbackClientListener(this); + } + + public async Task Connect(bool useIPv6Network = false) + { + if (_tcpClient.Connected) return true; + + var servers = await OptimumServer(false, useIPv6Network); + ServerUri = servers.First(); + return await _tcpClient.Connect(ServerUri.Host, ServerUri.Port); + } + + public async Task Reconnect() + { + if (ServerUri != null) return await _tcpClient.Connect(ServerUri.Host, ServerUri.Port); + + return false; + } + + public async Task Send(byte[] packet) => await _tcpClient.Send(packet); + + public uint GetPacketLength(ReadOnlySpan header) => BinaryPrimitives.ReadUInt32BigEndian(header); + + public void OnRecvPacket(ReadOnlySpan packet) + { + var binary = new BinaryPacket(packet.ToArray()); + Collection.Packet.DispatchPacket(binary); + } + + public void OnDisconnect() + { + throw new NotImplementedException(); + } + + public void OnSocketError(Exception e) + { + Console.WriteLine(e); + } + + private static readonly Uri[] MsfUris = + { + new("http://msfwifi.3g.qq.com:8080"), // IPv4 + new("http://msfwifiv6.3g.qq.com:14000") // IPv6 + }; + + private static readonly Uri[] HardCodeIPv4Uris = + { + new("http://msfwifi.3g.qq.com:8080"), + new("http://163.177.89.195:14000"), + new("http://120.232.18.27:443"), + new("http://157.255.13.77:443"), + new("http://140.207.123.177:8080"), + new("http://221.198.69.96:443"), + new("http://123.150.76.143:14000"), + new("http://183.3.235.162:443"), + new("http://61.129.6.101:8080"), + new("http://42.81.169.100:443"), + new("http://183.232.94.44:14000"), + new("http://msfxg.3g.qq.com:80"), + new("http://117.144.244.33:8080"), + new("http://111.30.138.152:443"), + new("http://203.205.255.221:14000"), + new("http://203.205.255.224:443"), + new("http://183.3.235.162:8080"), + new("http://183.47.102.193:8080") + }; + + private static readonly Uri[] HardCodeIPv6Uris = + { + new("http://msfwifiv6.3g.qq.com:14000") + }; + + /// + /// 好像这才是真货 + /// + private static readonly Uri[] TestIPv4HardCodes = + { + new("http://183.47.102.193:8080"), + new("http://14.22.9.84:8080"), + new("http://119.147.190.138:8080") + }; + + private async Task> OptimumServer(bool requestMsf, bool useIPv6Network = false) + { + Uri[] result; + + if (requestMsf) + { + result = Array.Empty(); + // Implement MSF server request + // TODO: tx自己用的都tm是硬编码 实现尼玛 + } + else + { + result = useIPv6Network ? HardCodeIPv6Uris : TestIPv4HardCodes; + } + + var latencyTasks = result.Select(uri => Icmp.PingAsync(uri)).ToArray(); + var latency = await Task.WhenAll(latencyTasks); + Array.Sort(latency, result); + + var list = result.ToList(); + for (int i = 0; i < list.Count; i++) Collection.Log.LogVerbose(Tag, $"Server: {list[i]} Latency: {latency[i]}"); + return list; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Core.md b/Lagrange.Core/Core/Core.md new file mode 100644 index 0000000..a83a928 --- /dev/null +++ b/Lagrange.Core/Core/Core.md @@ -0,0 +1,7 @@ +# Lagrange.Core +## Core + +This is the core of the Lagrange framework. It contains the basic classes and interfaces that are used throughout the framework. + + +Most of the internal data structures are implemented in this namespace \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventArg/BotLogEvent.cs b/Lagrange.Core/Core/Event/EventArg/BotLogEvent.cs new file mode 100644 index 0000000..60bf99a --- /dev/null +++ b/Lagrange.Core/Core/Event/EventArg/BotLogEvent.cs @@ -0,0 +1,30 @@ +namespace Lagrange.Core.Core.Event.EventArg; + +public enum LogLevel +{ + Debug, + Verbose, + Information, + Warning, + Exception, + Fatal +} + +public class BotLogEvent : EventBase +{ + private const string DateFormat = "yyyy-MM-dd HH:mm:ss"; + + public string Tag { get; } + + public LogLevel Level { get; } + + internal BotLogEvent(string tag, LogLevel level, string content) + { + Tag = tag; + Level = level; + EventMessage = content; + } + + public override string ToString() => + $"[{EventTime.ToString(DateFormat)}] [{Tag}] [{Level.ToString().ToUpper()}]: {EventMessage}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventArg/BotOfflineEvent.cs b/Lagrange.Core/Core/Event/EventArg/BotOfflineEvent.cs new file mode 100644 index 0000000..ef98728 --- /dev/null +++ b/Lagrange.Core/Core/Event/EventArg/BotOfflineEvent.cs @@ -0,0 +1,6 @@ +namespace Lagrange.Core.Core.Event.EventArg; + +public class BotOfflineEvent : EventBase +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventArg/BotOnlineEvent.cs b/Lagrange.Core/Core/Event/EventArg/BotOnlineEvent.cs new file mode 100644 index 0000000..6de547d --- /dev/null +++ b/Lagrange.Core/Core/Event/EventArg/BotOnlineEvent.cs @@ -0,0 +1,6 @@ +namespace Lagrange.Core.Core.Event.EventArg; + +public class BotOnlineEvent : EventBase +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventBase.cs b/Lagrange.Core/Core/Event/EventBase.cs new file mode 100644 index 0000000..45ef07a --- /dev/null +++ b/Lagrange.Core/Core/Event/EventBase.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Core.Event; + +/// +/// Event that exposed to user +/// +public abstract class EventBase : EventArgs +{ + public DateTime EventTime { get; } + + public string EventMessage { get; protected set; } + + internal EventBase() + { + EventTime = DateTime.Now; + EventMessage = "[Empty Event Message]"; + } + + public override string ToString() + { + return $"[{EventTime:HH:mm:ss}] {EventMessage}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventInvoker.Events.cs b/Lagrange.Core/Core/Event/EventInvoker.Events.cs new file mode 100644 index 0000000..d47c5c9 --- /dev/null +++ b/Lagrange.Core/Core/Event/EventInvoker.Events.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Core.Event.EventArg; + +namespace Lagrange.Core.Core.Event; + +public partial class EventInvoker +{ + public event LagrangeEvent? OnBotOnlineEvent; + + public event LagrangeEvent? OnBotOfflineEvent; + + public event LagrangeEvent? OnBotLogEvent; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/EventInvoker.cs b/Lagrange.Core/Core/Event/EventInvoker.cs new file mode 100644 index 0000000..f091c14 --- /dev/null +++ b/Lagrange.Core/Core/Event/EventInvoker.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Core.Event.EventArg; + +namespace Lagrange.Core.Core.Event; + +public partial class EventInvoker : IDisposable +{ + private readonly Dictionary> _events; + + public delegate void LagrangeEvent(BotContext context, TEvent e) where TEvent : EventBase; + + internal EventInvoker(BotContext context) + { + _events = new Dictionary> + { + { typeof(BotOnlineEvent), e => OnBotOnlineEvent?.Invoke(context, (BotOnlineEvent)e) }, + { typeof(BotOfflineEvent), e => OnBotOfflineEvent?.Invoke(context, (BotOfflineEvent)e) }, + { typeof(BotLogEvent), e => OnBotLogEvent?.Invoke(context, (BotLogEvent)e) } + }; + } + + internal void PostEvent(EventBase e) + { + Task.Run(() => + { + try + { + if (_events.TryGetValue(e.GetType(), out var action)) action(e); + else PostEvent(new BotLogEvent("BotContext", LogLevel.Warning, $"Event {e.GetType().Name} is not registered but pushed to invoker")); + } + catch (Exception ex) + { + PostEvent(new BotLogEvent("BotContext", LogLevel.Exception, $"{ex.StackTrace}\n{ex.Message}")); + } + }); + } + + public void Dispose() + { + _events.Clear(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Login/EasyLoginEvent.cs b/Lagrange.Core/Core/Event/Protocol/Login/EasyLoginEvent.cs new file mode 100644 index 0000000..fd95098 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Login/EasyLoginEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Core.Event.Protocol.Login; + +internal class EasyLoginEvent : ProtocolEvent +{ + public bool Success { get; set; } + + public bool UnusualVerify { get; set; } + + protected EasyLoginEvent() : base(true) { } + + protected EasyLoginEvent(bool success, bool unusualVerify) : base(0) + { + Success = success; + UnusualVerify = unusualVerify; + } + + public static EasyLoginEvent Create() + { + return new EasyLoginEvent(); + } + + public static EasyLoginEvent Result(bool success, bool unusualVerify = false) + { + return new EasyLoginEvent(success, unusualVerify); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Login/Ecdh/KeyExchangeEvent.cs b/Lagrange.Core/Core/Event/Protocol/Login/Ecdh/KeyExchangeEvent.cs new file mode 100644 index 0000000..a72c34d --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Login/Ecdh/KeyExchangeEvent.cs @@ -0,0 +1,23 @@ +namespace Lagrange.Core.Core.Event.Protocol.Login.Ecdh; + +internal class KeyExchangeEvent : ProtocolEvent +{ + private KeyExchangeEvent() : base(true) + { + } + + private KeyExchangeEvent(int resultCode) : base(resultCode) + { + } + + public static KeyExchangeEvent Create() + { + return new KeyExchangeEvent(0); + } + + + public static KeyExchangeEvent Result() + { + return new KeyExchangeEvent(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Login/LoginEvent.cs b/Lagrange.Core/Core/Event/Protocol/Login/LoginEvent.cs new file mode 100644 index 0000000..5c7a89f --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Login/LoginEvent.cs @@ -0,0 +1,40 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Event.Protocol.Login; + +internal class LoginEvent : ProtocolEvent +{ + public byte Age { get; } + public byte Sex { get; } + public string Name { get; } + public string Tag { get; } = string.Empty; + + public string Message { get; } = string.Empty; + + private LoginEvent() : base(true) { } + + private LoginEvent(int resultCode) : base(resultCode) { } + + private LoginEvent(int resultCode, byte age, byte sex, string name) : base(resultCode) + { + Age = age; + Sex = sex; + Name = name; + } + + private LoginEvent(int resultCode, string tag, string message) : base(resultCode) + { + Tag = tag; + Message = message; + } + + public static LoginEvent Create() => new(); + + public static LoginEvent Result(int resultCode, byte age, byte sex, string name) + => new(resultCode, age, sex, name); + + public static LoginEvent Result(int resultCode) => new(resultCode); + + + public static LoginEvent Result(int resultCode, string tag, string message) => new(resultCode, tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Login/PasswordLoginEvent.cs b/Lagrange.Core/Core/Event/Protocol/Login/PasswordLoginEvent.cs new file mode 100644 index 0000000..396d957 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Login/PasswordLoginEvent.cs @@ -0,0 +1,26 @@ +namespace Lagrange.Core.Core.Event.Protocol.Login; + +internal class PasswordLoginEvent : ProtocolEvent +{ + public bool Success { get; set; } + + public bool UnusualVerify { get; set; } + + protected PasswordLoginEvent() : base(true) { } + + protected PasswordLoginEvent(bool success, bool unusualVerify) : base(0) + { + Success = success; + UnusualVerify = unusualVerify; + } + + public static PasswordLoginEvent Create() + { + return new PasswordLoginEvent(); + } + + public static PasswordLoginEvent Result(bool success, bool unusualVerify = false) + { + return new PasswordLoginEvent(success, unusualVerify); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Login/TransEmpEvent.cs b/Lagrange.Core/Core/Event/Protocol/Login/TransEmpEvent.cs new file mode 100644 index 0000000..a17b4b7 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Login/TransEmpEvent.cs @@ -0,0 +1,69 @@ +using Lagrange.Core.Core.Packets.Login.WtLogin.Entity; + +#pragma warning disable CS8618 +namespace Lagrange.Core.Core.Event.Protocol.Login; + +internal class TransEmpEvent : ProtocolEvent +{ + public State EventState { get; set; } + + #region TransEmp CMD0x31 + + public byte[] QrCode { get; } + + public uint Expiration { get; } + + public string Url { get; } + + public string QrSig { get; } + + public byte[] Signature { get; } + + #endregion + + #region TransEmp CMD0x12 + + public byte[]? TgtgtKey { get; } + + public byte[]? TempPassword { get; } + + public byte[]? NoPicSig { get; } + + #endregion + + private TransEmpEvent(State eventState) : base(true) => EventState = eventState; + + private TransEmpEvent(int result, byte[] qrCode, uint expiration, string url, string qrSig, byte[] signature) + : base(result) + { + EventState = State.FetchQrCode; + + QrCode = qrCode; + Expiration = expiration; + Url = url; + QrSig = qrSig; + Signature = signature; + } + + private TransEmpEvent(int result, byte[]? tgtgtKey, byte[]? tempPassword, byte[]? noPicSig) : base(result) + { + TgtgtKey = tgtgtKey; + TempPassword = tempPassword; + NoPicSig = noPicSig; + EventState = State.QueryResult; + } + + public static TransEmpEvent Create(State eventState) => new(eventState); + + public static TransEmpEvent Result(byte[] qrCode, uint expiration, string url, string qrSig, byte[] signature) => + new(0, qrCode, expiration, url, qrSig, signature); + + public static TransEmpEvent Result(TransEmp12.State state, byte[]? tgtgtKey, byte[]? tempPassword, byte[]? noPicSig) => + new((int)state, tgtgtKey, tempPassword, noPicSig); + + public enum State : byte + { + FetchQrCode = 0x31, + QueryResult = 0x12 + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Message/PushMessageEvent.cs b/Lagrange.Core/Core/Event/Protocol/Message/PushMessageEvent.cs new file mode 100644 index 0000000..3ea6fef --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Message/PushMessageEvent.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Core.Event.Protocol.Message; + +internal class PushMessageEvent : ProtocolEvent +{ + private PushMessageEvent() : base(false) + { + } + + private PushMessageEvent(int resultCode) : base(resultCode) + { + } + + public static PushMessageEvent Create() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/Message/SendMessageEvent.cs b/Lagrange.Core/Core/Event/Protocol/Message/SendMessageEvent.cs new file mode 100644 index 0000000..400478c --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/Message/SendMessageEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Core.Event.Protocol.Message; + +internal class SendMessageEvent : ProtocolEvent +{ + protected SendMessageEvent(bool waitResponse) : base(true) + { + } + + protected SendMessageEvent(int resultCode) : base(resultCode) + { + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/ProtocolEvent.cs b/Lagrange.Core/Core/Event/Protocol/ProtocolEvent.cs new file mode 100644 index 0000000..008aa92 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/ProtocolEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Core.Event.Protocol; + +internal class ProtocolEvent +{ + public bool WaitResponse { get; } + + public int ResultCode { get; private set; } + + protected ProtocolEvent(bool waitResponse) => WaitResponse = waitResponse; + + protected ProtocolEvent(int resultCode) => ResultCode = resultCode; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/System/AliveEvent.cs b/Lagrange.Core/Core/Event/Protocol/System/AliveEvent.cs new file mode 100644 index 0000000..eb629d9 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/System/AliveEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Core.Event.Protocol.System; + +internal class AliveEvent : ProtocolEvent +{ + protected AliveEvent() : base(false) { } + + protected AliveEvent(int resultCode) : base(resultCode) { } + + public static AliveEvent Create() => new(); + + public static AliveEvent Result() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/System/CorrectTimeEvent.cs b/Lagrange.Core/Core/Event/Protocol/System/CorrectTimeEvent.cs new file mode 100644 index 0000000..ff9c1fd --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/System/CorrectTimeEvent.cs @@ -0,0 +1,16 @@ +namespace Lagrange.Core.Core.Event.Protocol.System; + +internal class CorrectTimeEvent : ProtocolEvent +{ + protected CorrectTimeEvent() : base(false) + { + } + + protected CorrectTimeEvent(int resultCode) : base(resultCode) + { + } + + public static CorrectTimeEvent Create() => new(); + + public static CorrectTimeEvent Result() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/System/KickNTEvent.cs b/Lagrange.Core/Core/Event/Protocol/System/KickNTEvent.cs new file mode 100644 index 0000000..4fd0207 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/System/KickNTEvent.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Core.Event.Protocol.System; + +// ReSharper disable once InconsistentNaming + +internal class KickNTEvent : ProtocolEvent +{ + public string Tag { get; set; } + + public string Message { get; set; } + + private KickNTEvent(string tag, string message) : base(0) + { + Tag = tag; + Message = message; + } + + public static KickNTEvent Create(string tag, string message) => new(tag, message); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/System/SsoAliveEvent.cs b/Lagrange.Core/Core/Event/Protocol/System/SsoAliveEvent.cs new file mode 100644 index 0000000..0f30d8d --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/System/SsoAliveEvent.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Core.Event.Protocol.System; + +internal class SsoAliveEvent : ProtocolEvent +{ + private SsoAliveEvent() : base(true) { } + + private SsoAliveEvent(int resultCode) : base(resultCode) { } + + public static SsoAliveEvent Create() => new(); + + public static SsoAliveEvent Result() => new(0); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Event/Protocol/System/StatusRegisterEvent.cs b/Lagrange.Core/Core/Event/Protocol/System/StatusRegisterEvent.cs new file mode 100644 index 0000000..87da487 --- /dev/null +++ b/Lagrange.Core/Core/Event/Protocol/System/StatusRegisterEvent.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Event.Protocol.System; + +internal class StatusRegisterEvent : ProtocolEvent +{ + public string Message { get; set; } + + private StatusRegisterEvent() : base(true) { } + + private StatusRegisterEvent(string result) : base(0) => Message = result; + + public static StatusRegisterEvent Create() => new(); + + public static StatusRegisterEvent Result(string result) => new(result); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Network/Tcp/CallbackClientListener.cs b/Lagrange.Core/Core/Network/Tcp/CallbackClientListener.cs new file mode 100644 index 0000000..fad5a9c --- /dev/null +++ b/Lagrange.Core/Core/Network/Tcp/CallbackClientListener.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Core.Network.Tcp; + +internal sealed class CallbackClientListener : ClientListener +{ + public override uint HeaderSize => _listener.HeaderSize; + + private readonly IClientListener _listener; + + public CallbackClientListener(IClientListener listener) => _listener = listener; + + public override uint GetPacketLength(ReadOnlySpan header) => _listener.GetPacketLength(header); + + public override void OnDisconnect() => _listener.OnDisconnect(); + + public override void OnRecvPacket(ReadOnlySpan packet) => _listener.OnRecvPacket(packet); + + public override void OnSocketError(Exception e) => _listener.OnSocketError(e); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Network/Tcp/ClientListener.SocketSession.cs b/Lagrange.Core/Core/Network/Tcp/ClientListener.SocketSession.cs new file mode 100644 index 0000000..e7cd45b --- /dev/null +++ b/Lagrange.Core/Core/Network/Tcp/ClientListener.SocketSession.cs @@ -0,0 +1,32 @@ +using System.Net.Sockets; + +namespace Lagrange.Core.Core.Network.Tcp; + +internal abstract partial class ClientListener +{ + protected sealed class SocketSession : IDisposable + { + public Socket Socket { get; } + + private CancellationTokenSource? _cts; + + public CancellationToken Token { get; } + + public SocketSession() + { + Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _cts = new CancellationTokenSource(); + Token = _cts.Token; + } + + public void Dispose() + { + var cts = Interlocked.Exchange(ref _cts, null); + if (cts == null) return; + + cts.Cancel(); + cts.Dispose(); + Socket.Dispose(); + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Network/Tcp/ClientListener.cs b/Lagrange.Core/Core/Network/Tcp/ClientListener.cs new file mode 100644 index 0000000..3bef429 --- /dev/null +++ b/Lagrange.Core/Core/Network/Tcp/ClientListener.cs @@ -0,0 +1,175 @@ +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Core.Network.Tcp; + +internal abstract partial class ClientListener : IClientListener +{ + /// + /// Socket connected + /// + public bool Connected => Session?.Socket.Connected ?? false; + + public abstract uint HeaderSize { get; } + + protected SocketSession? Session; + + /// + /// Construct a tcp client + /// + public ClientListener() { } + + private async Task InternalConnectAsync(SocketSession session, string host, int port) + { + try + { + await session.Socket.ConnectAsync(host, port); + _ = ReceiveLoop(session); + return true; + } + catch (Exception e) + { + RemoveSession(session); + OnSocketError(e); + return false; + } + } + + /// + /// Connect to the server + /// + /// + /// + /// + public Task Connect(string host, int port) + { + SocketSession? previousSession = Session, createdSession = null; + if (previousSession != null || // The client has been connected + Interlocked.CompareExchange(ref Session, createdSession = new SocketSession(), null) != null) // Another connect request before this request + { + createdSession?.Dispose(); + return Task.FromResult(false); + } + + return InternalConnectAsync(createdSession, host, port); // Connect to server + } + + /// + /// Disconnect + /// + /// + public void Disconnect() + { + if (Session is { } session) RemoveSession(session); + } + + /// + /// Send data + /// + /// + /// + /// + public async Task Send(byte[] buffer, int timeout = -1) + { + try + { + var session = Session; // Send the data + if (session == null) return false; + + CancellationTokenSource? userCts; + CancellationTokenSource? linkedCts; + CancellationToken token; + if (timeout == -1) + { + userCts = null; + linkedCts = null; + token = session.Token; + } + else + { + userCts = new CancellationTokenSource(timeout); + linkedCts = CancellationTokenSource.CreateLinkedTokenSource(session.Token, userCts.Token); + token = linkedCts.Token; + } + + try + { + return await session.Socket.SendAsync(buffer, SocketFlags.None, token) == buffer.Length; + } + finally + { + userCts?.Dispose(); + linkedCts?.Dispose(); + } + } + catch (Exception e) + { + OnSocketError(e); + return false; + } + } + + /// + /// Receive the data + /// + private async Task ReceiveLoop(SocketSession session, CancellationToken token = default) + { + try + { + await Task.CompletedTask.ForceAsync(); + Socket socket = session.Socket; + byte[] buffer = new byte[Math.Max(HeaderSize, 2048)]; + int headerSize = (int)HeaderSize; + while (true) + { + await socket.ReceiveFullyAsync(buffer.AsMemory(0, headerSize), token); + int packetLength = (int)GetPacketLength(buffer.AsSpan(0, headerSize)); + if (packetLength > 1024 * 1024 * 64) // limit to 64MiB + { + throw new InvalidDataException($"PackageLength was too long({packetLength})."); + } + if (packetLength > buffer.Length) + { + byte[] newBuffer = new byte[packetLength]; + Unsafe.CopyBlock(ref newBuffer[0], ref buffer[0], (uint)headerSize); + buffer = newBuffer; + } + await socket.ReceiveFullyAsync(buffer.AsMemory(headerSize, packetLength - headerSize), token); + OnRecvPacket(buffer.AsSpan(0, packetLength)); + } + } + catch (OperationCanceledException) when (token.IsCancellationRequested) + { + + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.OperationAborted) + { + + } + catch (Exception e) + { + OnSocketError(e); + } + finally + { + RemoveSession(session); + } + } + + private void RemoveSession(SocketSession session) + { + if (Interlocked.CompareExchange(ref Session, null, session) == session) + { + session.Dispose(); + } + } + + public abstract uint GetPacketLength(ReadOnlySpan header); + + public abstract void OnRecvPacket(ReadOnlySpan packet); + + public abstract void OnDisconnect(); + + public abstract void OnSocketError(Exception e); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Network/Tcp/IClientListener.cs b/Lagrange.Core/Core/Network/Tcp/IClientListener.cs new file mode 100644 index 0000000..994e5c4 --- /dev/null +++ b/Lagrange.Core/Core/Network/Tcp/IClientListener.cs @@ -0,0 +1,27 @@ +namespace Lagrange.Core.Core.Network.Tcp; + +internal interface IClientListener +{ + uint HeaderSize { get; } + + /// + /// Dissect a stream + /// + /// + public uint GetPacketLength(ReadOnlySpan header); + + /// + /// On handle a packet + /// + public void OnRecvPacket(ReadOnlySpan packet); + + /// + /// On client disconnect + /// + public void OnDisconnect(); + + /// + /// On socket error + /// + public void OnSocketError(Exception e); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/FirstViewReq.cs b/Lagrange.Core/Core/Packets/Action/FirstViewReq.cs new file mode 100644 index 0000000..157a3cb --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/FirstViewReq.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class FirstViewReq +{ + [ProtoMember(1)] public int LastMsgTime { get; set; } + + [ProtoMember(3)] public int Seq { get; set; } + + [ProtoMember(4)] public int DirectMessageFlag { get; set; } + + [ProtoMember(5)] public int UdcFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/GetGroupMsgReq.cs b/Lagrange.Core/Core/Packets/Action/GetGroupMsgReq.cs new file mode 100644 index 0000000..7ee7019 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/GetGroupMsgReq.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class GetGroupMsgReq +{ + [ProtoMember(1)] public ulong GroupCode { get; set; } + + [ProtoMember(2)] public ulong BeginSeq { get; set; } + + [ProtoMember(3)] public ulong EndSeq { get; set; } + + [ProtoMember(4)] public uint Filter { get; set; } + + [ProtoMember(5)] public ulong MemberSeq { get; set; } + + [ProtoMember(6)] public bool PublicGroup { get; set; } + + [ProtoMember(7)] public uint ShieldFlag { get; set; } + + [ProtoMember(8)] public uint SaveTrafficFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/GetGroupMsgResp.cs b/Lagrange.Core/Core/Packets/Action/GetGroupMsgResp.cs new file mode 100644 index 0000000..8f11112 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/GetGroupMsgResp.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class GetGroupMsgResp +{ + [ProtoMember(1)] public uint Result { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } + + [ProtoMember(3)] public ulong GroupCode { get; set; } + + [ProtoMember(4)] public ulong ReturnBeginSeq { get; set; } + + [ProtoMember(5)] public ulong ReturnEndSeq { get; set; } + + [ProtoMember(6)] public List Msg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn.cs b/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn.cs new file mode 100644 index 0000000..31b60ea --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn.cs @@ -0,0 +1,30 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.HttpConn; + +internal class HttpConn +{ + [ProtoMember(1)] public int Field1 { get; set; } + + [ProtoMember(2)] public int Field2 { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(5)] public string Tgt { get; set; } + + [ProtoMember(6)] public int Field6 { get; set; } + + [ProtoMember(7)] public List Field7 { get; set; } + + [ProtoMember(9)] public int Field9 { get; set; } + + [ProtoMember(10)] public int Field10 { get; set; } + + [ProtoMember(11)] public int Field11 { get; set; } + + [ProtoMember(15)] public string Ver { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn0x6ff_501.cs b/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn0x6ff_501.cs new file mode 100644 index 0000000..aedbc3a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/HttpConn/HttpConn0x6ff_501.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.HttpConn; + +internal class HttpConn0x6ff_501 +{ + [ProtoMember(0x501)] public HttpConn HttpConn { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgReq.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgReq.cs new file mode 100644 index 0000000..302279f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgReq.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbGetOneDayRoamMsgReq +{ + [ProtoMember(1)] public ulong PeerUin { get; set; } + + [ProtoMember(2)] public ulong LastMsgTime { get; set; } + + [ProtoMember(3)] public ulong Random { get; set; } + + [ProtoMember(4)] public uint ReadCnt { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgResp.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgResp.cs new file mode 100644 index 0000000..113faa5 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbGetOneDayRoamMsgResp.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbGetOneDayRoamMsgResp +{ + [ProtoMember(1)] public uint Result { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } + + [ProtoMember(3)] public ulong PeerUin { get; set; } + + [ProtoMember(4)] public ulong LastMsgTime { get; set; } + + [ProtoMember(5)] public ulong Random { get; set; } + + [ProtoMember(6)] public List Msg { get; set; } + + [ProtoMember(7)] public uint IsComplete { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgItem.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgItem.cs new file mode 100644 index 0000000..e473700 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgItem.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbMultiMsgItem +{ + [ProtoMember(1)] public string FileName { get; set; } + + [ProtoMember(2)] public PbMultiMsgNew Buffer { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgNew.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgNew.cs new file mode 100644 index 0000000..b404c49 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgNew.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbMultiMsgNew +{ + [ProtoMember(1)] public List Msg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgTransmit.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgTransmit.cs new file mode 100644 index 0000000..b82a2b0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbMultiMsgTransmit.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbMultiMsgTransmit +{ + [ProtoMember(1)] public List Msg { get; set; } + + [ProtoMember(2)] public List PbItemList { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/Pb/PbPushMsg.cs b/Lagrange.Core/Core/Packets/Action/Pb/PbPushMsg.cs new file mode 100644 index 0000000..af8fac0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/Pb/PbPushMsg.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action.Pb; + +[ProtoContract] +internal class PbPushMsg +{ + [ProtoMember(1)] public Message.Message Msg { get; set; } + + [ProtoMember(2)] public int Svrip { get; set; } + + [ProtoMember(3)] public byte[] PushToken { get; set; } + + [ProtoMember(4)] public uint PingFlag { get; set; } + + [ProtoMember(9)] public uint GeneralFlag { get; set; } + + [ProtoMember(10)] public ulong BindUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/PushMessagePacket.cs b/Lagrange.Core/Core/Packets/Action/PushMessagePacket.cs new file mode 100644 index 0000000..04a7a1c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/PushMessagePacket.cs @@ -0,0 +1,20 @@ +using Lagrange.Core.Core.Packets.System; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class PushMessagePacket +{ + [ProtoMember(1)] public Message.Message Message { get; set; } + + [ProtoMember(3)] public int Status { get; set; } + + [ProtoMember(4)] public NTSysEvent NtEvent { get; set; } + + [ProtoMember(5)] public int PingFLag { get; set; } + + [ProtoMember(9)] public int GeneralFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/SendMessageRequest.cs b/Lagrange.Core/Core/Packets/Action/SendMessageRequest.cs new file mode 100644 index 0000000..ce67093 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/SendMessageRequest.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Core.Packets.Message; +using Lagrange.Core.Core.Packets.Message.Routing; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class SendMessageRequest +{ + [ProtoMember(1)] public int State { get; set; } + + [ProtoMember(2)] public int SizeCache { get; set; } + + [ProtoMember(3)] public byte[] UnknownFields { get; set; } + + [ProtoMember(4)] public RoutingHead RoutingHead { get; set; } + + [ProtoMember(5)] public ContentHead ContentHead { get; set; } + + [ProtoMember(6)] public MessageBody MessageBody { get; set; } + + [ProtoMember(7)] public int MsgSeq { get; set; } + + [ProtoMember(8)] public int MsgRand { get; set; } + + [ProtoMember(9)] public byte[] SyncCookie { get; set; } + + [ProtoMember(10)] public int MsgVia { get; set; } + + [ProtoMember(11)] public int DataStatist { get; set; } + + [ProtoMember(12)] public MessageControl MessageControl { get; set; } + + [ProtoMember(13)] public int MultiSendSeq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/SendMessageResponse.cs b/Lagrange.Core/Core/Packets/Action/SendMessageResponse.cs new file mode 100644 index 0000000..7dbdf41 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/SendMessageResponse.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class SendMessageResponse +{ + [ProtoMember(1)] public int Result { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Action/SyncCookie.cs b/Lagrange.Core/Core/Packets/Action/SyncCookie.cs new file mode 100644 index 0000000..526ce59 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Action/SyncCookie.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Action; + +[ProtoContract] +internal class SyncCookie +{ + [ProtoMember(1)] public long Time1 { get; set; } + + [ProtoMember(2)] public long Time { get; set; } + + [ProtoMember(3)] public long Ran1 { get; set; } + + [ProtoMember(4)] public long Ran2 { get; set; } + + [ProtoMember(5)] public long Const1 { get; set; } + + [ProtoMember(11)] public long Const2 { get; set; } + + [ProtoMember(12)] public long Const3 { get; set; } + + [ProtoMember(13)] public long LastSyncTime { get; set; } + + [ProtoMember(14)] public long Const4 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs new file mode 100644 index 0000000..6047453 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangeDecrypted.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.Ecdh.Plain; + +[ProtoContract] +internal class SsoKeyExchangeDecrypted +{ + [ProtoMember(1)] public byte[] GcmKey { get; set; } + + [ProtoMember(2)] public byte[] Sign { get; set; } + + [ProtoMember(3)] public uint Expiration { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs new file mode 100644 index 0000000..bf8fda2 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.Ecdh.Plain; + +[ProtoContract] +internal class SsoKeyExchangePlain +{ + [ProtoMember(1)] public string? Uin { get; set; } + + [ProtoMember(2)] public byte[]? Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs new file mode 100644 index 0000000..c85afc0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/Ecdh/Plain/SsoKeyExchangePlain2.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Utility.Binary; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.Ecdh.Plain; + +/// +/// Sha256 Encrypt this packet to get content of GcmCalc2, Key is constant +/// +[Serializable] +internal class SsoKeyExchangePlain2 +{ + [BinaryProperty(BinaryPacket.Prefix.None)] public byte[] PublicKey { get; set; } + + [BinaryProperty] public uint Type { get; set; } + + [BinaryProperty(BinaryPacket.Prefix.None)] public byte[] EncryptedGcm { get; set; } + + [BinaryProperty] public uint Const { get; set; } = 0; + + [BinaryProperty] public uint Timestamp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchange.cs b/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchange.cs new file mode 100644 index 0000000..5a7403b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchange.cs @@ -0,0 +1,22 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.Ecdh; + +/// +/// Request for trpc.login.ecdh.EcdhService.SsoKeyExchange +/// +[ProtoContract] +internal class SsoKeyExchange +{ + [ProtoMember(1)] public byte[] PubKey { get; set; } + + [ProtoMember(2)] public int Type { get; set; } + + [ProtoMember(3)] public byte[] GcmCalc1 { get; set; } + + [ProtoMember(4)] public long Timestamp { get; set; } + + [ProtoMember(5)] public byte[] GcmCalc2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs b/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs new file mode 100644 index 0000000..10e73bc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/Ecdh/SsoKeyExchangeResponse.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.Ecdh; + +/// +/// Response for trpc.login.ecdh.EcdhService.SsoKeyExchange +/// +[ProtoContract] +internal class SsoKeyExchangeResponse +{ + [ProtoMember(1)] public byte[] GcmEncrypted { get; set; } + + [ProtoMember(2)] public byte[] Body { get; set; } // 腾讯你个傻逼这什么东西啊怎么还带Tag是0的Member + + [ProtoMember(3)] public byte[] PublicKey { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpRequest.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpRequest.cs new file mode 100644 index 0000000..768d207 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpRequest.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.NTLogin; + +public class NTLoginHttpRequest +{ + [JsonPropertyName("appid")] public long Appid { get; set; } + + [JsonPropertyName("faceUpdateTime")] public long FaceUpdateTime { get; set; } + + [JsonPropertyName("qrsig")] public string Qrsig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpResponse.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpResponse.cs new file mode 100644 index 0000000..fb7a58b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/NTLoginHttpResponse.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Login.NTLogin; + +public class NTLoginHttpResponse +{ + [JsonPropertyName("retCode")] public int RetCode { get; set; } + + [JsonPropertyName("errMsg")] public string ErrMsg { get; set; } + + [JsonPropertyName("qrSig")] public string QrSig { get; set; } + + [JsonPropertyName("uin")] public uint Uin { get; set; } + + [JsonPropertyName("faceUrl")] public string FaceUrl { get; set; } + + [JsonPropertyName("faceUpdateTime")] public long FaceUpdateTime { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs new file mode 100644 index 0000000..cd64c6d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginEasyLogin.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Body; + +// ReSharper disable once InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginEasyLogin +{ + [ProtoMember(1)] public byte[]? TempPassword { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs new file mode 100644 index 0000000..89d30d5 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Body/SsoNTLoginPasswordLogin.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Utility.Binary; +using static Lagrange.Core.Utility.Binary.BinaryPacket; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Body; + +#pragma warning disable CS8618 +// ReSharper disable once InconsistentNaming + +/// +/// Should be serialized with and encrypted with Tea, key to be investigated +/// +internal class SsoNTLoginPasswordLogin +{ + [BinaryProperty] public ushort Const4 { get; set; } = 4; + + [BinaryProperty] public uint Random { get; set; } + + [BinaryProperty] public uint UnknownConst2 { get; set; } = 0; + + [BinaryProperty] public uint Ver { get; set; } = 8001; + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty] public ushort UnknownConst3 { get; set; } = 0; + + [BinaryProperty] public uint Uin { get; set; } + + [BinaryProperty] public uint Timestamp { get; set; } + + [BinaryProperty] public uint UnknownConst4 { get; set; } = 0; + + [BinaryProperty] public byte Flag { get; set; } = 1; + + [BinaryProperty(Prefix.None)] public byte[] PasswordMd5 { get; set; } + + [BinaryProperty(Prefix.None)] public byte[] RandomBytes { get; set; } + + [BinaryProperty] public uint UnknownConst5 { get; set; } = 0; + + [BinaryProperty] public byte Flag2 { get; set; } = 1; + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; set; } + + [BinaryProperty] public uint UnknownConst6 { get; set; } = 1; + + [BinaryProperty] public uint UnknownConst7 { get; set; } = 1; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string UinString { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs new file mode 100644 index 0000000..601c8ed --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginBase.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain; + +// Resharper disable InconsistentNaming + +/// +/// Base for trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin and trpc.login.ecdh.EcdhService.SsoNTLoginEasyLogin +/// Should be encrypted and put into +/// +/// Body Type +[ProtoContract] +internal class SsoNTLoginBase where T : class +{ + [ProtoMember(1)] public SsoNTLoginHeader? Header { get; set; } + + [ProtoMember(2)] public T? Body { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs new file mode 100644 index 0000000..2a90aac --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginHeader.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginHeader +{ + [ProtoMember(1)] public SsoNTLoginUin? Uin { get; set; } + + [ProtoMember(2)] public SsoNTLoginSystem? System { get; set; } + + [ProtoMember(3)] public SsoNTLoginVersion? Version { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs new file mode 100644 index 0000000..0c040de --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/SsoNTLoginResponse.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain; + +// ReSharper disable once InconsistentNaming + +/// +/// Response for trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin +/// +[ProtoContract] +internal class SsoNTLoginResponse +{ + [ProtoMember(1)] public SsoNTLoginCredentials? Credentials { get; set; } + + [ProtoMember(3)] public SsoNTLoginUnusual? Unusual { get; set; } + + [ProtoMember(4)] public SsoNTLoginUid? Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs new file mode 100644 index 0000000..6fddf9f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginCredentials.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginCredentials +{ + [ProtoMember(3)] public byte[] TempPassword { get; set; } + + [ProtoMember(4)] public byte[] Tgt { get; set; } + + [ProtoMember(5)] public byte[] D2 { get; set; } + + [ProtoMember(6)] public byte[] D2Key { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs new file mode 100644 index 0000000..3b173ec --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginSystem.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginSystem +{ + [ProtoMember(1)] public string? Os { get; set; } + + [ProtoMember(2)] public string? DeviceName { get; set; } + + [ProtoMember(3)] public int Type { get; set; } = 7; // ? + + [ProtoMember(4)] public byte[]? Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs new file mode 100644 index 0000000..e9e4488 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUid.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming +#pragma warning disable CS8618 + +[ProtoContract] +internal class SsoNTLoginUid +{ + [ProtoMember(2)] public string Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs new file mode 100644 index 0000000..e44f9d4 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUin.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginUin +{ + [ProtoMember(1)] public string? Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs new file mode 100644 index 0000000..c41ed6a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginUnusual.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable once InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginUnusual +{ + [ProtoMember(2)] public byte[]? Sig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs new file mode 100644 index 0000000..ef8e15f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/Plain/Universal/SsoNTLoginVersion.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; + +// ReSharper disable InconsistentNaming + +[ProtoContract] +internal class SsoNTLoginVersion +{ + [ProtoMember(1)] public string? KernelVersion { get; set; } + + [ProtoMember(2)] public int AppId { get; set; } + + [ProtoMember(3)] public string? PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs b/Lagrange.Core/Core/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs new file mode 100644 index 0000000..f61049b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/NTLogin/SsoNTLoginEncryptedData.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Core.Packets.Login.Ecdh.Plain; +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Login.NTLogin; + +[ProtoContract] +internal class SsoNTLoginEncryptedData +{ + /// From Sign field, just simply store that in keystore + [ProtoMember(1)] public byte[]? Sign { get; set; } + + [ProtoMember(3)] public byte[]? GcmCalc { get; set; } + + [ProtoMember(4)] public int Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/Login.cs b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/Login.cs new file mode 100644 index 0000000..a19cdf9 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/Login.cs @@ -0,0 +1,68 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Packets.Tlv; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Core.Packets.Login.WtLogin.Entity; + +internal class Login : WtLoginBase +{ + private const string PacketCommand = "wtlogin.login"; + + private const ushort WtLoginCommand = 2064; + + private const ushort InternalCommand = 0x09; + + private static readonly ushort[] ConstructTlvs = + { + 0x106, 0x144, 0x116, 0x142, 0x145, 0x018, 0x141, 0x177, 0x191, 0x100, 0x107, 0x318, 0x16A, 0x166, 0x521 + }; + + public Login(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(PacketCommand, WtLoginCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructBody() + { + var packet = new BinaryPacket() + .WriteUshort(InternalCommand, false) + .WritePacket(TlvPacker.Pack(ConstructTlvs)); + + Console.WriteLine(packet.ToArray().Hex()); + + return packet; + } + + public static Dictionary Deserialize(BinaryPacket packet, BotKeystore keystore, out State state) + { + packet = DeserializePacket(keystore, packet); + + ushort command = packet.ReadUshort(false); + if (command != InternalCommand) throw new Exception("Invalid command"); + + state = (State)packet.ReadByte(); + if (state == State.Success) + { + var tlvs = TlvPacker.ReadTlvCollections(packet); + if (tlvs[0x119] is Tlv119 tlv119) + { + var decrypted = keystore.TeaImpl.Decrypt(tlv119.EncryptedTlv, keystore.Stub.TgtgtKey); + var tlv119Packet = new BinaryPacket(decrypted); + return TlvPacker.ReadTlvCollections(tlv119Packet); + } + } + else + { + return TlvPacker.ReadTlvCollections(packet); + } + + return new Dictionary(); + } + + public enum State : byte + { + Success = 0, + Slider = 2, + SmsRequired = 160, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp.cs b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp.cs new file mode 100644 index 0000000..7a28c36 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp.cs @@ -0,0 +1,67 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Packets.Login.WtLogin.Entity; + +internal abstract class TransEmp : WtLoginBase +{ + private readonly ushort _qrCodeCommand; + + private const string PacketCommand = "wtlogin.trans_emp"; + private const ushort WtLoginCommand = 2066; + + protected TransEmp(ushort qrCmd, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(PacketCommand, WtLoginCommand, keystore, appInfo, device) + { + _qrCodeCommand = qrCmd; + } + + protected override BinaryPacket ConstructBody() + { + var packet = new BinaryPacket().WriteByte(0); // known const + + packet.Barrier(typeof(ushort), () => + { + var writer = new BinaryPacket() + .WriteUint((uint)AppInfo.AppId, false) + .WriteUint(0x00000072, false) // const + .WriteUshort(0, false) // const 0 + .WriteByte(0) // const 0 + .WriteUint((uint)DateTimeOffset.Now.ToUnixTimeSeconds(), false) // length actually starts here + .WriteByte(0x02); // header for packet, counted into length of next barrier manually + + writer.Barrier(typeof(ushort), () => new BinaryPacket() + .WriteUshort(_qrCodeCommand, false) + .WriteUlong(0, false) // const 0 + .WriteUint(0, false) // const 0 + .WriteUlong(0, false) // const 0 + .WriteUshort(3, false) // const 3 + .WriteUshort(0, false) // const 0 + .WriteUshort(50, false) // unknown const + .WriteUlong(0, false) + .WriteUint(0, false) + .WriteUshort(0, false) + .WriteUint((uint)AppInfo.AppId, false) + .WritePacket(ConstructTransEmp()), false, true, 1); + + return writer; + }, false, true, -13); + + return packet; + } + + public static BinaryPacket DeserializeBody(BotKeystore keystore, BinaryPacket packet, out ushort command) + { + packet = DeserializePacket(keystore, packet); + + uint packetLength = packet.ReadUint(false); + packet.Skip(4); // misc unknown data + command = packet.ReadUshort(false); + packet.Skip(40); + uint appId = packet.ReadUint(false); + + return packet; + } + + protected abstract BinaryPacket ConstructTransEmp(); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp12.cs b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp12.cs new file mode 100644 index 0000000..d87c54c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp12.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Core.Packets.Login.WtLogin.Entity; + +internal class TransEmp12 : TransEmp +{ + private const ushort QrCodeCommand = 0x12; + + public TransEmp12(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(QrCodeCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructTransEmp() + { + if (Keystore.Session.QrSign != null) + { + return new BinaryPacket() + .WriteBytes(Keystore.Session.QrSign, BinaryPacket.Prefix.Uint16 | BinaryPacket.Prefix.LengthOnly) + .WriteUlong(0, false) // const 0 + .WriteUint(0, false) // const 0 + .WriteByte(0) // const 0 + .WriteByte(0x03); // packet end + } + + throw new Exception("QrSign is null"); + } + + public static Dictionary Deserialize(BinaryPacket packet, out State qrState) + { + qrState = (State)packet.ReadByte(); + if (qrState == State.Confirmed) + { + packet.Skip(12); // misc unknown data + return TlvPacker.ReadTlvCollections(packet, true); + } + + return new Dictionary(); + } + + internal enum State : byte + { + Confirmed = 0, + CodeExpired = 17, + WaitingForScan = 48, + WaitingForConfirm = 53, + Canceled = 54, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp31.cs b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp31.cs new file mode 100644 index 0000000..abcfccd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/WtLogin/Entity/TransEmp31.cs @@ -0,0 +1,40 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Packets.Tlv; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Core.Packets.Login.WtLogin.Entity; + +internal class TransEmp31 : TransEmp +{ + private const ushort QrCodeCommand = 0x31; + + private static readonly ushort[] ConstructTlvs = + { + 0x016, 0x01B, 0x01D, 0x033, 0x035, 0x066, 0x0D1 + }; + + private static readonly ushort[] ConstructTlvsPassword = + { + 0x011, 0x016, 0x01B, 0x01D, 0x033, 0x035, 0x066, 0x0D1 + }; + + public TransEmp31(BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + : base(QrCodeCommand, keystore, appInfo, device) { } + + protected override BinaryPacket ConstructTransEmp() => new BinaryPacket() + .WriteUshort(0, false) + .WriteUlong(0, false) + .WriteByte(0) + .WritePacket(TlvPacker.PackQrCode(Keystore.Session.UnusualSign == null ? ConstructTlvs : ConstructTlvsPassword)) + .WriteByte(0x03); + + public static Dictionary Deserialize(BinaryPacket packet, BotKeystore keystore, out byte[] signature) + { + byte dummy = packet.ReadByte(); + signature = packet.ReadBytes(BinaryPacket.Prefix.Uint16 | BinaryPacket.Prefix.LengthOnly); + keystore.Session.QrSign = signature; + + return TlvPacker.ReadTlvCollections(packet, true); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Login/WtLogin/WtLoginBase.cs b/Lagrange.Core/Core/Packets/Login/WtLogin/WtLoginBase.cs new file mode 100644 index 0000000..24a0783 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Login/WtLogin/WtLoginBase.cs @@ -0,0 +1,78 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Core.Packets.Login.WtLogin; + +internal abstract class WtLoginBase +{ + protected readonly string Command; + protected readonly ushort Cmd; + protected readonly BotKeystore Keystore; + protected readonly BotAppInfo AppInfo; + protected readonly BotDeviceInfo Device; + + protected readonly TlvPacker TlvPacker; + + protected WtLoginBase(string command, ushort cmd, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device) + { + Command = command; + Cmd = cmd; + Keystore = keystore; + AppInfo = appInfo; + Device = device; + TlvPacker = new TlvPacker(appInfo, keystore, device); + } + + public BinaryPacket ConstructPacket() + { + var body = ConstructBody(); + var encrypt = Keystore.SecpImpl.Encrypt(body.ToArray()); + + var packet = new BinaryPacket().WriteByte(2); // packet start + + packet.Barrier(typeof(ushort), () => new BinaryPacket() + .WriteUshort(8001, false) // ver + .WriteUshort(Cmd, false) // cmd: wtlogin.trans_emp: 2066, wtlogin.login: 2064 + .WriteUshort(Keystore.Session.Sequence, false) // unique wtLoginSequence for wtlogin packets only, should be stored in KeyStore + .WriteUint(Keystore.Uin, false) // uin, 0 for wtlogin.trans_emp + .WriteByte(3) // extVer + .WriteByte(135) // cmdVer + .WriteUint(0, false) // actually unknown const 0 + .WriteByte(19) // pubId + .WriteUshort(0, false) // insId + .WriteUshort(AppInfo.AppClientVersion, false) // cliType + .WriteUint(0, false) // retryTime + .WriteByte(1) // const + .WriteByte(1) // const + .WriteBytes(Keystore.Stub.RandomKey.AsSpan()) // randKey + .WriteUshort(0x102, false) // unknown const, 腾讯你妈妈死啦 + .WriteBytes(Keystore.SecpImpl.GetPublicKey(), BinaryPacket.Prefix.Uint16 | BinaryPacket.Prefix.LengthOnly) // pubKey + .WriteBytes(encrypt.AsSpan()) + .WriteByte(3), false, true, 1); // 0x03 is the packet end + + return packet; + } + + protected static BinaryPacket DeserializePacket(BotKeystore keystore, BinaryPacket packet) + { + uint packetLength = packet.ReadUint(false); + if (packet.ReadByte() != 0x02) return new BinaryPacket(); // packet header + + ushort internalLength = packet.ReadUshort(false); + ushort ver = packet.ReadUshort(false); + ushort cmd = packet.ReadUshort(false); + ushort sequence = packet.ReadUshort(false); + uint uin = packet.ReadUint(false); + byte flag = packet.ReadByte(); + ushort retryTime = packet.ReadUshort(false); + + var encrypted = packet.ReadBytes((int)(packet.Remaining - 1)); + var decrypted = new BinaryPacket(keystore.SecpImpl.Decrypt(encrypted)); + if (packet.ReadByte() != 0x03) throw new Exception("Packet end not found"); // packet end + + return decrypted; + } + + protected abstract BinaryPacket ConstructBody(); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/C2C/C2C.cs b/Lagrange.Core/Core/Packets/Message/C2C/C2C.cs new file mode 100644 index 0000000..8a1e32b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/C2C/C2C.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.C2C; + +[ProtoContract] +internal class C2C +{ + [ProtoMember(1)] public uint? Uin { get; set; } + + [ProtoMember(2)] public string? Uid { get; set; } + + [ProtoMember(3)] public uint? Field3 { get; set; } + + [ProtoMember(4)] public uint? Sig { get; set; } + + [ProtoMember(5)] public uint? ReceiverUin { get; set; } + + [ProtoMember(6)] public string? ReceiverUid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgInfo.cs b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgInfo.cs new file mode 100644 index 0000000..bdb857a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgInfo.cs @@ -0,0 +1,33 @@ +using Lagrange.Core.Core.Packets.Message.Routing; +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.C2C; + +[ProtoContract] +internal class C2CMsgInfo +{ + [ProtoMember(1)] public ulong FromUin { get; set; } + + [ProtoMember(2)] public ulong ToUin { get; set; } + + [ProtoMember(3)] public int MsgSeq { get; set; } + + [ProtoMember(4)] public long MsgUid { get; set; } + + [ProtoMember(5)] public long MsgTime { get; set; } + + [ProtoMember(6)] public int MsgRandom { get; set; } + + [ProtoMember(7)] public int PkgNum { get; set; } + + [ProtoMember(8)] public int PkgIndex { get; set; } + + [ProtoMember(9)] public int DivSeq { get; set; } + + [ProtoMember(10)] public int MsgType { get; set; } + + [ProtoMember(20)] public RoutingHead RoutingHead { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawReq.cs b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawReq.cs new file mode 100644 index 0000000..d4fb831 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawReq.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.C2C; + +[ProtoContract] +internal class C2CMsgWithDrawReq +{ + [ProtoMember(1)] public C2CMsgInfo[] MsgInfo { get; set; } + + [ProtoMember(2)] public int LongMessageFlag { get; set; } + + [ProtoMember(3)] public byte[] Reserved { get; set; } + + [ProtoMember(4)] public int SubCmd { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawResp.cs b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawResp.cs new file mode 100644 index 0000000..cd2f9a9 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/C2C/C2CMsgWithDrawResp.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.C2C; + +[ProtoContract] +internal class C2CMsgWithDrawResp +{ + [ProtoMember(1)] public int Result { get; set; } + + [ProtoMember(2)] public string ErrMsg { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/C2C/C2CTempMessageHead.cs b/Lagrange.Core/Core/Packets/Message/C2C/C2CTempMessageHead.cs new file mode 100644 index 0000000..2337092 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/C2C/C2CTempMessageHead.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.C2C; + +[ProtoContract] +internal class C2CTempMessageHead +{ + [ProtoMember(1)] public ulong GroupUin { get; set; } + + [ProtoMember(2)] public int C2CType { get; set; } + + [ProtoMember(3)] public int ServiceType { get; set; } + + [ProtoMember(4)] public string Card { get; set; } + + [ProtoMember(5)] public byte[] Sig { get; set; } + + [ProtoMember(6)] public int SigType { get; set; } + + [ProtoMember(7)] public string FromPhone { get; set; } + + [ProtoMember(8)] public string ToPhone { get; set; } + + [ProtoMember(9)] public int LockDisplay { get; set; } + + [ProtoMember(10)] public int DirectionFlag { get; set; } + + [ProtoMember(11)] public byte[] Reserved { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/Attr.cs b/Lagrange.Core/Core/Packets/Message/Component/Attr.cs new file mode 100644 index 0000000..09f8fcc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/Attr.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class Attr +{ + [ProtoMember(1)] public int CodePage { get; set; } + + [ProtoMember(2)] public int Time { get; set; } + + [ProtoMember(3)] public int Random { get; set; } + + [ProtoMember(4)] public int Color { get; set; } + + [ProtoMember(5)] public int Size { get; set; } + + [ProtoMember(6)] public int Effect { get; set; } + + [ProtoMember(7)] public int CharSet { get; set; } + + [ProtoMember(8)] public int PitchAndFamily { get; set; } + + [ProtoMember(9)] public string FontName { get; set; } + + [ProtoMember(10)] public byte[] ReserveData { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/Extra/FileExtra.cs b/Lagrange.Core/Core/Packets/Message/Component/Extra/FileExtra.cs new file mode 100644 index 0000000..69bdbce --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/Extra/FileExtra.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Component.Extra; + +[ProtoContract] +internal class FileExtra +{ + [ProtoMember(1)] public NotOnlineFile? File { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/GroupInfo.cs b/Lagrange.Core/Core/Packets/Message/Component/GroupInfo.cs new file mode 100644 index 0000000..7732c1e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/GroupInfo.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class GroupInfo +{ + [ProtoMember(1)] public ulong GroupCode { get; set; } + + [ProtoMember(2)] public int GroupType { get; set; } + + [ProtoMember(3)] public long GroupInfoSeq { get; set; } + + [ProtoMember(4)] public string GroupCard { get; set; } + + [ProtoMember(5)] public int GroupLevel { get; set; } + + [ProtoMember(6)] public int GroupCardType { get; set; } + + [ProtoMember(7)] public byte[] GroupName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/MutilTransHead.cs b/Lagrange.Core/Core/Packets/Message/Component/MutilTransHead.cs new file mode 100644 index 0000000..f21211d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/MutilTransHead.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class MutilTransHead +{ + [ProtoMember(1)] public int Status { get; set; } + + [ProtoMember(2)] public int MsgId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/NotOnlineFile.cs b/Lagrange.Core/Core/Packets/Message/Component/NotOnlineFile.cs new file mode 100644 index 0000000..980fcbd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/NotOnlineFile.cs @@ -0,0 +1,49 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class NotOnlineFile +{ + [ProtoMember(1)] public int? FileType { get; set; } + + [ProtoMember(2)] public byte[]? Sig { get; set; } + + [ProtoMember(3)] public string? FileUuid { get; set; } + + [ProtoMember(4)] public byte[]? FileMd5 { get; set; } + + [ProtoMember(5)] public string? FileName { get; set; } + + [ProtoMember(6)] public long? FileSize { get; set; } + + [ProtoMember(7)] public byte[]? Note { get; set; } + + [ProtoMember(8)] public int? Reserved { get; set; } + + [ProtoMember(9)] public int? Subcmd { get; set; } + + [ProtoMember(10)] public int? MicroCloud { get; set; } + + [ProtoMember(11)] public List? BytesFileUrls { get; set; } + + [ProtoMember(12)] public int? DownloadFlag { get; set; } + + [ProtoMember(50)] public int? DangerEvel { get; set; } + + [ProtoMember(51)] public int? LifeTime { get; set; } + + [ProtoMember(52)] public int? UploadTime { get; set; } + + [ProtoMember(53)] public int? AbsFileType { get; set; } + + [ProtoMember(54)] public int? ClientType { get; set; } + + [ProtoMember(55)] public int? ExpireTime { get; set; } + + [ProtoMember(56)] public byte[]? PbReserve { get; set; } + + [ProtoMember(57)] public string? FileHash { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/PcSupportDef.cs b/Lagrange.Core/Core/Packets/Message/Component/PcSupportDef.cs new file mode 100644 index 0000000..1445121 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/PcSupportDef.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class PcSupportDef +{ + [ProtoMember(1)] public uint PcPtlBegin { get; set; } + + [ProtoMember(2)] public uint PcPtlEnd { get; set; } + + [ProtoMember(3)] public uint MacPtlBegin { get; set; } + + [ProtoMember(4)] public uint MacPtlEnd { get; set; } + + [ProtoMember(5)] public List PtlsSupport { get; set; } + + [ProtoMember(6)] public List PtlsNotSupport { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/Ptt.cs b/Lagrange.Core/Core/Packets/Message/Component/Ptt.cs new file mode 100644 index 0000000..9de1585 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/Ptt.cs @@ -0,0 +1,57 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class Ptt +{ + [ProtoMember(1)] public int FileType { get; set; } + + [ProtoMember(2)] public long SrcUin { get; set; } + + [ProtoMember(3)] public byte[] FileUuid { get; set; } + + [ProtoMember(4)] public byte[] FileMd5 { get; set; } + + [ProtoMember(5)] public string FileName { get; set; } + + [ProtoMember(6)] public int FileSize { get; set; } + + [ProtoMember(7)] public byte[] Reserve { get; set; } + + [ProtoMember(8)] public int FileId { get; set; } + + [ProtoMember(9)] public int ServerIp { get; set; } + + [ProtoMember(10)] public int ServerPort { get; set; } + + [ProtoMember(11)] public bool BoolValid { get; set; } + + [ProtoMember(12)] public byte[] Signature { get; set; } + + [ProtoMember(13)] public byte[] Shortcut { get; set; } + + [ProtoMember(14)] public byte[] FileKey { get; set; } + + [ProtoMember(15)] public int MagicPttIndex { get; set; } + + [ProtoMember(16)] public int VoiceSwitch { get; set; } + + [ProtoMember(17)] public byte[] PttUrl { get; set; } + + [ProtoMember(18)] public byte[] GroupFileKey { get; set; } + + [ProtoMember(19)] public int Time { get; set; } + + [ProtoMember(20)] public byte[] DownPara { get; set; } + + [ProtoMember(29)] public int Format { get; set; } + + [ProtoMember(30)] public byte[] PbReserve { get; set; } + + [ProtoMember(31)] public List BytesPttUrls { get; set; } + + [ProtoMember(32)] public int DownloadFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Component/RichText.cs b/Lagrange.Core/Core/Packets/Message/Component/RichText.cs new file mode 100644 index 0000000..483e795 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Component/RichText.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Component; + +[ProtoContract] +internal class RichText +{ + [ProtoMember(1)] public Attr? Attr { get; set; } + + [ProtoMember(2)] public List Elems { get; set; } + + [ProtoMember(3)] public NotOnlineFile? NotOnlineFile { get; set; } + + [ProtoMember(4)] public Ptt? Ptt { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/ContentHead.cs b/Lagrange.Core/Core/Packets/Message/ContentHead.cs new file mode 100644 index 0000000..78882de --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/ContentHead.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message; + +[ProtoContract] +internal class ContentHead +{ + [ProtoMember(1)] public uint? PkgNum { get; set; } + + [ProtoMember(2)] public uint? PkgIndex { get; set; } + + [ProtoMember(3)] public uint? DivSeq { get; set; } + + [ProtoMember(4)] public long? MsgId { get; set; } + + [ProtoMember(5)] public int? MsgSe { get; set; } + + [ProtoMember(6)] public long? T { get; set; } + + [ProtoMember(7)] public long? Type { get; set; } + + [ProtoMember(12)] public long? NewId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Elem.cs b/Lagrange.Core/Core/Packets/Message/Element/Elem.cs new file mode 100644 index 0000000..3b94173 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Elem.cs @@ -0,0 +1,48 @@ +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Message.Element; + +[ProtoContract] +internal class Elem +{ + [ProtoMember(1)] public Text? Text { get; set; } // TextEntity + + [ProtoMember(2)] public Face? Face { get; set; } + + [ProtoMember(3)] public OnlineImage? OnlineImage { get; set; } + + [ProtoMember(4)] public NotOnlineImage? NotOnlineImage { get; set; } // Offline Image + + [ProtoMember(5)] public TransElem? TransElemInfo { get; set; } + + [ProtoMember(6)] public MarketFace? MarketFace { get; set; } + + [ProtoMember(8)] public CustomFace? CustomFace { get; set; } + + [ProtoMember(9)] public ElemFlags2? ElemFlags2 { get; set; } + + [ProtoMember(12)] public RichMsg? RichMsg { get; set; } + + [ProtoMember(13)] public GroupFile? GroupFile { get; set; } + + [ProtoMember(16)] public ExtraInfo? ExtraInfo { get; set; } + + [ProtoMember(19)] public VideoFile? VideoFile { get; set; } + + [ProtoMember(21)] public AnonymousGroupMessage? AnonGroupMsg { get; set; } + + [ProtoMember(24)] public QQWalletMsg? QQWalletMsg { get; set; } + + [ProtoMember(31)] public CustomElem? CustomElem { get; set; } + + [ProtoMember(37)] public GeneralFlags? GeneralFlags { get; set; } + + [ProtoMember(45)] public SrcMsg? SrcMsg { get; set; } // Forward/ReplyEntity + + [ProtoMember(51)] public LightAppElem? LightApp { get; set; } + + [ProtoMember(53)] public CommonElem? CommonElem { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs new file mode 100644 index 0000000..c8005f9 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/AnonymousGroupMessage.cs @@ -0,0 +1,23 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class AnonymousGroupMessage +{ + [ProtoMember(1)] public int Flags { get; set; } + + [ProtoMember(2)] public byte[] AnonId { get; set; } + + [ProtoMember(3)] public byte[] AnonNick { get; set; } + + [ProtoMember(4)] public int HeadPortrait { get; set; } + + [ProtoMember(5)] public int ExpireTime { get; set; } + + [ProtoMember(6)] public int BubbleId { get; set; } + + [ProtoMember(7)] public byte[] RankColor { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/CommonElem.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CommonElem.cs new file mode 100644 index 0000000..fc1dd06 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CommonElem.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CommonElem +{ + [ProtoMember(1)] public int ServiceType { get; set; } + + [ProtoMember(2)] public byte[] PbElem { get; set; } + + [ProtoMember(3)] public uint BusinessType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomElem.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomElem.cs new file mode 100644 index 0000000..68a3494 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomElem.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CustomElem +{ + [ProtoMember(1)] public byte[] Desc { get; set; } + + [ProtoMember(2)] public byte[] Data { get; set; } + + [ProtoMember(3)] public int EnumType { get; set; } + + [ProtoMember(4)] public byte[] Ext { get; set; } + + [ProtoMember(5)] public byte[] Sound { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomFace.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomFace.cs new file mode 100644 index 0000000..c0bb6df --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/CustomFace.cs @@ -0,0 +1,78 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class CustomFace +{ + [ProtoMember(1)] public byte[] Guid { get; set; } + + [ProtoMember(2)] public string FilePath { get; set; } + + [ProtoMember(3)] public string Shortcut { get; set; } + + [ProtoMember(4)] public byte[] Buffer { get; set; } + + [ProtoMember(5)] public byte[] Flag { get; set; } + + [ProtoMember(6)] public byte[] OldData { get; set; } + + [ProtoMember(7)] public int FileId { get; set; } + + [ProtoMember(8)] public int ServerIp { get; set; } + + [ProtoMember(9)] public int ServerPort { get; set; } + + [ProtoMember(10)] public int FileType { get; set; } + + [ProtoMember(11)] public byte[] Signature { get; set; } + + [ProtoMember(12)] public int Useful { get; set; } + + [ProtoMember(13)] public byte[] Md5 { get; set; } + + [ProtoMember(14)] public string ThumbUrl { get; set; } + + [ProtoMember(15)] public string BigUrl { get; set; } + + [ProtoMember(16)] public string OrigUrl { get; set; } + + [ProtoMember(17)] public int BizType { get; set; } + + [ProtoMember(18)] public int RepeatIndex { get; set; } + + [ProtoMember(19)] public int RepeatImage { get; set; } + + [ProtoMember(20)] public int ImageType { get; set; } + + [ProtoMember(21)] public int Index { get; set; } + + [ProtoMember(22)] public int Width { get; set; } + + [ProtoMember(23)] public int Height { get; set; } + + [ProtoMember(24)] public int Source { get; set; } + + [ProtoMember(25)] public int Size { get; set; } + + [ProtoMember(26)] public int Origin { get; set; } + + [ProtoMember(27)] public int ThumbWidth { get; set; } + + [ProtoMember(28)] public int ThumbHeight { get; set; } + + [ProtoMember(29)] public int ShowLen { get; set; } + + [ProtoMember(30)] public int DownloadLen { get; set; } + + [ProtoMember(31)] public string X400Url { get; set; } + + [ProtoMember(32)] public int X400Width { get; set; } + + [ProtoMember(33)] public int X400Height { get; set; } + + [ProtoMember(34)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs new file mode 100644 index 0000000..253a3fd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.Instance.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +internal partial class ElemFlags2 +{ + [ProtoContract] + public class Instance + { + [ProtoMember(1)] public uint AppId { get; set; } + + [ProtoMember(2)] public uint InstId { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.cs new file mode 100644 index 0000000..2fb0462 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ElemFlags2.cs @@ -0,0 +1,38 @@ +using Lagrange.Core.Core.Packets.Message.Component; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class ElemFlags2 +{ + [ProtoMember(1)] public uint ColorTextId { get; set; } + + [ProtoMember(2)] public ulong MsgId { get; set; } + + [ProtoMember(3)] public uint WhisperSessionId { get; set; } + + [ProtoMember(4)] public uint PttChangeBit { get; set; } + + [ProtoMember(5)] public uint VipStatus { get; set; } + + [ProtoMember(6)] public uint CompatibleId { get; set; } + + [ProtoMember(7)] public List Insts { get; set; } + + [ProtoMember(8)] public uint MsgRptCnt { get; set; } + + [ProtoMember(9)] public Instance SrcInst { get; set; } + + [ProtoMember(10)] public uint Longtitude { get; set; } + + [ProtoMember(11)] public uint Latitude { get; set; } + + [ProtoMember(12)] public uint CustomFont { get; set; } + + [ProtoMember(13)] public PcSupportDef PcSupportDef { get; set; } + + [ProtoMember(14)] public uint? CrmFlags { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/FaceExtra.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/FaceExtra.cs new file mode 100644 index 0000000..c3d6c4a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/FaceExtra.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; + +/// +/// +/// +[ProtoContract] +internal class FaceExtra +{ + [ProtoMember(1)] public int? FaceId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/ImageExtra.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/ImageExtra.cs new file mode 100644 index 0000000..9d7ced8 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/ImageExtra.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; + +/// +/// Used in conjunction with , should be created in a dedicated and put in Leaf 53 +/// NTQQ Exclusive +/// +[ProtoContract] +internal class ImageExtra +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/MentionExtra.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/MentionExtra.cs new file mode 100644 index 0000000..bdbe92c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/MentionExtra.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; + +/// +/// , ProtoMember12 (PbReserve) +/// +[ProtoContract] +internal class MentionExtra +{ + [ProtoMember(3)] public int? Type { get; set; } // 1 for All Member, 2 for Specific Member + + [ProtoMember(4)] public int? Field4 { get; set; } // Const Zero + + [ProtoMember(5)] public int? Field5 { get; set; } // Const Zero + + [ProtoMember(9)] public string? Uid { get; set; } // set this string to "all" to mention everyone in the Group +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/QFaceExtra.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/QFaceExtra.cs new file mode 100644 index 0000000..4749b13 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Extra/QFaceExtra.cs @@ -0,0 +1,26 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; + +/// +/// Constructed at , Service Type 33, Big face +/// +[ProtoContract] +internal class QFaceExtra +{ + [ProtoMember(1)] public string? Field1 { get; set; } + + [ProtoMember(2)] public string? Field2 { get; set; } + + [ProtoMember(3)] public int? FaceId { get; set; } // 318 + + [ProtoMember(4)] public int? Field4 { get; set; } + + [ProtoMember(5)] public int? Field5 { get; set; } + + [ProtoMember(6)] public string? Field6 { get; set; } + + [ProtoMember(7)] public string? Preview { get; set; } + + [ProtoMember(9)] public int? Field9 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/ExtraInfo.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ExtraInfo.cs new file mode 100644 index 0000000..f807681 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/ExtraInfo.cs @@ -0,0 +1,34 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class ExtraInfo +{ + [ProtoMember(1)] public byte[] Nick { get; set; } + + [ProtoMember(2)] public byte[] GroupCard { get; set; } + + [ProtoMember(3)] public int Level { get; set; } + + [ProtoMember(4)] public int Flags { get; set; } + + [ProtoMember(5)] public int GroupMask { get; set; } + + [ProtoMember(6)] public int MsgTailId { get; set; } + + [ProtoMember(7)] public byte[] SenderTitle { get; set; } + + [ProtoMember(8)] public byte[] ApnsTips { get; set; } + + [ProtoMember(9)] public ulong Uin { get; set; } + + [ProtoMember(10)] public int MsgStateFlag { get; set; } + + [ProtoMember(11)] public int ApnsSoundType { get; set; } + + [ProtoMember(12)] public int NewGroupFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Face.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Face.cs new file mode 100644 index 0000000..b753415 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Face.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class Face +{ + [ProtoMember(1)] public int? Index { get; set; } + + [ProtoMember(2)] public byte[]? Old { get; set; } + + [ProtoMember(11)] public byte[]? Buf { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/GeneralFlags.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/GeneralFlags.cs new file mode 100644 index 0000000..5735675 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/GeneralFlags.cs @@ -0,0 +1,47 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class GeneralFlags +{ + [ProtoMember(1)] public int BubbleDiyTextId { get; set; } + + [ProtoMember(2)] public int GroupFlagNew { get; set; } + + [ProtoMember(3)] public ulong Uin { get; set; } + + [ProtoMember(4)] public byte[] RpId { get; set; } + + [ProtoMember(5)] public int PrpFold { get; set; } + + [ProtoMember(6)] public int LongTextFlag { get; set; } + + [ProtoMember(7)] public string LongTextResId { get; set; } + + [ProtoMember(8)] public int GroupType { get; set; } + + [ProtoMember(9)] public int ToUinFlag { get; set; } + + [ProtoMember(10)] public int GlamourLevel { get; set; } + + [ProtoMember(11)] public int MemberLevel { get; set; } + + [ProtoMember(12)] public long GroupRankSeq { get; set; } + + [ProtoMember(13)] public int OlympicTorch { get; set; } + + [ProtoMember(14)] public byte[] BabyqGuideMsgCookie { get; set; } + + [ProtoMember(15)] public int Uin32ExpertFlag { get; set; } + + [ProtoMember(16)] public int BubbleSubId { get; set; } + + [ProtoMember(17)] public long PendantId { get; set; } + + [ProtoMember(18)] public byte[] RpIndex { get; set; } + + [ProtoMember(19)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/GroupFile.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/GroupFile.cs new file mode 100644 index 0000000..58fbfca --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/GroupFile.cs @@ -0,0 +1,30 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class GroupFile +{ + [ProtoMember(1)] public byte[] Filename { get; set; } + + [ProtoMember(2)] public long FileSize { get; set; } + + [ProtoMember(3)] public byte[] FileId { get; set; } + + [ProtoMember(4)] public byte[] BatchId { get; set; } + + [ProtoMember(5)] public byte[] FileKey { get; set; } + + [ProtoMember(6)] public byte[] Mark { get; set; } + + [ProtoMember(7)] public long Sequence { get; set; } + + [ProtoMember(8)] public byte[] BatchItemId { get; set; } + + [ProtoMember(9)] public int FeedMsgTime { get; set; } + + [ProtoMember(10)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/LightAppElem.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/LightAppElem.cs new file mode 100644 index 0000000..7beb058 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/LightAppElem.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class LightAppElem +{ + [ProtoMember(1)] public byte[] Data { get; set; } + + [ProtoMember(2)] public byte[] MsgResid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/MarketFace.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/MarketFace.cs new file mode 100644 index 0000000..277e509 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/MarketFace.cs @@ -0,0 +1,36 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class MarketFace +{ + [ProtoMember(1)] public byte[] FaceName { get; set; } + + [ProtoMember(2)] public int ItemType { get; set; } + + [ProtoMember(3)] public int FaceInfo { get; set; } + + [ProtoMember(4)] public byte[] FaceId { get; set; } + + [ProtoMember(5)] public int TabId { get; set; } + + [ProtoMember(6)] public int SubType { get; set; } + + [ProtoMember(7)] public byte[] Key { get; set; } + + [ProtoMember(8)] public byte[] Param { get; set; } + + [ProtoMember(9)] public int MediaType { get; set; } + + [ProtoMember(10)] public int ImageWidth { get; set; } + + [ProtoMember(11)] public int ImageHeight { get; set; } + + [ProtoMember(12)] public byte[] Mobileparam { get; set; } + + [ProtoMember(13)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.PbReserve.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.PbReserve.cs new file mode 100644 index 0000000..c715c8e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.PbReserve.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +internal partial class NotOnlineImage +{ + [ProtoContract] + internal class PbReserve + { + [ProtoMember(1)] public int Field1 { get; set; } // Zero Constant + + [ProtoMember(8)] public string Field8 { get; set; } // Empty String + + [ProtoMember(30)] public string Url { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.cs new file mode 100644 index 0000000..9aac341 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/NotOnlineImage.cs @@ -0,0 +1,62 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class NotOnlineImage +{ + [ProtoMember(1)] public string FilePath { get; set; } + + [ProtoMember(2)] public int FileLen { get; set; } + + [ProtoMember(3)] public string DownloadPath { get; set; } + + [ProtoMember(4)] public byte[] OldVerSendFile { get; set; } + + [ProtoMember(5)] public int ImgType { get; set; } + + [ProtoMember(6)] public byte[] PreviewsImage { get; set; } + + [ProtoMember(7)] public byte[] PicMd5 { get; set; } + + [ProtoMember(8)] public int PicHeight { get; set; } + + [ProtoMember(9)] public int PicWidth { get; set; } + + [ProtoMember(10)] public string ResId { get; set; } + + [ProtoMember(11)] public byte[] Flag { get; set; } + + [ProtoMember(12)] public string ThumbUrl { get; set; } + + [ProtoMember(13)] public int Original { get; set; } + + [ProtoMember(14)] public string BigUrl { get; set; } + + [ProtoMember(15)] public string OrigUrl { get; set; } + + [ProtoMember(16)] public int BizType { get; set; } + + [ProtoMember(17)] public int Result { get; set; } + + [ProtoMember(18)] public int Index { get; set; } + + [ProtoMember(19)] public byte[] OpFaceBuf { get; set; } + + [ProtoMember(20)] public bool OldPicMd5 { get; set; } + + [ProtoMember(21)] public int ThumbWidth { get; set; } + + [ProtoMember(22)] public int ThumbHeight { get; set; } + + [ProtoMember(23)] public int FileId { get; set; } + + [ProtoMember(24)] public int ShowLen { get; set; } + + [ProtoMember(25)] public int DownloadLen { get; set; } + + [ProtoMember(29)] public PbReserve PbRes { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/OnlineImage.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/OnlineImage.cs new file mode 100644 index 0000000..3a22a64 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/OnlineImage.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class OnlineImage +{ + [ProtoMember(1)] public byte[] Guid { get; set; } + + [ProtoMember(2)] public byte[] FilePath { get; set; } + + [ProtoMember(3)] public byte[] OldVerSendFile { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs new file mode 100644 index 0000000..30fcc67 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioBody.cs @@ -0,0 +1,55 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +internal partial class QQWalletMsg +{ + [ProtoContract] + internal class QQWalletAioBody + { + [ProtoMember(1)] public ulong SendUin { get; set; } + + [ProtoMember(2)] public QQWalletAioElem Sender { get; set; } + + [ProtoMember(3)] public QQWalletAioElem Receiver { get; set; } + + [ProtoMember(4, DataFormat = DataFormat.ZigZag)] public int ChannelId { get; set; } + + [ProtoMember(5, DataFormat = DataFormat.ZigZag)] public int TemplateId { get; set; } + + [ProtoMember(6)] public uint Resend { get; set; } + + [ProtoMember(7)] public uint MsgPriority { get; set; } + + [ProtoMember(8, DataFormat = DataFormat.ZigZag)] public int RedType { get; set; } + + [ProtoMember(9)] public byte[] BillNo { get; set; } + + [ProtoMember(10)] public byte[] AuthKey { get; set; } + + [ProtoMember(11, DataFormat = DataFormat.ZigZag)] public int SessionType { get; set; } + + [ProtoMember(12, DataFormat = DataFormat.ZigZag)] public int MsgType { get; set; } + + [ProtoMember(13, DataFormat = DataFormat.ZigZag)] public int EnvelOpeId { get; set; } + + [ProtoMember(14)] public byte[] Name { get; set; } + + [ProtoMember(15, DataFormat = DataFormat.ZigZag)] public int ConfType { get; set; } + + [ProtoMember(16, DataFormat = DataFormat.ZigZag)] public int MsgFrom { get; set; } + + [ProtoMember(17)] public byte[] PcBody { get; set; } + + [ProtoMember(18)] public byte[] Index { get; set; } + + [ProtoMember(19)] public uint RedChannel { get; set; } + + [ProtoMember(20)] public ulong[] GrapUin { get; set; } + + [ProtoMember(21)] public byte[] PbReserve { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs new file mode 100644 index 0000000..97287d8 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.QQWalletAioElem.cs @@ -0,0 +1,55 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +internal partial class QQWalletMsg +{ + [ProtoContract] + internal class QQWalletAioElem + { + [ProtoMember(1)] public uint Background { get; set; } + + [ProtoMember(2)] public uint Icon { get; set; } + + [ProtoMember(3)] public string Title { get; set; } + + [ProtoMember(4)] public string Subtitle { get; set; } + + [ProtoMember(5)] public string Content { get; set; } + + [ProtoMember(6)] public byte[] LinkUrl { get; set; } + + [ProtoMember(7)] public byte[] BlackStripe { get; set; } + + [ProtoMember(8)] public byte[] Notice { get; set; } + + [ProtoMember(9)] public uint TitleColor { get; set; } + + [ProtoMember(10)] public uint SubtitleColor { get; set; } + + [ProtoMember(11)] public byte[] ActionsPriority { get; set; } + + [ProtoMember(12)] public byte[] JumpUrl { get; set; } + + [ProtoMember(13)] public byte[] NativeIos { get; set; } + + [ProtoMember(14)] public byte[] NativeAndroid { get; set; } + + [ProtoMember(15)] public byte[] IconUrl { get; set; } + + [ProtoMember(16)] public uint ContentColor { get; set; } + + [ProtoMember(17)] public uint ContentBgColor { get; set; } + + [ProtoMember(18)] public byte[] AioImageLeft { get; set; } + + [ProtoMember(19)] public byte[] AioImageRight { get; set; } + + [ProtoMember(20)] public byte[] CftImage { get; set; } + + [ProtoMember(21)] public byte[] PbReserve { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.cs new file mode 100644 index 0000000..6dbaaec --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/QQWalletMsg.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class QQWalletMsg +{ + [ProtoMember(1)] public QQWalletAioBody Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/RedBagInfo.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/RedBagInfo.cs new file mode 100644 index 0000000..5cd3844 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/RedBagInfo.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class RedBagInfo +{ + [ProtoMember(1)] public uint? RedBagType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/RichMsg.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/RichMsg.cs new file mode 100644 index 0000000..8fbe080 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/RichMsg.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class RichMsg +{ + [ProtoMember(1)] public byte[]? Template1 { get; set; } + + [ProtoMember(2)] public int? ServiceId { get; set; } + + [ProtoMember(3)] public byte[]? MsgResId { get; set; } + + [ProtoMember(4)] public int? Rand { get; set; } + + [ProtoMember(5)] public int? Seq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs new file mode 100644 index 0000000..bc9a55d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/SourceMsg.PbPreserve.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +internal partial class SrcMsg +{ + [ProtoContract] + internal class Preserve + { + [ProtoMember(3)] public long? Field3 { get; set; } + + [ProtoMember(6)] public string? SenderUid { get; set; } + + [ProtoMember(7)] public string? ReceiverUid { get; set; } + + [ProtoMember(8)] public int? Field8 { get; set; } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/SrcMsg.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/SrcMsg.cs new file mode 100644 index 0000000..0cc991b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/SrcMsg.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal partial class SrcMsg +{ + [ProtoMember(1)] public List? OrigSeqs { get; set; } + + [ProtoMember(2)] public ulong? SenderUin { get; set; } + + [ProtoMember(3)] public int? Time { get; set; } + + [ProtoMember(4)] public int? Flag { get; set; } + + [ProtoMember(5)] public List? Elems { get; set; } + + [ProtoMember(6)] public int? Type { get; set; } + + [ProtoMember(7)] public byte[]? RichMsg { get; set; } + + [ProtoMember(8)] public byte[]? PbReserve { get; set; } + + [ProtoMember(9)] public byte[]? SourceMsg { get; set; } // Metadata + + [ProtoMember(10)] public ulong? ToUin { get; set; } + + [ProtoMember(11)] public byte[]? TroopName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/Text.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Text.cs new file mode 100644 index 0000000..3688e7c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/Text.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class Text +{ + [ProtoMember(1)] public string? Str { get; set; } + + [ProtoMember(2)] public string? Link { get; set; } + + [ProtoMember(3)] public byte[]? Attr6Buf { get; set; } + + [ProtoMember(4)] public byte[]? Attr7Buf { get; set; } + + [ProtoMember(11)] public byte[]? Buf { get; set; } + + [ProtoMember(12)] public byte[]? PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/TransElem.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/TransElem.cs new file mode 100644 index 0000000..965644e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/TransElem.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class TransElem +{ + [ProtoMember(1)] public int ElemType { get; set; } + + [ProtoMember(2)] public byte[] ElemValue { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Element/Implementation/VideoFile.cs b/Lagrange.Core/Core/Packets/Message/Element/Implementation/VideoFile.cs new file mode 100644 index 0000000..3ef7341 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Element/Implementation/VideoFile.cs @@ -0,0 +1,57 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Element.Implementation; + +[ProtoContract] +internal class VideoFile +{ + [ProtoMember(1)] public byte[] FileUuid { get; set; } + + [ProtoMember(2)] public byte[] FileMd5 { get; set; } + + [ProtoMember(3)] public byte[] FileName { get; set; } + + [ProtoMember(4)] public int FileFormat { get; set; } + + [ProtoMember(5)] public int FileTime { get; set; } + + [ProtoMember(6)] public int FileSize { get; set; } + + [ProtoMember(7)] public int ThumbWidth { get; set; } + + [ProtoMember(8)] public int ThumbHeight { get; set; } + + [ProtoMember(9)] public byte[] ThumbFileMd5 { get; set; } + + [ProtoMember(10)] public byte[] Source { get; set; } + + [ProtoMember(11)] public int ThumbFileSize { get; set; } + + [ProtoMember(12)] public int BusiType { get; set; } + + [ProtoMember(13)] public int FromChatType { get; set; } + + [ProtoMember(14)] public int ToChatType { get; set; } + + [ProtoMember(15)] public bool BoolSupportProgressive { get; set; } + + [ProtoMember(16)] public int FileWidth { get; set; } + + [ProtoMember(17)] public int FileHeight { get; set; } + + [ProtoMember(18)] public int SubBusiType { get; set; } + + [ProtoMember(19)] public int VideoAttr { get; set; } + + [ProtoMember(20)] public byte[][] BytesThumbFileUrls { get; set; } + + [ProtoMember(21)] public byte[][] BytesVideoFileUrls { get; set; } + + [ProtoMember(22)] public int ThumbDownloadFlag { get; set; } + + [ProtoMember(23)] public int VideoDownloadFlag { get; set; } + + [ProtoMember(24)] public byte[] PbReserve { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Message.cs b/Lagrange.Core/Core/Packets/Message/Message.cs new file mode 100644 index 0000000..439f7c6 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Message.cs @@ -0,0 +1,38 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message; + +/// +/// MessageSvc.PbSendMsg for Send, trpc.msg.olpush.OlPushService.MsgPush for Receive +/// +[ProtoContract] +internal class Message +{ + [ProtoMember(1)] public RoutingHead? RoutingHead { get; set; } + + [ProtoMember(2)] public ContentHead? ContentHead { get; set; } + + [ProtoMember(3)] public MessageBody? Body { get; set; } + + [ProtoMember(4)] public uint? Seq { get; set; } + + [ProtoMember(5)] public uint? Rand { get; set; } + + [ProtoMember(6)] public byte[]? SyncCookie { get; set; } + + // [ProtoMember(7)] public AppShareInfo? AppShare { get; set; } + + [ProtoMember(8)] public uint? Via { get; set; } + + [ProtoMember(9)] public uint? DataStatist { get; set; } + + // [ProtoMember(10)] public MultiMsgAssist? MultiMsgAssist { get; set; } + + // [ProtoMember(11)] public InputNotifyInfo? InputNotifyInfo { get; set; } + + [ProtoMember(12)] public MessageControl? Ctrl { get; set; } + + // [ProtoMember(13)] public ReceiptReq? ReceiptReq { get; set; } + + [ProtoMember(14)] public uint MultiSendSeq { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/MessageBody.cs b/Lagrange.Core/Core/Packets/Message/MessageBody.cs new file mode 100644 index 0000000..7c46c24 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/MessageBody.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Core.Packets.Message.Component; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message; + +[ProtoContract] +internal class MessageBody +{ + [ProtoMember(1)] public RichText RichText { get; set; } + + [ProtoMember(2)] public byte[]? MsgContent { get; set; } // Offline file is now put here(? + + [ProtoMember(3)] public byte[]? MsgEncryptContent { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/MessageControl.cs b/Lagrange.Core/Core/Packets/Message/MessageControl.cs new file mode 100644 index 0000000..51e8a4f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/MessageControl.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message; + +[ProtoContract] +internal class MessageControl +{ + [ProtoMember(1)] public int MsgFlag { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/PushMsg.cs b/Lagrange.Core/Core/Packets/Message/PushMsg.cs new file mode 100644 index 0000000..4520d8c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/PushMsg.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message; + +#pragma warning disable CS8618 + +[ProtoContract] +internal class PushMsg +{ + [ProtoMember(1)] public PushMsgBody Message { get; set; } +} + +[ProtoContract] +internal class PushMsgBody +{ + [ProtoMember(1)] public ResponseHead ResponseHead { get; set; } + + [ProtoMember(2)] public ContentHead ContentHead { get; set; } + + [ProtoMember(3)] public MessageBody Body { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/ResponseHead.cs b/Lagrange.Core/Core/Packets/Message/ResponseHead.cs new file mode 100644 index 0000000..c1c7d8b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/ResponseHead.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Core.Packets.Message.Routing; +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message; + +[ProtoContract] +internal class ResponseHead +{ + [ProtoMember(1)] public uint FromUin { get; set; } + + [ProtoMember(2)] public string? FromUid { get; set; } + + [ProtoMember(3)] public uint Type { get; set; } + + [ProtoMember(4)] public uint SigMap { get; set; } // 鬼知道是啥 + + [ProtoMember(5)] public uint ToUin { get; set; } + + [ProtoMember(6)] public string? ToUid { get; set; } + + [ProtoMember(8)] public ResponseGrp? Grp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Routing/Grp.cs b/Lagrange.Core/Core/Packets/Message/Routing/Grp.cs new file mode 100644 index 0000000..3bf46f3 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Routing/Grp.cs @@ -0,0 +1,9 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Routing; + +[ProtoContract] +internal class Grp +{ + [ProtoMember(1)] public uint? GroupCode { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Routing/GrpTmp.cs b/Lagrange.Core/Core/Packets/Message/Routing/GrpTmp.cs new file mode 100644 index 0000000..9684d8f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Routing/GrpTmp.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Routing; + +[ProtoContract] +internal class GrpTmp +{ + [ProtoMember(1)] public uint? GroupUin { get; set; } + + [ProtoMember(2)] public uint? ToUin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Routing/ResponseGrp.cs b/Lagrange.Core/Core/Packets/Message/Routing/ResponseGrp.cs new file mode 100644 index 0000000..2d86e88 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Routing/ResponseGrp.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Routing; + +[ProtoContract] +internal class ResponseGrp +{ + [ProtoMember(1)] public uint GroupUin { get; set; } + + [ProtoMember(4)] public string MemberName { get; set; } + + [ProtoMember(7)] public string GroupName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Routing/Trans0X211.cs b/Lagrange.Core/Core/Packets/Message/Routing/Trans0X211.cs new file mode 100644 index 0000000..ab5fc80 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Routing/Trans0X211.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message.Routing; + +[ProtoContract] +internal class Trans0X211 +{ + [ProtoMember(1)] public ulong? ToUin { get; set; } + + [ProtoMember(2)] public uint? CcCmd { get; set; } + + [ProtoMember(8)] public string? Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/Routing/WPATmp.cs b/Lagrange.Core/Core/Packets/Message/Routing/WPATmp.cs new file mode 100644 index 0000000..ec4e086 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/Routing/WPATmp.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Message.Routing; + +[ProtoContract] +internal class WPATmp +{ + [ProtoMember(1)] public ulong ToUin { get; set; } + + [ProtoMember(2)] public byte[] Sig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Message/RoutingHead.cs b/Lagrange.Core/Core/Packets/Message/RoutingHead.cs new file mode 100644 index 0000000..62268da --- /dev/null +++ b/Lagrange.Core/Core/Packets/Message/RoutingHead.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Core.Packets.Message.Routing; +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Message; + +[ProtoContract] +internal class RoutingHead +{ + [ProtoMember(1)] public C2C.C2C? C2C { get; set; } + + [ProtoMember(2)] public Grp? Grp { get; set; } + + [ProtoMember(3)] public GrpTmp? GrpTmp { get; set; } + + [ProtoMember(6)] public WPATmp? WpaTmp { get; set; } + + [ProtoMember(15)] public Trans0X211? Trans0X211 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbLafter.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbLafter.cs new file mode 100644 index 0000000..eeedfc2 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbLafter.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbLafter +{ + [ProtoMember(1)] public int Type { get; set; } + + [ProtoMember(2)] public byte[] D2 { get; set; } + + [ProtoMember(3)] public uint SubAppid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbProperty.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbProperty.cs new file mode 100644 index 0000000..0cb506f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Generics/OidbProperty.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Generics; + +[ProtoContract] +internal class OidbProperty +{ + [ProtoMember(1)] public string Key { get; set; } + + [ProtoMember(2)] public byte[] Value { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x1092_0.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x1092_0.cs new file mode 100644 index 0000000..16b47bd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x1092_0.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x1092, 0)] +internal class OidbSvcTrpcTcp0x1092_0 +{ + [ProtoMember(1)] public string Field1 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_1.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_1.cs new file mode 100644 index 0000000..e5745e3 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_1.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x10C0, 1)] +internal class OidbSvcTrpcTcp0x10C0_1 +{ + [ProtoMember(1)] public int Field1 { get; set; } // Unknown + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_2.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_2.cs new file mode 100644 index 0000000..40e04f9 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x10C0_2.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x10C0, 2)] +internal class OidbSvcTrpcTcp0x10C0_2 +{ + [ProtoMember(1)] public int Field1 { get; set; } // Unknown + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x116D_1.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x116D_1.cs new file mode 100644 index 0000000..6bf231a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x116D_1.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x116D, 1)] +internal class OidbSvcTrpcTcp0x116D_1 +{ + [ProtoMember(1)] public string Uid { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } // Unknown + + [ProtoMember(7)] public int Field7 { get; set; } // Unknown + + [ProtoMember(100)] public int Field100 { get; set; } // Unknown + + [ProtoMember(101)] public int Field101 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x5CF_11.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x5CF_11.cs new file mode 100644 index 0000000..0e737cc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x5CF_11.cs @@ -0,0 +1,29 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x5CF, 11)] +internal class OidbSvcTrpcTcp0x5CF_11 +{ + [ProtoMember(1)] public int Field1 { get; set; } // Unknown + + [ProtoMember(3)] public int Field3 { get; set; } // Unknown + + [ProtoMember(4)] public string Field4 { get; set; } // Unknown + + [ProtoMember(5)] public int Field5 { get; set; } // Unknown + + [ProtoMember(6)] public int Field6 { get; set; } // Unknown + + [ProtoMember(8)] public int Field8 { get; set; } // Unknown + + [ProtoMember(9)] public int Field9 { get; set; } // Unknown + + [ProtoMember(12)] public int Field12 { get; set; } // Unknown + + [ProtoMember(22)] public int Field22 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x644_1.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x644_1.cs new file mode 100644 index 0000000..edc60ba --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x644_1.cs @@ -0,0 +1,25 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x644, 1)] +internal class OidbSvcTrpcTcp0x644_1 +{ + [ProtoMember(1)] public OidbSvcTrpcTcp0x644_1Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0x644_1Body +{ + [ProtoMember(1)] public int Field1 { get; set; } // Unknown + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown + + [ProtoMember(3)] public int Field3 { get; set; } // Unknown + + [ProtoMember(4)] public int Field4 { get; set; } // Unknown +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x758_1.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x758_1.cs new file mode 100644 index 0000000..7f06c8c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x758_1.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +/// +/// Invites a user to a group. +/// +[ProtoContract] +[OidbSvcTrpcTcp(0x758, 1)] +public class OidbSvcTrpcTcp0x758_1 +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x773_0.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x773_0.cs new file mode 100644 index 0000000..c713adb --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0x773_0.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0x773, 0, true)] +internal class OidbSvcTrpcTcp0x773_0 +{ + [ProtoMember(1)] public int Field1 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xCDE_2.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xCDE_2.cs new file mode 100644 index 0000000..dac5fba --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xCDE_2.cs @@ -0,0 +1,19 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0xCDE, 2, true)] +internal class OidbSvcTrpcTcp0xCDE_2 +{ + [ProtoMember(2)] public OidbSvcTrpcTcp0xCDE_2Body Body { get; set; } +} + +[ProtoContract] +internal class OidbSvcTrpcTcp0xCDE_2Body +{ + [ProtoMember(1)] public string Field1 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_1700.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_1700.cs new file mode 100644 index 0000000..6417d01 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_1700.cs @@ -0,0 +1,43 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0xE37, 1700)] +internal class OidbSvcTrpcTcp0xE37_1700 +{ + [ProtoMember(1)] public uint SubCommand => 1700; + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown + + [ProtoMember(101)] public int Field101 { get; set; } // Unknown + + [ProtoMember(102)] public int Field102 { get; set; } // Unknown + + [ProtoMember(200)] public int Field200 { get; set; } // Unknown + } + +[ProtoContract] +internal class OidbSvcTrpcTcp0xE37_1700Body +{ + [ProtoMember(10)] public string SenderUid { get; set; } + + [ProtoMember(20)] public string ReceiverUid { get; set; } + + [ProtoMember(30)] public uint FileSize { get; set; } + + [ProtoMember(40)] public string FileName { get; set; } + + [ProtoMember(50)] public byte[] Md510MCheckSum { get; set; } + + [ProtoMember(60)] public byte[] Sha1CheckSum { get; set; } + + [ProtoMember(70)] public string LocalPath { get; set; } + + [ProtoMember(110)] public byte[] Md5CheckSum { get; set; } + + [ProtoMember(120)] public byte[] Sha3CheckSum { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_800.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_800.cs new file mode 100644 index 0000000..3750227 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xE37_800.cs @@ -0,0 +1,33 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0xE37, 800)] +public class OidbSvcTrpcTcp0xE37_800 +{ + [ProtoMember(1)] public uint SubCommand => 800; + + [ProtoMember(2)] public int Field2 { get; set; } // Unknown + + [ProtoMember(101)] public int Field101 { get; set; } // Unknown + + [ProtoMember(102)] public int Field102 { get; set; } // Unknown + + [ProtoMember(200)] public int Field200 { get; set; } // Unknown +} + +[ProtoContract] +public class OidbSvcTrpcTcp0xE37_800Body +{ + [ProtoMember(10)] public string SenderUid { get; set; } + + [ProtoMember(20)] public string ReceiverUid { get; set; } + + [ProtoMember(30)] public string FileUuid { get; set; } + + [ProtoMember(40)] public string FileHash { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xFE1_2.cs b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xFE1_2.cs new file mode 100644 index 0000000..c38ed46 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/Internal/OidbSvcTrpcTcp0xFE1_2.cs @@ -0,0 +1,21 @@ +using ProtoBuf; + +#pragma warning disable CS8618 +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Service.Oidb.Internal; + +[ProtoContract] +[OidbSvcTrpcTcp(0xFE1, 2)] +public class OidbSvcTrpcTcp0xFE1_2 +{ + [ProtoMember(1)] public string Uid { get; set; } + + [ProtoMember(3)] public OidbSvcTrpcTcp0xFE1_2Body Body { get; set; } +} + +[ProtoContract] +public class OidbSvcTrpcTcp0xFE1_2Body +{ + [ProtoMember(1)] public List Friends { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs new file mode 100644 index 0000000..4791ba0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpAttribute.cs @@ -0,0 +1,18 @@ +namespace Lagrange.Core.Core.Packets.Service.Oidb; + +[AttributeUsage(AttributeTargets.Class)] +internal class OidbSvcTrpcTcpAttribute : Attribute +{ + public uint Command { get; set; } + + public uint SubCommand { get; set; } + + public bool IsLafter { get; set; } + + public OidbSvcTrpcTcpAttribute(uint command, uint subCommand, bool isLafter = false) + { + Command = command; + SubCommand = subCommand; + IsLafter = isLafter; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs new file mode 100644 index 0000000..c96ac3a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpBase.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using Lagrange.Core.Core.Packets.Service.Oidb.Generics; +using Lagrange.Core.Core.Packets.Service.Oidb.Internal; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.Service.Oidb; + +/// +/// This class to declear a OidbSvcTrpcTcp packet 我愿称之为Protobuf版Tlv +/// Responsible for the uploading of NotOnlineFile, originally from OfflineFilleHandler_1700 of legacy oicq protocol +/// +[ProtoContract] +internal class OidbSvcTrpcTcpBase where T : class +{ + private static readonly Dictionary OidbReference; + + static OidbSvcTrpcTcpBase() + { + OidbReference = new Dictionary(); + + var assembly = Assembly.GetExecutingAssembly(); + var types = assembly.GetTypeByAttributes(out var attributes); + + for (int i = 0; i < types.Count; i++) + { + var type = types[i]; + var attribute = attributes[i]; + OidbReference[type] = (attribute.Command, attribute.SubCommand); + } + } + + [ProtoMember(1)] public uint Command { get; set; } + + [ProtoMember(2)] public uint SubCommand { get; set; } + + [ProtoMember(4)] public T Body { get; set; } + + [ProtoMember(7)] public OidbLafter? Lafter { get; set; } // if Lafter is null, it would not be serialized + + [ProtoMember(12)] public int Reserved { get; set; } + + protected OidbSvcTrpcTcpBase(T body, bool isLafter) + { + var (command, subCommand) = OidbReference[typeof(T)]; + + Command = command; + SubCommand = subCommand; + Body = body; + Reserved = 0; // const + Lafter = isLafter ? new OidbLafter() : null; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpResponse.cs b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpResponse.cs new file mode 100644 index 0000000..c56a8b6 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Service/Oidb/OidbSvcTrpcTcpResponse.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Core.Packets.Service.Oidb.Generics; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Service.Oidb; + +[ProtoContract] +internal class OidbSvcTrpcTcpResponse +{ + [ProtoMember(1)] public uint Command { get; set; } + + [ProtoMember(2)] public uint SubCommand { get; set; } + + [ProtoMember(3)] public uint Field3 { get; set; } + + [ProtoMember(4)] public T Body { get; set; } + + [ProtoMember(5)] public string Field5 { get; set; } + + [ProtoMember(11)] public List Properties { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/ServicePacker.cs b/Lagrange.Core/Core/Packets/ServicePacker.cs new file mode 100644 index 0000000..5a21bec --- /dev/null +++ b/Lagrange.Core/Core/Packets/ServicePacker.cs @@ -0,0 +1,67 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using static Lagrange.Core.Utility.Binary.BinaryPacket; + +namespace Lagrange.Core.Core.Packets; + +internal static class ServicePacker +{ + public static BinaryPacket BuildProtocol13(BinaryPacket packet, string command, uint sequence) + { + var frame = new BinaryPacket(); + + frame.Barrier(typeof(uint), () => new BinaryPacket() + .WriteUint(13, false) + .WriteByte(0) // flag + .WriteUint(sequence, false) + .WriteUint(0, false) + .WriteUshort(0x530, false) + .Barrier(typeof(uint), () => new BinaryPacket() + .WriteString(command, Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: Unknown + .WriteUint(4, false), false, true) + .WriteBytes(packet.ToArray(), Prefix.Uint32 | Prefix.WithPrefix), false, true); + + return frame; + } + + /// + /// Build Universal Packet, every service should derive from this, protocol 12 only + /// + public static BinaryPacket Build(BinaryPacket packet, BotKeystore keystore) + { + var frame = new BinaryPacket(); + + frame.Barrier(typeof(uint), () => new BinaryPacket() + .WriteUint(12, false) // protocolVersion + .WriteByte((byte)(keystore.Session.D2.Length == 0 ? 2 : 1)) // flag + .WriteBytes(keystore.Session.D2, Prefix.Uint32 | Prefix.WithPrefix) + .WriteByte(0) // unknown + .WriteString(keystore.Uin.ToString(), Prefix.Uint32 | Prefix.WithPrefix) // 帅 + .WriteBytes(keystore.TeaImpl.Encrypt(packet.ToArray(), keystore.Session.D2Key).AsSpan()), false, true); + + return frame; + } + + /// + /// Parse Universal Packet, every service should derive from this, protocol 12 only + /// + public static BinaryPacket Parse(BinaryPacket packet, BotKeystore keystore) + { + uint length = packet.ReadUint(false); + uint protocol = packet.ReadUint(false); + byte header = packet.ReadByte(); + uint flag = packet.ReadByte(); + string uin = packet.ReadString(Prefix.Uint32 | Prefix.WithPrefix); + + if (protocol == 13) return new BinaryPacket(); // Only Client.CorrectTime and Heartbeat.Alive so Ignored + if (protocol != 12) throw new Exception($"Unrecognized protocol: {protocol}"); + if (uin != keystore.Uin.ToString()) throw new Exception($"Uin mismatch: {uin} != {keystore.Uin}"); + + var encrypted = packet.ReadBytes((int)packet.Remaining); + var decrypted = keystore.TeaImpl.Decrypt(encrypted, keystore.Session.D2Key); + + return new BinaryPacket(decrypted); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/SsoPacker.cs b/Lagrange.Core/Core/Packets/SsoPacker.cs new file mode 100644 index 0000000..dc6dcc5 --- /dev/null +++ b/Lagrange.Core/Core/Packets/SsoPacker.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Packets.System; +using Lagrange.Core.Utility; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; +using ProtoBuf; +using static Lagrange.Core.Utility.Binary.BinaryPacket; + +namespace Lagrange.Core.Core.Packets; + +internal static class SsoPacker +{ + /// + /// Build Protocol 12 SSO packet + /// + public static BinaryPacket Build(SsoPacket packet, BotAppInfo appInfo, BotDeviceInfo device, BotKeystore keystore) + { + var writer = new BinaryPacket(); + + var signature = new NTDeviceSign + { + Sign = new Sign + { + S = new Software { Ver = appInfo.PackageSign }, + Signature = Signature.GetSignature(packet.Command, packet.Sequence, packet.Payload.ToArray()) + }, + Trace = StringGen.GenerateTrace(), + Uid = keystore.Uid + }; + var stream = new MemoryStream(); + Serializer.Serialize(stream, signature); + + writer.Barrier(typeof(uint), () => new BinaryPacket() // Barrier is used to calculate the length of the packet header only + .WriteUint(packet.Sequence, false) // sequence + .WriteByte(32) + .WriteUint((uint)appInfo.UnknownIdentifier, false) // appId + .WriteBytes("000804020000000000000000000000".UnHex().AsSpan()) + .WriteBytes(keystore.Session.Tgt, Prefix.Uint32 | Prefix.WithPrefix) + .WriteString(packet.Command, Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: unknown + .WriteString(device.Guid.ToByteArray().Hex().ToLower(), Prefix.Uint32 | Prefix.WithPrefix) + .WriteBytes(Array.Empty(), Prefix.Uint32 | Prefix.WithPrefix) // TODO: unknown + .WriteString(appInfo.CurrentVersion, Prefix.Uint16 | Prefix.WithPrefix) + .WriteBytes(stream.ToArray(), Prefix.Uint32 | Prefix.WithPrefix), false, true); // packet end + + return writer.WriteBytes(packet.Payload.ToArray(), Prefix.Uint32 | Prefix.WithPrefix); + } + + /// + /// Parse Protocol 12 SSO packet + /// + public static SsoPacket Parse(BinaryPacket packet) + { + uint headerLength = packet.ReadUint(false); + uint sequence = packet.ReadUint(false); + ulong dummy = packet.ReadUlong(false); + string command = packet.ReadString(Prefix.Uint32 | Prefix.WithPrefix); + packet.ReadString(Prefix.Uint32 | Prefix.WithPrefix); // TODO: unknown + packet.Skip((int)(headerLength - 4 - 4 - 8 - command.Length - 4 - 4)); + + return new SsoPacket(12, command, sequence, packet); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/SsoPacket.cs b/Lagrange.Core/Core/Packets/SsoPacket.cs new file mode 100644 index 0000000..20ddc53 --- /dev/null +++ b/Lagrange.Core/Core/Packets/SsoPacket.cs @@ -0,0 +1,22 @@ +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Packets; + +internal class SsoPacket +{ + public byte PacketType { get; set; } + + public string Command { get; } + + public uint Sequence { get; } + + public BinaryPacket Payload { get; } + + public SsoPacket(byte packetType, string command, uint sequence, BinaryPacket payload) + { + PacketType = packetType; + Command = command; + Sequence = sequence; + Payload = payload; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/NTDeviceSign.cs b/Lagrange.Core/Core/Packets/System/NTDeviceSign.cs new file mode 100644 index 0000000..c810990 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/NTDeviceSign.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class NTDeviceSign +{ + [ProtoMember(15)] public string Trace { get; set; } + + [ProtoMember(16)] public string? Uid { get; set; } + + [ProtoMember(24)] public Sign? Sign { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/NTOS.cs b/Lagrange.Core/Core/Packets/System/NTOS.cs new file mode 100644 index 0000000..8a6f671 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/NTOS.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class NTOS +{ + [ProtoMember(1)] public string OS { get; set; } + + [ProtoMember(2)] public string Name { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/NTSsoHeartBeat.cs b/Lagrange.Core/Core/Packets/System/NTSsoHeartBeat.cs new file mode 100644 index 0000000..435409e --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/NTSsoHeartBeat.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.System; + +// ReSharper disable once InconsistentNaming + +/// +/// trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat +/// +[ProtoContract] +internal class NTSsoHeartBeat +{ + [ProtoMember(1)] public int Type { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/NTSysEvent.cs b/Lagrange.Core/Core/Packets/System/NTSysEvent.cs new file mode 100644 index 0000000..9bba7d0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/NTSysEvent.cs @@ -0,0 +1,16 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class NTSysEvent +{ + [ProtoMember(1)] public string Ip { get; set; } + + [ProtoMember(2)] public long Sid { get; set; } + + [ProtoMember(3)] public NTSysEventSub Sub { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/NTSysEventSub.cs b/Lagrange.Core/Core/Packets/System/NTSysEventSub.cs new file mode 100644 index 0000000..59f74f1 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/NTSysEventSub.cs @@ -0,0 +1,22 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class NTSysEventSub +{ + [ProtoMember(2)] public long State { get; set; } + + [ProtoMember(3)] public int Field3 { get; set; } + + [ProtoMember(4)] public byte[] Field4 { get; set; } + + [ProtoMember(5)] public long Uin { get; set; } + + [ProtoMember(6)] public int Flag { get; set; } + + [ProtoMember(7)] public int On { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/OnlineOsInfo.cs b/Lagrange.Core/Core/Packets/System/OnlineOsInfo.cs new file mode 100644 index 0000000..e2188b7 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/OnlineOsInfo.cs @@ -0,0 +1,20 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class OnlineOsInfo +{ + [ProtoMember(1)] public string User { get; set; } + + [ProtoMember(2)] public string Os { get; set; } + + [ProtoMember(3)] public string OsVer { get; set; } + + [ProtoMember(4)] public string? Field4 { get; set; } + + [ProtoMember(5)] public string OsLower { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/ServiceKickNTResponse.cs b/Lagrange.Core/Core/Packets/System/ServiceKickNTResponse.cs new file mode 100644 index 0000000..fe64c3f --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/ServiceKickNTResponse.cs @@ -0,0 +1,22 @@ +using ProtoBuf; + +// ReSharper disable InconsistentNaming +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class ServiceKickNTResponse +{ + [ProtoMember(1)] public uint Uin { get; set; } + + [ProtoMember(3)] public string Tips { get; set; } + + [ProtoMember(4)] public string Title { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } + + [ProtoMember(6)] public int Field6 { get; set; } + + [ProtoMember(8)] public int Field8 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/ServiceRegister.cs b/Lagrange.Core/Core/Packets/System/ServiceRegister.cs new file mode 100644 index 0000000..551bf6f --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/ServiceRegister.cs @@ -0,0 +1,28 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.System; + +/// +/// trpc.qq_new_tech.status_svc.StatusService.Register +/// +[ProtoContract] +internal class ServiceRegister +{ + [ProtoMember(1)] public string? Guid { get; set; } + + [ProtoMember(2)] public int? Type { get; set; } + + [ProtoMember(3)] public string? CurrentVersion { get; set; } + + [ProtoMember(4)] public int? Field4 { get; set; } + + [ProtoMember(5)] public int? Field5 { get; set; } // 2052 + + [ProtoMember(6)] public OnlineOsInfo? Online { get; set; } + + [ProtoMember(7)] public int? Field7 { get; set; } + + [ProtoMember(8)] public int? Field8 { get; set; } + + [ProtoMember(9)] public int? Field9 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/ServiceRegisterResponse.cs b/Lagrange.Core/Core/Packets/System/ServiceRegisterResponse.cs new file mode 100644 index 0000000..aa3b803 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/ServiceRegisterResponse.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class ServiceRegisterResponse +{ + [ProtoMember(2)] public string? Message { get; set; } + + [ProtoMember(3)] public uint Timestamp { get; set; } + + [ProtoMember(4)] public int Field4 { get; set; } + + [ProtoMember(5)] public int Field5 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/Sign.cs b/Lagrange.Core/Core/Packets/System/Sign.cs new file mode 100644 index 0000000..6ef4a97 --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/Sign.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +/// +/// wtlogin.trans_emp and wtlogin.login packet, has tag of C201 in ushort and length of 0x35 in byte +/// 没错 这个就是臭名昭著的0C03算法 每一个包都有这个几把 +/// +[ProtoContract] +internal class Sign +{ + [ProtoMember(1)] public byte[] Signature { get; set; } + + [ProtoMember(3)] public Software S { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/System/Software.cs b/Lagrange.Core/Core/Packets/System/Software.cs new file mode 100644 index 0000000..78e427e --- /dev/null +++ b/Lagrange.Core/Core/Packets/System/Software.cs @@ -0,0 +1,11 @@ +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.System; + +[ProtoContract] +internal class Software +{ + [ProtoMember(2) ]public string Ver { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv100.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv100.cs new file mode 100644 index 0000000..78111f6 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv100.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x100)] +internal class Tlv100 : TlvBody +{ + public Tlv100(BotAppInfo appInfo) + { + AppId = (uint)appInfo.AppId; + SubAppId = (uint)appInfo.SubAppId; + AppClientVersion = appInfo.AppClientVersion; + MainSigMap = appInfo.MainSigMap; + } + + [BinaryProperty] public ushort DbBufVersion { get; set; } = 0; // originally 0x1 + + [BinaryProperty] public uint SsoVersion { get; set; } = 0x00000005; + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty] public uint SubAppId { get; set; } + + [BinaryProperty] public uint AppClientVersion { get; set; } + + [BinaryProperty] public uint MainSigMap { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv103.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv103.cs new file mode 100644 index 0000000..d4a9093 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv103.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X103)] +internal class Tlv103 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv106.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv106.cs new file mode 100644 index 0000000..9cb1d84 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv106.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Body; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x106)] +internal class Tlv106 : TlvBody +{ + /// + /// manually construct Tlv106 by tempPassword, from TlvQrCode18, not through dependency injection + /// This field does not only use as the request, but also response + /// Response should be referred to + + /// Password is depreciated, so such field is now injected through dependency injection + /// + public Tlv106(BotKeystore keystore) => Temp = keystore.Session.TempPassword ?? throw new ArgumentNullException(nameof(keystore.Session.TempPassword)); + + [BinaryProperty(Prefix.None)] public byte[] Temp { get; set; } +} + +[Tlv(0x106, true)] +internal class Tlv106Response : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Temp { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv107.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv107.cs new file mode 100644 index 0000000..6357ea3 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv107.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x107)] +internal class Tlv107 : TlvBody +{ + [BinaryProperty] public ushort PicType { get; set; } = 0x0001; + + [BinaryProperty] public byte CapType { get; set; } = 0x0D; + + [BinaryProperty] public ushort PicSize { get; set; } = 0x0000; + + [BinaryProperty] public byte RetType { get; set; } = 0x01; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv108.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv108.cs new file mode 100644 index 0000000..8a9d88d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv108.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x108)] +internal class Tlv108 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv10A.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv10A.cs new file mode 100644 index 0000000..9cb920f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv10A.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x10A)] +internal class Tlv10A : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Tgt { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv10C.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv10C.cs new file mode 100644 index 0000000..53d45be --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv10C.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x10C)] +internal class Tlv10C : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv10D.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv10D.cs new file mode 100644 index 0000000..b7276f1 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv10D.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X10D)] +internal class Tlv10D : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv10E.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv10E.cs new file mode 100644 index 0000000..9e11a91 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv10E.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X10E)] +internal class Tlv10E : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv11.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv11.cs new file mode 100644 index 0000000..8c90875 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv11.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x011)] +internal class Tlv11 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv114.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv114.cs new file mode 100644 index 0000000..6c80fae --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv114.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X114)] +internal class Tlv114 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv116.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv116.cs new file mode 100644 index 0000000..7238adc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv116.cs @@ -0,0 +1,26 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x116)] +internal class Tlv116 : TlvBody +{ + public Tlv116(BotAppInfo appInfo) => SubSigMap = appInfo.SubSigMap; + + private static readonly uint[] AppIds = { }; + + [BinaryProperty] public byte Version { get; set; } = 0; + + [BinaryProperty] public uint MiscBitmap { get; set; } = 12058620; + + [BinaryProperty] public uint SubSigMap { get; set; } + + [BinaryProperty] public byte AppIdCount { get; set; } = (byte)AppIds.Length; + + [BinaryProperty(Prefix.None)] public byte[] AppIdBytes => + AppIds.Select(x => BitConverter.GetBytes(x, false)).SelectMany(x => x).ToArray(); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv118.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv118.cs new file mode 100644 index 0000000..f7bcda6 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv118.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X118)] +internal class Tlv118 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv119.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv119.cs new file mode 100644 index 0000000..e205f50 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv119.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x119)] +internal class Tlv119 : TlvBody +{ + /// + /// The encrypted TLV data that should be decrypted with the TGTGT key. + /// + [BinaryProperty(Prefix.None)] public byte[] EncryptedTlv { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv11A.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv11A.cs new file mode 100644 index 0000000..e2514ff --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv11A.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X11A)] +internal class Tlv11A : TlvBody +{ + [BinaryProperty] public ushort FaceId { get; set; } + + [BinaryProperty] public byte Age { get; set; } + + [BinaryProperty] public byte Gender { get; set; } + + [BinaryProperty(Prefix.Uint8 | Prefix.None)] public string Nickname { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv11F.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv11F.cs new file mode 100644 index 0000000..0319102 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv11F.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X11F)] +internal class Tlv11F : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv12.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv12.cs new file mode 100644 index 0000000..f2d7bab --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv12.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x012)] +internal class Tlv12 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv124.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv124.cs new file mode 100644 index 0000000..4db462e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv124.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x124)] +internal class Tlv124 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] Field0 { get; set; } = new byte[12]; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv128.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv128.cs new file mode 100644 index 0000000..11e7271 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv128.cs @@ -0,0 +1,32 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x128)] +internal class Tlv128 : TlvBody +{ + public Tlv128(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + Os = appInfo.Os; + Guid = deviceInfo.Guid.ToByteArray(); + } + + [BinaryProperty] public ushort Const0 { get; set; } = 0; + + [BinaryProperty] public byte GuidNew { get; set; } = 0; + + [BinaryProperty] public byte GuidAvailable { get; set; } = 0; + + [BinaryProperty] public byte GuidChanged { get; set; } = 0; + + [BinaryProperty] public uint GuidFlag { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Os { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public byte[] Guid { get; set; } + + [BinaryProperty] public ushort Const1 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv130.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv130.cs new file mode 100644 index 0000000..db4d86f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv130.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X130)] +internal class Tlv130 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv133.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv133.cs new file mode 100644 index 0000000..d9c180d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv133.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X133)] +internal class Tlv133 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv134.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv134.cs new file mode 100644 index 0000000..c3dc509 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv134.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X134)] +internal class Tlv134 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv138.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv138.cs new file mode 100644 index 0000000..017759e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv138.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X138)] +internal class Tlv138 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv141.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv141.cs new file mode 100644 index 0000000..556884b --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv141.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x141)] +internal class Tlv141 : TlvBody +{ + [BinaryProperty(Prefix.Uint32 | Prefix.LengthOnly)] public string Unknown { get; set; } = "Unknown"; + + [BinaryProperty] public uint Const0 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv142.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv142.cs new file mode 100644 index 0000000..ff72b2a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv142.cs @@ -0,0 +1,16 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x142)] +internal class Tlv142 : TlvBody +{ + public Tlv142(BotAppInfo appInfo) => PackageName = appInfo.PackageName; + + [BinaryProperty] public ushort Version { get; set; } = 0; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv143.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv143.cs new file mode 100644 index 0000000..4896a4c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv143.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X143)] +internal class Tlv143 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] D2 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv144.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv144.cs new file mode 100644 index 0000000..35bd794 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv144.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x144)] +[TlvEncrypt(TlvEncryptAttribute.KeyType.TgtgtKey)] +internal class Tlv144 : TlvBody +{ + public Tlv144(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + TlvCount = 4; + Tlv16E = new TlvPacket(0x16E, new Tlv16E(deviceInfo)).ToArray(); + Tlv147 = new TlvPacket(0x147, new Tlv147(appInfo)).ToArray(); + Tlv128 = new TlvPacket(0x128, new Tlv128(appInfo, deviceInfo)).ToArray(); + Tlv124 = new TlvPacket(0x124, new Tlv124()).ToArray(); + } + + [BinaryProperty] public ushort TlvCount { get; set; } + + [BinaryProperty] public byte[] Tlv16E { get; set; } + + [BinaryProperty] public byte[] Tlv147 { get; set; } + + [BinaryProperty] public byte[] Tlv128 { get; set; } + + [BinaryProperty] public byte[] Tlv124 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv145.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv145.cs new file mode 100644 index 0000000..b5d5d3f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv145.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x145)] +internal class Tlv145 : TlvBody +{ + public Tlv145(BotDeviceInfo deviceInfo) => Guid = deviceInfo.Guid.ToByteArray(); + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv146.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv146.cs new file mode 100644 index 0000000..7132934 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv146.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x146)] +internal class Tlv146 : TlvBody +{ + [BinaryProperty] public uint State { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Tag { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string Message { get; set; } + + [BinaryProperty] public uint Field0 { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv147.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv147.cs new file mode 100644 index 0000000..ab7c6fd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv147.cs @@ -0,0 +1,23 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x147)] +internal class Tlv147 : TlvBody +{ + public Tlv147(BotAppInfo appInfo) + { + AppId = (uint)appInfo.AppId; + PtVersion = appInfo.PtVersion; + PackageName = appInfo.PackageName; + } + + [BinaryProperty] public uint AppId { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PtVersion { get; set; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv161.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv161.cs new file mode 100644 index 0000000..4255ccd --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv161.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x161)] +internal class Tlv161 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv163.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv163.cs new file mode 100644 index 0000000..b5fb760 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv163.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X163)] +internal class Tlv163 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv166.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv166.cs new file mode 100644 index 0000000..e97605d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv166.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x166)] +internal class Tlv166 : TlvBody +{ + [BinaryProperty] public byte ImageType { get; set; } = 5; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv167.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv167.cs new file mode 100644 index 0000000..0e61d72 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv167.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X167)] +internal class Tlv167 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv16A.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv16A.cs new file mode 100644 index 0000000..8d145e5 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv16A.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x16A)] +internal class Tlv16A : TlvBody +{ + /// + /// Tlv0x16A + /// + public Tlv16A(BotKeystore keystore) + { + NoPicSig = keystore.Session.NoPicSig ?? Array.Empty(); + } + + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} + +[Tlv(0x16A, true)] +internal class Tlv16AResponse : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv16D.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv16D.cs new file mode 100644 index 0000000..d7a235e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv16D.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X16D)] +internal class Tlv16D : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv16E.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv16E.cs new file mode 100644 index 0000000..959527a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv16E.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x16E)] +internal class Tlv16E : TlvBody +{ + public Tlv16E(BotDeviceInfo deviceInfo) => DeviceName = deviceInfo.DeviceName; + + [BinaryProperty(Prefix.None)] public string DeviceName { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv177.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv177.cs new file mode 100644 index 0000000..b99a78a --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv177.cs @@ -0,0 +1,18 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x177)] +internal class Tlv177 : TlvBody +{ + public Tlv177(BotAppInfo appInfo) => WtLoginSdk = appInfo.WtLoginSdk; + + [BinaryProperty] public byte Field1 { get; set; } = 1; + + [BinaryProperty] public uint BuildTime { get; set; } = 0; // const 0, not sure what this is + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string WtLoginSdk { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv18.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv18.cs new file mode 100644 index 0000000..c0bec58 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv18.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x018)] +internal class Tlv18 : TlvBody +{ + public Tlv18(BotKeystore keystore) => Uin = keystore.Uin; + + [BinaryProperty] public ushort PingVersion { get; set; } = 0; + + [BinaryProperty] public uint SsoVersion { get; set; } = 0x00000005; + + [BinaryProperty] public uint AppId { get; set; } = 0; // const 0, not sure what this is + + [BinaryProperty] public uint AppClientVersion { get; set; } = 8001; + + [BinaryProperty] public uint Uin { get; set; } + + [BinaryProperty] public ushort Field0 { get; set; } = 0; + + [BinaryProperty] public ushort Field1 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv191.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv191.cs new file mode 100644 index 0000000..0220cbf --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv191.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x191)] +internal class Tlv191 : TlvBody +{ + [BinaryProperty] public byte K { get; set; } = 0; // originally 0x82 +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv305.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv305.cs new file mode 100644 index 0000000..df9adff --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv305.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X305)] +internal class Tlv305 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] D2Key { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv318.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv318.cs new file mode 100644 index 0000000..a0388b0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv318.cs @@ -0,0 +1,11 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x318)] +internal class Tlv318 : TlvBody +{ + // Just empty + // after discovered, NTQQ has already depreciated this tlv +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv508.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv508.cs new file mode 100644 index 0000000..9a4ab85 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv508.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x508)] +internal class Tlv508 : TlvBody +{ + // 猜不透 算了 +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv510.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv510.cs new file mode 100644 index 0000000..3e7921f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv510.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X510)] +internal class Tlv510 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv521.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv521.cs new file mode 100644 index 0000000..23b6efc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv521.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x521)] +internal class Tlv521 : TlvBody +{ + [BinaryProperty] public uint ProductType { get; set; } = 0x13; + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string ProductDesc { get; set; } = "basicim"; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv523.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv523.cs new file mode 100644 index 0000000..f6d5826 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv523.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X523)] +internal class Tlv523 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv528.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv528.cs new file mode 100644 index 0000000..9e38a4c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv528.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X528)] +internal class Tlv528 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv543.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv543.cs new file mode 100644 index 0000000..a17b981 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv543.cs @@ -0,0 +1,26 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0x543)] +[ProtoContract] +internal class Tlv543 : TlvBody +{ + [ProtoMember(9)] public Tlv543Layer1 Layer1 { get; set; } +} + +[ProtoContract] +internal class Tlv543Layer1 +{ + [ProtoMember(11)] public Tlv543Layer2 Layer2 { get; set; } +} + +[ProtoContract] +internal class Tlv543Layer2 +{ + [ProtoMember(1)] public string Uid { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/Tlv550.cs b/Lagrange.Core/Core/Packets/Tlv/Tlv550.cs new file mode 100644 index 0000000..7546b87 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/Tlv550.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[Tlv(0X550)] +internal class Tlv550 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode11.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode11.cs new file mode 100644 index 0000000..54df9ea --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode11.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x11)] +internal class TlvQrCode11 : TlvBody +{ + public TlvQrCode11(BotKeystore keystore) + { + UnusualSign = keystore.Session.UnusualSign ?? throw new InvalidOperationException(); + } + + [BinaryProperty(Prefix.None)] public byte[] UnusualSign { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode15.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode15.cs new file mode 100644 index 0000000..c3923f0 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode15.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x015)] +internal class TlvQrCode15 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode16.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode16.cs new file mode 100644 index 0000000..cab3c69 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode16.cs @@ -0,0 +1,34 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x016)] +internal class Tlv16QrCode : TlvBody +{ + public Tlv16QrCode(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + SubAppId = (uint)appInfo.AppId; + AppIdQrCode = (uint)appInfo.AppIdQrCode; + Guid = deviceInfo.Guid.ToByteArray(); + PackageName = appInfo.PackageName; + PtVersion = appInfo.PtVersion; + PackageName2 = appInfo.PackageName; + } + + [BinaryProperty] public uint Field0 { get; } = 0; // unknown + + [BinaryProperty] public uint SubAppId { get; } + + [BinaryProperty] public uint AppIdQrCode { get; } + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PtVersion { get; } + + [BinaryProperty(Prefix.Uint16 | Prefix.LengthOnly)] public string PackageName2 { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode17.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode17.cs new file mode 100644 index 0000000..430783c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode17.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x017)] +internal class TlvQrCode17 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] QrCode { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode18.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode18.cs new file mode 100644 index 0000000..e984c7d --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode18.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x018)] +internal class TlvQrCode18 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] TempPassword { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode19.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode19.cs new file mode 100644 index 0000000..9997842 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode19.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x019)] +internal class TlvQrCode19 : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] NoPicSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1B.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1B.cs new file mode 100644 index 0000000..c58f830 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1B.cs @@ -0,0 +1,25 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x01b)] +internal class TlvQrCode1B : TlvBody +{ + [BinaryProperty] public uint Micro { get; set; } = 0; + + [BinaryProperty] public uint Version { get; set; } = 0; + + [BinaryProperty] public uint Size { get; set; } = 3; + + [BinaryProperty] public uint Margin { get; set; } = 4; + + [BinaryProperty] public uint Dpi { get; set; } = 72; + + [BinaryProperty] public uint EcLevel { get; set; } = 2; + + [BinaryProperty] public uint Hint { get; set; } = 2; + + [BinaryProperty] public ushort Field0 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1C.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1C.cs new file mode 100644 index 0000000..1c8e1fc --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1C.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x01c)] +internal class TlvQrCode1C : TlvBody +{ + [BinaryProperty] public uint ExpireSec { get; set; } + + [BinaryProperty] public ushort ExpireMin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1D.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1D.cs new file mode 100644 index 0000000..9e85981 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1D.cs @@ -0,0 +1,23 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +/// +/// Different from oicq +/// +[TlvQrCode(0x01d)] +internal class TlvQrCode1D : TlvBody +{ + public TlvQrCode1D(BotAppInfo appInfo) => MiscBitmap = (uint)appInfo.MiscBitmap; + + [BinaryProperty] public byte FieldByte { get; set; } = 1; + + [BinaryProperty] public uint MiscBitmap { get; set; } + + [BinaryProperty] public uint Field0 { get; set; } = 0; + + [BinaryProperty] public byte FieldByte0 { get; set; } = 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1E.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1E.cs new file mode 100644 index 0000000..836a5ba --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode1E.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x01e)] +internal class TlvQrCode1E : TlvBody +{ + [BinaryProperty(Prefix.None)] public byte[] TgtgtKey { get; set; } // a1_sig +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode2.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode2.cs new file mode 100644 index 0000000..76b890e --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode2.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x002)] +internal class TlvQrCode2 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode33.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode33.cs new file mode 100644 index 0000000..3363c3c --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode33.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x033)] +internal class TlvQrCode33 : TlvBody +{ + public TlvQrCode33(BotDeviceInfo deviceInfo) => Guid = deviceInfo.Guid.ToByteArray(); + + [BinaryProperty(Prefix.None)] public byte[] Guid { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode35.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode35.cs new file mode 100644 index 0000000..1bc24e1 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode35.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x035)] +internal class TlvQrCode35 : TlvBody +{ + public TlvQrCode35(BotAppInfo appInfo) => PtOsVersion = appInfo.PtOsVersion; + + [BinaryProperty] public int PtOsVersion { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode4.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode4.cs new file mode 100644 index 0000000..9470750 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode4.cs @@ -0,0 +1,13 @@ +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x004)] +internal class TlvQrCode4 : TlvBody +{ + [BinaryProperty(Prefix.Uint32 | Prefix.LengthOnly)] public string Uin { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode65.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode65.cs new file mode 100644 index 0000000..af0436f --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode65.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x065)] +internal class TlvQrCode65 : TlvBody +{ + // Just Empty +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode66.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode66.cs new file mode 100644 index 0000000..b20fe33 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode66.cs @@ -0,0 +1,14 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x066)] +internal class TlvQrCode66 : TlvBody +{ + public TlvQrCode66(BotAppInfo appInfo) => PtOsVersion = appInfo.PtOsVersion; + + [BinaryProperty] public int PtOsVersion { get; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCode7.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode7.cs new file mode 100644 index 0000000..5ba97a8 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCode7.cs @@ -0,0 +1,10 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x007)] +internal class Tlv7 : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeCE.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeCE.cs new file mode 100644 index 0000000..1302ee2 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeCE.cs @@ -0,0 +1,12 @@ +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; + +// ReSharper disable InconsistentNaming + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x0CE)] +internal class TlvQrCodeCE : TlvBody +{ + +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeD1.cs b/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeD1.cs new file mode 100644 index 0000000..ce2f4f2 --- /dev/null +++ b/Lagrange.Core/Core/Packets/Tlv/TlvQrCodeD1.cs @@ -0,0 +1,37 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Packets.System; +using Lagrange.Core.Utility.Binary.Tlv; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using ProtoBuf; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Core.Packets.Tlv; + +[TlvQrCode(0x0d1)] +[ProtoContract] +internal class TlvQrCodeD1 : TlvBody +{ + public TlvQrCodeD1(BotAppInfo appInfo, BotDeviceInfo deviceInfo) + { + Sys = new NTOS + { + Name = deviceInfo.DeviceName, + OS = appInfo.Os, + }; + Type = new byte[] { 0x30, 0x01 }; // actually there is a type ext but i'm too lazy to implement it so i just hardcode it + } + + [ProtoMember(1)] public NTOS Sys { get; set; } + + [ProtoMember(4)] public byte[] Type { get; set; } +} + +[TlvQrCode(0x0d1, true)] +[ProtoContract] +internal class TlvQrCodeD1Resp : TlvBody +{ + [ProtoMember(2)] public string Url { get; set; } + + [ProtoMember(3)] public string QrSig { get; set; } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Abstraction/BaseService.cs b/Lagrange.Core/Core/Service/Abstraction/BaseService.cs new file mode 100644 index 0000000..285fb48 --- /dev/null +++ b/Lagrange.Core/Core/Service/Abstraction/BaseService.cs @@ -0,0 +1,35 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.Abstraction; + +internal class BaseService : IService where TEvent : ProtocolEvent +{ + protected virtual bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out TEvent output, out List? extraEvents) + { + extraEvents = null; + return (output = null!) != null; + } + + protected virtual bool Build(TEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + extraPackets = null; + return (output = null!) != null; + } + + bool IService.Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, out ProtocolEvent output, + out List? extraEvents) + { + bool result = Parse(input, keystore, appInfo, device, out var @event, out extraEvents); + output = @event; + return result; + } + + bool IService.Build(ProtocolEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) => + Build((TEvent) input, keystore, appInfo, device, out output, out extraPackets); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Abstraction/IService.cs b/Lagrange.Core/Core/Service/Abstraction/IService.cs new file mode 100644 index 0000000..7298073 --- /dev/null +++ b/Lagrange.Core/Core/Service/Abstraction/IService.cs @@ -0,0 +1,15 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.Abstraction; + +internal interface IService +{ + public bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out ProtocolEvent output, out List? extraEvents); + + public bool Build(ProtocolEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets); +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/EventSubscribeAttribute.cs b/Lagrange.Core/Core/Service/EventSubscribeAttribute.cs new file mode 100644 index 0000000..88e0b58 --- /dev/null +++ b/Lagrange.Core/Core/Service/EventSubscribeAttribute.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Core.Service; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal class EventSubscribeAttribute : Attribute +{ + public Type EventType { get; } + + public EventSubscribeAttribute(Type eventType) + { + EventType = eventType; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Login/EasyLoginService.cs b/Lagrange.Core/Core/Service/Login/EasyLoginService.cs new file mode 100644 index 0000000..05088f1 --- /dev/null +++ b/Lagrange.Core/Core/Service/Login/EasyLoginService.cs @@ -0,0 +1,112 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Login.NTLogin; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Crypto; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.Login; + +[EventSubscribe(typeof(EasyLoginEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginEasyLogin")] +internal class EasyLoginService : BaseService +{ + protected override bool Build(EasyLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + if (keystore.Session.TempPassword == null || keystore.Session.ExchangeKey == null || keystore.Session.KeySign == null) + throw new InvalidOperationException("TempPassword is null"); + + var packet = new SsoNTLoginBase + { + Header = new SsoNTLoginHeader + { + Uin = new SsoNTLoginUin { Uin = keystore.Uin.ToString() }, + System = new SsoNTLoginSystem + { + Os = appInfo.Os, + DeviceName = device.DeviceName, + Type = 1, + Guid = device.Guid.ToByteArray() + }, + Version = new SsoNTLoginVersion + { + KernelVersion = device.KernelVersion, + AppId = appInfo.AppId, + PackageName = appInfo.PackageName + } + }, + Body = new SsoNTLoginEasyLogin + { + TempPassword = keystore.Session.TempPassword + } + }; + + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + var encrypted = new AesGcmImpl().Encrypt(stream.ToArray(), keystore.Session.ExchangeKey); + + var encryptPacket = new SsoNTLoginEncryptedData + { + Sign = keystore.Session.KeySign, + GcmCalc = encrypted, + Type = 1 + }; + + using var encryptStream = new MemoryStream(); + Serializer.Serialize(encryptStream, encryptPacket); + output = new BinaryPacket(encryptStream.ToArray()); + + extraPackets = null; + return true; + } + + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out EasyLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var encrypted = Serializer.Deserialize(payload.AsSpan()); + + if (encrypted.GcmCalc != null) + { + var decrypted = new AesGcmImpl().Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()).Body; + + if (response != null) + { + if (response.Unusual != null || response.Credentials == null) + { + keystore.Session.UnusualSign = response.Unusual?.Sig; + } + else + { + keystore.Session.Tgt = response.Credentials.Tgt; + keystore.Session.D2 = response.Credentials.D2; + keystore.Session.D2Key = response.Credentials.D2Key; + keystore.Session.TempPassword = response.Credentials.TempPassword; + } + + output = EasyLoginEvent.Result(true, response.Unusual != null); + } + else + { + output = EasyLoginEvent.Result(false); + } + } + else + { + output = EasyLoginEvent.Result(false); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Login/Ecdh/KeyExchangeService.cs b/Lagrange.Core/Core/Service/Login/Ecdh/KeyExchangeService.cs new file mode 100644 index 0000000..6ee2aaa --- /dev/null +++ b/Lagrange.Core/Core/Service/Login/Ecdh/KeyExchangeService.cs @@ -0,0 +1,86 @@ +using System.Security.Cryptography; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login.Ecdh; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Login.Ecdh; +using Lagrange.Core.Core.Packets.Login.Ecdh.Plain; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.Login.Ecdh; + +[EventSubscribe(typeof(KeyExchangeEvent))] +[Service("trpc.login.ecdh.EcdhService.SsoKeyExchange")] +internal class KeyExchangeService : BaseService +{ + private const string GcmCalc2Key = "e2733bf403149913cbf80c7a95168bd4ca6935ee53cd39764beebe2e007e3aee"; + + protected override bool Build(KeyExchangeEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + var packet = BuildPacket(keystore, device); + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + output = new BinaryPacket(stream.ToArray()); + + extraPackets = null; + return true; + } + + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out KeyExchangeEvent output, out List? extraEvents) + { + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var response = Serializer.Deserialize(payload.AsSpan()); + + var shareKey = keystore.PrimeImpl.GenerateShared(response.PublicKey, false); + var gcmDecrypted = new AesGcmImpl().Decrypt(response.GcmEncrypted, shareKey); + var decrypted = Serializer.Deserialize(gcmDecrypted.AsSpan()); + + keystore.Session.ExchangeKey = decrypted.GcmKey; + keystore.Session.KeySign = decrypted.Sign; + output = KeyExchangeEvent.Result(); + + extraEvents = null; + return true; + } + + private static SsoKeyExchange BuildPacket(BotKeystore keystore, BotDeviceInfo deviceInfo) + { + using var stream = new MemoryStream(); + var plain1 = new SsoKeyExchangePlain + { + Uin = keystore.Uin.ToString(), + Guid = deviceInfo.Guid.ToByteArray() + }; + Serializer.Serialize(stream, plain1); + var gcmCalc1 = new AesGcmImpl().Encrypt(stream.ToArray(), keystore.PrimeImpl.ShareKey); + + var timestamp = (uint)(DateTime.UtcNow - DateTime.UnixEpoch).TotalSeconds; + var plain2 = new SsoKeyExchangePlain2 + { + PublicKey = keystore.PrimeImpl.GetPublicKey(false), + Type = 1, + EncryptedGcm = gcmCalc1, + Const = 0, + Timestamp = timestamp + }; + var stream2 = BinarySerializer.Serialize(plain2); + using var sha256 = SHA256.Create(); + var hash = sha256.ComputeHash(stream2.ToArray()) ?? throw new InvalidOperationException(); + var gcmCalc2 = new AesGcmImpl().Encrypt(hash, GcmCalc2Key.UnHex()); + + return new SsoKeyExchange + { + PubKey = keystore.PrimeImpl.GetPublicKey(false), + Type = 1, + GcmCalc1 = gcmCalc1, + Timestamp = timestamp, + GcmCalc2 = gcmCalc2 + }; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Login/LoginService.cs b/Lagrange.Core/Core/Service/Login/LoginService.cs new file mode 100644 index 0000000..173652a --- /dev/null +++ b/Lagrange.Core/Core/Service/Login/LoginService.cs @@ -0,0 +1,64 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Tlv; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.Login; + +[EventSubscribe(typeof(LoginEvent))] +[Service("wtlogin.login")] +internal class LoginService : BaseService +{ + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out LoginEvent output, out List? extraEvents) + { + var tlvs = Packets.Login.WtLogin.Entity.Login.Deserialize(input.Payload, keystore, out var state); + + if (state == Packets.Login.WtLogin.Entity.Login.State.Success) + { + var tlv11A = (Tlv11A)tlvs[0x11A]; + var tlv305 = (Tlv305)tlvs[0x305]; + var tlv543 = (Tlv543)tlvs[0x543]; + var tlv10A = (Tlv10A)tlvs[0x10A]; + var tlv143 = (Tlv143)tlvs[0x143]; + var tlv106 = (Tlv106Response)tlvs[0x106]; + + keystore.Session.D2Key = tlv305.D2Key; + keystore.Uid = tlv543.Layer1.Layer2.Uid; + keystore.Session.Tgt = tlv10A.Tgt; + keystore.Session.D2 = tlv143.D2; + keystore.Session.TempPassword = tlv106.Temp; + + output = LoginEvent.Result((int)state, tlv11A.Age, tlv11A.Gender, tlv11A.Nickname); + extraEvents = null; + return true; + } + else + { + if (tlvs.TryGetValue(0x146, out var tlv)) + { + var tlv146 = (Tlv146)tlv; + output = LoginEvent.Result((int)state, tlv146.Tag, tlv146.Message); + extraEvents = null; + return true; + } + + output = LoginEvent.Result((int)state); + extraEvents = null; + return true; + } + } + + protected override bool Build(LoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + var packet = new Packets.Login.WtLogin.Entity.Login(keystore, appInfo, device); + output = packet.ConstructPacket(); + + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Login/PasswordLoginService.cs b/Lagrange.Core/Core/Service/Login/PasswordLoginService.cs new file mode 100644 index 0000000..b7eb428 --- /dev/null +++ b/Lagrange.Core/Core/Service/Login/PasswordLoginService.cs @@ -0,0 +1,125 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Login.NTLogin; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Body; +using Lagrange.Core.Core.Packets.Login.NTLogin.Plain.Universal; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Generator; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.Login; + +[Service("trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin")] +[EventSubscribe(typeof(PasswordLoginEvent))] +internal class PasswordLoginService : BaseService +{ + protected override bool Build(PasswordLoginEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + if (keystore.Session.ExchangeKey == null || keystore.Session.KeySign == null) throw new InvalidOperationException("TempPassword is null"); + + var plainBody = new SsoNTLoginPasswordLogin + { + Random = (uint)Random.Shared.Next(), + AppId = (uint)appInfo.AppId, + Uin = keystore.Uin, + Timestamp = (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), + PasswordMd5 = keystore.PasswordMd5.UnHex(), + RandomBytes = ByteGen.GenRandomBytes(16), + Guid = device.Guid.ToByteArray(), + UinString = keystore.Uin.ToString() + }; + var plainBytes = BinarySerializer.Serialize(plainBody); + var encryptedPlain = keystore.TeaImpl.Encrypt(plainBytes.ToArray(), new byte[16]); // TODO: Investigate key + + var packet = new SsoNTLoginBase + { + Header = new SsoNTLoginHeader + { + Uin = new SsoNTLoginUin { Uin = keystore.Uin.ToString() }, + System = new SsoNTLoginSystem + { + Os = appInfo.Os, + DeviceName = device.DeviceName, + Type = 1, + Guid = device.Guid.ToByteArray() + }, + Version = new SsoNTLoginVersion + { + KernelVersion = device.KernelVersion, + AppId = appInfo.AppId, + PackageName = appInfo.PackageName + } + }, + Body = new SsoNTLoginEasyLogin // 这样也没事 省的再开一个类 + { + TempPassword = encryptedPlain + } + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + + var encrypted = new AesGcmImpl().Encrypt(stream.ToArray(), keystore.Session.ExchangeKey); + var encryptPacket = new SsoNTLoginEncryptedData + { + Sign = keystore.Session.KeySign, + GcmCalc = encrypted, + Type = 1 + }; + + using var encryptStream = new MemoryStream(); + Serializer.Serialize(encryptStream, encryptPacket); + output = new BinaryPacket(encryptStream.ToArray()); + + extraPackets = null; + return true; + } + + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out PasswordLoginEvent output, out List? extraEvents) + { + if (keystore.Session.ExchangeKey == null) throw new InvalidOperationException("ExchangeKey is null"); + + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var encrypted = Serializer.Deserialize(payload.AsSpan()); + if (encrypted.GcmCalc != null) + { + var decrypted = new AesGcmImpl().Decrypt(encrypted.GcmCalc, keystore.Session.ExchangeKey); + var response = Serializer.Deserialize>(decrypted.AsSpan()).Body; + + if (response != null) + { + if (response.Unusual != null || response.Credentials == null) + { + keystore.Session.UnusualSign = response.Unusual?.Sig; + } + else + { + keystore.Session.Tgt = response.Credentials.Tgt; + keystore.Session.D2 = response.Credentials.D2; + keystore.Session.D2Key = response.Credentials.D2Key; + keystore.Session.TempPassword = response.Credentials.TempPassword; + } + + output = PasswordLoginEvent.Result(true, response.Unusual != null); + } + else + { + output = PasswordLoginEvent.Result(false); + } + } + else + { + output = PasswordLoginEvent.Result(false); + } + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Login/TransEmpService.cs b/Lagrange.Core/Core/Service/Login/TransEmpService.cs new file mode 100644 index 0000000..f5c63c1 --- /dev/null +++ b/Lagrange.Core/Core/Service/Login/TransEmpService.cs @@ -0,0 +1,65 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Login; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Login.WtLogin.Entity; +using Lagrange.Core.Core.Packets.Tlv; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.Login; + +[EventSubscribe(typeof(TransEmpEvent))] +[Service("wtlogin.trans_emp")] +internal class TransEmpService : BaseService +{ + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out TransEmpEvent output, out List? extraEvents) + { + var payload = input.Payload; + var packet = TransEmp.DeserializeBody(keystore, payload, out ushort command); + + if (command == 0x31) + { + var tlvs = TransEmp31.Deserialize(packet, keystore, out var signature); + + var qrCode = ((TlvQrCode17)tlvs[0x17]).QrCode; + uint expiration = ((TlvQrCode1C)tlvs[0x01C]).ExpireSec; + string url = ((TlvQrCodeD1Resp)tlvs[0x0D1]).Url; + string qrSig = ((TlvQrCodeD1Resp)tlvs[0x0D1]).QrSig; + + output = TransEmpEvent.Result(qrCode, expiration, url, qrSig, signature); + } + else + { + var tlvs = TransEmp12.Deserialize(packet, out var state); + + if (state == TransEmp12.State.Confirmed) + { + var tgtgtKey = ((TlvQrCode1E)tlvs[0x1E]).TgtgtKey; + var tempPassword = ((TlvQrCode18)tlvs[0x18]).TempPassword; + var noPicSig = ((TlvQrCode19)tlvs[0x19]).NoPicSig; + + output = TransEmpEvent.Result(state, tgtgtKey, tempPassword, noPicSig); + } + else + { + output = TransEmpEvent.Result(state, null, null, null); + } + } + + extraEvents = null; + return true; + } + + protected override bool Build(TransEmpEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + output = input.EventState == TransEmpEvent.State.FetchQrCode + ? new TransEmp31(keystore, appInfo, device).ConstructPacket() + : new TransEmp12(keystore, appInfo, device).ConstructPacket(); + + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Message/PushMessageService.cs b/Lagrange.Core/Core/Service/Message/PushMessageService.cs new file mode 100644 index 0000000..382785b --- /dev/null +++ b/Lagrange.Core/Core/Service/Message/PushMessageService.cs @@ -0,0 +1,49 @@ +using System.Text; +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.Message; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.Message; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Message; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.Message; + +[EventSubscribe(typeof(PushMessageEvent))] +[Service("trpc.msg.olpush.OlPushService.MsgPush")] +internal class PushMessageService : BaseService +{ + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out PushMessageEvent output, out List? extraEvents) + { + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var message = Serializer.Deserialize(payload.AsSpan()); + try + { + var chain = MessagePacker.Parse(message.Message); + var chainBuilder = new StringBuilder(); + + chainBuilder.Append("[MessageChain]: "); + foreach (var entity in chain) + { + chainBuilder.Append(entity.ToPreviewString()); + chainBuilder.Append(" | "); + } + + Console.WriteLine(chainBuilder.ToString()); + } + catch (Exception e) + { + Console.WriteLine(e); + } + + Console.WriteLine(payload.Hex()); + + output = PushMessageEvent.Create(); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/Message/SendMessageService.cs b/Lagrange.Core/Core/Service/Message/SendMessageService.cs new file mode 100644 index 0000000..d376f9a --- /dev/null +++ b/Lagrange.Core/Core/Service/Message/SendMessageService.cs @@ -0,0 +1,17 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol.Message; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.Message; + +[EventSubscribe(typeof(SendMessageEvent))] +[Service("MessageSvc.PbSendMsg")] +internal class SendMessageService : BaseService +{ + protected override bool Build(SendMessageEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + return base.Build(input, keystore, appInfo, device, out output, out extraPackets); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/ServiceAttribute.cs b/Lagrange.Core/Core/Service/ServiceAttribute.cs new file mode 100644 index 0000000..50ea749 --- /dev/null +++ b/Lagrange.Core/Core/Service/ServiceAttribute.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Core.Service; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +internal class ServiceAttribute : Attribute +{ + public string Command { get; } + + public byte PacketType { get; set; } + + public ServiceAttribute(string command, byte packetType = 12) + { + Command = command; + PacketType = packetType; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/System/AliveService.cs b/Lagrange.Core/Core/Service/System/AliveService.cs new file mode 100644 index 0000000..f52ce90 --- /dev/null +++ b/Lagrange.Core/Core/Service/System/AliveService.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.System; + +[EventSubscribe(typeof(AliveEvent))] +[Service("Heartbeat.Alive", 13)] +internal class AliveService : BaseService +{ + protected override bool Build(AliveEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + output = new BinaryPacket().WriteUint(4, false); + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/System/CorrectTimeService.cs b/Lagrange.Core/Core/Service/System/CorrectTimeService.cs new file mode 100644 index 0000000..83711ba --- /dev/null +++ b/Lagrange.Core/Core/Service/System/CorrectTimeService.cs @@ -0,0 +1,19 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; + +namespace Lagrange.Core.Core.Service.System; + +[EventSubscribe(typeof(CorrectTimeEvent))] +[Service("Client.CorrectTime", 13)] +internal class CorrectTimeService : BaseService +{ + protected override bool Build(CorrectTimeEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + output = new BinaryPacket().WriteUint(4, false); + extraPackets = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/System/KickNTService.cs b/Lagrange.Core/Core/Service/System/KickNTService.cs new file mode 100644 index 0000000..7ac3aed --- /dev/null +++ b/Lagrange.Core/Core/Service/System/KickNTService.cs @@ -0,0 +1,28 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.System; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.System; + +// ReSharper disable once InconsistentNaming + +[EventSubscribe(typeof(KickNTEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.KickNT")] +internal class KickNTService : BaseService +{ + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out KickNTEvent output, out List? extraEvents) + { + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var response = Serializer.Deserialize(payload.AsSpan()); + output = KickNTEvent.Create(response.Tips, response.Title); + + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/System/SsoAliveService.cs b/Lagrange.Core/Core/Service/System/SsoAliveService.cs new file mode 100644 index 0000000..a75557f --- /dev/null +++ b/Lagrange.Core/Core/Service/System/SsoAliveService.cs @@ -0,0 +1,35 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.System; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.System; + +[EventSubscribe(typeof(SsoAliveEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.SsoHeartBeat")] +internal class SsoAliveService : BaseService +{ + protected override bool Build(SsoAliveEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + using var stream = new MemoryStream(); + var packet = new NTSsoHeartBeat { Type = 1 }; + Serializer.Serialize(stream, packet); + + output = new BinaryPacket(stream.ToArray()); + extraPackets = null; + return true; + } + + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out SsoAliveEvent output, out List? extraEvents) + { + output = SsoAliveEvent.Result(); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Core/Service/System/StatusRegisterService.cs b/Lagrange.Core/Core/Service/System/StatusRegisterService.cs new file mode 100644 index 0000000..d679db4 --- /dev/null +++ b/Lagrange.Core/Core/Service/System/StatusRegisterService.cs @@ -0,0 +1,57 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Core.Event.Protocol; +using Lagrange.Core.Core.Event.Protocol.System; +using Lagrange.Core.Core.Packets; +using Lagrange.Core.Core.Packets.System; +using Lagrange.Core.Core.Service.Abstraction; +using Lagrange.Core.Utility.Binary; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Core.Service.System; + +[EventSubscribe(typeof(StatusRegisterEvent))] +[Service("trpc.qq_new_tech.status_svc.StatusService.Register")] +internal class StatusRegisterService : BaseService +{ + protected override bool Build(StatusRegisterEvent input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out BinaryPacket output, out List? extraPackets) + { + var packet = new ServiceRegister + { + Guid = device.Guid.ToByteArray().Hex(true), + Type = 0, + CurrentVersion = appInfo.CurrentVersion, + Field4 = 0, + Field5 = 2052, + Online = new OnlineOsInfo + { + User = device.DeviceName, + Os = appInfo.Os, + OsVer = device.SystemKernel, + Field4 = "", + OsLower = appInfo.Os.ToLower(), + }, + Field7 = 0, + Field8 = 0, + Field9 = 1, + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, packet); + + output = new BinaryPacket(stream.ToArray()); + extraPackets = null; + return true; + } + + protected override bool Parse(SsoPacket input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device, + out StatusRegisterEvent output, out List? extraEvents) + { + var payload = input.Payload.ReadBytes(BinaryPacket.Prefix.Uint32 | BinaryPacket.Prefix.WithPrefix); + var response = Serializer.Deserialize(payload.AsSpan()); + + output = StatusRegisterEvent.Result(response.Message ?? "OK"); + extraEvents = null; + return true; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Lagrange.Core.csproj b/Lagrange.Core/Lagrange.Core.csproj new file mode 100644 index 0000000..779b7e5 --- /dev/null +++ b/Lagrange.Core/Lagrange.Core.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + enable + enable + true + Lagrange.Core + Linwenxuan04, Tiger0132, LXY1226, TheSnowfield, and KonataDev/Contributors + The Implementation of NTQQ for Pure C#, Event Driven, derived from Konata.Core + 10 + true + 0.0.1 + + + + + + + + + + + + + diff --git a/Lagrange.Core/Message/Entity/FaceEntity.cs b/Lagrange.Core/Message/Entity/FaceEntity.cs new file mode 100644 index 0000000..455083b --- /dev/null +++ b/Lagrange.Core/Message/Entity/FaceEntity.cs @@ -0,0 +1,88 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(Face)), MessageElement(typeof(CommonElem))] +internal class FaceEntity : IMessageEntity +{ + public ushort FaceId { get; } + + public bool IsLargeFace { get; } + + public FaceEntity() { } + + public FaceEntity(ushort faceId, bool isLargeFace) + { + FaceId = faceId; + IsLargeFace = isLargeFace; + } + + IEnumerable IMessageEntity.PackElement() + { + if (IsLargeFace) + { + var qFace = new QFaceExtra + { + Field1 = "1", + Field2 = "8", + FaceId = FaceId, + Field4 = 1, + Field5 = 1, + Field6 = "", + Preview = "[骚扰]", + Field9 = 1 + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, qFace); + + return new Elem[] + { + new() + { + CommonElem = new CommonElem + { + ServiceType = 33, + PbElem = stream.ToArray(), + BusinessType = 1 + } + } + }; + } + else + { + var face = new FaceExtra { FaceId = FaceId }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, face); + + return new Elem[] { new() { Face = new Face { Old = stream.ToArray() } } }; + } + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.Face is { Old: not null }) + { + using var stream = new MemoryStream(elems.Face.Old); + var face = Serializer.Deserialize(stream); + + ushort? faceId = (ushort?)face.FaceId; + if (faceId != null) return new FaceEntity((ushort)faceId, false); + } + + if (elems.CommonElem is { PbElem: not null }) + { + using var stream = new MemoryStream(elems.CommonElem.PbElem); + var qFace = Serializer.Deserialize(stream); + + ushort? faceId = (ushort?)qFace.FaceId; + if (faceId != null) return new FaceEntity((ushort)faceId, true); + } + + return null; + } + + public string ToPreviewString() => $"[Face][{(IsLargeFace ? "Small" : "Large")}]: {FaceId}"; +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/FileEntity.cs b/Lagrange.Core/Message/Entity/FileEntity.cs new file mode 100644 index 0000000..d87bb07 --- /dev/null +++ b/Lagrange.Core/Message/Entity/FileEntity.cs @@ -0,0 +1,73 @@ +using System.Security.Cryptography; +using Lagrange.Core.Core.Packets.Message.Component; +using Lagrange.Core.Core.Packets.Message.Component.Extra; +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +public class FileEntity : IMessageEntity +{ + public long FileSize { get; set; } + + public string FileName { get; set; } + + public byte[] FileMd5 { get; set; } + + public FileEntity() + { + FileName = ""; + FileMd5 = Array.Empty(); + } + + public FileEntity(string path) + { + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + using var md5 = MD5.Create(); + FileMd5 = md5.ComputeHash(stream); + FileSize = stream.Length; + FileName = Path.GetFileName(path); + } + + private FileEntity(long fileSize, string fileName, byte[] fileMd5) + { + FileSize = fileSize; + FileName = fileName; + FileMd5 = fileMd5; + } + + IEnumerable IMessageEntity.PackElement() => Array.Empty(); + + object IMessageEntity.PackMessageContent() => new FileExtra + { + File = new NotOnlineFile + { + FileType = 0, + FileUuid = "", + FileMd5 = FileMd5, + FileName = FileName, + FileSize = FileSize, + Subcmd = 1, + DangerEvel = 0, + ExpireTime = DateTime.Now.AddDays(7).Second, + FileHash = "" // TODO: Send out Oidb + } + }; + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) => null; + + IMessageEntity? IMessageEntity.UnpackMessageContent(ReadOnlySpan content) + { + var notOnlineFile = Serializer.Deserialize(content); + + return notOnlineFile is { FileSize: not null, FileName: not null, FileMd5: not null } + ? new FileEntity((long)notOnlineFile.FileSize, notOnlineFile.FileName, notOnlineFile.FileMd5) + : null; + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/ForwardEntity.cs b/Lagrange.Core/Message/Entity/ForwardEntity.cs new file mode 100644 index 0000000..3219d42 --- /dev/null +++ b/Lagrange.Core/Message/Entity/ForwardEntity.cs @@ -0,0 +1,76 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using ProtoBuf; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(SrcMsg))] +public class ForwardEntity : IMessageEntity +{ + public uint Sequence { get; } + + public string? Uid { get; } + + public uint TargetUin { get; internal set; } + + private List Elements { get; } + + private string? SelfUin { get; set; } + + public ForwardEntity() + { + Sequence = 0; + Uid = null; + Elements = new List(); + } + + public ForwardEntity(MessageChain chain) + { + Sequence = chain.Sequence; + Uid = chain.Uid; + Elements = chain.Elements; + } + + IEnumerable IMessageEntity.PackElement() + { + var reserve = new SrcMsg.Preserve + { + Field3 = Random.Shared.Next(0, int.MaxValue), + ReceiverUid = Uid, + SenderUid = SelfUin, + Field8 = Random.Shared.Next(0, 10000) + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, reserve); + + return new Elem[] + { + new() + { + SrcMsg = new SrcMsg + { + OrigSeqs = new List { Sequence }, + SenderUin = 0, + Time = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), + Elems = Elements, + PbReserve = stream.ToArray(), + ToUin = 0 + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.SrcMsg != null) + { + + } + + throw new NotImplementedException(); + } + + public void SetSelfUid(string selfUid) => SelfUin = selfUid; + + public string ToPreviewString() => $"[Forward]: "; +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/ImageEntity.cs b/Lagrange.Core/Message/Entity/ImageEntity.cs new file mode 100644 index 0000000..2d86c25 --- /dev/null +++ b/Lagrange.Core/Message/Entity/ImageEntity.cs @@ -0,0 +1,41 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(NotOnlineImage))] +public class ImageEntity : IMessageEntity +{ + public ImageEntity() { } + + IEnumerable IMessageEntity.PackElement() + { + return new Elem[] + { + new() + { + CommonElem = new CommonElem + { + + } + }, + new() + { + NotOnlineImage = new NotOnlineImage + { + + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + throw new NotImplementedException(); + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/JsonEntity.cs b/Lagrange.Core/Message/Entity/JsonEntity.cs new file mode 100644 index 0000000..d4aac0a --- /dev/null +++ b/Lagrange.Core/Message/Entity/JsonEntity.cs @@ -0,0 +1,68 @@ +using System.Text; +using System.Text.Json.Nodes; +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Binary.Compression; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(RichMsg))] +public class JsonEntity : IMessageEntity +{ + public string Json { get; set; } + + public string ResId { get; set; } + + public JsonEntity() + { + Json = ""; + ResId = ""; + } + + public JsonEntity(string json, string resId = "") + { + Json = json; + ResId = resId; + } + + public JsonEntity(JsonNode json, string resId = "") + { + Json = json.ToJsonString(); + ResId = resId; + } + + IEnumerable IMessageEntity.PackElement() + { + return new Elem[] + { + new() + { + Text = new Text { Str = ResId } + }, + new() + { + RichMsg = new RichMsg + { + ServiceId = 1, + Template1 = ZCompression.ZCompress(Json, new byte[] { 0x01 }), + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.RichMsg is { ServiceId: 1, Template1: not null }) + { + var json = ZCompression.ZDecompress(elems.RichMsg.Template1[1..]); + return new XmlEntity(Encoding.UTF8.GetString(json)); + } + + return null; + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/MentionEntity.cs b/Lagrange.Core/Message/Entity/MentionEntity.cs new file mode 100644 index 0000000..12ecb27 --- /dev/null +++ b/Lagrange.Core/Message/Entity/MentionEntity.cs @@ -0,0 +1,84 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using Lagrange.Core.Core.Packets.Message.Element.Implementation.Extra; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using BitConverter = Lagrange.Core.Utility.Binary.BitConverter; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(Text))] +public class MentionEntity : IMessageEntity +{ + public uint Uin { get; set; } + + public string Uid { get; set; } + + public string Name { get; set; } + + public MentionEntity() + { + Uin = 0; + Uid = ""; + Name = ""; + } + + /// + /// Set target to 0 to mention everyone + /// + public MentionEntity(string name, uint target = 0) + { + Uin = target; + Uid = ""; // TODO: Get UID from UIN + Name = name; + } + + IEnumerable IMessageEntity.PackElement() + { + var reserve = new MentionExtra + { + Type = Uin == 0 ? 1 : 2, + Field4 = 0, + Field5 = 0, + Uid = Uid + }; + using var stream = new MemoryStream(); + Serializer.Serialize(stream, reserve); + + return new Elem[] + { + new() + { + Text = new Text + { + Str = Name, + Attr6Buf = stream.ToArray() + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.Text is { Str: not null, Attr6Buf: not null }) + { + var uin = elems.Text.Attr6Buf[7..11]; + + if (uin != null) + { + var uintUin = BitConverter.ToUInt32(uin, false); + return new MentionEntity(elems.Text.Str) + { + Uin = uintUin + }; + } + } + + return null; + } + + public string ToPreviewString() + { + return $"[Mention]: {Name}({Uin})"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/PacketEntity.cs b/Lagrange.Core/Message/Entity/PacketEntity.cs new file mode 100644 index 0000000..207da49 --- /dev/null +++ b/Lagrange.Core/Message/Entity/PacketEntity.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Core.Packets.Message.Element; + +namespace Lagrange.Core.Message.Entity; + +/// +/// 没错就是你想的那个Packet +/// +public class PacketEntity : IMessageEntity +{ + IEnumerable IMessageEntity.PackElement() + { + throw new NotImplementedException(); + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + throw new NotImplementedException(); + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/TextEntity.cs b/Lagrange.Core/Message/Entity/TextEntity.cs new file mode 100644 index 0000000..5879d96 --- /dev/null +++ b/Lagrange.Core/Message/Entity/TextEntity.cs @@ -0,0 +1,30 @@ +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(Text))] +public class TextEntity : IMessageEntity +{ + public string Text { get; } + + public TextEntity() => Text = ""; + + public TextEntity(string text) => Text = text; + + IEnumerable IMessageEntity.PackElement() + { + return new Elem[] // explicit interface implementation + { + new() { Text = new Text { Str = Text, } } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) => + elems.Text is { Str: not null, Attr6Buf: null } ? new TextEntity(elems.Text.Str) : null; + + public string ToPreviewString() + { + return $"[Text]: {Text}"; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/Entity/XmlEntity.cs b/Lagrange.Core/Message/Entity/XmlEntity.cs new file mode 100644 index 0000000..77521df --- /dev/null +++ b/Lagrange.Core/Message/Entity/XmlEntity.cs @@ -0,0 +1,50 @@ +using System.Text; +using System.Xml; +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Element.Implementation; +using Lagrange.Core.Utility.Binary.Compression; + +namespace Lagrange.Core.Message.Entity; + +[MessageElement(typeof(RichMsg))] +public class XmlEntity : IMessageEntity +{ + public string Xml { get; set; } + + public XmlEntity() => Xml = ""; + + public XmlEntity(string xml) => Xml = xml; + + public XmlEntity(XmlNode xml) => Xml = xml.OuterXml; + + IEnumerable IMessageEntity.PackElement() + { + return new Elem[] + { + new() + { + RichMsg = new RichMsg + { + ServiceId = 35, + Template1 = ZCompression.ZCompress(Xml, new byte[] { 0x01 }), + } + } + }; + } + + IMessageEntity? IMessageEntity.UnpackElement(Elem elems) + { + if (elems.RichMsg is { ServiceId: 35, Template1: not null }) + { + var xml = ZCompression.ZDecompress(elems.RichMsg.Template1[1..]); + return new XmlEntity(Encoding.UTF8.GetString(xml)); + } + + return null; + } + + public string ToPreviewString() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Message/IMessageEntity.cs b/Lagrange.Core/Message/IMessageEntity.cs new file mode 100644 index 0000000..3c7231c --- /dev/null +++ b/Lagrange.Core/Message/IMessageEntity.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Core.Packets.Message.Component; +using Lagrange.Core.Core.Packets.Message.Element; + +namespace Lagrange.Core.Message; + +public interface IMessageEntity +{ + /// + /// Pack the message into the Protobuf, and this would be packed into the list of elements. + /// The Final Packet would be here + /// + /// + internal IEnumerable PackElement(); // abstract method + + internal object? PackMessageContent() => null; // virtual method + + internal IMessageEntity? UnpackElement(Elem elem); // abstract method + + internal IMessageEntity? UnpackMessageContent(ReadOnlySpan content) => null; // virtual method + + internal void SetSelfUid(string selfUid) { } // virtual method + + public string ToPreviewString(); // abstract method +} \ No newline at end of file diff --git a/Lagrange.Core/Message/MessageBuilder.cs b/Lagrange.Core/Message/MessageBuilder.cs new file mode 100644 index 0000000..5540edb --- /dev/null +++ b/Lagrange.Core/Message/MessageBuilder.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Message; + +/// +/// MessageBuilder is used to build the , every MessageChain should be created with this class +/// +public sealed class MessageBuilder +{ + private MessageChain _chain; + + private MessageBuilder(MessageChain chain) => _chain = chain; + + public static MessageBuilder Friend(uint friendUin) + { + // TODO: Get the real UID + return new MessageBuilder(new MessageChain(friendUin, "")); + } + + public static MessageBuilder Group(uint groupUin, uint memberUin) => new(new MessageChain(groupUin, memberUin)); +} \ No newline at end of file diff --git a/Lagrange.Core/Message/MessageChain.cs b/Lagrange.Core/Message/MessageChain.cs new file mode 100644 index 0000000..ef63ac8 --- /dev/null +++ b/Lagrange.Core/Message/MessageChain.cs @@ -0,0 +1,42 @@ +using Lagrange.Core.Core.Packets.Message.Element; + +namespace Lagrange.Core.Message; + +public sealed class MessageChain : List +{ + public uint? GroupUin { get; } + + public uint FriendUin { get; } + + internal uint Sequence { get; } + + internal string? Uid { get; } + + internal bool IsGroup { get; } + + internal List Elements { get; } + + internal MessageChain(uint friendUin, string friendUid) + { + GroupUin = null; + FriendUin = friendUin; + Sequence = 0; // TODO: Allocate a dedicated sequence to this field + Uid = friendUid; + IsGroup = false; + Elements = new List(); + } + + internal MessageChain(uint groupUin, uint memberUin) + { + GroupUin = groupUin; + FriendUin = memberUin; + Sequence = 0; // TODO: Allocate a dedicated sequence to this field + Uid = null; + IsGroup = true; + Elements = new List(); + } + + public bool HasTypeOf() where T : IMessageEntity => this.Any(entity => entity is T); + + public T? GetEntity() where T : class, IMessageEntity => this.First(entity => entity is T) as T; +} \ No newline at end of file diff --git a/Lagrange.Core/Message/MessageElementAttribute.cs b/Lagrange.Core/Message/MessageElementAttribute.cs new file mode 100644 index 0000000..4b0e768 --- /dev/null +++ b/Lagrange.Core/Message/MessageElementAttribute.cs @@ -0,0 +1,9 @@ +namespace Lagrange.Core.Message; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public class MessageElementAttribute : Attribute +{ + public Type Element { get; } + + public MessageElementAttribute(Type element) => Element = element; +} \ No newline at end of file diff --git a/Lagrange.Core/Message/MessagePacker.cs b/Lagrange.Core/Message/MessagePacker.cs new file mode 100644 index 0000000..fd68191 --- /dev/null +++ b/Lagrange.Core/Message/MessagePacker.cs @@ -0,0 +1,157 @@ +using System.Reflection; +using ProtoBuf; +using Lagrange.Core.Core.Packets.Message.C2C; +using Lagrange.Core.Core.Packets.Message.Component; +using Lagrange.Core.Core.Packets.Message.Element; +using Lagrange.Core.Core.Packets.Message.Routing; +using Lagrange.Core.Message.Entity; +using Lagrange.Core.Utility.Extension; +using MessageControl = Lagrange.Core.Core.Packets.Message.MessageControl; + +namespace Lagrange.Core.Message; + +/// +/// Pack up message into the Protobuf +/// +internal class MessagePacker +{ + private static readonly Dictionary EntityToElem; + private static readonly Dictionary Factory; + private static readonly List MsgFactory; + + private readonly string _selfUid; + + static MessagePacker() + { + EntityToElem = new Dictionary(); + Factory = new Dictionary(); + + var assembly = Assembly.GetExecutingAssembly(); + var types = assembly.GetTypeWithMultipleAttributes(out var attributeArrays); + var elemType = typeof(Elem); + + for (int i = 0; i < types.Count; i++) + { + var type = types[i]; + var attributes = attributeArrays[i]; + + foreach (var attribute in attributes) + { + var property = elemType.GetProperty(attribute.Element.Name); + if (property != null) EntityToElem[type] = property; + } + + if (type.CreateInstance() is IMessageEntity factory) Factory[type] = factory; + } + + MsgFactory = assembly.GetImplementations().Select(type => (IMessageEntity)type.CreateInstance()).ToList(); + } + + public MessagePacker(string selfUid) + { + _selfUid = selfUid; + } + + public Core.Packets.Message.Message Build(MessageChain chain) + { + var message = BuildPacketBase(chain); + + foreach (var entity in chain) + { + entity.SetSelfUid(_selfUid); + + if (message.Body != null) + { + message.Body.RichText.Elems.AddRange(entity.PackElement()); + + var msgContent = entity.PackMessageContent(); + if (msgContent is not null) + { + using var stream = new MemoryStream(); + Serializer.Serialize(stream, msgContent); + message.Body.MsgContent = stream.ToArray(); + } + } + } + + return message; + } + + public static MessageChain Parse(Core.Packets.Message.PushMsgBody message) + { + var chain = ParseChain(message); + + if (message.Body?.RichText.Elems != null) + { + foreach (var element in message.Body.RichText.Elems) + { + foreach (var (entityType, expectElem) in EntityToElem) + { + var val = expectElem.GetValueByExpr(element); + if (val != null) + { + var entity = Factory[entityType].UnpackElement(element); + if (entity != null) + { + chain.Add(entity); + break; + } + } + } + } + } + + if (message.Body is { MsgContent: not null, RichText: null }) // if RichText is not null, it means that the message is from Tencent's SSO server + { + foreach (var factory in MsgFactory) + { + var entity = factory.UnpackMessageContent(message.Body.MsgContent); + if (entity != null) + { + chain.Add(entity); + break; + } + } + } + + return chain; + } + + private static Core.Packets.Message.Message BuildPacketBase(MessageChain chain) => new() + { + RoutingHead = new Core.Packets.Message.RoutingHead + { + C2C = chain.IsGroup ? null : new C2C + { + Uid = chain.Uid, + Uin = chain.FriendUin + }, + Grp = !chain.IsGroup ? null : new Grp // for consistency of code so inverted condition + { + GroupCode = chain.GroupUin + }, + Trans0X211 = !chain.HasTypeOf() ? null : new Trans0X211 + { + CcCmd = 4, + Uid = chain.Uid + } + }, + ContentHead = new Core.Packets.Message.ContentHead + { + PkgNum = 1, // regarded as the const + PkgIndex = 0, + DivSeq = 0 + }, + Body = new Core.Packets.Message.MessageBody { RichText = new RichText { Elems = new List() } }, + Seq = 0, // TODO: Add the dedicated sequence to the service + Rand = (uint?)Random.Shared.Next(10000000, int.MaxValue), + Ctrl = chain.IsGroup ? null : new MessageControl { MsgFlag = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } + }; + + private static MessageChain ParseChain(Core.Packets.Message.PushMsgBody message) + { + return message.ResponseHead.Grp == null + ? new MessageChain(message.ResponseHead.FromUin, message.ResponseHead.FromUid ?? string.Empty) + : new MessageChain(message.ResponseHead.Grp.GroupUin, message.ResponseHead.FromUin); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/BInaryPacket.Prefix.cs b/Lagrange.Core/Utility/Binary/BInaryPacket.Prefix.cs new file mode 100644 index 0000000..c903d04 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/BInaryPacket.Prefix.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Utility.Binary; + +internal partial class BinaryPacket +{ + [Flags] + public enum Prefix + { + None = 0, + Uint8 = 0b0001, + Uint16 = 0b0010, + Uint32 = 0b0100, + LengthOnly = 0, + WithPrefix = 0b1000, + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/BinaryPacket.cs b/Lagrange.Core/Utility/Binary/BinaryPacket.cs new file mode 100644 index 0000000..8ce976c --- /dev/null +++ b/Lagrange.Core/Utility/Binary/BinaryPacket.cs @@ -0,0 +1,292 @@ +using System.Text; +using Lagrange.Core.Utility.Binary.Tlv; + +namespace Lagrange.Core.Utility.Binary; + +/// +/// Binary Writer, Inspired by +/// Provide only Sync Apis +/// +internal partial class BinaryPacket +{ + private readonly MemoryStream _stream; + + private readonly BinaryReader _reader; + + public long Length => _stream.Length; + + public long Remaining => _stream.Length - _stream.Position; + + public BinaryPacket() + { + _stream = new MemoryStream(); + _reader = new BinaryReader(_stream); + } + + public BinaryPacket(byte[] data) + { + _stream = new MemoryStream(data); + _reader = new BinaryReader(_stream); + } + + public BinaryPacket WriteByte(byte value) + { + _stream.WriteByte(value); + return this; + } + + public BinaryPacket WriteBytes(ReadOnlySpan value) + { + _stream.Write(value); + return this; + } + + public BinaryPacket WriteBytes(byte[] value, Prefix prefixFlag = Prefix.None, byte limitedLength = 0) + { + int prefixLength = (int)prefixFlag & 0b0111; + byte[] array; // 处理后的数据 + + if (limitedLength > 0) // 限制长度时,写入数据长度=前缀+限制 + { + limitedLength = (byte)value.Length; + array = new byte[prefixLength + limitedLength]; + int len = value.Length > limitedLength ? limitedLength : value.Length; + if (len > 0) Buffer.BlockCopy(value, 0, array, prefixLength, len); + } + else if (prefixLength > 0) // 不限制长度且有前缀时,写入数据长度 = 前缀 + value长度 + { + array = new byte[prefixLength + value.Length]; + if (value.Length > 0) Buffer.BlockCopy(value, 0, array, prefixLength, value.Length); + } + else // 不限制又没有前缀,写入的就是value本身,不用处理,直接写入 + { + WriteBytes(value.AsSpan()); + return this; + } + + if (prefixLength > 0) // 添加前缀,使用大端序 + { + int len = value.Length; + if ((prefixFlag & Prefix.WithPrefix) > 0) len += prefixLength; + if (!InsertPrefix(array, 0, (uint)len, (Prefix)prefixLength, false)) + { + throw new IOException("Given prefix length is too small for value bytes."); // 给定的prefix不够填充value.Length,终止写入 + } + } + WriteBytes(array.AsSpan()); + + return this; + } + + public BinaryPacket WriteString(string value, Prefix prefixFlag = Prefix.None, Encoding? encoding = null, byte limitedLength = 0) + { + encoding ??= Encoding.UTF8; + byte[] bytes = encoding.GetBytes(value); + WriteBytes(bytes, prefixFlag, limitedLength); + return this; + } + + public BinaryPacket WriteBool(bool value) + { + _stream.WriteByte(value ? (byte)1 : (byte)0); + return this; + } + + public BinaryPacket WriteShort(short value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WriteUshort(ushort value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WriteInt(int value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WriteUint(uint value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WriteLong(long value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WriteUlong(ulong value, bool isLittleEndian = true) + { + _stream.Write(BitConverter.GetBytes(value, isLittleEndian).AsSpan()); + return this; + } + + public BinaryPacket WritePacket(BinaryPacket packet) + { + _stream.Write(packet.ToArray()); + return this; + } + + public byte ReadByte() => _reader.ReadByte(); + + public byte[] ReadBytes(int length) => _reader.ReadBytes(length); + + public byte[] ReadBytes(Prefix prefixFlag) + { + uint length; + bool reduce = (prefixFlag & Prefix.WithPrefix) > 0; + uint preLen = (uint)prefixFlag & 0b0111; + + switch (preLen) + { + case 0: // Read all remaining bytes + length = (uint)(_stream.Length - _stream.Position); + break; + case 1: case 2: case 4: // Read length from prefix + if (IsAvailable((int)preLen)) + { + length = preLen switch + { + 1 => _reader.ReadByte(), + 2 => ReadUshort(false), + 4 => ReadUint(false), + _ => throw new ArgumentOutOfRangeException($"Invalid prefix length.") + }; + + if (reduce) + { + if (length < preLen) throw new IOException("Data length is less than prefix length."); + length -= preLen; + } + break; + } + throw new IOException("Data length is less than prefix length."); + default: + throw new ArgumentOutOfRangeException($"Invalid prefix flag."); + } + + if (IsAvailable((int)length)) return _reader.ReadBytes((int)length); + throw new IOException("Data length is less than prefix length."); + } + + public string ReadString(int length, Encoding? encoding = null) + { + encoding ??= Encoding.UTF8; + return encoding.GetString(_reader.ReadBytes(length)); + } + + public string ReadString(Prefix prefixFlag, Encoding? encoding = null) + { + encoding ??= Encoding.UTF8; + return encoding.GetString(ReadBytes(prefixFlag)); + } + + public bool ReadBool() => _reader.ReadByte() == 1; + + public bool ReadBool(int length, bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(length); + var offset = _reader.BaseStream.Position; + return bytes[isLittleEndian ? offset : offset + length - 1] > 0; + } + + public short ReadShort(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(2); + return BitConverter.ToInt16(bytes, isLittleEndian); + } + + public ushort ReadUshort(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(2); + return BitConverter.ToUInt16(bytes, isLittleEndian); + } + + public int ReadInt(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(4); + return BitConverter.ToInt32(bytes, isLittleEndian); + } + + public uint ReadUint(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(4); + return BitConverter.ToUInt32(bytes, isLittleEndian); + } + + public long ReadLong(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(8); + return BitConverter.ToInt64(bytes, isLittleEndian); + } + + public ulong ReadUlong(bool isLittleEndian = true) + { + var bytes = _reader.ReadBytes(8); + return BitConverter.ToUInt64(bytes, isLittleEndian); + } + + public BinaryPacket ReadPacket(int length) + { + var bytes = _reader.ReadBytes(length); + return new BinaryPacket(bytes); + } + + /// + /// After the barrier is entered, the Following data will be recorded for length calculation and the written to the packet + /// + public BinaryPacket Barrier(Type barrierType, Func writer, bool isLittleEndian = true, bool withPrefix = false, int addition = 0) + { + var barrierWriter = writer(); + var barrierBytes = barrierWriter.ToArray(); + int length = barrierBytes.Length + addition; + + if (barrierType == typeof(byte)) WriteByte((byte)(length + (withPrefix ? 1 : 0))); + else if (barrierType == typeof(ushort)) WriteShort((short)(length + (withPrefix ? 2 : 0)), isLittleEndian); + else if (barrierType == typeof(uint)) WriteUint((uint)(length + (withPrefix ? 4 : 0)), isLittleEndian); + else if (barrierType == typeof(ulong)) WriteUlong((ulong)(length + (withPrefix ? 8 : 0)), isLittleEndian); + else throw new ArgumentException("Barrier Type must be byte, ushort, uint or ulong"); + + WritePacket(barrierWriter); + + return this; + } + + public void Skip(int length) => _reader.BaseStream.Seek(length, SeekOrigin.Current); + + public static bool InsertPrefix(byte[] buffer, uint offset, uint value, Prefix prefixFlag, bool isLittleEndian = true) + { + switch (prefixFlag) + { + case Prefix.Uint8: + if (value <= byte.MaxValue) + { + Buffer.BlockCopy(BitConverter.GetBytes((byte)value), 0, buffer, (int)offset, 1); + return true; + } + break; + case Prefix.Uint16: + if (value <= ushort.MaxValue) + { + Buffer.BlockCopy(BitConverter.GetBytes((ushort)value, isLittleEndian), 0, buffer, (int)offset, 2); + return true; + } + break; + case Prefix.Uint32: + Buffer.BlockCopy(BitConverter.GetBytes(value, isLittleEndian), 0, buffer, (int)offset, 4); + return true; + } + return false; + } + + public bool IsAvailable(int length) => _stream.Length - _stream.Position >= length; + + public byte[] ToArray() => _stream.ToArray(); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/BinaryPropertyAttribute.cs b/Lagrange.Core/Utility/Binary/BinaryPropertyAttribute.cs new file mode 100644 index 0000000..77e8642 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/BinaryPropertyAttribute.cs @@ -0,0 +1,19 @@ +namespace Lagrange.Core.Utility.Binary; + +[AttributeUsage(AttributeTargets.Property)] +internal class BinaryPropertyAttribute : Attribute +{ + public Type? Type { get; } + + public BinaryPacket.Prefix? Prefix { get; } + + public BinaryPropertyAttribute(Type type) => Type = type; + + public BinaryPropertyAttribute(BinaryPacket.Prefix prefix) => Prefix = prefix; + + public BinaryPropertyAttribute() + { + Type = null; + Prefix = null; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/BinarySerializer.cs b/Lagrange.Core/Utility/Binary/BinarySerializer.cs new file mode 100644 index 0000000..0ed2563 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/BinarySerializer.cs @@ -0,0 +1,123 @@ +using System.Reflection; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Utility.Binary; + +internal static class BinarySerializer +{ + private static readonly Dictionary> SerializeActions = new() + { + { typeof(byte), (body, value) => body.WriteByte((byte)value) }, + { typeof(short), (body, value) => body.WriteShort((short)value, false) }, + { typeof(ushort), (body, value) => body.WriteUshort((ushort)value, false) }, + { typeof(int), (body, value) => body.WriteInt((int)value, false) }, + { typeof(uint), (body, value) => body.WriteUint((uint)value, false) }, + { typeof(long), (body, value) => body.WriteLong((long)value, false) }, + { typeof(ulong), (body, value) => body.WriteUlong((ulong)value, false) }, + { typeof(bool), (body, value) => body.WriteBool((bool)value) }, + { typeof(BinaryPacket), (body, value) => body.WritePacket(Serialize((BinaryPacket)value)) } + }; + + private static readonly Dictionary> EnumSerializeActions = new() + { + { typeof(string), (body, value, prefix) => body.WriteString((string)value, prefix) }, + { typeof(byte[]), (body, value, prefix) => body.WriteBytes((byte[])value, prefix) }, + }; + + private static readonly Dictionary> DeserializeActions = new() + { + { typeof(byte), body => body.ReadByte() }, + { typeof(short), body => body.ReadShort(false) }, + { typeof(ushort), body => body.ReadUshort(false) }, + { typeof(int), body => body.ReadInt(false) }, + { typeof(uint), body => body.ReadUint(false) }, + { typeof(long), body => body.ReadLong(false) }, + { typeof(ulong), body => body.ReadUlong(false) }, + { typeof(bool), body => body.ReadBool() }, + }; + + private static readonly Dictionary> EnumDeserializeActions = new() + { + { typeof(string), (body, prefix) => body.ReadString(prefix) }, + { typeof(byte[]), (body, prefix) => body.ReadBytes(prefix) }, + }; + + public static BinaryPacket Serialize(object obj) + { + var type = obj.GetType(); + var body = new BinaryPacket(); + + foreach (var property in type.GetPropertiesFromCache()) + { + if (property.GetCustomAttribute() == null) continue; + + var value = property.GetValueByExpr(obj) ?? throw new InvalidOperationException($"Value is null for {property}"); + if (property.PropertyType == typeof(byte[]) || property.PropertyType == typeof(string)) + { + var func = EnumSerializeActions[property.PropertyType]; + var prefix = property.GetCustomAttribute()?.Prefix ?? BinaryPacket.Prefix.None; + func(body, value, prefix); + } + else + { + var func = SerializeActions[property.PropertyType]; + func(body, value); + } + } + + return body; + } + + public static T Deserialize(this BinaryPacket body) + { + var type = typeof(T); + var obj = type.CreateInstance(); + + foreach (var property in type.GetPropertiesFromCache()) + { + if (property.GetCustomAttribute() == null) continue; + + object value; + if (property.PropertyType == typeof(byte[]) || property.PropertyType == typeof(string)) + { + var func = EnumDeserializeActions[property.PropertyType]; + var prefix = property.GetCustomAttribute()?.Prefix ?? BinaryPacket.Prefix.None; + value = func(body, prefix); + } + else + { + var func = DeserializeActions[property.PropertyType]; + value = func(body); + } + property.SetValueByExpr(obj, value); + } + + return obj; + } + + public static object Deserialize(this BinaryPacket body, Type type) + { + var obj = type.CreateInstance(); + + foreach (var property in type.GetPropertiesFromCache()) + { + if (property.GetCustomAttribute() == null) continue; + + object value; + if (property.PropertyType == typeof(byte[]) || property.PropertyType == typeof(string)) + { + var func = EnumDeserializeActions[property.PropertyType]; + var prefix = property.GetCustomAttribute()?.Prefix ?? BinaryPacket.Prefix.None; + value = func(body, prefix); + } + else + { + var func = DeserializeActions[property.PropertyType]; + value = func(body); + } + property.SetValueByExpr(obj, value); + } + + return obj; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/BitConverter.cs b/Lagrange.Core/Utility/Binary/BitConverter.cs new file mode 100644 index 0000000..9b1b847 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/BitConverter.cs @@ -0,0 +1,93 @@ +using SysBitConverter = System.BitConverter; + +namespace Lagrange.Core.Utility.Binary; + +/// +/// Provide the same APIs as System.BitConverter, but supports both BigEndian and LittleEndian +/// +internal static class BitConverter +{ + public static byte[] GetBytes(short value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 8), (byte)value }; + + public static byte[] GetBytes(ushort value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 8), (byte)value }; + + public static byte[] GetBytes(int value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; + + public static byte[] GetBytes(uint value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; + + public static byte[] GetBytes(long value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 56), (byte)(value >> 48), (byte)(value >> 40), (byte)(value >> 32), (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; + + public static byte[] GetBytes(ulong value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 56), (byte)(value >> 48), (byte)(value >> 40), (byte)(value >> 32), (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; + + public static short ToInt16(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToInt16(value) : (short)((value[0] << 8) | value[1]); + + public static int ToInt32(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToInt32(value) : (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + + public static long ToInt64(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToInt64(value) : ((long)value[0] << 56) | ((long)value[1] << 48) | ((long)value[2] << 40) | ((long)value[3] << 32) | ((long)value[4] << 24) | ((long)value[5] << 16) | ((long)value[6] << 8) | value[7]; + + public static ushort ToUInt16(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToUInt16(value) : (ushort)((value[0] << 8) | value[1]); + + public static uint ToUInt32(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToUInt32(value) : ((uint)value[0] << 24) | ((uint)value[1] << 16) | ((uint)value[2] << 8) | value[3]; + + public static ulong ToUInt64(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToUInt64(value) : ((ulong)value[0] << 56) | ((ulong)value[1] << 48) | ((ulong)value[2] << 40) | ((ulong)value[3] << 32) | ((ulong)value[4] << 24) | ((ulong)value[5] << 16) | ((ulong)value[6] << 8) | value[7]; + + public static float ToSingle(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToSingle(value) : ToSingleBigEndian(value); + + public static double ToDouble(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToDouble(value) : ToDoubleBigEndian(value); + + private static unsafe float ToSingleBigEndian(ReadOnlySpan value) + { + var tmp = ToUInt32(value, false); + return *(float*)&tmp; + } + + private static unsafe double ToDoubleBigEndian(ReadOnlySpan value) + { + var tmp = ToUInt64(value, false); + return *(double*)&tmp; + } + + public static byte[] GetBytes(float value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : GetBytesBigEndian(value); + + public static byte[] GetBytes(double value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : GetBytesBigEndian(value); + + private static unsafe byte[] GetBytesBigEndian(float value) + { + var tmp = *(uint*)&value; + return GetBytes(tmp, false); + } + + private static unsafe byte[] GetBytesBigEndian(double value) + { + var tmp = *(ulong*)&value; + return GetBytes(tmp, false); + } + + public static byte[] GetBytes(bool value) + => SysBitConverter.GetBytes(value); + + public static bool ToBoolean(ReadOnlySpan value) + => SysBitConverter.ToBoolean(value); + + public static byte[] GetBytes(char value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.GetBytes(value) : new[] { (byte)(value >> 8), (byte)value }; + + public static char ToChar(ReadOnlySpan value, bool isLittleEndian = true) + => isLittleEndian ? SysBitConverter.ToChar(value) : (char)((value[0] << 8) | value[1]); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Compression/Common.cs b/Lagrange.Core/Utility/Binary/Compression/Common.cs new file mode 100644 index 0000000..4b0d7c4 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Compression/Common.cs @@ -0,0 +1,23 @@ +using System.IO.Compression; + +namespace Lagrange.Core.Utility.Binary.Compression; + +internal static class Common +{ + public static byte[] Deflate(byte[] data) + { + using var memoryStream = new MemoryStream(); + using var deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress); + deflateStream.Write(data, 0, data.Length); + deflateStream.Close(); + return memoryStream.ToArray(); + } + + public static byte[] Inflate(byte[] data) + { + using var memoryStream = new MemoryStream(); + using var deflateStream = new DeflateStream(memoryStream, CompressionMode.Decompress); + deflateStream.Write(data, 0, data.Length); + deflateStream.Close(); + return memoryStream.ToArray(); + } } \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Compression/ZCompression.cs b/Lagrange.Core/Utility/Binary/Compression/ZCompression.cs new file mode 100644 index 0000000..7645101 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Compression/ZCompression.cs @@ -0,0 +1,59 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO.Compression; +using System.Text; + +namespace Lagrange.Core.Utility.Binary.Compression; + +internal static class ZCompression +{ + public static byte[] ZCompress(byte[] data, byte[]? header = null) + { + using var stream = new MemoryStream(); + var deflate = Common.Deflate(data); + + stream.Write(header); + stream.WriteByte(0x78); // Zlib header + stream.WriteByte(0xDA); // Zlib header + + stream.Write(deflate.AsSpan()); + + var checksum = Adler32(data); + stream.Write(checksum.AsSpan()); + + return stream.ToArray(); + } + + public static byte[] ZCompress(string data, byte[]? header = null) => ZCompress(Encoding.UTF8.GetBytes(data), header); + + [SuppressMessage("ReSharper", "MustUseReturnValue")] + public static byte[] ZDecompress(byte[] data) + { + using var stream = new MemoryStream(data); + var header = new byte[2]; + stream.Read(header); + if (header[0] != 0x78 || header[1] != 0xDA) throw new InvalidDataException("Invalid Zlib header"); + + var deflate = new byte[stream.Length - stream.Position - 4]; + stream.Read(deflate); + + var checksum = new byte[4]; + stream.Read(checksum); + + var decompressed = Common.Inflate(deflate); + var checksum2 = Adler32(decompressed); + if (!checksum.AsSpan().SequenceEqual(checksum2.AsSpan())) throw new InvalidDataException("Invalid Zlib checksum"); + + return decompressed; + } + + private static byte[] Adler32(byte[] data) + { + uint a = 1, b = 0; + foreach (byte t in data) + { + a = (a + t) % 65521; + b = (b + a) % 65521; + } + return BitConverter.GetBytes((b << 16) | a, false); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/JceStruct/JceReader.cs b/Lagrange.Core/Utility/Binary/JceStruct/JceReader.cs new file mode 100644 index 0000000..bb67739 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/JceStruct/JceReader.cs @@ -0,0 +1,105 @@ +namespace Lagrange.Core.Utility.Binary.JceStruct; + +internal class JceReader +{ + private readonly BinaryPacket _packet; + + private bool _simpleListStruct; + + public JceReader(BinaryPacket packet) => _packet = packet; + + public JceReader(byte[] data) => _packet = new BinaryPacket(data); + + public JceStruct Deserialize(bool simpleListStruct = false) + { + _simpleListStruct = simpleListStruct; + var jceStruct = new JceStruct(); + + while (_packet.Remaining > 0) + { + var jceObject = ReadJceObject(out byte tag); + if (jceObject != null) jceStruct[tag] = jceObject; + else break; // represents struct end + } + + return jceStruct; + } + + private object? ReadJceObject(out byte tag) + { + var type = ReadType(out tag); + return type switch + { + JceType.Byte => _packet.ReadByte(), + JceType.Short => _packet.ReadShort(false), + JceType.Int => _packet.ReadInt(false), + JceType.Long => _packet.ReadLong(false), + JceType.Float => throw new NotImplementedException(), + JceType.Double => throw new NotImplementedException(), + JceType.StringByte => _packet.ReadString(BinaryPacket.Prefix.None | BinaryPacket.Prefix.Uint8), + JceType.StringInt => _packet.ReadString(BinaryPacket.Prefix.None | BinaryPacket.Prefix.Uint32), + JceType.Map => ReadJceMap(), + JceType.List => ReadJceList(), + JceType.StructBegin => new JceReader(_packet).Deserialize(_simpleListStruct), + JceType.StructEnd => null, + JceType.ZeroTag => (byte)0, + JceType.SimpleList => ReadJceSimpleList(), + _ => throw new InvalidOperationException() + }; + } + + private int ReadJceNumber() => ReadType(out _) switch + { + JceType.Byte => _packet.ReadByte(), + JceType.Short => _packet.ReadShort(false), + JceType.Int => _packet.ReadInt(false), + JceType.Long => (int)_packet.ReadLong(false), + JceType.ZeroTag => 0, + _ => throw new InvalidOperationException(), + }; + + private Dictionary ReadJceMap() + { + int count = ReadJceNumber(); + var map = new Dictionary(count); + for (int i = 0; i < count; i++) + { + var key = ReadJceObject(out _); + var value = ReadJceObject(out _); + if (key != null && value != null) map[key] = value; + } + + return map; + } + + private List ReadJceList() + { + int count = ReadJceNumber(); + var list = new List(count); + for (int i = 0; i < count; i++) + { + var value = ReadJceObject(out _); + if (value != null) list.Add(value); + } + + return list; + } + + private object ReadJceSimpleList() + { + _packet.Skip(1); + + var array = _packet.ReadBytes(ReadJceNumber()); + return _simpleListStruct ? new JceReader(array).Deserialize(true) : array; + } + + private JceType ReadType(out byte tag) + { + byte value = _packet.ReadByte(); // lower 4 bit is type, higher 4 bit is tag + byte type = (byte)(value & 0xF); // lower 4 bit + tag = (byte)(value >> 4); // higher 4 bit + + if (tag == 0xF) tag = (byte)(_packet.ReadByte() & 0xFF); + return (JceType)type; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/JceStruct/JceStruct.cs b/Lagrange.Core/Utility/Binary/JceStruct/JceStruct.cs new file mode 100644 index 0000000..926e405 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/JceStruct/JceStruct.cs @@ -0,0 +1,14 @@ +namespace Lagrange.Core.Utility.Binary.JceStruct; + +/// +/// JceStruct Entity class, which is derived from +/// JceStruct is the binary format with TTLV(Tag, Type, Length, Value) arrangement +/// TKey is , which represents the Tag of the field +/// TValue is , you should manually resolve the type +/// +internal class JceStruct : Dictionary +{ + public T GetValue (byte tag) => (T)this[tag]; + + public void SetValue(byte tag, T value) => this[tag] = value!; +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/JceStruct/JceType.cs b/Lagrange.Core/Utility/Binary/JceStruct/JceType.cs new file mode 100644 index 0000000..45b4df1 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/JceStruct/JceType.cs @@ -0,0 +1,22 @@ +namespace Lagrange.Core.Utility.Binary.JceStruct; + +internal enum JceType : byte +{ + /// Byte or Boolean + Byte = 0, + Short = 1, + Int = 2, + Long = 3, + Float = 4, + Double = 5, + /// Indicates that the length of the string is a single byte + StringByte = 6, + /// Indicates that the length of the string is a int + StringInt = 7, + Map = 8, + List = 9, + StructBegin = 10, + StructEnd = 11, + ZeroTag = 12, + SimpleList = 13, +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/JceStruct/JceWriter.cs b/Lagrange.Core/Utility/Binary/JceStruct/JceWriter.cs new file mode 100644 index 0000000..32b4533 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/JceStruct/JceWriter.cs @@ -0,0 +1,104 @@ +namespace Lagrange.Core.Utility.Binary.JceStruct; + +internal class JceWriter +{ + private readonly BinaryPacket _packet; + + private readonly JceStruct _jce; + public JceWriter(JceStruct jce) + { + _jce = jce; + _packet = new BinaryPacket(); + } + + public BinaryPacket Serialize() + { + foreach (var (tag, obj) in _jce) WriteType(ResolveType(obj), tag); + + return _packet; + } + + private void WriteJceObject(object obj) + { + + switch (ResolveType(obj)) + { + case JceType.Byte: + _packet.WriteByte((byte)obj); + break; + case JceType.Short: + _packet.WriteShort((short)obj, false); + break; + case JceType.Int: + _packet.WriteInt((int)obj, false); + break; + case JceType.Long: + _packet.WriteLong((long)obj, false); + break; + case JceType.Float: + throw new NotImplementedException(); + case JceType.Double: + throw new NotImplementedException(); + case JceType.StringByte: + _packet.WriteString((string)obj, BinaryPacket.Prefix.None | BinaryPacket.Prefix.Uint8); + break; + case JceType.StringInt: + _packet.WriteString((string)obj, BinaryPacket.Prefix.None | BinaryPacket.Prefix.Uint32); + break; + case JceType.Map: + WriteJceMap((Dictionary)obj); + break; + case JceType.List: + WriteJceList((List)obj); + break; + case JceType.StructBegin: + _packet.WritePacket(new JceWriter((JceStruct)obj).Serialize()); + break; + case JceType.StructEnd: + break; + case JceType.ZeroTag: + break; + default: + throw new InvalidOperationException(); + } + } + + private void WriteJceMap(Dictionary map) + { + _packet.WriteInt(map.Count, false); + foreach (var (key, value) in map) + { + WriteJceObject(key); + WriteJceObject(value); + } + } + + private void WriteJceList(List list) + { + _packet.WriteInt(list.Count, false); + foreach (var item in list) WriteJceObject(item); + } + + private void WriteType(JceType type, byte tag) + { + if (tag < 15) _packet.WriteByte((byte)((byte)type << 4 | tag)); + else _packet.WriteByte((byte)((byte)type << 4 | 0xF)).WriteByte(tag); + } + + private static JceType ResolveType(object? obj) => obj switch + { + byte => JceType.Byte, + bool => JceType.Byte, + short => JceType.Short, + int => JceType.Int, + long => JceType.Long, + float => JceType.Float, + double => JceType.Double, + string str => str.Length < 256 ? JceType.StringByte : JceType.StringInt, + Dictionary => JceType.Map, + List => JceType.List, + JceStruct => JceType.StructBegin, + null => JceType.StructEnd, + _ => throw new InvalidOperationException() + }; +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/JceStruct/UniPacket.cs b/Lagrange.Core/Utility/Binary/JceStruct/UniPacket.cs new file mode 100644 index 0000000..bcbb56c --- /dev/null +++ b/Lagrange.Core/Utility/Binary/JceStruct/UniPacket.cs @@ -0,0 +1,63 @@ +namespace Lagrange.Core.Utility.Binary.JceStruct; + +[Serializable] +internal class UniPacket +{ + public byte Version { get; set; } + + public byte PacketType { get; set; } + + public byte MessageType { get; set; } + + public int RequestId { get; set; } + + public string ServantName { get; set; } + + public string FuncName { get; set; } + + public JceStruct Buffer { get; set; } + + public byte Timeout { get; set; } + + public Dictionary Status { get; set; } + + public Dictionary Context { get; set; } + + public UniPacket(byte version, byte packetType, byte messageType, int requestId, string servantName, string funcName, + JceStruct buffer, byte timeout, Dictionary? status = null, Dictionary? context = null) + { + Version = version; + PacketType = packetType; + MessageType = messageType; + RequestId = requestId; + ServantName = servantName; + FuncName = funcName; + Buffer = buffer; + Timeout = timeout; + Status = status ?? new Dictionary(); + Context = context ?? new Dictionary(); + } + + public UniPacket(byte[] buffer) + { + var jce = new JceReader(buffer).Deserialize(); + + Version = (byte)jce[1]; + PacketType = (byte)jce[2]; + MessageType = (byte)jce[3]; + RequestId = (byte)jce[4]; + ServantName = (string)jce[5]; + FuncName = (string)jce[6]; + Buffer = new JceReader((byte[])jce[7]).Deserialize(true); + Timeout = (byte)jce[8]; + Status = Cast((Dictionary)jce[9]); + Context = Cast((Dictionary)jce[10]); + } + + private static Dictionary Cast(Dictionary dict) where TU : notnull + { + var result = new Dictionary(); + foreach (var (key, value) in dict) result[(TU)key] = (TV)value; + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvAttribute.cs b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvAttribute.cs new file mode 100644 index 0000000..c700f30 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvAttribute.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Utility.Binary.Tlv.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +internal class TlvAttribute : Attribute +{ + public readonly ushort TlvCommand; + + public readonly bool IsResponse; + + public TlvAttribute(ushort tlvCommand) + { + TlvCommand = tlvCommand; + IsResponse = false; + } + + public TlvAttribute(ushort tlvCommand, bool isResponse) + { + TlvCommand = tlvCommand; + IsResponse = isResponse; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvEncryptAttribute.cs b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvEncryptAttribute.cs new file mode 100644 index 0000000..75a6576 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvEncryptAttribute.cs @@ -0,0 +1,15 @@ +namespace Lagrange.Core.Utility.Binary.Tlv.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +internal class TlvEncryptAttribute : Attribute +{ + public TlvEncryptAttribute(KeyType type) => Type = type; + + public KeyType Type { get; } + + public enum KeyType + { + TgtgtKey, + Password + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvQrCodeAttribute.cs b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvQrCodeAttribute.cs new file mode 100644 index 0000000..2b18bb0 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/Attributes/TlvQrCodeAttribute.cs @@ -0,0 +1,21 @@ +namespace Lagrange.Core.Utility.Binary.Tlv.Attributes; + +[AttributeUsage(AttributeTargets.Class)] +internal class TlvQrCodeAttribute : Attribute +{ + public readonly ushort TlvCommand; + + public readonly bool IsResponse; + + public TlvQrCodeAttribute(ushort tlvCommand) + { + TlvCommand = tlvCommand; + IsResponse = false; + } + + public TlvQrCodeAttribute(ushort tlvCommand, bool isResponse) + { + TlvCommand = tlvCommand; + IsResponse = isResponse; + } +} diff --git a/Lagrange.Core/Utility/Binary/Tlv/TlvBody.cs b/Lagrange.Core/Utility/Binary/Tlv/TlvBody.cs new file mode 100644 index 0000000..6260c6b --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/TlvBody.cs @@ -0,0 +1,8 @@ +namespace Lagrange.Core.Utility.Binary.Tlv; + +internal abstract class TlvBody : BinaryPacket +{ + protected TlvBody() { } + + protected TlvBody(byte[] bytes) : base(bytes) { } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Tlv/TlvPacker.cs b/Lagrange.Core/Utility/Binary/Tlv/TlvPacker.cs new file mode 100644 index 0000000..d84e44e --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/TlvPacker.cs @@ -0,0 +1,143 @@ +using Lagrange.Core.Common; +using Lagrange.Core.Utility.Binary.Tlv.Attributes; +using Lagrange.Core.Utility.Crypto; +using Lagrange.Core.Utility.Extension; +using ProtoBuf; +using System.Reflection; + +namespace Lagrange.Core.Utility.Binary.Tlv; + +internal class TlvPacker +{ + private static readonly Dictionary Tlvs = new(); + private static readonly Dictionary TlvResps = new(); + private static readonly Dictionary TlvQrCodes = new(); + private static readonly Dictionary TlvQrCodeResps = new(); + + private static readonly Dictionary TlvEncrypt = new(); + + private readonly ServiceInjector _injector; + + static TlvPacker() + { + var assembly = Assembly.GetExecutingAssembly(); + + var tlvs = assembly.GetTypeByAttributes(out var tlvAttributes); + for (int i = 0; i < tlvs.Count; i++) + { + var attribute = tlvAttributes[i]; + (attribute.IsResponse ? TlvResps : Tlvs)[attribute.TlvCommand] = tlvs[i]; + + var encrypt = tlvs[i].GetCustomAttribute(); + if (encrypt != null) TlvEncrypt[tlvs[i]] = encrypt; + } + + var tlvQrCodes = assembly.GetTypeByAttributes(out var tlvQrCodeAttributes); + for (int i = 0; i < tlvQrCodes.Count; i++) + { + var attribute = tlvQrCodeAttributes[i]; + (attribute.IsResponse ? TlvQrCodeResps : TlvQrCodes)[attribute.TlvCommand] = tlvQrCodes[i]; + + var encrypt = tlvQrCodes[i].GetCustomAttribute(); + if (encrypt != null) TlvEncrypt[tlvQrCodes[i]] = encrypt; + } + } + + public TlvPacker(BotAppInfo appInfo, BotKeystore keystore, BotDeviceInfo deviceInfo) + { + _injector = new ServiceInjector(); + + _injector.AddService(appInfo); + _injector.AddService(keystore); + _injector.AddService(deviceInfo); + } + + public TlvPacket Pack(ushort cmd) + { + var tlvBody = (TlvBody)_injector.Inject(Tlvs[cmd]); + var encrypt = ResolveEncryption(Tlvs[cmd]); + return new TlvPacket(cmd, tlvBody, encrypt); + } + + public TlvPacket PackQrCode(ushort cmd) + { + var tlvBody = (TlvBody)_injector.Inject(TlvQrCodes[cmd]); + var encrypt = ResolveEncryption(TlvQrCodes[cmd]); + return new TlvPacket(cmd, tlvBody, encrypt); + } + + public BinaryPacket Pack(ushort[] cmd) + { + var packet = new BinaryPacket().WriteUshort((ushort)cmd.Length, false); + + foreach (var tlv in cmd) packet.WritePacket(Pack(tlv)); + return packet; + } + + public BinaryPacket PackQrCode(ushort[] cmd) + { + var packet = new BinaryPacket().WriteUshort((ushort)cmd.Length, false); + + foreach (var tlv in cmd) packet.WritePacket(PackQrCode(tlv)); + return packet; + } + + public static TlvPacket Pack(TlvBody tlv, ushort cmd) => new(cmd, tlv); + + public static TlvBody ReadTlvBody(ushort cmd, BinaryPacket packet) + { + if (!TlvResps.TryGetValue(cmd, out var type)) type = Tlvs[cmd]; + + if (type.GetCustomAttribute() != null) + { + using var stream = new MemoryStream(packet.ToArray()); + return (TlvBody)Serializer.Deserialize(type, stream); + } + + return (TlvBody)packet.Deserialize(type); + } + + public static TlvBody ReadTlvBodyQrCode(ushort cmd, BinaryPacket packet) + { + if (!TlvQrCodeResps.TryGetValue(cmd, out var type)) type = TlvQrCodes[cmd]; + + if (type.GetCustomAttribute() != null) + { + using var stream = new MemoryStream(packet.ToArray()); + return (TlvBody)Serializer.Deserialize(type, stream); + } + + return (TlvBody)packet.Deserialize(type); + } + + public static Dictionary ReadTlvCollections(BinaryPacket payload, bool isQrCode = false) + { + ushort tlvCount = payload.ReadUshort(false); + var tlvs = new Dictionary(tlvCount); + + for (int i = 0; i < tlvCount; i++) + { + ushort cmd = payload.ReadUshort(false); + ushort length = payload.ReadUshort(false); + + var packet = payload.ReadPacket(length); + var tlvBody = isQrCode ? ReadTlvBodyQrCode(cmd, packet) : ReadTlvBody(cmd, packet); + tlvs[cmd] = tlvBody; + } + + return tlvs; + } + + private (TeaImpl, byte[])? ResolveEncryption(Type type) + { + if (!TlvEncrypt.TryGetValue(type, out var encrypt)) return null; + + var keystore = _injector.GetService(); + return encrypt.Type switch + { + TlvEncryptAttribute.KeyType.TgtgtKey => (keystore.TeaImpl, keystore.Stub.TgtgtKey), + TlvEncryptAttribute.KeyType.Password => (keystore.TeaImpl, keystore.Stub.TgtgtKey), // TODO: Implement password encryption + _ => null + }; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Binary/Tlv/TlvPacket.cs b/Lagrange.Core/Utility/Binary/Tlv/TlvPacket.cs new file mode 100644 index 0000000..1194761 --- /dev/null +++ b/Lagrange.Core/Utility/Binary/Tlv/TlvPacket.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using Lagrange.Core.Utility.Crypto; +using ProtoBuf; + +namespace Lagrange.Core.Utility.Binary.Tlv; + +/// +/// Tlv is the structure that composed of T(TAG) L(LENGTH) V(VALUE) +/// +internal class TlvPacket : BinaryPacket +{ + [BinaryProperty] public ushort TlvCommand { get; } + [BinaryProperty] public ushort TlvBodyLength { get; } + + [BinaryProperty] public TlvBody TlvBody { get; } + + public TlvPacket(ushort tlvCommand, TlvBody tlvBody, (TeaImpl tea, byte[] key)? encrypt = null) + { + TlvCommand = tlvCommand; + TlvBodyLength = (ushort) tlvBody.Length; + TlvBody = tlvBody; + + Serialize(tlvBody, encrypt); + } + + private void Serialize(TlvBody tlvBody, (TeaImpl tea, byte[] key)? encrypt) + { + BinaryPacket packet; + if (tlvBody.GetType().GetCustomAttribute() != null) + { + using var stream = new MemoryStream(); + Serializer.Serialize(stream, tlvBody); + + packet = new BinaryPacket(stream.ToArray()); // Write V(VALUE) + } + else + { + packet = BinarySerializer.Serialize(tlvBody); // Write V(VALUE) + } + + Func func; + if (encrypt != null) + { + var (tea, key) = encrypt.Value; + func = () => new BinaryPacket(tea.Encrypt(packet.ToArray(), key)); + } + else + { + func = () => packet; + } + + WriteUshort(TlvCommand, false); // Write T(TAG) + Barrier(typeof(ushort), func, false); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/AesGcmImpl.cs b/Lagrange.Core/Utility/Crypto/AesGcmImpl.cs new file mode 100644 index 0000000..7c1ad6c --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/AesGcmImpl.cs @@ -0,0 +1,39 @@ +using System.Security.Cryptography; +using Lagrange.Core.Utility.Generator; + +namespace Lagrange.Core.Utility.Crypto; + +internal class AesGcmImpl : ICryptoImpl +{ + public byte[] Encrypt(byte[] data, byte[] key) + { + using var aes = new AesGcm(key); + var iv = ByteGen.GenRandomBytes(12); + var tag = new byte[16]; + var cipher = new byte[data.Length]; + aes.Encrypt(iv, data, cipher, tag); + + var result = new List(); + result.AddRange(iv); + result.AddRange(cipher); + result.AddRange(tag); + + return result.ToArray(); + } + + public byte[] Encrypt(byte[] data) => throw new InvalidOperationException(); + + public byte[] Decrypt(byte[] data, byte[] key) + { + using var aes = new AesGcm(key); + var iv = data[..12]; + var cipher = data[12..^16]; + var tag = data[^16..]; + var result = new byte[data.Length - 28]; + aes.Decrypt(iv, cipher, tag, result); + + return result; + } + + public byte[] Decrypt(byte[] data) => throw new InvalidOperationException(); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/EcdhImpl.Constants.cs b/Lagrange.Core/Utility/Crypto/EcdhImpl.Constants.cs new file mode 100644 index 0000000..db02ac0 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/EcdhImpl.Constants.cs @@ -0,0 +1,62 @@ +using Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +namespace Lagrange.Core.Utility.Crypto; + +internal partial class EcdhImpl +{ + public enum CryptMethod : uint + { + Secp192K1 = 1, + Prime256V1 = 0x0201 << 16 | 0x0131 + } + + public enum CryptId + { + Ecdh135 = 0x87, + Ecdh7 = 0x07 + } + + private static readonly Dictionary CurveTable = new() + { + { + CryptMethod.Prime256V1, new EcdhInfo + { + Curve = EllipticCurve.Prime256V1, + Id = CryptId.Ecdh135, + + PubKey = new byte[] // From NTQQ Binary, by hook + { + 0x04, + 0x9D, 0x14, 0x23, 0x33, 0x27, 0x35, 0x98, 0x0E, + 0xDA, 0xBE, 0x7E, 0x9E, 0xA4, 0x51, 0xB3, 0x39, + 0x5B, 0x6F, 0x35, 0x25, 0x0D, 0xB8, 0xFC, 0x56, + 0xF2, 0x58, 0x89, 0xF6, 0x28, 0xCB, 0xAE, 0x3E, + 0x8E, 0x73, 0x07, 0x79, 0x14, 0x07, 0x1E, 0xEE, + 0xBC, 0x10, 0x8F, 0x4E, 0x01, 0x70, 0x05, 0x77, + 0x92, 0xBB, 0x17, 0xAA, 0x30, 0x3A, 0xF6, 0x52, + 0x31, 0x3D, 0x17, 0xC1, 0xAC, 0x81, 0x5E, 0x79 + } + } + }, + { + CryptMethod.Secp192K1, new EcdhInfo + { + Curve = EllipticCurve.Secp192K1, + Id = CryptId.Ecdh7, + + PubKey = new byte[] + { + 0x04, 0x92, 0x8D, 0x88, 0x50, 0x67, 0x30, 0x88, + 0xB3, 0x43, 0x26, 0x4E, 0x0C, 0x6B, 0xAC, 0xB8, + 0x49, 0x6D, 0x69, 0x77, 0x99, 0xF3, 0x72, 0x11, + 0xDE, 0xB2, 0x5B, 0xB7, 0x39, 0x06, 0xCB, 0x08, + 0x9F, 0xEA, 0x96, 0x39, 0xB4, 0xE0, 0x26, 0x04, + 0x98, 0xB5, 0x1A, 0x99, 0x2D, 0x50, 0x81, 0x3D, + 0xA8 + } + } + } + }; + + private readonly record struct EcdhInfo(EllipticCurve Curve, CryptId Id, byte[] PubKey, byte[] SubPubKey); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/EcdhImpl.cs b/Lagrange.Core/Utility/Crypto/EcdhImpl.cs new file mode 100644 index 0000000..029cb66 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/EcdhImpl.cs @@ -0,0 +1,49 @@ +using Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +namespace Lagrange.Core.Utility.Crypto; + +internal partial class EcdhImpl : ICryptoImpl +{ + private readonly EcdhProvider _ecdhProvider; + + private readonly TeaImpl _teaImpl; + + public CryptMethod Method { get; } + + public CryptId MethodId { get; } + + public byte[] ShareKey { get; private set; } + + public EcdhImpl(CryptMethod method, bool isHash = true) + { + Method = method; + _teaImpl = new TeaImpl(); + + var crypt = CurveTable[method]; // Select the curve + + _ecdhProvider = new EcdhProvider(crypt.Curve); + MethodId = crypt.Id; + ShareKey = GenerateShared(crypt.PubKey, isHash); + } + + public byte[] GenerateShared(byte[] bobPublic, bool isHash = true) + { + var unpack = _ecdhProvider.UnpackPublic(bobPublic); + ShareKey = _ecdhProvider.KeyExchange(unpack, isHash); + + return ShareKey; + } + + public byte[] GetPublicKey(bool compress = true) => _ecdhProvider.PackPublic(compress); + + public byte[] Encrypt(byte[] data, byte[] key) + { + throw new NotImplementedException(); + } + + public byte[] Encrypt(byte[] data) => _teaImpl.Encrypt(data, ShareKey); + + public byte[] Decrypt(byte[] data) => _teaImpl.Decrypt(data, ShareKey); + + public byte[] Decrypt(byte[] data, byte[] key) => Decrypt(data); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/ICryptoImpl.cs b/Lagrange.Core/Utility/Crypto/ICryptoImpl.cs new file mode 100644 index 0000000..2e2b967 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/ICryptoImpl.cs @@ -0,0 +1,12 @@ +namespace Lagrange.Core.Utility.Crypto; + +internal interface ICryptoImpl +{ + public byte[] Encrypt(byte[] data, byte[] key); + + public byte[] Encrypt(byte[] data); + + public byte[] Decrypt(byte[] data, byte[] key); + + public byte[] Decrypt(byte[] data); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EcdhProvider.cs b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EcdhProvider.cs new file mode 100644 index 0000000..2ea9ae0 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EcdhProvider.cs @@ -0,0 +1,256 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +namespace Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +/// +/// EC Diffie-Hellman key exchange provider, implemented with unsafe code to enhance performance. +/// +internal sealed unsafe class EcdhProvider +{ + public EllipticCurve Curve { get; } + + private BigInteger Secret { get; set; } + + private EllipticPoint Public { get; set; } + + public EcdhProvider(EllipticCurve curve) + { + Curve = curve; + Secret = CreateSecret(); + Public = CreatePublic(Secret); + } + + public void SetSecret(BigInteger secret) + { + Secret = secret; + Public = CreatePublic(Secret); + } + + /// + /// Key exchange with bob + /// + /// + /// + public byte[] KeyExchange(EllipticPoint ecPub, bool isHash) + { + var shared = CreateShared(Secret, ecPub); + return PackShared(shared, isHash); + } + + public EllipticPoint UnpackPublic(byte[] publicKey) + { + int length = publicKey.Length; + if (length != Curve.Size * 2 + 1 && length != Curve.Size + 1) throw new Exception("Length does not match."); + + var x = new byte[Curve.Size]; // Teardown x + Buffer.BlockCopy(publicKey, 1, x, 0, Curve.Size); + Array.Reverse(x); // To LE + Array.Resize(ref x, x.Length + 1); // Append 0x00 + + // Not compressed + if (publicKey[0] == 0x04) + { + var y = new byte[Curve.Size]; // Teardown y + Buffer.BlockCopy(publicKey, Curve.Size + 1, y, 0, Curve.Size); + Array.Reverse(y); // To LE + Array.Resize(ref y, y.Length + 1); // Append 0x00 + + return new EllipticPoint(new BigInteger(x), new BigInteger(y)); + } + + var px = new BigInteger(x); + var x3 = px * px * px % Curve.P; + var ax = px * Curve.P; + var right = (x3 + ax + Curve.B) % Curve.P; + + var tmp = (Curve.P + 1) >> 2; + var py = BigInteger.ModPow(right, tmp, Curve.P); + + if (py.IsEven) + { + tmp = Curve.P; + tmp -= py; + py = tmp; + } + + return new EllipticPoint(px, py); + } + + + private byte[] PackShared(EllipticPoint ecShared, bool isHash) + { + var x = TakeReverse(ecShared.X.ToByteArray(), Curve.Size); + if (!isHash) return x; + + using var md5 = MD5.Create(); + return md5.ComputeHash(x[..Curve.PackSize]); + } + + /// + /// Unpack secret + /// + private BigInteger UnpackSecret(byte[] ecSec) + { + var length = ecSec.Length - 4; + if (length != ecSec[3]) throw new Exception("Length does not match."); + + var temp = new byte[length]; + Buffer.BlockCopy(ecSec, 4, temp, 0, length); + + return new BigInteger(temp); + } + + private byte[] PackSecret(BigInteger ecSec) + { + var result = ecSec.ToByteArray(); + var length = result.Length; + Array.Resize(ref result, length + 4); + Array.Reverse(result); + + result[3] = (byte)length; + + return result; + } + + private BigInteger GenerateSecret(EllipticPoint point) + { + BigInteger result; + var array = new byte[Curve.Size + 1]; + + do + { + RandomNumberGenerator.Fill(array); + array[Curve.Size] = 0; + result = new BigInteger(array); + } while (result < 1 || result >= Curve.N); + + return result; + } + + public byte[] PackPublic(bool compress = true) => PackPublic(Public, compress); + + public byte[] PackPublic(EllipticPoint ecPub, bool compress = true) + { + if (compress) + { + var result = ecPub.X.ToByteArray(); + if (result.Length == Curve.Size) Array.Resize(ref result, Curve.Size + 1); + + Array.Reverse(result); + result[0] = (byte) (ecPub.Y.IsEven ^ ecPub.Y.Sign < 0 ? 0x02 : 0x03); + return result; + } + + var x = TakeReverse(ecPub.X.ToByteArray(), Curve.Size); + var y = TakeReverse(ecPub.Y.ToByteArray(), Curve.Size); + var buffer = new byte [Curve.Size * 2 + 1]; + + buffer[0] = 0x04; + Buffer.BlockCopy(x, 0, buffer, 1, x.Length); + Buffer.BlockCopy(y, 0, buffer, y.Length + 1, x.Length); + + return buffer; + } + + private EllipticPoint CreatePublic(BigInteger ecSec) => CreateShared(ecSec, Curve.G); + + private BigInteger CreateSecret() + { + BigInteger result; + var array = new byte[Curve.Size + 1]; + + do + { + RandomNumberGenerator.Fill(array); + array[Curve.Size] = 0; + result = new BigInteger(array); + } while (result < 1 || result >= Curve.N); + + return result; + } + + private EllipticPoint CreateShared(BigInteger ecSec, EllipticPoint ecPub) + { + if (ecSec % Curve.N == 0 || ecPub.IsDefault) return default; + if (ecSec < 0) return CreateShared(-ecSec, -ecPub); + + if (!Curve.CheckOn(ecPub)) throw new Exception("Public key does not correct."); + + var pr = new EllipticPoint(); + var pa = ecPub; + while (ecSec > 0) + { + if ((ecSec & 1) > 0) pr = PointAdd(Curve, pr, pa); + + pa = PointAdd(Curve, pa, pa); + ecSec >>= 1; + } + + if (!Curve.CheckOn(pr)) throw new Exception("Unknown error."); + + return pr; + } + + private static EllipticPoint PointAdd(EllipticCurve curve, EllipticPoint p1, EllipticPoint p2) + { + if (p1.IsDefault) return p2; + if (p2.IsDefault) return p1; + if (!curve.CheckOn(p1) || !curve.CheckOn(p2)) throw new Exception(); + + var x1 = p1.X; + var x2 = p2.X; + var y1 = p1.Y; + var y2 = p2.Y; + BigInteger m; + + if (x1 == x2) + { + if (y1 == y2) m = (3 * x1 * x1 + curve.A) * ModInverse(y1 << 1, curve.P); + else return default; + } + else + { + m = (y1 - y2) * ModInverse(x1 - x2, curve.P); + } + + var xr = Mod(m * m - x1 - x2, curve.P); + var yr = Mod(m * (x1 - xr) - y1, curve.P); + var pr = new EllipticPoint(xr, yr); + + if (!curve.CheckOn(pr)) throw new Exception(); + return pr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte[] TakeReverse(byte[] array, int length) + { + var result = new byte[length]; + fixed (byte* resultPtr = result, arrayPtr = array) + { + for (int i = 0, j = length - 1; i < length; ++i, --j) resultPtr[i] = arrayPtr[j]; + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BigInteger ModInverse(BigInteger a, BigInteger p) + { + if (a < 0) return p - ModInverse(-a, p); + + var g = BigInteger.GreatestCommonDivisor(a, p); + if (g != 1) throw new Exception("Inverse does not exist."); + + return BigInteger.ModPow(a, p - 2, p); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BigInteger Mod(BigInteger a, BigInteger b) + { + var result = a % b; + if (result < 0) result += b; + return result; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticCurve.cs b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticCurve.cs new file mode 100644 index 0000000..2f3a848 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticCurve.cs @@ -0,0 +1,100 @@ +using System.Numerics; + +namespace Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +/// +/// Represents an elliptic curve in the form of y^2 = x^3 + ax + b (mod p), used in EC Diffie-Hellman key exchange. +/// +internal readonly struct EllipticCurve +{ + public static readonly EllipticCurve Secp192K1 = new() + { + P = new BigInteger(new byte[] + { + 0x37, 0xEE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 + }), + A = 0, + B = 3, + G = new EllipticPoint + { + X = new BigInteger(new byte[] + { + 0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D, 0x34, 0xF4, 0xB7, 0x80, + 0x02, 0x7D, 0xB0, 0x26, 0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB, 0 + }), + Y = new BigInteger(new byte[] + { + 0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40, 0x34, 0x86, 0xBE, 0x15, + 0xD0, 0x63, 0x41, 0x84, 0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B, 0 + }) + }, + N = new BigInteger(new byte[] + { + 0x8D, 0xFD, 0xDE, 0x74, 0x6A, 0x46, 0x69, 0x0F, 0x17, 0xFC, 0xF2, 0x26, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 + }), + H = 1, + PackSize = 24, + Size = 24 + }; + + public static readonly EllipticCurve Prime256V1 = new() + { + P = new BigInteger(new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0 + }), + A = new BigInteger(new byte[] + { + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0 + }), + B = new BigInteger(new byte[] + { + 0x4B, 0x60, 0xD2, 0x27, 0x3E, 0x3C, 0xCE, 0x3B, 0xF6, 0xB0, 0x53, 0xCC, 0xB0, 0x06, 0x1D, 0x65, + 0xBC, 0x86, 0x98, 0x76, 0x55, 0xBD, 0xEB, 0xB3, 0xE7, 0x93, 0x3A, 0xAA, 0xD8, 0x35, 0xC6, 0x5A, 0 + }), + G = new EllipticPoint + { + X = new BigInteger(new byte[] + { + 0x96, 0xC2, 0x98, 0xD8, 0x45, 0x39, 0xA1, 0xF4, 0xA0, 0x33, 0xEB, 0X2D, 0x81, 0x7D, 0x03, 0x77, + 0xF2, 0x40, 0xA4, 0x63, 0xE5, 0xE6, 0xBC, 0xF8, 0x47, 0x42, 0x2C, 0xE1, 0xF2, 0xD1, 0x17, 0x6B, 0 + }), + Y = new BigInteger(new byte[] + { + 0xF5, 0x51, 0xBF, 0x37, 0x68, 0x40, 0xB6, 0xCB, 0xCE, 0x5E, 0x31, 0x6B, 0x57, 0x33, 0xCE, 0x2B, + 0x16, 0x9E, 0x0F, 0x7C, 0x4A, 0xEB, 0xE7, 0x8E, 0x9B, 0x7F, 0x1A, 0xFE, 0xE2, 0x42, 0xE3, 0x4F, 0 + }) + }, + N = new BigInteger(new byte[] + { + 0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0 + }), + H = 1, + Size = 32, + PackSize = 16 + }; + + public BigInteger P { get; private init; } + + public BigInteger A { get; private init; } + + public BigInteger B { get; private init; } + + public EllipticPoint G { get; private init; } + + public BigInteger N { get; private init; } + + public BigInteger H { get; private init; } + + public int Size { get; private init; } + + public int PackSize { get; private init; } + + public bool CheckOn(EllipticPoint point) => + (point.Y * point.Y - point.X * point.X * point.X - A * point.X - B) % P == 0; +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticPoint.cs b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticPoint.cs new file mode 100644 index 0000000..6dfe094 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/Provider/Ecdh/EllipticPoint.cs @@ -0,0 +1,35 @@ +using System.Numerics; + +namespace Lagrange.Core.Utility.Crypto.Provider.Ecdh; + +/// +/// The Elliptic Point used in EC Diffie-Hellman key exchange. +/// +internal struct EllipticPoint : IEquatable +{ + public BigInteger X { get; set; } + + public BigInteger Y { get; set; } + + public bool IsDefault => X.IsZero && Y.IsZero; + + public EllipticPoint(BigInteger x, BigInteger y) + { + X = x; + Y = y; + } + + public bool Equals(EllipticPoint other) => other.X.Equals(X) && other.Y.Equals(Y); + + public override bool Equals(object? obj) => obj is EllipticPoint other && Equals(other); + + public override int GetHashCode() => Y.GetHashCode() + X.GetHashCode(); + + public override string ToString() => $"({X:X}, {Y:X})"; + + public static bool operator ==(EllipticPoint left, EllipticPoint right) => left.Equals(right); + + public static bool operator !=(EllipticPoint left, EllipticPoint right) => !(left == right); + + public static EllipticPoint operator -(EllipticPoint p) => new(-p.X, -p.Y); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs b/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs new file mode 100644 index 0000000..b6b4874 --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/Provider/Tea/TeaProvider.cs @@ -0,0 +1,140 @@ +using System.Runtime.CompilerServices; + +namespace Lagrange.Core.Utility.Crypto.Provider.Tea; + +internal static class TeaProvider +{ + private const long Delta = 2654435769L; // 0x9E3779B9, Fibonacci's hashing constant. + + private const long SumMax = (Delta << 4) & uint.MaxValue; // 0x9E3779B9 * 16 = 0x3C6EF35F, max sum value. + + public static byte[] Encrypt(byte[] data, byte[] key) + { + if (key.Length < 16) throw new Exception(); + + int inputLength = data.Length; + int fill = ((8 - ((inputLength + 10) & 7)) & 7) + 2; + int length = fill + inputLength + 8; + + byte[] plain = new byte[length]; + byte[] cipher = new byte[length]; + byte[] plainXorPrev = new byte[8]; + byte[] tempCipher = new byte[8]; + plain[0] = (byte)(RandomByte(248) | (fill - 2)); + for (int i = 1; i <= fill; ++i) plain[i] = RandomByte(); + + Buffer.BlockCopy(data, 0, plain, fill + 1, inputLength); + for (int i = 0; i < length; i += 8) + { + byte[] plainXor = Xor8(plain, tempCipher, i); + tempCipher = Xor8(EnCipher(plainXor, key), plainXorPrev); + plainXorPrev = plainXor; + Buffer.BlockCopy(tempCipher, 0, cipher, i, 8); + } + + return cipher; + } + + public static byte[] Decrypt(byte[] data, byte[] key) + { + if (key.Length < 16) throw new Exception(); + + int length = data.Length; + if ((length & 7) != 0 || (length >> 4) == 0) throw new Exception("Invalid cipher data length."); + + byte[] plain = new byte[length]; + byte[] plainSub = new byte[8]; + for (int i = 0; i < length; i += 8) // Decrypt data. + { + plainSub = DeCipher(Xor8(data, plainSub, i), key); + Buffer.BlockCopy(Xor8(plainSub, data, 0, i - 8), 0, plain, i, 8); + } + + for (int i = length - 7; i < length; ++i) // Verify that the last 7 bytes are 0. + { + if (plain[i] != 0) throw new Exception("Verification failed."); + } + + int from = (plain[0] & 7) + 3; // Extract valid data. + byte[] output = new byte[length - from - 7]; + Buffer.BlockCopy(plain, from, output, 0, output.Length); + return output; + } + + private static byte[] EnCipher(byte[] data, byte[] key) + { + byte[] array = new byte[8]; + long sum = 0; + long y = ReadUInt32(data, 0); + long z = ReadUInt32(data, 4); + long a = ReadUInt32(key, 0); + long b = ReadUInt32(key, 4); + long c = ReadUInt32(key, 8); + long d = ReadUInt32(key, 12); + for (int i = 0; i < 16; ++i) + { + sum += Delta; + sum &= uint.MaxValue; + y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); + y &= uint.MaxValue; + z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); + z &= uint.MaxValue; + } + + WriteUInt32(array, 0, (uint)y); + WriteUInt32(array, 4, (uint)z); + return array; + } + + private static byte[] DeCipher(byte[] data, byte[] key, long index = 0) + { + byte[] array = new byte[8]; + long sum = SumMax; + long y = ReadUInt32(data, (int)index); + long z = ReadUInt32(data, (int)index + 4); + long a = ReadUInt32(key, 0); + long b = ReadUInt32(key, 4); + long c = ReadUInt32(key, 8); + long d = ReadUInt32(key, 12); + for (int i = 0; i < 16; ++i) + { + z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); + z &= uint.MaxValue; + y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); + y &= uint.MaxValue; + sum -= Delta; + sum &= uint.MaxValue; + } + + WriteUInt32(array, 0, (uint)y); + WriteUInt32(array, 4, (uint)z); + return array; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe byte[] Xor8(byte[] a, byte[] b, int ai = 0, int bi = 0) + { + if (bi < 0) return a; + + byte[] r = new byte[8]; + fixed (byte* ap = a, bp = b, rp = r) *(long*)rp = *(long*)(ap + ai) ^ *(long*)(bp + bi); + + return r; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte RandomByte(int max = byte.MaxValue) => (byte) (Random.Shared.Next() & max); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteUInt32(byte[] data, int index, uint value) + { + for (int i = 0; i < 4; ++i) data[index + i] = (byte)(value >> (24 - i * 8)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint ReadUInt32(byte[] data, int index) + { + fixed (byte* dp = data) return (uint)(dp[index] << 24 | dp[index + 1] << 16 | dp[index + 2] << 8 | dp[index + 3]); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Crypto/TeaImpl.cs b/Lagrange.Core/Utility/Crypto/TeaImpl.cs new file mode 100644 index 0000000..21a844b --- /dev/null +++ b/Lagrange.Core/Utility/Crypto/TeaImpl.cs @@ -0,0 +1,24 @@ +using Lagrange.Core.Utility.Crypto.Provider.Tea; +using Lagrange.Core.Utility.Extension; + +namespace Lagrange.Core.Utility.Crypto; + +internal class TeaImpl : ICryptoImpl +{ + public byte[] Encrypt(byte[] data, byte[] key) + { + // Console.WriteLine("Data: " + data.Hex() + $" Key: {key.Hex()}"); + return TeaProvider.Encrypt(data, key); + } + + public byte[] Encrypt(byte[] data) => Encrypt(data, new byte[16]); + + public byte[] Decrypt(byte[] data, byte[] key) + { + var decipher = TeaProvider.Decrypt(data, key); + // Console.WriteLine("Decrypted Data: " + decipher.Hex() + $" Key: {key.Hex()}"); + return decipher; + } + + public byte[] Decrypt(byte[] data) => Decrypt(data, new byte[16]); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/ByteExtension.cs b/Lagrange.Core/Utility/Extension/ByteExtension.cs new file mode 100644 index 0000000..28a3663 --- /dev/null +++ b/Lagrange.Core/Utility/Extension/ByteExtension.cs @@ -0,0 +1,59 @@ +using System.Text; +using System.Security.Cryptography; + +namespace Lagrange.Core.Utility.Extension; + +internal static class ByteExtension +{ + public static string Hex(this byte[] bytes, bool lower = false, bool space = false) + { + var sb = new StringBuilder(); + foreach (byte b in bytes) + { + sb.Append(b.ToString(lower ? "x2" : "X2")); + if (space) sb.Append(' '); + } + return sb.ToString(); + } + + public static string Hex(this ReadOnlySpan bytes, bool lower = false, bool space = false) + { + var sb = new StringBuilder(); + foreach (byte b in bytes) + { + sb.Append(b.ToString(lower ? "x2" : "X2")); + if (space) sb.Append(' '); + } + return sb.ToString(); + } + + public static string Md5(this byte[] bytes, bool lower = false) + { + using var md5 = MD5.Create(); + var hash = md5.ComputeHash(bytes); + return hash.Hex(lower); + } + + public static async Task Md5Async(this byte[] bytes, bool lower = false) + { + using var md5 = MD5.Create(); + using var stream = new MemoryStream(bytes); + var hash = await md5.ComputeHashAsync(stream); + return hash.Hex(lower); + } + + public static string ComputeBlockMd5(this byte[] bytes, int offset, int count, bool lower = false) + { + using var md5 = MD5.Create(); + var hash = md5.ComputeHash(bytes, offset, count); + return hash.Hex(lower); + } + + public static async Task ComputeBlockMd5Async(this byte[] bytes, int offset, int count, bool lower = false) + { + using var md5 = MD5.Create(); + using var stream = new MemoryStream(bytes, offset, count); + var hash = await md5.ComputeHashAsync(stream); + return hash.Hex(lower); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/ExpressionExt.cs b/Lagrange.Core/Utility/Extension/ExpressionExt.cs new file mode 100644 index 0000000..27e927f --- /dev/null +++ b/Lagrange.Core/Utility/Extension/ExpressionExt.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using Expr = System.Linq.Expressions.Expression; + +namespace Lagrange.Core.Utility.Extension; + +internal static class ExpressionExt +{ + private static readonly Dictionary Properties = new(); + + private static readonly Dictionary> Getters = new(); + + private static readonly Dictionary> Setters = new(); + + public static PropertyInfo[] GetPropertiesFromCache(this Type type) + { + if (!Properties.TryGetValue(type, out var properties)) + { + properties = type.GetProperties(); + Properties[type] = properties; + return properties; + } + + return properties; + } + + public static void SetValueByExpr(this PropertyInfo property, object? obj, object val) + { + if (obj == null) return; + + if (!Setters.TryGetValue(property, out var lambda)) + { + var expr = Expr.Parameter(typeof(object), "obj"); + var valExpr = Expr.Parameter(typeof(object), "val"); + + var castExpr = Expr.Convert(expr, property.DeclaringType ?? throw new InvalidOperationException()); + var castValExpr = Expr.Convert(valExpr, property.PropertyType); + + var propertyExpr = Expr.Property(castExpr, property); + var assignExpr = Expr.Assign(propertyExpr, castValExpr); + var lambdaExpr = Expr.Lambda>(assignExpr, expr, valExpr); + + lambda = lambdaExpr.Compile(); + Setters[property] = lambda; + } + + lambda(obj, val); + } + + public static object? GetValueByExpr(this PropertyInfo property, object? obj) + { + if (obj == null) return null; + + if (!Getters.TryGetValue(property, out var lambda)) + { + var expr = Expr.Parameter(typeof(object), "obj"); + + var castExpr = Expr.Convert(expr, property.DeclaringType ?? throw new InvalidOperationException()); + var propertyExpr = Expr.Property(castExpr, property); + var castPropertyExpr = Expr.Convert(propertyExpr, typeof(object)); + + var lambdaExpr = Expr.Lambda>(castPropertyExpr, expr); + + lambda = lambdaExpr.Compile(); + Getters[property] = lambda; + } + + return lambda(obj); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/ReflectionExt.cs b/Lagrange.Core/Utility/Extension/ReflectionExt.cs new file mode 100644 index 0000000..010335e --- /dev/null +++ b/Lagrange.Core/Utility/Extension/ReflectionExt.cs @@ -0,0 +1,54 @@ +using System.Reflection; + +namespace Lagrange.Core.Utility.Extension; + +internal static class ReflectionExt +{ + public static List GetTypeByAttributes(this Assembly assembly, out List attributes) where T : Attribute + { + var list = new List(); + attributes = new List(); + + foreach (var type in assembly.GetTypes()) + { + var attribute = type.GetCustomAttribute(); + if (attribute != null) + { + list.Add(type); + attributes.Add(attribute); + } + } + + return list; + } + + public static List GetTypeWithMultipleAttributes(this Assembly assembly, out List attributes) where T : Attribute + { + var list = new List(); + attributes = new List(); + + foreach (var type in assembly.GetTypes()) + { + var attribute = type.GetCustomAttributes().ToArray(); + if (attribute.Length > 0) + { + list.Add(type); + attributes.Add(attribute); + } + } + + return list; + } + + public static List GetDerivedTypes(this Assembly assembly) where T : class => + assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(T))).ToList(); + + public static List GetImplementations(this Assembly assembly) => + assembly.GetTypes().Where(type => type.GetInterfaces().Contains(typeof(T))).ToList(); + + public static MemberInfo[] GetMemberInfoByAttribute(this Type type) where T : Attribute => + type.GetMembers().Where(x => x.GetCustomAttribute() != null).ToArray(); + + public static PropertyInfo[] GetPropertyInfoByAttribute(this Type type) where T : Attribute => + type.GetProperties().Where(x => x.GetCustomAttribute() != null).ToArray(); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/SocketExt.cs b/Lagrange.Core/Utility/Extension/SocketExt.cs new file mode 100644 index 0000000..89b5973 --- /dev/null +++ b/Lagrange.Core/Utility/Extension/SocketExt.cs @@ -0,0 +1,40 @@ +using System.Net.Sockets; + +namespace Lagrange.Core.Utility.Extension; + +internal static class SocketExt +{ + public static ValueTask ReceiveFullyAsync(this Socket socket, byte[] buffer, CancellationToken token = default) + => socket.ReceiveFullyAsync(new Memory(buffer, 0, buffer.Length), token); + + public static ValueTask ReceiveFullyAsync(this Socket socket, byte[] buffer, int offset, int size, CancellationToken token = default) + { + if (offset + size > buffer.Length) throw new ArgumentException("Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection."); + var memory = new Memory(buffer, offset, size); + return socket.ReceiveFullyAsync(memory, token); + } + + public static ValueTask ReceiveFullyAsync(this Socket socket, Memory memory, CancellationToken token = default) + { + var vt = socket.ReceiveAsync(memory, SocketFlags.None, token); + if (vt.IsCompletedSuccessfully) + { + int received = vt.Result; + if (received == memory.Length) return default; + vt = new ValueTask(received); // ValueTask.Result may not be consumed twice, so we recreate a new ValueTask to store the result. + } + return Await(socket, memory, vt, token); + + static async ValueTask Await(Socket socket, Memory memory, ValueTask recvTask, CancellationToken token) + { + while (true) + { + int n = await recvTask; + if (n < 1) throw new SocketException(10054); + if (n == memory.Length) return; + memory = memory[n..]; + recvTask = socket.ReceiveAsync(memory, SocketFlags.None, token); + } + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/StringExtension.cs b/Lagrange.Core/Utility/Extension/StringExtension.cs new file mode 100644 index 0000000..3472943 --- /dev/null +++ b/Lagrange.Core/Utility/Extension/StringExtension.cs @@ -0,0 +1,24 @@ +using System.Globalization; + +namespace Lagrange.Core.Utility.Extension; + +internal static class StringExtension +{ + public static byte[] UnHex(this string hex) + { + if (hex.Length % 2 != 0) throw new ArgumentException("Invalid hex string"); + + byte[] bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) bytes[i / 2] = byte.Parse(hex.Substring(i, 2), NumberStyles.HexNumber); + return bytes; + } + + public static byte[] UnHex(this ReadOnlySpan hex) + { + if (hex.Length % 2 != 0) throw new ArgumentException("Invalid hex string"); + + byte[] bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) bytes[i / 2] = byte.Parse(hex.Slice(i, 2), NumberStyles.HexNumber); + return bytes; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/TaskAwaitExt.cs b/Lagrange.Core/Utility/Extension/TaskAwaitExt.cs new file mode 100644 index 0000000..40ea3b3 --- /dev/null +++ b/Lagrange.Core/Utility/Extension/TaskAwaitExt.cs @@ -0,0 +1,34 @@ +using System.Runtime.CompilerServices; + +namespace Lagrange.Core.Utility.Extension; + +internal static class TaskAwaiters +{ + /// + /// Returns an awaitable/awaiter that will ensure the continuation is executed + /// asynchronously on the thread pool, even if the task is already completed + /// by the time the await occurs. Effectively, it is equivalent to awaiting + /// with ConfigureAwait(false) and then queuing the continuation with Task.Run, + /// but it avoids the extra hop if the continuation already executed asynchronously. + /// + public static ForceAsyncAwaiter ForceAsync(this Task task) => new(task); +} + +internal readonly struct ForceAsyncAwaiter : ICriticalNotifyCompletion +{ + private readonly Task _task; + + internal ForceAsyncAwaiter(Task task) => _task = task; + + public ForceAsyncAwaiter GetAwaiter() => this; + + public bool IsCompleted => false; // the purpose of this type is to always force a continuation + + public void GetResult() => _task.GetAwaiter().GetResult(); + + public void OnCompleted(Action action) => + _task.ConfigureAwait(false).GetAwaiter().OnCompleted(action); + + public void UnsafeOnCompleted(Action action) => + _task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(action); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Extension/TypeExtension.cs b/Lagrange.Core/Utility/Extension/TypeExtension.cs new file mode 100644 index 0000000..8bbade0 --- /dev/null +++ b/Lagrange.Core/Utility/Extension/TypeExtension.cs @@ -0,0 +1,28 @@ +using System.Linq.Expressions; + +namespace Lagrange.Core.Utility.Extension; + +internal static class TypeExtension +{ + private static readonly Dictionary CachedExpressions = new(); + + public static object CreateInstance(this Type type, bool cache = true) + { + if (!cache) return Activator.CreateInstance(type) ?? throw new InvalidOperationException(); + + if (CachedExpressions.TryGetValue(type, out var instance)) + { + var unbox = (Func)instance; + return unbox(); + } + + var expression = Expression.New(type); + var lambda = Expression.Lambda>(expression); + var func = lambda.Compile(); + + CachedExpressions[type] = func; + return func(); + } + + public static T CreateInstance(this Type type, bool cache = true) => (T)CreateInstance(type, cache); +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Generator/ByteGen.cs b/Lagrange.Core/Utility/Generator/ByteGen.cs new file mode 100644 index 0000000..a833238 --- /dev/null +++ b/Lagrange.Core/Utility/Generator/ByteGen.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; + +namespace Lagrange.Core.Utility.Generator; + +internal static class ByteGen +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] GenRandomBytes(int length) + { + var bytes = new byte[length]; + for (int i = 0; i < length; ++i) bytes[i] = (byte) Random.Shared.Next(0, 256); + return bytes; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Generator/StringGen.cs b/Lagrange.Core/Utility/Generator/StringGen.cs new file mode 100644 index 0000000..bed0d5b --- /dev/null +++ b/Lagrange.Core/Utility/Generator/StringGen.cs @@ -0,0 +1,36 @@ +using System.Text; + +namespace Lagrange.Core.Utility.Generator; + +internal static class StringGen +{ + private const string Hex = "1234567890abcdef"; + + public static string GenerateTrace() + { + var sb = new StringBuilder(55); + + sb.Append(0.ToString("X2")); // 2 chars + sb.Append('-'); // 1 char + + for (var i = 0; i < 32; i++) sb.Append(Hex[Random.Shared.Next(0, Hex.Length)]); // 32 chars + sb.Append('-'); // 1 char + + for (var i = 0; i < 16; i++) sb.Append(Hex[Random.Shared.Next(0, Hex.Length)]); // 16 chars + sb.Append('-'); // 1 char + + sb.Append(01.ToString("X2")); // 2 chars + + return sb.ToString(); + } + + public static string GenerateHex(int length) => RandomGenerate(Hex, length); + + public static string RandomGenerate(string table, int length) + { + var sb = new StringBuilder(length); + for (var i = 0; i < length; i++) sb.Append(table[Random.Shared.Next(0, Hex.Length)]); + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Network/Http.cs b/Lagrange.Core/Utility/Network/Http.cs new file mode 100644 index 0000000..b720e21 --- /dev/null +++ b/Lagrange.Core/Utility/Network/Http.cs @@ -0,0 +1,45 @@ +using System.Net.Http.Headers; +using System.Web; + +namespace Lagrange.Core.Utility.Network; + +internal static class Http +{ + private static readonly HttpClient Client = new(); + + public static async Task GetAsync(string url, Dictionary? param = null) + { + var uriBuilder = new UriBuilder(url); + + var query = HttpUtility.ParseQueryString(uriBuilder.Query); + if (param != null) + { + foreach (var (key, value) in param) query[key] = value; + } + uriBuilder.Query = query.ToString(); + + var response = await Client.GetAsync(uriBuilder.Uri); + return await response.Content.ReadAsStringAsync(); + } + + /// + /// Post form data to url, using application/x-www-form-urlencoded + /// + /// + /// + /// + public static async Task PostAsync(string url, Dictionary? payload = null) + { + var content = new FormUrlEncodedContent(payload ?? new Dictionary()); + var response = await Client.PostAsync(url, content); + return await response.Content.ReadAsStringAsync(); + } + + public static async Task PostAsync(string url, byte[] payload, string content) + { + var contentData = new ByteArrayContent(payload); + contentData.Headers.ContentType = new MediaTypeHeaderValue(content); + var response = await Client.PostAsync(url, contentData); + return await response.Content.ReadAsByteArrayAsync(); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Network/Icmp.cs b/Lagrange.Core/Utility/Network/Icmp.cs new file mode 100644 index 0000000..4bf2701 --- /dev/null +++ b/Lagrange.Core/Utility/Network/Icmp.cs @@ -0,0 +1,13 @@ +using System.Net.NetworkInformation; + +namespace Lagrange.Core.Utility.Network; + +internal static class Icmp +{ + public static async Task PingAsync(Uri hostIp, int timeout = 1000) + { + using var ping = new Ping(); + var reply = await ping.SendPingAsync(hostIp.Host, timeout); + return reply?.RoundtripTime ?? long.MaxValue; + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/ServiceInjector.cs b/Lagrange.Core/Utility/ServiceInjector.cs new file mode 100644 index 0000000..32487f5 --- /dev/null +++ b/Lagrange.Core/Utility/ServiceInjector.cs @@ -0,0 +1,101 @@ +namespace Lagrange.Core.Utility; + +/// +/// A class that can inject services into other classes. +/// +internal sealed class ServiceInjector +{ + private readonly Dictionary _services = new(); + + public bool AddService(T service) + { + if (service == null) return false; + + _services.Add(typeof(T), service); + return true; + } + + public bool RemoveService() => _services.Remove(typeof(T)); + + public T GetService() => (T)_services[typeof(T)]; + + public bool TryGetService(out T? service) + { + if (_services.TryGetValue(typeof(T), out object? obj)) + { + service = (T)obj; + return true; + } + + service = default; + return false; + } + + public bool ContainsService() => _services.ContainsKey(typeof(T)); + + public void Clear() => _services.Clear(); + + public object this[Type type] + { + get => _services[type]; + set => _services[type] = value; + } + + /// + /// Injects a service into the specified object. + /// + /// The parameters exclude the service that should be injected + /// The class that should be injected to have new instance + /// + public TInject Inject(object?[]? parameters = null) + { + var constructors = typeof(TInject).GetConstructors(); + if (constructors.Length != 1) throw new InvalidOperationException("The class must have only one constructor"); + + var constructor = constructors[0]; + var parametersInfo = constructor.GetParameters(); + var constructed = new object?[parametersInfo.Length]; + for (int i = 0; i < parametersInfo.Length; i++) + { + var parameter = parametersInfo[i]; + + if (_services.TryGetValue(parameter.ParameterType, out var service)) + { + constructed[i] = service; + } + else + { + if (parameters == null) constructed[i] = null; + else constructed[i] = parameters[i]; + } + } + + return (TInject)constructor.Invoke(constructed); + } + + public object Inject(Type type, object?[]? parameters = null) + { + var constructors = type.GetConstructors(); + if (constructors.Length != 1) throw new InvalidOperationException("The class must have only one constructor"); + + var constructor = constructors[0]; + var parametersInfo = constructor.GetParameters(); + var constructed = new object?[parametersInfo.Length]; + for (int i = 0; i < parametersInfo.Length; i++) + { + var parameter = parametersInfo[i]; + + if (_services.TryGetValue(parameter.ParameterType, out var service)) + { + constructed[i] = service; + } + else + { + if (parameters == null) constructed[i] = null; + else constructed[i] = parameters[i]; + } + } + + return constructor.Invoke(constructed); + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/Signature.cs b/Lagrange.Core/Utility/Signature.cs new file mode 100644 index 0000000..418001e --- /dev/null +++ b/Lagrange.Core/Utility/Signature.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Lagrange.Core.Utility.Extension; +using Lagrange.Core.Utility.Network; + +#pragma warning disable CS8618 + +namespace Lagrange.Core.Utility; + +internal static class Signature +{ + private const string Url = ""; + + private const string Key = ""; + + private static bool _available = true; + + /// + /// Get O3Signature + /// + public static byte[] GetSignature(string cmd, uint seq, byte[] body) + { + if (!_available || string.IsNullOrEmpty(Url)) return new byte[20]; // Dummy signature + + try + { + var payload = new Dictionary + { + { "cmd", cmd }, + { "seq", seq.ToString() }, + { "src", body.Hex() }, + { "key", Key } + }; + string response = Http.GetAsync(Url, payload).GetAwaiter().GetResult(); + var json = JsonSerializer.Deserialize(response); + + return json?["value"]?["sign"]?.ToJsonString()[1..^1].UnHex() ?? new byte[20]; + } + catch (Exception) + { + _available = false; + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{nameof(Signature)}] Failed to get signature, using dummy signature"); + return new byte[20]; // Dummy signature + } + } +} \ No newline at end of file diff --git a/Lagrange.Core/Utility/TaskScheduler.cs b/Lagrange.Core/Utility/TaskScheduler.cs new file mode 100644 index 0000000..a784d38 --- /dev/null +++ b/Lagrange.Core/Utility/TaskScheduler.cs @@ -0,0 +1,299 @@ +using System.Collections.Concurrent; + +// ReSharper disable InvertIf +// ReSharper disable FunctionNeverReturns +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable +// ReSharper disable ArrangeObjectCreationWhenTypeNotEvident + +namespace Lagrange.Core.Utility; + +internal class TaskScheduler : IDisposable +{ + private class Schedule + { + public const int Infinity = int.MaxValue; + + /// + /// Task name + /// + public string Name { get; } + + /// + /// Task action + /// + public Action Action { get; } + + /// + /// Task interval + /// + public int Interval { get; } + + /// + /// Execute how many times + /// + public int Times { get; } + + /// + /// Task remain interval + /// + internal int RemainInterval { get; set; } + + /// + /// Task remain times + /// + internal int RemainTimes { get; set; } + + public Schedule(string name, Action action, int interval, int times) + { + Name = name; + Action = action; + Interval = interval; + Times = times; + } + + public override int GetHashCode() + => Name.GetHashCode(); + } + + private class ScheduleSorter : IComparer + { + public int Compare(Schedule? x, Schedule? y) => x!.RemainInterval - y!.RemainInterval; + } + + private readonly Thread _taskThread; + private readonly ConcurrentDictionary _taskDict; + private readonly ManualResetEvent _taskNotify; + private bool _taskThreadExit; + + public TaskScheduler() + { + _taskDict = new ConcurrentDictionary(); + _taskNotify = new ManualResetEvent(false); + _taskThread = new Thread(SchedulerThread); + _taskThreadExit = false; + + _taskThread.Start(); // Start task thread + } + + public void Dispose() + { + _taskThreadExit = true; + _taskNotify.Set(); + _taskThread.Join(); + } + + /// + /// Scheduler thread + /// + private void SchedulerThread() + { + int minInterval; + DateTime startTime; + List todoList; + List taskTable; + ScheduleSorter taskSorter; + { + todoList = new(); + taskTable = new(); + taskSorter = new(); + + while (!_taskThreadExit) // Scheduler steps + { + Update(); + WaitOne(); + DoTheTask(); + } + } + + void Update() // Select the task + { + if (_taskDict.Count != taskTable.Count) + { + // Try get the new tasks from outside + foreach (var (_, value) in _taskDict) + { + if (taskTable.Find(i => i.GetHashCode() == value.GetHashCode()) == null) + { + // Set the value + value.RemainTimes = value.Times; + value.RemainInterval = value.Interval; + + // Join the queue + taskTable.Add(value); + // LogI(TAG, $"Join the task => {key}"); + } + } + } + + // Sort the task + taskTable.Sort(taskSorter); + + // Pickup minimal interval to wait + minInterval = 0; + if (taskTable.Count > 0) + { + minInterval = taskTable[0].RemainInterval; + } + } + + // Wait the task and + // calculate the remaining + void WaitOne() + { + startTime = DateTime.Now; + { + // Set sleep time + var sleepTime = minInterval == 0 + ? int.MaxValue + : minInterval; + + // Note: If the sleep time less than zero + // We need to run the task immediately + if (sleepTime >= 0) + { + // Cache broken + // Please ref issue #129 + if (_taskDict.Count != taskTable.Count) return; + + // Reset event and wait + _taskNotify.Reset(); + _taskNotify.WaitOne(sleepTime); + } + } + var passedTime = (int)((DateTime.Now - startTime).TotalSeconds * 1000); + + todoList.Clear(); // Calculate the remain + for (var i = taskTable.Count - 1; i >= 0; --i) + { + // Reduce the interval + taskTable[i].RemainInterval -= passedTime; + + // Task timeout + if (taskTable[i].RemainInterval <= 0) + { + if (taskTable[i].Times != Schedule.Infinity) // Reduce the counter + { + taskTable[i].RemainTimes -= 1; + } + else + { + taskTable[i].RemainInterval = taskTable[i].Interval; // Reset the interval + } + + // Mark the tasks + // we are going to do + todoList.Add(taskTable[i]); + } + + if (taskTable[i].RemainTimes <= 0) // Cleanup died tasks + { + _taskDict.TryRemove(taskTable[i].Name, out _); + taskTable.RemoveAt(i); + } + } + } + + void DoTheTask() // Do the task + { + foreach (var i in todoList) + { + // Perform tasks + ThreadPool.QueueUserWorkItem(_ => + { + try + { + i.Action.Invoke(); + } + catch (Exception) + { + // LogE(TAG, "Task failed."); + // LogE(TAG, e); + } + }); + } + } + } + + /// + /// Cancel the task + /// + /// [In] Task identity name + /// + public void Cancel(string name) + { + // Remove the task + if (_taskDict.TryGetValue(name, out var task)) + { + task.RemainTimes = -1; // Cancel the task + task.RemainInterval = Schedule.Infinity; + + _taskNotify.Set(); // Wakeup the scheduler thread + } + } + + /// + /// Execute the task with a specific interval + /// + /// [In] Task identity name + /// [In] Interval in milliseconds + /// [In] Execute times + /// [In] Callback action + /// + /// + public void Interval(string name, int interval, int times, Action action) + { + var task = new Schedule(name, action, interval, times); + + if (_taskDict.ContainsKey(name)) _taskDict[name] = task; // Check duplicate + + _taskDict.TryAdd(name, task); // Add new task + _taskNotify.Set(); // Wakeup the scheduler thread + } + + /// + /// Execute the task with a specific interval + /// + /// [In] Task identity name + /// [In] Interval in milliseconds + /// [In] Callback action + /// + /// + public void Interval(string name, int interval, Action action) + => Interval(name, interval, Schedule.Infinity, action); + + /// + /// Execute the task once + /// + /// [In] Task identity name + /// [In] Delay time in milliseconds + /// [In] Callback action + /// + /// + public void RunOnce(string name, int delay, Action action) + => Interval(name, delay, 1, action); + + /// + /// Execute the task once + /// + /// [In] Task identity name + /// [In] Execute date + /// [In] Callback action + /// + /// + public void RunOnce(string name, DateTime date, Action action) + => RunOnce(name, (int) ((date - DateTime.Now).TotalSeconds * 1000), action); + + /// + /// Trigger a task to run + /// + /// + public void Trigger(string name) + { + if (!_taskDict.TryGetValue(name, out var task)) return; // Check the task + + task.RemainInterval = 0; // Set interval to 0 + _taskNotify.Set(); // Wakeup the scheduler thread + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Constant.cs b/Lagrange.OneBot/Constant.cs new file mode 100644 index 0000000..df79ac1 --- /dev/null +++ b/Lagrange.OneBot/Constant.cs @@ -0,0 +1,10 @@ +namespace Lagrange.OneBot; + +internal static class Constant +{ + public const string OneBotImpl = "Lagrange.OneBot"; + + public const int OneBotProtocolVersion = 12; + + public const string OneBotImplVersion = "0.0.1-alpha"; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Generic/OneBotBot.cs b/Lagrange.OneBot/Core/Entity/Generic/OneBotBot.cs new file mode 100644 index 0000000..0abebae --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Generic/OneBotBot.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.Generic; + +[Serializable] +[MessagePackObject] +internal class OneBotBot +{ + public OneBotBot(uint uin, bool online) + { + Self = new OneBotSelf(uin); + Online = online; + } + + [JsonPropertyName("self")] [Key("self")] public OneBotSelf Self { get; set; } + + [JsonPropertyName("online")] [Key("online")] public bool Online { get; set; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Generic/OneBotRequest.cs b/Lagrange.OneBot/Core/Entity/Generic/OneBotRequest.cs new file mode 100644 index 0000000..1b9fd52 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Generic/OneBotRequest.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.Generic; + +[Serializable] +[MessagePackObject] +internal abstract class OneBotRequest +{ + protected OneBotRequest(string type, string detailType, string subType) + { + Id = Guid.NewGuid(); + Time = DateTimeOffset.Now.ToUnixTimeSeconds(); + Type = type; + DetailType = detailType; + SubType = subType; + } + + [JsonPropertyName("id")] [Key("id")] public Guid Id { get; set; } + + [JsonPropertyName("time")] [Key("time")] public float Time { get; set; } + + [JsonPropertyName("type")] [Key("type")] public string Type { get; set; } + + [JsonPropertyName("detail_type")] [Key("detail_type")] public string DetailType { get; set; } + + [JsonPropertyName("sub_type")] [Key("sub_type")] public string SubType { get; set; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Generic/OneBotSelf.cs b/Lagrange.OneBot/Core/Entity/Generic/OneBotSelf.cs new file mode 100644 index 0000000..266245d --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Generic/OneBotSelf.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.Generic; + +[Serializable] +[MessagePackObject] +internal class OneBotSelf +{ + public OneBotSelf(uint userId) => UserId = userId.ToString(); + + [JsonPropertyName("platform")] [Key("platform")] public string Platform { get; set; } = "qq"; + + [JsonPropertyName("user_id")] [Key("user_id")] public string UserId { get; set; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/Generic/OneBotVersion.cs b/Lagrange.OneBot/Core/Entity/Generic/OneBotVersion.cs new file mode 100644 index 0000000..8a3cd4e --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/Generic/OneBotVersion.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.Generic; + +[Serializable] +[MessagePackObject] +internal class OneBotVersion +{ + [JsonPropertyName("impl")] [Key("impl")] public string Impl { get; set; } = Constant.OneBotImpl; + + [JsonPropertyName("version")] [Key("version")] public string Version { get; set; } = Constant.OneBotImplVersion; + + [JsonPropertyName("onebot_version")] [Key("onebot_version")] public int OneBotVersionNumber { get; set; } = Constant.OneBotProtocolVersion; +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/MetaEvent/Connect.cs b/Lagrange.OneBot/Core/Entity/MetaEvent/Connect.cs new file mode 100644 index 0000000..fc276eb --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/MetaEvent/Connect.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using Lagrange.OneBot.Core.Entity.Generic; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.MetaEvent; + +internal class Connect : OneBotRequest +{ + private const string EventType = "meta"; + + private const string EventDetailType = "connect"; + + private const string EventSubType = ""; + + public Connect() : base(EventType, EventDetailType, EventSubType) { } + + [JsonPropertyName("version")] [Key("version")] public OneBotVersion Version { get; set; } = new(); +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/MetaEvent/HeartBeat.cs b/Lagrange.OneBot/Core/Entity/MetaEvent/HeartBeat.cs new file mode 100644 index 0000000..1eb742a --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/MetaEvent/HeartBeat.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using Lagrange.OneBot.Core.Entity.Generic; +using MessagePack; + +namespace Lagrange.OneBot.Core.Entity.MetaEvent; + +internal class HeartBeat : OneBotRequest +{ + private const string EventType = "meta"; + + private const string EventDetailType = "heartbeat"; + + private const string EventSubType = ""; + + public HeartBeat(long interval) : base(EventType, EventDetailType, EventSubType) => Interval = interval; + + [JsonPropertyName("interval")] [Key("interval")] public long Interval { get; } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Entity/MetaEvent/StatusUpdate.cs b/Lagrange.OneBot/Core/Entity/MetaEvent/StatusUpdate.cs new file mode 100644 index 0000000..49c29f3 --- /dev/null +++ b/Lagrange.OneBot/Core/Entity/MetaEvent/StatusUpdate.cs @@ -0,0 +1,17 @@ +using Lagrange.OneBot.Core.Entity.Generic; + +namespace Lagrange.OneBot.Core.Entity.MetaEvent; + +internal class StatusUpdate : OneBotRequest +{ + private const string EventType = "meta"; + + private const string EventDetailType = "status_update"; + + private const string EventSubType = ""; + + public StatusUpdate() : base(EventType, EventDetailType, EventSubType) + { + + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Core/Network/WebSocket.cs b/Lagrange.OneBot/Core/Network/WebSocket.cs new file mode 100644 index 0000000..55cc6ed --- /dev/null +++ b/Lagrange.OneBot/Core/Network/WebSocket.cs @@ -0,0 +1,56 @@ +using System.Net.Sockets; + +namespace Lagrange.OneBot.Core.Network; + +/// +/// Provide a simple WebSocket client and server +/// +internal class WebSocket : IDisposable +{ + private readonly Socket _socket; + + private readonly Thread _receiveLoop; + + public bool IsConnected => _socket.Connected; + + public event MessageReceivedEventHandler? OnMessageReceived; + + public delegate void MessageReceivedEventHandler(WebSocket sender, byte[] data); + + public WebSocket() + { + _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _receiveLoop = new Thread(() => + { + while (_socket.Connected) + { + var buffer = new byte[1024 * 1024 * 64]; // max 64MB + int length = _socket.Receive(buffer); + if (length > 0) + { + var data = buffer[..length]; + OnMessageReceived?.Invoke(this, data); + } + } + }); + } + + public async Task ConnectAsync(string host, int port) + { + await _socket.ConnectAsync(host, port); + return true; + } + + public async Task SendAsync(byte[] data) + { + await _socket.SendAsync(data, SocketFlags.None); + return true; + } + + public void Dispose() + { + _socket.Disconnect(false); // The thread will exit automatically + _socket.Dispose(); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Lagrange.OneBot.csproj b/Lagrange.OneBot/Lagrange.OneBot.csproj new file mode 100644 index 0000000..bf8f722 --- /dev/null +++ b/Lagrange.OneBot/Lagrange.OneBot.csproj @@ -0,0 +1,21 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Lagrange.OneBot/LagrangeApp.cs b/Lagrange.OneBot/LagrangeApp.cs new file mode 100644 index 0000000..9a8aeb7 --- /dev/null +++ b/Lagrange.OneBot/LagrangeApp.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Lagrange.OneBot; + +public class LagrangeApp : IHost +{ + private readonly IHost _hostApp; + + public IServiceProvider Services => _hostApp.Services; + + public ILogger Logger { get; } + + internal LagrangeApp(IHost host) + { + _hostApp = host; + Logger = Services.GetRequiredService>(); + } + + public async Task StartAsync(CancellationToken cancellationToken = new()) + { + Logger.LogInformation("Lagrange.OneBot Implementation has started"); + await _hostApp.StartAsync(cancellationToken); + } + + public async Task StopAsync(CancellationToken cancellationToken = new()) + { + Logger.LogInformation("Lagrange.OneBot Implementation has stopped"); + await _hostApp.StopAsync(cancellationToken); + } + + public void Dispose() + { + _hostApp.Dispose(); + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/LagrangeAppBuilder.cs b/Lagrange.OneBot/LagrangeAppBuilder.cs new file mode 100644 index 0000000..70c7d8c --- /dev/null +++ b/Lagrange.OneBot/LagrangeAppBuilder.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Lagrange.OneBot; + +public sealed class LagrangeAppBuilder +{ + private IServiceCollection Services => _hostAppBuilder.Services; + + private ConfigurationManager Configuration => _hostAppBuilder.Configuration; + + private readonly HostApplicationBuilder _hostAppBuilder; + + internal LagrangeAppBuilder(string[] args) + { + _hostAppBuilder = new HostApplicationBuilder(args); + } + + public LagrangeAppBuilder ConfigureConfiguration(string path, bool optional = false, bool reloadOnChange = false) + { + Configuration.AddJsonFile(path, optional, reloadOnChange); + return this; + } + + public LagrangeApp Build() + { + var app = new LagrangeApp(_hostAppBuilder.Build()); + return app; + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Program.cs b/Lagrange.OneBot/Program.cs new file mode 100644 index 0000000..cff4ead --- /dev/null +++ b/Lagrange.OneBot/Program.cs @@ -0,0 +1,21 @@ +using System.Text; +using Lagrange.OneBot.Utility; +using Microsoft.Extensions.Hosting; + +namespace Lagrange.OneBot; + +internal abstract class Program +{ + public static void Main(string[] args) + { + Console.OutputEncoding = Encoding.UTF8; + Console.InputEncoding = Encoding.UTF8; + + QrCodeHelper.Output("https://txz.qq.com/p?k=1GF4r45MPwoy6ZSJSuRdlwyKlKOleKyi&f=1600001615"); + + var hostBuilder = new LagrangeAppBuilder(args) + .ConfigureConfiguration("appsettings.json", false, true); + + hostBuilder.Build().Run(); + } +} \ No newline at end of file diff --git a/Lagrange.OneBot/Utility/QrCodeHelper.cs b/Lagrange.OneBot/Utility/QrCodeHelper.cs new file mode 100644 index 0000000..7634faf --- /dev/null +++ b/Lagrange.OneBot/Utility/QrCodeHelper.cs @@ -0,0 +1,56 @@ +using ZXing; +using ZXing.QrCode; + +namespace Lagrange.OneBot.Utility; + +public static class QrCodeHelper +{ + public static void Output(string text) + { + const int threshold = 180; + + var writer = new ZXing.SkiaSharp.BarcodeWriter + { + Format = BarcodeFormat.QR_CODE, + Options = new QrCodeEncodingOptions + { + Width = 33, + Height = 33, + Margin = 1 + } + }; + var image = writer.Write(text); + var points = new int[image.Width, image.Height]; + + for (var i = 0; i < image.Width; i++) + { + for (var j = 0; j < image.Height; j++) + { + var color = image.GetPixel(i, j); + points[i, j] = color.Blue <= threshold ? 1 : 0; + } + } + + + for (var i = 0; i < image.Width; i++) + { + for (var j = 0; j < image.Height; j++) + { + if (points[i, j] == 0) + { + Console.BackgroundColor = ConsoleColor.Black; + Console.ForegroundColor = ConsoleColor.Black; + } + else + { + Console.BackgroundColor = ConsoleColor.White; + Console.ForegroundColor = ConsoleColor.White; + } + Console.Write(" "); + Console.ResetColor(); + } + Console.Write("\n"); + } + } + +} \ No newline at end of file diff --git a/Lagrange.Signature/Lagrange.Signature.csproj b/Lagrange.Signature/Lagrange.Signature.csproj new file mode 100644 index 0000000..2917b6f --- /dev/null +++ b/Lagrange.Signature/Lagrange.Signature.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Lagrange.Signature/Program.cs b/Lagrange.Signature/Program.cs new file mode 100644 index 0000000..ec8e4f4 --- /dev/null +++ b/Lagrange.Signature/Program.cs @@ -0,0 +1,6 @@ +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.MapGet("/", () => "Hello World!"); + +app.Run(); \ No newline at end of file diff --git a/Lagrange.Signature/Properties/launchSettings.json b/Lagrange.Signature/Properties/launchSettings.json new file mode 100644 index 0000000..3e01a60 --- /dev/null +++ b/Lagrange.Signature/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:20608", + "sslPort": 44313 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5261", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7078;http://localhost:5261", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Lagrange.Signature/appsettings.Development.json b/Lagrange.Signature/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Lagrange.Signature/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Lagrange.Signature/appsettings.json b/Lagrange.Signature/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/Lagrange.Signature/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}