From e5ed1b8c3a0f8a1c3d93823a1cfc090d2d393e0e Mon Sep 17 00:00:00 2001
From: sandtechnology <a1294790523@hotmail.com>
Date: Fri, 25 Dec 2020 11:08:38 +0800
Subject: [PATCH] Support decode SvcRespRegister

---
 .../protocol/data/jce/SvcRespRegister.kt      |  27 ++++
 .../network/protocol/data/proto/Oidb0x769.kt  | 150 +++++++++++-------
 .../network/protocol/packet/login/StatSvc.kt  |  22 ++-
 3 files changed, 140 insertions(+), 59 deletions(-)
 create mode 100644 mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt

diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt
new file mode 100644
index 000000000..ab806210a
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt
@@ -0,0 +1,27 @@
+package net.mamoe.mirai.internal.network.protocol.data.jce
+
+import kotlinx.serialization.Serializable
+import net.mamoe.mirai.internal.utils.io.JceStruct
+import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
+
+
+@Serializable
+internal class SvcRespRegister(
+    @JvmField @TarsId(0) val uin: Long = 0L,
+    @JvmField @TarsId(1) val bid: Long = 0L,
+    @JvmField @TarsId(2) val replyCode: Byte = 0,
+    @JvmField @TarsId(3) val result: String = "",
+    @JvmField @TarsId(4) val serverTime: Long = 0L,
+    @JvmField @TarsId(5) val logQQ: Byte = 0,
+    @JvmField @TarsId(6) val needKik: Byte = 0,
+    @JvmField @TarsId(7) val updateFlag: Byte = 0,
+    @JvmField @TarsId(8) val timeStamp: Long = 0L,
+    @JvmField @TarsId(9) val crashFlag: Byte? = 0,
+    @JvmField @TarsId(10) val clientIP: String = "",
+    @JvmField @TarsId(11) val iClientPort: Int = 0,
+    @JvmField @TarsId(12) val iHelloInterval: Int = 300,
+    @JvmField @TarsId(13) val iLargeSeq: Long = 0L,
+    @JvmField @TarsId(14) val largeSeqUpdate: Byte = 0,
+    @JvmField @TarsId(15) val bytes_0x769_rspBody: ByteArray? = null,
+    @JvmField @TarsId(16) val iStatus: Int? = 0
+) : JceStruct
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt
index 83ebfbbff..7efcb52aa 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt
@@ -7,90 +7,134 @@
  *  https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("unused", "SpellCheckingInspection")
+
 package net.mamoe.mirai.internal.network.protocol.data.proto
 
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.protobuf.ProtoNumber
+import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.internal.utils.io.ProtoBuf
-import kotlin.jvm.JvmField
 
