diff --git a/mirai-core-api/src/commonMain/kotlin/contact/OtherClient.kt b/mirai-core-api/src/commonMain/kotlin/contact/OtherClient.kt index 9194efc88..b5a7acfe6 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/OtherClient.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/OtherClient.kt @@ -115,6 +115,7 @@ public enum class Platform( public enum class ClientKind( @MiraiInternalApi public val id: Int, ) { + ANDROID_PAD(68104), AOL_CHAOJIHUIYUAN(73730), AOL_HUIYUAN(73474), @@ -132,7 +133,8 @@ public enum class ClientKind( MOBILE_IPAD_NEW(72194), MOBILE_IPHONE(67586), MOBILE_OTHER(65794), - MOBILE_PC(65793), + MOBILE_PC_QQ(65793), + MOBILE_PC_TIM(77313), MOBILE_WINPHONE_NEW(72706), QQ_FORELDER(70922), QQ_SERVICE(71170), diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt new file mode 100644 index 000000000..2eac217d0 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2020-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +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 RequestPushStatus( + @JvmField @TarsId(0) val uin: Long, + @JvmField @TarsId(1) val status: Byte, + @JvmField @TarsId(2) val dataLine: Byte? = null, + @JvmField @TarsId(3) val printable: Byte? = null, + @JvmField @TarsId(4) val viewFile: Byte? = null, + @JvmField @TarsId(5) val nPCVer: Long? = null, + @JvmField @TarsId(6) val nClientType: Long? = null, + @JvmField @TarsId(7) val nInstanceId: Long? = null, + @JvmField @TarsId(8) val vecInstanceList: List? = null +) : JceStruct + + diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index 987fec03a..a820960a4 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -161,6 +161,7 @@ internal object KnownPacketFactories { OnlinePushPbPushTransMsg, MessageSvcPushNotify, MessageSvcPushReaded, + MessageSvcRequestPushStatus, ConfigPushSvc.PushReq, StatSvc.ReqMSFOffline, StatSvc.SvcReqMSFLoginNotify diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt new file mode 100644 index 000000000..812f0c4bf --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt @@ -0,0 +1,60 @@ +package net.mamoe.mirai.internal.network.protocol.packet.chat.receive + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.cancel +import kotlinx.coroutines.sync.withLock +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.Mirai +import net.mamoe.mirai.contact.ClientKind +import net.mamoe.mirai.event.events.OtherClientOfflineEvent +import net.mamoe.mirai.event.events.OtherClientOnlineEvent +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.contact.appId +import net.mamoe.mirai.internal.createOtherClient +import net.mamoe.mirai.internal.message.contextualBugReportException +import net.mamoe.mirai.internal.network.Packet +import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus +import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory +import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket + +internal object MessageSvcRequestPushStatus : IncomingPacketFactory( + "MessageSvc.RequestPushStatus", "" +) { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { + val packet = readUniPacket(RequestPushStatus.serializer()) + bot.otherClientsLock.withLock { + val appId = packet.vecInstanceList?.firstOrNull()?.iAppId ?: 1 + return when (packet.status.toInt()) { + 1 -> { // online + if (bot.otherClients.any { appId == it.appId }) return null + val info = Mirai.getOnlineOtherClientsList(bot).firstOrNull { appId == it.appId } + ?: throw contextualBugReportException( + "SvcRequestPushStatus (OtherClient online)", + packet._miraiContentToString(), + additional = "Failed to find corresponding instanceInfo." + ) + val client = bot.createOtherClient(info) + bot.otherClients.delegate.add(client) + OtherClientOnlineEvent( + client, + ClientKind[packet.nClientType?.toInt() ?: 0] + ) + } + + 2 -> { // off + val client = bot.otherClients.find { it.appId == appId } ?: return null + client.cancel(CancellationException("Offline")) + bot.otherClients.delegate.remove(client) + OtherClientOfflineEvent(client) + } + + else -> throw contextualBugReportException( + "decode SvcRequestPushStatus (PC Client status change)", + packet._miraiContentToString(), + additional = "unknown status=${packet.status}" + ) + } + } + } +} \ No newline at end of file