diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 9a7a8b0ec..58ff8a668 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -243,6 +243,28 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler bot.logger.info("====================Mirai Bot List初始化完毕====================") bot.firstLoginSucceed = true + + launch { + while (this.isActive) { + delay(bot.configuration.heartbeatPeriodMillis) + var lastException: Exception? + try { + check( + StatSvc.GetOnlineStatus(bot.client) + .sendAndExpect( + timeoutMillis = bot.configuration.heartbeatTimeoutMillis, + retry = 1 + ) is StatSvc.GetOnlineStatus.Response.Success + ) + continue + } catch (e: Exception) { + lastException = e + } + delay(bot.configuration.firstReconnectDelayMillis) + close() + bot.tryReinitializeNetworkHandler(lastException) + } + } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/StatSvcGetOnline.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/StatSvcGetOnline.kt new file mode 100644 index 000000000..8faead074 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/StatSvcGetOnline.kt @@ -0,0 +1,29 @@ +package net.mamoe.mirai.qqandroid.network.protocol.data.proto + +import kotlinx.serialization.SerialId +import kotlinx.serialization.Serializable +import net.mamoe.mirai.qqandroid.io.ProtoBuf + +class StatSvcGetOnline { + @Serializable + class Instance( + @SerialId(1) val instanceId: Int = 0, + @SerialId(2) val clientType: Int = 0 + ) : ProtoBuf + + @Serializable + class ReqBody( + @SerialId(1) val uin: Long = 0L, + @SerialId(2) val appid: Int = 0 + ) : ProtoBuf + + @Serializable + class RspBody( + @SerialId(1) val errorCode: Int = 0, + @SerialId(2) val errorMsg: String = "", + @SerialId(3) val uin: Long = 0L, + @SerialId(4) val appid: Int = 0, + @SerialId(5) val timeInterval: Int = 0, + @SerialId(6) val msgInstances: List? = null + ) : ProtoBuf +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index 04a87cdee..a9cfe3046 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -112,6 +112,7 @@ internal object KnownPacketFactories { object OutgoingFactories : List> by mutableListOf( LoginPacket, StatSvc.Register, + StatSvc.GetOnlineStatus, MessageSvc.PbGetMsg, MessageSvc.PushForceOffline, MessageSvc.PbSendMsg, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt index 8113708a5..a66ce7431 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt @@ -3,12 +3,11 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.login import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport -import net.mamoe.mirai.qqandroid.io.serialization.jceRequestSBuffer -import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct +import net.mamoe.mirai.qqandroid.io.serialization.* import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.StatSvcGetOnline import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket @@ -33,6 +32,46 @@ internal enum class RegPushReason { } internal class StatSvc { + internal object GetOnlineStatus : OutgoingPacketFactory("StatSvc.GetOnlineStatus") { + + internal sealed class Response : Packet { + override fun toString(): String = "StatSvc.GetOnlineStatus.Response" + + object Success : Response() { + override fun toString(): String { + return "StatSvc.GetOnlineStatus.Response.Success" + } + } + + class Failed(val errno: Int, val message: String) : Response() { + override fun toString(): String { + return "StatSvc.GetOnlineStatus.Response.Failed(errno=$errno, message=$message)" + } + } + } + + operator fun invoke( + client: QQAndroidClient + ): OutgoingPacket = buildLoginOutgoingPacket(client, 1) { + writeProtoBuf( + StatSvcGetOnline.ReqBody.serializer(), StatSvcGetOnline.ReqBody( + uin = client.uin, + appid = 0 + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val resp = readProtoBuf(StatSvcGetOnline.RspBody.serializer()) + return if (resp.errorCode != 0) { + Response.Failed(resp.errorCode, resp.errorMsg) + } else { + Response.Success + } + } + } + + internal object Register : OutgoingPacketFactory("StatSvc.register") { internal object Response : Packet { diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt index 5a4bcbc23..0cb54c6a2 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt @@ -16,7 +16,7 @@ fun main() { println( File( """ - /Users/jiahua.liu/Desktop/QQAndroid-F/app/src/main/java/tencent/im/oidb/cmd0x8a0/ + E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\statsvc\getonline """.trimIndent() ) .generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt index 07e795fab..8f213b87e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt @@ -49,10 +49,10 @@ class BotConfiguration { /** * 心跳周期. 过长会导致被服务器断开连接. */ - var heartbeatPeriodMillis: Long = 60.secondsToMillis + var heartbeatPeriodMillis: Long = 30.secondsToMillis /** * 每次心跳时等待结果的时间. - * 一旦心跳超时, 整个网络服务将会重启 (将消耗约 1s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响. + * 一旦心跳超时, 整个网络服务将会重启 (将消耗约 5s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响. */ var heartbeatTimeoutMillis: Long = 2.secondsToMillis /**