mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-31 03:22:36 +08:00
Support OtherClient online status:
- Add ClientKind - Add OtherClientOnlineEvent,OtherClientOfflineEvent - Add Bot.otherClients - Add OtherClientList
This commit is contained in:
parent
f1136e9b37
commit
3b35dbcac5
@ -76,6 +76,12 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
|
||||
|
||||
// region contacts
|
||||
|
||||
/**
|
||||
* 其他设备列表
|
||||
*/
|
||||
public val otherClients: OtherClientList
|
||||
|
||||
|
||||
/**
|
||||
* [User.id] 与 [Bot.id] 相同的 [Friend] 实例
|
||||
*/
|
||||
|
@ -7,6 +7,8 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
@ -17,6 +19,11 @@ import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol.ANDROID_PHONE
|
||||
* 其他设备. 如当 [Bot] 以 [ANDROID_PHONE] 登录时, 还可以有其他设备以 [ANDROID_PAD], iOS, PC 或其他设备登录.
|
||||
*/
|
||||
public interface OtherClient : Contact {
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
public val kind: ClientKind
|
||||
|
||||
/**
|
||||
* 此设备属于的 [Bot]
|
||||
*/
|
||||
@ -28,14 +35,40 @@ public interface OtherClient : Contact {
|
||||
public override val id: Long get() = bot.id
|
||||
}
|
||||
|
||||
/*
|
||||
public enum class ClientKind {
|
||||
ANDROID_PHONE,
|
||||
ANDROID_PAD,
|
||||
ANDROID_WATCH,
|
||||
IOS_PHONE,
|
||||
IOS_PAD,
|
||||
MAC_OS,
|
||||
WINDOWS_QQ,
|
||||
WINDOWS_TIM
|
||||
}*/
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
public enum class ClientKind(
|
||||
public val id: Int,
|
||||
) {
|
||||
ANDROID_PAD(68104),
|
||||
AOL_CHAOJIHUIYUAN(73730),
|
||||
AOL_HUIYUAN(73474),
|
||||
AOL_SQQ(69378),
|
||||
CAR(65806),
|
||||
HRTX_IPHONE(66566),
|
||||
HRTX_PC(66561),
|
||||
MC_3G(65795),
|
||||
MISRO_MSG(69634),
|
||||
MOBILE_ANDROID(65799),
|
||||
MOBILE_ANDROID_NEW(72450),
|
||||
MOBILE_HD(65805),
|
||||
MOBILE_HD_NEW(71426),
|
||||
MOBILE_IPAD(68361),
|
||||
MOBILE_IPAD_NEW(72194),
|
||||
MOBILE_IPHONE(67586),
|
||||
MOBILE_OTHER(65794),
|
||||
MOBILE_PC(65793),
|
||||
MOBILE_WINPHONE_NEW(72706),
|
||||
QQ_FORELDER(70922),
|
||||
QQ_SERVICE(71170),
|
||||
TV_QQ(69130),
|
||||
WIN8(69899),
|
||||
WINPHONE(65804),
|
||||
|
||||
UNKNOWN(-1);
|
||||
|
||||
public companion object {
|
||||
public operator fun get(id: Int): ClientKind? = values().find { it.id == id }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.contact
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
public class OtherClientList internal constructor(
|
||||
@MiraiInternalApi @JvmField
|
||||
public val delegate: MutableCollection<OtherClient> = ConcurrentLinkedQueue()
|
||||
) : Collection<OtherClient> by delegate {
|
||||
public operator fun get(kind: ClientKind): OtherClient? = this.find { it.kind == kind }
|
||||
|
||||
public fun getOrFail(kind: ClientKind): OtherClient =
|
||||
get(kind) ?: throw NoSuchElementException("OtherClient with kind=$kind not found.")
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.event.events
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.OtherClient
|
||||
import net.mamoe.mirai.event.AbstractEvent
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
|
||||
public interface OtherClientEvent : BotEvent, Packet {
|
||||
public val client: OtherClient
|
||||
override val bot: Bot
|
||||
get() = client.bot
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他设备上线
|
||||
*/
|
||||
public data class OtherClientOnlineEvent(
|
||||
override val client: OtherClient
|
||||
) : OtherClientEvent, AbstractEvent()
|
||||
|
||||
/**
|
||||
* 其他设备离线
|
||||
*/
|
||||
public data class OtherClientOfflineEvent(
|
||||
override val client: OtherClient
|
||||
) : OtherClientEvent, AbstractEvent()
|
@ -18,7 +18,9 @@
|
||||
package net.mamoe.mirai.internal
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.OtherClientList
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
||||
@ -72,6 +74,9 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
|
||||
|
||||
override val isOnline: Boolean get() = _network.areYouOk()
|
||||
|
||||
val otherClientsLock = Mutex() // lock sync
|
||||
override val otherClients: OtherClientList = OtherClientList()
|
||||
|
||||
/**
|
||||
* Close server connection, resend login packet, BUT DOESN'T [BotNetworkHandler.init]
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@ import net.mamoe.mirai.LowLevelApi
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.internal.contact.OtherClientImpl
|
||||
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
||||
import net.mamoe.mirai.internal.message.*
|
||||
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
|
||||
@ -38,6 +39,12 @@ internal fun Bot.asQQAndroidBot(): QQAndroidBot {
|
||||
return this as QQAndroidBot
|
||||
}
|
||||
|
||||
internal fun QQAndroidBot.createOtherClient(
|
||||
kind: ClientKind
|
||||
): OtherClientImpl {
|
||||
return OtherClientImpl(this, coroutineContext, kind)
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER", "BooleanLiteralArgument", "OverridingDeprecatedMember")
|
||||
internal class QQAndroidBot constructor(
|
||||
account: BotAccount,
|
||||
|
38
mirai-core/src/commonMain/kotlin/contact/OtherClientImpl.kt
Normal file
38
mirai-core/src/commonMain/kotlin/contact/OtherClientImpl.kt
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.contact
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.ClientKind
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.OtherClient
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
internal class OtherClientImpl(
|
||||
bot: Bot,
|
||||
coroutineContext: CoroutineContext,
|
||||
override val kind: ClientKind
|
||||
) : OtherClient, AbstractContact(bot, coroutineContext) {
|
||||
override suspend fun sendMessage(message: Message): MessageReceipt<Contact> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "OtherClient(bot=${bot.id},kind=$kind)"
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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 network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.ClientKind
|
||||
import net.mamoe.mirai.internal.utils.io.JceStruct
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
||||
|
||||
@Serializable
|
||||
internal data class InstanceInfo(
|
||||
@JvmField @TarsId(0) val iAppId: Int? = null,
|
||||
@JvmField @TarsId(1) val tablet: Byte? = null,
|
||||
@JvmField @TarsId(2) val iPlatform: Long? = null,
|
||||
/**
|
||||
* @see ClientKind
|
||||
*/
|
||||
@JvmField @TarsId(3) val iProductType: Long? = null,
|
||||
@JvmField @TarsId(4) val iClientType: Long? = null
|
||||
) : JceStruct
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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 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
|
||||
|
||||
// ANDROID PHONE QQ
|
||||
|
||||
// 2020-12-23 20:16:57 D/soutv: PK =
|
||||
//SvcReqMSFLoginNotifyData(iAppId=537066423,
|
||||
//status=2,
|
||||
//tablet=0,
|
||||
//iPlatform=109,
|
||||
//title=下线通知,
|
||||
//info=你的帐号在手机上退出了,
|
||||
//iProductType=0,
|
||||
//iClientType=65799,
|
||||
//vecInstanceList=[])
|
||||
|
||||
// ANDROID PHONE QQ
|
||||
// 2020-12-23 20:21:02 D/soutv: PK = SvcReqMSFLoginNotifyData(
|
||||
//iAppId=537066423,
|
||||
//status=1,
|
||||
//tablet=0,
|
||||
//iPlatform=109, title=上线通知, info=你的帐号在手机上登录了, iProductType=0, iClientType=65799, vecInstanceList=[InstanceInfo(iAppId=537066423, tablet=0, iPlatform=109, iProductType=0, iClientType=65799)])
|
||||
|
||||
@Serializable
|
||||
internal data class SvcReqMSFLoginNotifyData(
|
||||
@JvmField @TarsId(0) val iAppId: Long,
|
||||
@JvmField @TarsId(1) val status: Byte, // 上线=1, 下线=2
|
||||
@JvmField @TarsId(2) val tablet: Byte? = null,
|
||||
@JvmField @TarsId(3) val iPlatform: Long? = null,
|
||||
@JvmField @TarsId(4) val title: String? = "",
|
||||
@JvmField @TarsId(5) val info: String? = "",
|
||||
@JvmField @TarsId(6) val iProductType: Long? = null,
|
||||
@JvmField @TarsId(7) val iClientType: Long? = null,
|
||||
@JvmField @TarsId(8) val vecInstanceList: List<InstanceInfo>? = null
|
||||
) : JceStruct
|
@ -161,7 +161,8 @@ internal object KnownPacketFactories {
|
||||
OnlinePushPbPushTransMsg,
|
||||
MessageSvcPushNotify,
|
||||
ConfigPushSvc.PushReq,
|
||||
StatSvc.ReqMSFOffline
|
||||
StatSvc.ReqMSFOffline,
|
||||
StatSvc.SvcReqMSFLoginNotify
|
||||
)
|
||||
// SvcReqMSFLoginNotify 自己的其他设备上限
|
||||
// MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机
|
||||
|
@ -9,10 +9,17 @@
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.packet.login
|
||||
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import net.mamoe.mirai.contact.ClientKind
|
||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
||||
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.createOtherClient
|
||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.guid
|
||||
@ -23,11 +30,9 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.SvcReqRegister
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.*
|
||||
import net.mamoe.mirai.internal.utils.MiraiPlatformUtils
|
||||
import net.mamoe.mirai.internal.utils.NetworkType
|
||||
import net.mamoe.mirai.internal.utils.encodeToString
|
||||
import net.mamoe.mirai.internal.utils.*
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.*
|
||||
import net.mamoe.mirai.internal.utils.toReadPacket
|
||||
import java.util.concurrent.CancellationException
|
||||
|
||||
@Suppress("EnumEntryName", "unused")
|
||||
internal enum class RegPushReason {
|
||||
@ -220,4 +225,37 @@ internal class StatSvc {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object SvcReqMSFLoginNotify :
|
||||
IncomingPacketFactory<Packet?>("StatSvc.SvcReqMSFLoginNotify", "StatSvc.SvcReqMSFLoginNotify") {
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? =
|
||||
bot.otherClientsLock.withLock {
|
||||
val notify = readUniPacket(network.protocol.data.jce.SvcReqMSFLoginNotifyData.serializer())
|
||||
|
||||
val kind = notify.iClientType?.toInt()?.let(ClientKind::get) ?: return null
|
||||
|
||||
when (notify.status.toInt()) {
|
||||
1 -> {
|
||||
if (bot.otherClients.any { it.kind == kind }) return null
|
||||
val client = bot.createOtherClient(kind)
|
||||
bot.otherClients.delegate.add(client)
|
||||
OtherClientOnlineEvent(client)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
val client = bot.otherClients.find { it.kind == kind } ?: return null
|
||||
client.cancel(CancellationException("Offline"))
|
||||
bot.otherClients.delegate.remove(client)
|
||||
OtherClientOfflineEvent(client)
|
||||
}
|
||||
|
||||
else -> throw contextualBugReportException(
|
||||
"decode SvcReqMSFLoginNotify (OtherClient status change)",
|
||||
notify._miraiContentToString(),
|
||||
additional = "unknown notify.status=${notify.status}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user