-internal class Oidb0x769 {
+@Serializable
+internal class Oidb0x769 : ProtoBuf {
     @Serializable
-    internal class RequestBody(
-        @ProtoNumber(1) @JvmField val rpt_config_list: List<ConfigSeq>
-        // @SerialId(2) @JvmField val msg_device_info: DeviceInfo,
-        // @SerialId(3) @JvmField val str_info: String = "",
-        // @SerialId(4) @JvmField val province: String,
-        // @SerialId(5) @JvmField val city: String,
-        // @SerialId(6) @JvmField val req_debug_msg: Int = 0,
-        // @SerialId(101) @JvmField val query_uin_package_usage_req: QueryUinPackageUsageReq
+    internal class Camera(
+        @JvmField @ProtoNumber(1) val primary: Long = 0L,
+        @JvmField @ProtoNumber(2) val secondary: Long = 0L,
+        @JvmField @ProtoNumber(3) val flash: Boolean = false
     ) : ProtoBuf
 
     @Serializable
-    internal class QueryUinPackageUsageReq(
-        @ProtoNumber(1) @JvmField val type: Int,
-        @ProtoNumber(2) @JvmField val uinFileSize: Long = 0
+    internal class Config(
+        @JvmField @ProtoNumber(1) val type: Int = 0,
+        @JvmField @ProtoNumber(2) val version: Int = 0,
+        @JvmField @ProtoNumber(3) val contentList: List<String> = emptyList(),
+        @JvmField @ProtoNumber(4) val debugMsg: String = "",
+        @JvmField @ProtoNumber(5) val msgContentList: List<Content> = emptyList()
     ) : ProtoBuf
 
     @Serializable
     internal class ConfigSeq(
-        @ProtoNumber(1) @JvmField val type: Int, // uint
-        @ProtoNumber(2) @JvmField val version: Int // uint
+        @JvmField @ProtoNumber(1) val type: Int = 0,
+        @JvmField @ProtoNumber(2) val version: Int = 0
     ) : ProtoBuf
 
     @Serializable
-    internal class DeviceInfo(
-        @ProtoNumber(1) @JvmField val brand: String,
-        @ProtoNumber(2) @JvmField val model: String
-        //@SerialId(3) @JvmField val os: OS,
-        //@SerialId(4) @JvmField val cpu: CPU,
-        //@SerialId(5) @JvmField val memory: Memory,
-        //@SerialId(6) @JvmField val storage: Storage,
-        //@SerialId(7) @JvmField val screen: Screen,
-        //@SerialId(8) @JvmField val camera: Camera
-    ) : ProtoBuf
-
-    @Serializable
-    internal class OS(
-        @ProtoNumber(1) @JvmField val type: Int = 1,
-        @ProtoNumber(2) @JvmField val version: String,
-        @ProtoNumber(3) @JvmField val sdk: String,
-        @ProtoNumber(4) @JvmField val kernel: String,
-        @ProtoNumber(5) @JvmField val rom: String
-    ) : ProtoBuf
-
-    @Serializable
-    internal class Camera(
-        @ProtoNumber(1) @JvmField val primary: Long,
-        @ProtoNumber(2) @JvmField val secondary: Long,
-        @ProtoNumber(3) @JvmField val flag: Boolean
+    internal class Content(
+        @JvmField @ProtoNumber(1) val taskId: Int = 0,
+        @JvmField @ProtoNumber(2) val compress: Int = 0,
+        @JvmField @ProtoNumber(10) val content: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
     @Serializable
     internal class CPU(
-        @ProtoNumber(1) @JvmField val model: String,
-        @ProtoNumber(2) @JvmField val frequency: Int,
-        @ProtoNumber(3) @JvmField val cores: Int
+        @JvmField @ProtoNumber(1) val model: String = "",
+        @JvmField @ProtoNumber(2) val cores: Int = 0,
+        @JvmField @ProtoNumber(3) val frequency: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    internal class DeviceInfo(
+        @JvmField @ProtoNumber(1) val brand: String = "",
+        @JvmField @ProtoNumber(2) val model: String = "",
+        @JvmField @ProtoNumber(3) val os: OS? = null,
+        @JvmField @ProtoNumber(4) val cpu: CPU? = null,
+        @JvmField @ProtoNumber(5) val memory: Memory? = null,
+        @JvmField @ProtoNumber(6) val storage: Storage? = null,
+        @JvmField @ProtoNumber(7) val screen: Screen? = null,
+        @JvmField @ProtoNumber(8) val camera: Camera? = null
     ) : ProtoBuf
 
     @Serializable
     internal class Memory(
-        @ProtoNumber(1) @JvmField val total: Int,
-        @ProtoNumber(2) @JvmField val process: Int
+        @JvmField @ProtoNumber(1) val total: Long = 0L,
+        @JvmField @ProtoNumber(2) val process: Long = 0L
+    ) : ProtoBuf
+
+    @Serializable
+    internal class OS(
+        //1 IOS | 2 ANDROID | 3 OTHER
+        @JvmField @ProtoNumber(1) val type: Int /* enum */ = 1,
+        @JvmField @ProtoNumber(2) val version: String = "",
+        @JvmField @ProtoNumber(3) val sdk: String = "",
+        @JvmField @ProtoNumber(4) val kernel: String = "",
+        @JvmField @ProtoNumber(5) val rom: String = ""
+    ) : ProtoBuf
+
+    @Serializable
+    internal class QueryUinPackageUsageReq(
+        @JvmField @ProtoNumber(1) val type: Int = 0,
+        @JvmField @ProtoNumber(2) val uinFileSize: Long = 0L
+    ) : ProtoBuf
+
+    @Serializable
+    internal class QueryUinPackageUsageRsp(
+        @JvmField @ProtoNumber(1) val status: Int = 0,
+        @JvmField @ProtoNumber(2) val leftUinNum: Long = 0L,
+        @JvmField @ProtoNumber(3) val maxUinNum: Long = 0L,
+        @JvmField @ProtoNumber(4) val proportion: Int = 0,
+        @JvmField @ProtoNumber(10) val uinPackageUsedList: List<UinPackageUsedInfo> = emptyList()
+    ) : ProtoBuf
+
+    @Serializable
+    internal class ReqBody(
+        @JvmField @ProtoNumber(1) val configList: List<ConfigSeq> = emptyList(),
+        @JvmField @ProtoNumber(2) val msgDeviceInfo: DeviceInfo? = null,
+        @JvmField @ProtoNumber(3) val info: String = "",
+        @JvmField @ProtoNumber(4) val province: String = "",
+        @JvmField @ProtoNumber(5) val city: String = "",
+        @JvmField @ProtoNumber(6) val reqDebugMsg: Int = 0,
+        @JvmField @ProtoNumber(101) val queryUinPackageUsageReq: QueryUinPackageUsageReq? = null
+    ) : ProtoBuf
+
+    @Serializable
+    internal class RspBody(
+        @JvmField @ProtoNumber(1) val result: Int = 0,
+        @JvmField @ProtoNumber(2) val configList: List<Config> = emptyList(),
+        @JvmField @ProtoNumber(101) val queryUinPackageUsageRsp: QueryUinPackageUsageRsp? = null
     ) : ProtoBuf
 
     @Serializable
     internal class Screen(
-        @ProtoNumber(1) @JvmField val model: String,
-        @ProtoNumber(2) @JvmField val width: Int,
-        @ProtoNumber(3) @JvmField val height: Int,
-        @ProtoNumber(4) @JvmField val dpi: Int,
-        @ProtoNumber(5) @JvmField val multiTouch: Boolean
+        @JvmField @ProtoNumber(1) val model: String = "",
+        @JvmField @ProtoNumber(2) val width: Int = 0,
+        @JvmField @ProtoNumber(3) val height: Int = 0,
+        @JvmField @ProtoNumber(4) val dpi: Int = 0,
+        @JvmField @ProtoNumber(5) val multiTouch: Boolean = false
     ) : ProtoBuf
 
     @Serializable
     internal class Storage(
-        @ProtoNumber(1) @JvmField val builtin: Int,
-        @ProtoNumber(2) @JvmField val external: Int
+        @JvmField @ProtoNumber(1) val builtin: Long = 0L,
+        @JvmField @ProtoNumber(2) val external: Long = 0L
     ) : ProtoBuf
-}
\ No newline at end of file
+
+    @Serializable
+    internal class UinPackageUsedInfo(
+        @JvmField @ProtoNumber(1) val ruleId: Int = 0,
+        @JvmField @ProtoNumber(2) val author: String = "",
+        @JvmField @ProtoNumber(3) val url: String = "",
+        @JvmField @ProtoNumber(4) val uinNum: Long = 0L
+    ) : ProtoBuf
+}
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt
index abee85264..db1e69135 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt
@@ -94,6 +94,20 @@ internal class StatSvc {
             override fun toString(): String = "Response(StatSvc.register)"
         }
 
+        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
+            val packet = readUniPacket(SvcRespRegister.serializer())
+            if (packet.updateFlag.toInt() == 1) {
+                //TODO 加载好友列表
+            }
+            if (packet.largeSeqUpdate.toInt() == 1) {
+                //TODO 刷新好友列表
+            }
+            packet.iHelloInterval.let {
+                bot.configuration.heartbeatPeriodMillis = it.times(1000).toLong()
+            }
+
+            return Response
+        }
 
         operator fun invoke(
             client: QQAndroidClient,
@@ -155,8 +169,8 @@ internal class StatSvc {
                                 var44.strVendorOSName = ROMUtil.getRomVersion(20);
                                 */
                                 bytes_0x769_reqbody = ProtoBuf.encodeToByteArray(
-                                    Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
-                                        rpt_config_list = listOf(
+                                    Oidb0x769.ReqBody.serializer(), Oidb0x769.ReqBody(
+                                        configList = listOf(
                                             Oidb0x769.ConfigSeq(
                                                 type = 46,
                                                 version = 0
@@ -181,10 +195,6 @@ internal class StatSvc {
                 acc or (((s.toLongOrNull() ?: 0) shl (index * 16)))
             }
         }
-
-        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
-            return Response
-        }
     }
 
     internal object ReqMSFOffline :