mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-26 15:40:14 +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
|
// region contacts
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他设备列表
|
||||||
|
*/
|
||||||
|
public val otherClients: OtherClientList
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [User.id] 与 [Bot.id] 相同的 [Friend] 实例
|
* [User.id] 与 [Bot.id] 相同的 [Friend] 实例
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.contact
|
package net.mamoe.mirai.contact
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
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 或其他设备登录.
|
* 其他设备. 如当 [Bot] 以 [ANDROID_PHONE] 登录时, 还可以有其他设备以 [ANDROID_PAD], iOS, PC 或其他设备登录.
|
||||||
*/
|
*/
|
||||||
public interface OtherClient : Contact {
|
public interface OtherClient : Contact {
|
||||||
|
/**
|
||||||
|
* 设备类型
|
||||||
|
*/
|
||||||
|
public val kind: ClientKind
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 此设备属于的 [Bot]
|
* 此设备属于的 [Bot]
|
||||||
*/
|
*/
|
||||||
@ -28,14 +35,40 @@ public interface OtherClient : Contact {
|
|||||||
public override val id: Long get() = bot.id
|
public override val id: Long get() = bot.id
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
public enum class ClientKind {
|
* 设备类型
|
||||||
ANDROID_PHONE,
|
*/
|
||||||
ANDROID_PAD,
|
public enum class ClientKind(
|
||||||
ANDROID_WATCH,
|
public val id: Int,
|
||||||
IOS_PHONE,
|
) {
|
||||||
IOS_PAD,
|
ANDROID_PAD(68104),
|
||||||
MAC_OS,
|
AOL_CHAOJIHUIYUAN(73730),
|
||||||
WINDOWS_QQ,
|
AOL_HUIYUAN(73474),
|
||||||
WINDOWS_TIM
|
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
|
package net.mamoe.mirai.internal
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.contact.OtherClientList
|
||||||
import net.mamoe.mirai.event.Listener
|
import net.mamoe.mirai.event.Listener
|
||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
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()
|
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]
|
* 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.Mirai
|
||||||
import net.mamoe.mirai.contact.*
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.data.*
|
import net.mamoe.mirai.data.*
|
||||||
|
import net.mamoe.mirai.internal.contact.OtherClientImpl
|
||||||
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
||||||
import net.mamoe.mirai.internal.message.*
|
import net.mamoe.mirai.internal.message.*
|
||||||
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
|
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
|
||||||
@ -38,6 +39,12 @@ internal fun Bot.asQQAndroidBot(): QQAndroidBot {
|
|||||||
return this as QQAndroidBot
|
return this as QQAndroidBot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun QQAndroidBot.createOtherClient(
|
||||||
|
kind: ClientKind
|
||||||
|
): OtherClientImpl {
|
||||||
|
return OtherClientImpl(this, coroutineContext, kind)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("INVISIBLE_MEMBER", "BooleanLiteralArgument", "OverridingDeprecatedMember")
|
@Suppress("INVISIBLE_MEMBER", "BooleanLiteralArgument", "OverridingDeprecatedMember")
|
||||||
internal class QQAndroidBot constructor(
|
internal class QQAndroidBot constructor(
|
||||||
account: BotAccount,
|
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,
|
OnlinePushPbPushTransMsg,
|
||||||
MessageSvcPushNotify,
|
MessageSvcPushNotify,
|
||||||
ConfigPushSvc.PushReq,
|
ConfigPushSvc.PushReq,
|
||||||
StatSvc.ReqMSFOffline
|
StatSvc.ReqMSFOffline,
|
||||||
|
StatSvc.SvcReqMSFLoginNotify
|
||||||
)
|
)
|
||||||
// SvcReqMSFLoginNotify 自己的其他设备上限
|
// SvcReqMSFLoginNotify 自己的其他设备上限
|
||||||
// MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机
|
// MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机
|
||||||
|
@ -9,10 +9,17 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.network.protocol.packet.login
|
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.io.core.ByteReadPacket
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
|
import net.mamoe.mirai.contact.ClientKind
|
||||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
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.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.Packet
|
||||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||||
import net.mamoe.mirai.internal.network.guid
|
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.Oidb0x769
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline
|
import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.*
|
import net.mamoe.mirai.internal.network.protocol.packet.*
|
||||||
import net.mamoe.mirai.internal.utils.MiraiPlatformUtils
|
import net.mamoe.mirai.internal.utils.*
|
||||||
import net.mamoe.mirai.internal.utils.NetworkType
|
|
||||||
import net.mamoe.mirai.internal.utils.encodeToString
|
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.*
|
import net.mamoe.mirai.internal.utils.io.serialization.*
|
||||||
import net.mamoe.mirai.internal.utils.toReadPacket
|
import java.util.concurrent.CancellationException
|
||||||
|
|
||||||
@Suppress("EnumEntryName", "unused")
|
@Suppress("EnumEntryName", "unused")
|
||||||
internal enum class RegPushReason {
|
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