From 146615e66052cafbfeb99b7d7a9741ef5dedac03 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 14 Dec 2019 23:38:56 +0800 Subject: [PATCH] Change Bot to interface, implement Bot as BotImpl, fix #20 --- .../MiraiHttpApplication.kt | 2 +- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 266 +++++------------- .../kotlin/net.mamoe.mirai/BotAccount.kt | 10 + .../kotlin/net.mamoe.mirai/BotAddFriend.kt | 10 +- .../kotlin/net.mamoe.mirai/BotHelper.kt | 27 +- .../kotlin/net.mamoe.mirai/BotImpl.kt | 167 +++++++++++ .../contact/internal/ContactImpl.kt | 4 +- .../packet/event/FriendOnlineStatusChanged.kt | 1 - .../protocol/tim/packet/event/MemberJoin.kt | 1 - .../protocol/tim/packet/event/MemberMute.kt | 5 +- .../protocol/tim/packet/event/MessageEvent.kt | 1 - .../net.mamoe.mirai/utils/Exceptions.kt | 2 + 12 files changed, 259 insertions(+), 237 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt diff --git a/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/MiraiHttpApplication.kt b/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/MiraiHttpApplication.kt index 09e220487..4c990a704 100644 --- a/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/MiraiHttpApplication.kt +++ b/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/MiraiHttpApplication.kt @@ -20,7 +20,6 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.addFriend import net.mamoe.mirai.contact.sendMessage import net.mamoe.mirai.getGroup -import net.mamoe.mirai.getQQ import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToUBytes @@ -81,6 +80,7 @@ suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK") /** * 错误请求. 抛出这个异常后将会返回错误给一个请求 */ +@Suppress("unused") open class IllegalAccessException : Exception { override val message: String get() = super.message!! diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt index ce604fbf0..1028e5e68 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -1,256 +1,128 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE", "unused") +@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE") package net.mamoe.mirai -import kotlinx.coroutines.* -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import net.mamoe.mirai.Bot.ContactSystem +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job import net.mamoe.mirai.contact.* -import net.mamoe.mirai.contact.internal.Group -import net.mamoe.mirai.contact.internal.QQ import net.mamoe.mirai.network.BotNetworkHandler -import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler -import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId -import net.mamoe.mirai.network.protocol.tim.packet.action.GroupNotFound -import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket -import net.mamoe.mirai.network.protocol.tim.packet.action.RawGroupInfo import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult -import net.mamoe.mirai.network.protocol.tim.packet.login.isSuccess -import net.mamoe.mirai.network.qqAccount -import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.internal.PositiveNumbers -import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail -import net.mamoe.mirai.utils.io.inline -import net.mamoe.mirai.utils.io.logStacktrace +import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.GroupNotFoundException +import net.mamoe.mirai.utils.MiraiLogger import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext -import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmSynthetic -data class BotAccount( - val id: UInt, - val password: String -) { - constructor(id: Long, password: String) : this(id.toUInt(), password) -} - -// These pseudo 'constructors' are suspend, therefore they should not be inside the object - -@Suppress("FunctionName") -suspend inline fun Bot(account: BotAccount, logger: MiraiLogger): Bot = Bot(account, logger, coroutineContext) - -@Suppress("FunctionName") -suspend inline fun Bot(account: BotAccount): Bot = Bot(account, coroutineContext) - -@JvmSynthetic -@Suppress("FunctionName") -suspend inline fun Bot(qq: UInt, password: String): Bot = Bot(BotAccount(qq, password), coroutineContext) - -@Suppress("FunctionName") -suspend inline fun Bot(qq: Long, password: String): Bot = Bot(BotAccount(qq.toUInt(), password), coroutineContext) /** * Mirai 的机器人. 一个机器人实例登录一个 QQ 账号. * Mirai 为多账号设计, 可同时维护多个机器人. * - * [Bot] 由 3 个模块组成. - * [联系人管理][ContactSystem]: 可通过 [Bot.contacts] 访问 - * [网络处理器][TIMBotNetworkHandler]: 可通过 [Bot.network] 访问 - * [机器人账号信息][BotAccount]: 可通过 [Bot.qqAccount] 访问 - * - * 若需要得到机器人的 QQ 账号, 请访问 [Bot.qqAccount] - * 若需要得到服务器上所有机器人列表, 请访问 [Bot.instances] - * - * 在 BotHelper.kt 中有一些访问的捷径. 如 [Bot.getGroup] - * - * - * - * Bot that is the base of the whole program. - * It consists of - * a [ContactSystem], which manage contacts such as [QQ] and [Group]; - * a [TIMBotNetworkHandler], which manages the connection to the server; - * a [BotAccount], which stores the account information(e.g. qq number the bot) - * - * To of all the QQ contacts, access [Bot.qqAccount] - * To of all the Robot instance, access [Bot.instances] - * - * - * @author Him188moe - * @author NaturalHG * @see Contact */ -class Bot(val account: BotAccount, val logger: MiraiLogger, context: CoroutineContext) : CoroutineScope { - private val supervisorJob = SupervisorJob(context[Job]) - override val coroutineContext: CoroutineContext = - context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") } +interface Bot : CoroutineScope { + companion object { + suspend inline operator fun invoke(account: BotAccount, logger: MiraiLogger): Bot = BotImpl(account, logger, coroutineContext) + suspend inline operator fun invoke(account: BotAccount): Bot = BotImpl(account, context = coroutineContext) + @JvmSynthetic + suspend inline operator fun invoke(qq: UInt, password: String): Bot = BotImpl(BotAccount(qq, password), context = coroutineContext) - constructor(qq: Long, password: String, context: CoroutineContext) : this(BotAccount(qq, password), context) - constructor(qq: UInt, password: String, context: CoroutineContext) : this(BotAccount(qq, password), context) - constructor(account: BotAccount, context: CoroutineContext) : this(account, DefaultLogger("Bot(" + account.id + ")"), context) + suspend inline operator fun invoke(qq: Long, password: String): Bot = BotImpl(BotAccount(qq.toUInt(), password), context = coroutineContext) + operator fun invoke(qq: Long, password: String, context: CoroutineContext): Bot = BotImpl(BotAccount(qq, password), context = context) + @JvmSynthetic + operator fun invoke(qq: UInt, password: String, context: CoroutineContext): Bot = BotImpl(BotAccount(qq, password), context = context) - val contacts = ContactSystem() + operator fun invoke(account: BotAccount, context: CoroutineContext): Bot = BotImpl(account, context = context) - val network: BotNetworkHandler<*> get() = _network - private lateinit var _network: BotNetworkHandler<*> + inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block) - init { - launch { - addInstance(this@Bot) - } + fun instanceWhose(qq: UInt): Bot = BotImpl.instanceWhose(qq = qq) } - override fun toString(): String = "Bot(${account.id})" + /** + * 账号信息 + */ + val account: BotAccount + + /** + * 日志记录器 + */ + val logger: MiraiLogger + + override val coroutineContext: CoroutineContext + + /** + * 网络模块 + */ + val network: BotNetworkHandler<*> /** * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * 然后重新启动并尝试登录 */ - @JvmOverloads // shouldn't be suspend!! This function MUST NOT inherit the context from the caller because the caller(NetworkHandler) is going to close fun tryReinitializeNetworkHandler( configuration: BotConfiguration, cause: Throwable? = null - ): Job = launch { - repeat(configuration.reconnectionRetryTimes) { - if (reinitializeNetworkHandlerAsync(configuration, cause).await().isSuccess()) { - logger.info("Reconnected successfully") - return@launch - } else { - delay(configuration.reconnectPeriodMillis) - } - } - } + ): Job /** * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * 然后重新启动并尝试登录 */ - @JvmOverloads suspend fun reinitializeNetworkHandler( configuration: BotConfiguration, cause: Throwable? = null - ): LoginResult { - logger.info("BotAccount: ${qqAccount.toLong()}") - logger.info("Initializing BotNetworkHandler") - try { - if (::_network.isInitialized) { - _network.close(cause) - } - } catch (e: Exception) { - logger.error("Cannot close network handler", e) - } - _network = TIMBotNetworkHandler(this@Bot.coroutineContext + configuration, this@Bot) - - return _network.login() - } + ): LoginResult /** * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * 然后重新启动并尝试登录 */ - @JvmOverloads fun reinitializeNetworkHandlerAsync( configuration: BotConfiguration, cause: Throwable? = null - ): Deferred = async { reinitializeNetworkHandler(configuration, cause) } + ): Deferred /** - * Bot 联系人管理. - * - * @see Bot.contacts + * 与这个机器人相关的 QQ 列表. 机器人与 QQ 不一定是好友 */ - inner class ContactSystem internal constructor() { - val bot: Bot get() = this@Bot + val qqs: ContactList - @UseExperimental(MiraiInternalAPI::class) - val groups: ContactList = ContactList(MutableContactList()) + /** + * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个. + */ + fun getQQ(id: UInt): QQ - @UseExperimental(MiraiInternalAPI::class) - val qqs: ContactList = ContactList(MutableContactList()) + /** + * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个. + */ + fun getQQ(id: Long): QQ - /** - * 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个. - */ - @UseExperimental(MiraiInternalAPI::class) - @JvmSynthetic - fun getQQ(id: UInt): QQ = qqs.delegate.getOrAdd(id) { QQ(bot, id, coroutineContext) } + /** + * 与这个机器人相关的群列表. 机器人不一定是群成员. + */ + val groups: ContactList - /** - * 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个. - */ - // NO INLINE!! to help Java - @UseExperimental(MiraiInternalAPI::class) - fun getQQ(@PositiveNumbers id: Long): QQ = getQQ(id.coerceAtLeastOrFail(0).toUInt()) + /** + * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个. + * 若 [id] 无效, 将会抛出 [GroupNotFoundException] + */ + suspend fun getGroup(id: GroupId): Group - /** - * 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个. - */ - suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId()) + /** + * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个. + * 若 [internalId] 无效, 将会抛出 [GroupNotFoundException] + */ + suspend fun getGroup(internalId: GroupInternalId): Group - /** - * 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个. - */ - @UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class) - suspend fun getGroup(id: GroupId): Group = groups.delegate.getOrNull(id.value) ?: inline { - val info: RawGroupInfo = try { - when (val response = - bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, id.toInternalId(), sessionKey).sendAndExpect() }) { - is RawGroupInfo -> response - is GroupNotFound -> throw GroupNotFoundException("id=${id.value.toLong()}") - else -> assertUnreachable() - } - } catch (e: Exception) { - throw IllegalStateException("Cannot obtain group info for id ${id.value.toLong()}", e) - } + /** + * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个. + * 若 [id] 无效, 将会抛出 [GroupNotFoundException] + */ + suspend fun getGroup(id: Long): Group - return groups.delegate.getOrAdd(id.value) { Group(bot, id, info, coroutineContext) } - } - - /** - * 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个. - */ - // NO INLINE!! to help Java - @UseExperimental(MiraiInternalAPI::class) - suspend fun getGroup(@PositiveNumbers id: Long): Group = id.coerceAtLeastOrFail(0).toUInt().let { - groups.delegate.getOrNull(it) ?: inline { - val info: RawGroupInfo = try { - bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, GroupId(it).toInternalId(), sessionKey).sendAndExpect() } - } catch (e: Exception) { - e.logStacktrace() - error("Cannot obtain group info for id ${it.toLong()}") - } - - return groups.delegate.getOrAdd(it) { Group(bot, GroupId(it), info, coroutineContext) } - } - } - } - - @UseExperimental(MiraiInternalAPI::class) - fun close() { - _network.close() - this.coroutineContext.cancelChildren() - contacts.groups.delegate.clear() - contacts.qqs.delegate.clear() - } - - companion object { - @Suppress("ObjectPropertyName") - private val _instances: MutableList = mutableListOf() - private val instanceLock: Mutex = Mutex() - - private val instances: List get() = _instances - - suspend fun instanceWhose(qq: UInt) = instanceLock.withLock { - instances.first { it.qqAccount == qq } - } - - internal suspend fun addInstance(bot: Bot) = instanceLock.withLock { - _instances += bot - } - - init { - KnownPacketId.values() // load packet ids - } - } + fun close() } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt new file mode 100644 index 000000000..5a9427630 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt @@ -0,0 +1,10 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + +package net.mamoe.mirai + +data class BotAccount( + val id: UInt, + val password: String +) { + constructor(id: Long, password: String) : this(id.toUInt(), password) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAddFriend.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAddFriend.kt index 3367ddbb1..d413b9eb0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAddFriend.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAddFriend.kt @@ -68,15 +68,15 @@ Mirai 22:04:48 : Packet received: UnknownEventPacket(id=00 D6, identity=(2092749 * @param remark 好友备注 */ @UseExperimental(ExperimentalContracts::class) -suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = bot.withSession { - return when (CanAddFriendPacket(bot.qqAccount, id, bot.sessionKey).sendAndExpect()) { +suspend fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = withSession { + return when (CanAddFriendPacket(qqAccount, id, sessionKey).sendAndExpect()) { is CanAddFriendResponse.AlreadyAdded -> AddFriendResult.ALREADY_ADDED is CanAddFriendResponse.Rejected -> AddFriendResult.REJECTED is CanAddFriendResponse.ReadyToAdd, is CanAddFriendResponse.RequireVerification -> { - val key = RequestFriendAdditionKeyPacket(bot.qqAccount, id, sessionKey).sendAndExpect().key - AddFriendPacket.RequestAdd(bot.qqAccount, id, sessionKey, message, remark, key).sendAndExpect() + val key = RequestFriendAdditionKeyPacket(qqAccount, id, sessionKey).sendAndExpect().key + AddFriendPacket.RequestAdd(qqAccount, id, sessionKey, message, remark, key).sendAndExpect() AddFriendResult.WAITING_FOR_APPROVE } //这个做的是需要验证消息的情况, 不确定 ReadyToAdd 的是啥 @@ -87,7 +87,7 @@ suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remar /*is CanAddFriendResponse.ReadyToAdd -> { // TODO: 2019/11/11 这不需要验证信息的情况 - //AddFriendPacket(bot.qqAccount, id, bot.sessionKey, ).sendAndExpectAsync().await() + //AddFriendPacket(qqAccount, id, sessionKey, ).sendAndExpectAsync().await() TODO() }*/ } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt index a1a2024ad..075d716fc 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt @@ -26,25 +26,8 @@ import kotlin.jvm.JvmOverloads */ //Contacts - inline fun Bot.getQQ(@PositiveNumbers number: Long): QQ = this.contacts.getQQ(number) - inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number) +suspend inline fun Bot.getGroup(id: UInt): Group = this.getGroup(GroupId(id)) -suspend inline fun Bot.getGroup(id: UInt): Group = this.contacts.getGroup(GroupId(id)) -suspend inline fun Bot.getGroup(@PositiveNumbers id: Long): Group = - this.contacts.getGroup(GroupId(id.coerceAtLeastOrFail(0).toUInt())) - -suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id) -suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId) - -/** - * 取得群列表 - */ -inline val Bot.groups: ContactList get() = this.contacts.groups - -/** - * 取得好友列表 - */ -inline val Bot.qqs: ContactList get() = this.contacts.qqs /** * 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值. @@ -107,14 +90,6 @@ suspend inline fun Bot.alsoLogin(message: String): Bot { } } -/** - * 添加好友 - */ -@UseExperimental(ExperimentalContracts::class) -@JvmOverloads -suspend inline fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = - contacts.addFriend(id, message, remark) - /** * 取得机器人的 QQ 号 */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt new file mode 100644 index 000000000..2fcd52f8c --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -0,0 +1,167 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + +package net.mamoe.mirai + +import kotlinx.coroutines.* +import net.mamoe.mirai.contact.* +import net.mamoe.mirai.contact.internal.Group +import net.mamoe.mirai.contact.internal.QQ +import net.mamoe.mirai.network.BotNetworkHandler +import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler +import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupNotFound +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket +import net.mamoe.mirai.network.protocol.tim.packet.action.RawGroupInfo +import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult +import net.mamoe.mirai.network.protocol.tim.packet.login.isSuccess +import net.mamoe.mirai.network.qqAccount +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.internal.PositiveNumbers +import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail +import net.mamoe.mirai.utils.io.inline +import net.mamoe.mirai.utils.io.logStacktrace +import kotlin.coroutines.CoroutineContext +import kotlin.jvm.JvmSynthetic + +@PublishedApi +internal class BotImpl @PublishedApi internal constructor( + override val account: BotAccount, + override val logger: MiraiLogger = DefaultLogger("Bot(" + account.id + ")"), + context: CoroutineContext +) : Bot, CoroutineScope { + private val supervisorJob = SupervisorJob(context[Job]) + override val coroutineContext: CoroutineContext = + context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") } + + init { + launch { + instances.addLast(this@BotImpl) + } + } + + companion object { + init { + KnownPacketId.values() /* load id classes */ + } + + @PublishedApi + internal val instances: LockFreeLinkedList = LockFreeLinkedList() + + inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach(block) + + fun instanceWhose(qq: UInt): Bot { + instances.forEach { + if (it.qqAccount == qq) { + return it + } + } + throw NoSuchElementException() + } + } + + override fun toString(): String = "Bot(${account.id})" + + // region network + + override val network: BotNetworkHandler<*> get() = _network + private lateinit var _network: BotNetworkHandler<*> + + override fun tryReinitializeNetworkHandler(// shouldn't be suspend!! This function MUST NOT inherit the context from the caller because the caller(NetworkHandler) is going to close + configuration: BotConfiguration, + cause: Throwable? + ): Job = launch { + repeat(configuration.reconnectionRetryTimes) { + if (reinitializeNetworkHandlerAsync(configuration, cause).await().isSuccess()) { + logger.info("Reconnected successfully") + return@launch + } else { + delay(configuration.reconnectPeriodMillis) + } + } + } + + override suspend fun reinitializeNetworkHandler( + configuration: BotConfiguration, + cause: Throwable? + ): LoginResult { + logger.info("BotAccount: ${qqAccount.toLong()}") + logger.info("Initializing BotNetworkHandler") + try { + if (::_network.isInitialized) { + _network.close(cause) + } + } catch (e: Exception) { + logger.error("Cannot close network handler", e) + } + _network = TIMBotNetworkHandler(this.coroutineContext + configuration, this) + + return _network.login() + } + + override fun reinitializeNetworkHandlerAsync( + configuration: BotConfiguration, + cause: Throwable? + ): Deferred = async { reinitializeNetworkHandler(configuration, cause) } + + // endregion + + // region contacts + @UseExperimental(MiraiInternalAPI::class) + override val groups: ContactList = ContactList(MutableContactList()) + + @UseExperimental(MiraiInternalAPI::class) + override val qqs: ContactList = ContactList(MutableContactList()) + + /** + * 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个. + */ + @UseExperimental(MiraiInternalAPI::class) + @JvmSynthetic + override fun getQQ(id: UInt): QQ = qqs.delegate.getOrAdd(id) { QQ(this, id, coroutineContext) } + + // NO INLINE!! to help Java + @UseExperimental(MiraiInternalAPI::class) + override fun getQQ(@PositiveNumbers id: Long): QQ = getQQ(id.coerceAtLeastOrFail(0).toUInt()) + + override suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId()) + + @UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class) + override suspend fun getGroup(id: GroupId): Group = groups.delegate.getOrNull(id.value) ?: inline { + val info: RawGroupInfo = try { + when (val response = + withSession { GroupPacket.QueryGroupInfo(qqAccount, id.toInternalId(), sessionKey).sendAndExpect() }) { + is RawGroupInfo -> response + is GroupNotFound -> throw GroupNotFoundException("id=${id.value.toLong()}") + else -> assertUnreachable() + } + } catch (e: Exception) { + throw IllegalStateException("Cannot obtain group info for id ${id.value.toLong()}", e) + } + + return groups.delegate.getOrAdd(id.value) { Group(this, id, info, coroutineContext) } + } + + // NO INLINE!! to help Java + @UseExperimental(MiraiInternalAPI::class) + override suspend fun getGroup(@PositiveNumbers id: Long): Group = id.coerceAtLeastOrFail(0).toUInt().let { + groups.delegate.getOrNull(it) ?: inline { + val info: RawGroupInfo = try { + withSession { GroupPacket.QueryGroupInfo(qqAccount, GroupId(it).toInternalId(), sessionKey).sendAndExpect() } + } catch (e: Exception) { + e.logStacktrace() + error("Cannot obtain group info for id ${it.toLong()}") + } + + return groups.delegate.getOrAdd(it) { Group(this, GroupId(it), info, coroutineContext) } + } + } + // endregion + + @UseExperimental(MiraiInternalAPI::class) + override fun close() { + _network.close() + this.supervisorJob.complete() + groups.delegate.clear() + qqs.delegate.clear() + } +} diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt index 0c9321afb..4055c5e83 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt @@ -85,7 +85,7 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr } @Suppress("FunctionName", "NOTHING_TO_INLINE") -inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } } +internal inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } } @PublishedApi internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) : @@ -115,7 +115,7 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot: } @Suppress("FunctionName", "NOTHING_TO_INLINE") -inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member = +internal inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member = MemberImpl(delegate, this, permission, coroutineContext).apply { launch { startUpdater() } } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendOnlineStatusChanged.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendOnlineStatusChanged.kt index 40e7e660e..5e7924fdc 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendOnlineStatusChanged.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendOnlineStatusChanged.kt @@ -7,7 +7,6 @@ import kotlinx.io.core.discardExact import kotlinx.io.core.readUByte import kotlinx.io.core.readUInt import net.mamoe.mirai.contact.QQ -import net.mamoe.mirai.getQQ import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt index c9bac57c3..5d04c8af9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt @@ -13,7 +13,6 @@ import net.mamoe.mirai.contact.internal.MemberImpl import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.getGroup -import net.mamoe.mirai.getQQ import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.discardExact diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberMute.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberMute.kt index 7b65d6a23..8e57de969 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberMute.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberMute.kt @@ -76,8 +76,7 @@ sealed class UnmuteEvent : EventOfMute() { // endregion -@Suppress("ClassName") -internal object `Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket` : EventOfMute() { +internal object Unknown0x02DCPacketFlag0x0EMaybeMutePacket : EventOfMute() { override val operator: Member get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket") override val group: Group get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket") override fun toString(): String = "`Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket`" @@ -124,7 +123,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl 0x0Eu -> { //00 00 00 0E 00 08 00 02 00 01 00 // 0A 00 04 01 00 00 00 35 DB 60 A2 11 00 3E 08 07 20 A2 C1 ED AE 03 5A 34 08 A2 FF 8C F0 03 1A 19 08 F4 0E 10 FE 8C D3 EF 05 18 84 A1 F8 F9 06 20 00 28 00 30 A2 FF 8C F0 03 2A 0D 08 00 12 09 08 F4 0E 10 00 18 01 20 00 30 00 38 00 - `Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket` + Unknown0x02DCPacketFlag0x0EMaybeMutePacket } 0x11u -> { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEvent.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEvent.kt index 581064f86..3c5a06195 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEvent.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEvent.kt @@ -11,7 +11,6 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.getGroup -import net.mamoe.mirai.getQQ import net.mamoe.mirai.message.* import net.mamoe.mirai.message.internal.readMessageChain import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt index c75e17509..b42004faa 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt @@ -2,6 +2,8 @@ package net.mamoe.mirai.utils +import net.mamoe.mirai.contact.Group + /** * 在获取 [Group] 对象等操作时可能出现的异常 */