From 997ad1eb63df0460a9cbc5bddd9331364185e30b Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Fri, 16 Apr 2021 21:17:50 +0800 Subject: [PATCH] Rearrange QQAndroidBot --- .../src/commonMain/kotlin/AbstractBot.kt | 44 ++--- .../src/commonMain/kotlin/QQAndroidBot.kt | 186 +++++++++--------- 2 files changed, 113 insertions(+), 117 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt index a2a1dcbbd..2fed1e8bb 100644 --- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt +++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt @@ -20,13 +20,15 @@ package net.mamoe.mirai.internal import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import net.mamoe.mirai.Bot -import net.mamoe.mirai.contact.ContactList -import net.mamoe.mirai.contact.OtherClient +import net.mamoe.mirai.Mirai +import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.* import net.mamoe.mirai.event.EventPriority.MONITOR import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotOfflineEvent -import net.mamoe.mirai.internal.network.DefaultServerList +import net.mamoe.mirai.internal.contact.info.FriendInfoImpl +import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl +import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.ServerList import net.mamoe.mirai.supervisorJob @@ -71,11 +73,17 @@ internal abstract class AbstractBot constructor( // overrides /////////////////////////////////////////////////////////////////////////// - final override val isOnline: Boolean get() = _network.state == NetworkHandler.State.OK + final override val isOnline: Boolean get() = network.state == NetworkHandler.State.OK final override val eventChannel: EventChannel<BotEvent> = GlobalEventChannel.filterIsInstance<BotEvent>().filter { it.bot === this@AbstractBot } - override val otherClients: ContactList<OtherClient> = ContactList() + final override val otherClients: ContactList<OtherClient> = ContactList() + final override val friends: ContactList<Friend> = ContactList() + final override val groups: ContactList<Group> = ContactList() + final override val strangers: ContactList<Stranger> = ContactList() + + final override val asFriend: Friend by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")) } + final override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) } /////////////////////////////////////////////////////////////////////////// // sync (// TODO: 2021/4/14 extract sync logic @@ -96,7 +104,6 @@ internal abstract class AbstractBot constructor( val bot = bot.asQQAndroidBot() if ( !event.bot.isActive // bot closed - || !::_network.isInitialized // bot 还未登录就被 close // || _isConnecting // bot 还在登入 // TODO: 2021/4/14 处理还在登入? ) { // Close network to avoid endless reconnection while network is ok @@ -137,7 +144,7 @@ internal abstract class AbstractBot constructor( } if (event.reconnect) { - if (_network.state != NetworkHandler.State.OK) { + if (network.state != NetworkHandler.State.OK) { // normally closed return@subscribeAlways } @@ -145,15 +152,6 @@ internal abstract class AbstractBot constructor( val causeMessage = event.castOrNull<BotOfflineEvent.CauseAware>()?.cause?.toString() ?: event.toString() bot.logger.info { "Connection lost, retrying login ($causeMessage)" } - bot.asQQAndroidBot().client.run { - if (serverList.isEmpty()) { - bot.bdhSyncer.loadServerListFromCache() - if (serverList.isEmpty()) { - serverList.addAll(DefaultServerList) - } else Unit - } else serverList.removeAt(0) - } - bot.launch { val success: Boolean val time = measureTime { @@ -173,13 +171,13 @@ internal abstract class AbstractBot constructor( internal val serverList: MutableList<Pair<String, Int>> = mutableListOf() // TODO: 2021/4/16 remove old internal val serverListNew = ServerList() // TODO: 2021/4/16 load server list from cache (add a provider) + // bot.bdhSyncer.loadServerListFromCache() // TODO: 2021/4/14 handle serverList - val network: NetworkHandler get() = _network - - @Suppress("PropertyName") - internal lateinit var _network: NetworkHandler + val network: NetworkHandler by lazy { + createNetworkHandler(coroutineContext) + } /** @@ -188,10 +186,11 @@ internal abstract class AbstractBot constructor( */ final override suspend fun login() { if (!isActive) error("Bot is already closed and cannot relogin. Please create a new Bot instance then do login.") - network + network.resumeConnection() } protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler + protected abstract suspend fun sendLogout() // endregion @@ -211,11 +210,10 @@ internal abstract class AbstractBot constructor( } groups.delegate.clear() // job is cancelled, so child jobs are to be cancelled friends.delegate.clear() + strangers.delegate.clear() } } - protected abstract suspend fun sendLogout() - override fun close(cause: Throwable?) { if (!this.isActive) { // already cancelled diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index fa6f12343..0c65aca9e 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -12,13 +12,11 @@ package net.mamoe.mirai.internal import kotlinx.coroutines.sync.Mutex import net.mamoe.mirai.Bot -import net.mamoe.mirai.LowLevelApi import net.mamoe.mirai.Mirai -import net.mamoe.mirai.contact.* +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.OtherClientInfo import net.mamoe.mirai.internal.contact.OtherClientImpl import net.mamoe.mirai.internal.contact.checkIsGroupImpl -import net.mamoe.mirai.internal.contact.info.FriendInfoImpl -import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.handler.* @@ -56,58 +54,72 @@ internal class QQAndroidBot constructor( internal val account: BotAccount, configuration: BotConfiguration ) : AbstractBot(configuration, account.id), SsoContext { - val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this) - - /////////////////////////////////////////////////////////////////////////// - // Account secrets cache - /////////////////////////////////////////////////////////////////////////// - - // We cannot extract these logics until we rewrite the network framework. - - private val cacheDir: File by lazy { - configuration.workingDir.resolve(bot.configuration.cacheDir).apply { mkdirs() } - } - internal val accountSecretsFile: File by lazy { - cacheDir.resolve("account.secrets") - } - - private fun saveSecrets(secrets: AccountSecretsImpl) { - if (secrets.wLoginSigInfoField == null) return - - accountSecretsFile.writeBytes( - TEA.encrypt( - secrets.toByteArray(AccountSecretsImpl.serializer()), - account.passwordMd5 - ) - ) - - network.context.logger.info { "Saved account secrets to local cache for fast login." } - } - - init { - if (configuration.loginCacheEnabled) { - eventChannel.parentScope(this).subscribeAlways<WtLogin.Login.LoginPacketResponse> { event -> - if (event is WtLogin.Login.LoginPacketResponse.Success) { - if (client.wLoginSigInfoInitialized) { - saveSecrets(AccountSecretsImpl(client)) - } - } - } - } - } - - /////////////////////////// accounts secrets end - override lateinit var client: QQAndroidClient - - override val bot: QQAndroidBot get() = this + val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this) internal var firstLoginSucceed: Boolean = false - inline val json get() = configuration.json + /////////////////////////////////////////////////////////////////////////// + // network + /////////////////////////////////////////////////////////////////////////// - override val friends: ContactList<Friend> = ContactList() + // TODO: 2021/4/14 bdhSyncer.loadFromCache() when login + + override suspend fun sendLogout() { + network.sendWithoutExpect(StatSvc.Register.offline(client)) + } + + override fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler { + val context = NetworkHandlerContextImpl(this, this) + return SelectorNetworkHandler( + context, + FactoryKeepAliveNetworkHandlerSelector(NettyNetworkHandlerFactory, serverListNew, context) + ) // We can move the factory to configuration but this is not necessary for now. + } + + + suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect( + timeoutMillis: Long = 5000, + retry: Int = 2 + ): E = network.run { sendAndExpect(timeoutMillis, retry) } + + suspend inline fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E = + network.run { sendAndExpect(timeoutMillis, retry) } + + /** + * 获取 获取群公告 所需的 bkn 参数 + * */ + val bkn: Int + get() = client.wLoginSigInfo.sKey.data + .fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() } + .and(Int.MAX_VALUE) + + /////////////////////////////////////////////////////////////////////////// + // contacts + /////////////////////////////////////////////////////////////////////////// + + override lateinit var nick: String + + @JvmField + val groupListModifyLock = Mutex() + + // internally visible only + fun getGroupByUin(uin: Long): Group { + return getGroupByUinOrNull(uin) + ?: throw NoSuchElementException("Group ${Mirai.calculateGroupCodeByGroupUin(uin)} not found") + } + + fun getGroupByUinOrNull(uin: Long): Group? { + return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin } + } + + + /////////////////////////////////////////////////////////////////////////// + // contact cache + /////////////////////////////////////////////////////////////////////////// + + inline val json get() = configuration.json val friendListCache: FriendListCache? by lazy { if (!configuration.contactListCache.friendListCacheEnabled) return@lazy null @@ -146,58 +158,44 @@ internal class QQAndroidBot constructor( } } - override lateinit var nick: String + /////////////////////////////////////////////////////////////////////////// + // Account secrets cache + /////////////////////////////////////////////////////////////////////////// - override val asFriend: Friend by lazy { - @OptIn(LowLevelApi::class) - Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")) + // We cannot extract these logics until we rewrite the network framework. + + private val cacheDir: File by lazy { + configuration.workingDir.resolve(bot.configuration.cacheDir).apply { mkdirs() } + } + private val accountSecretsFile: File by lazy { + cacheDir.resolve("account.secrets") } - override val groups: ContactList<Group> = ContactList() + private fun saveSecrets(secrets: AccountSecretsImpl) { + if (secrets.wLoginSigInfoField == null) return - // TODO: 2021/4/14 bdhSyncer.loadFromCache() when login + accountSecretsFile.writeBytes( + TEA.encrypt( + secrets.toByteArray(AccountSecretsImpl.serializer()), + account.passwordMd5 + ) + ) - override suspend fun sendLogout() { - network.sendWithoutExpect(StatSvc.Register.offline(client)) + network.context.logger.info { "Saved account secrets to local cache for fast login." } } - override fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler { - val context = NetworkHandlerContextImpl(this, this) - return SelectorNetworkHandler( - context, - FactoryKeepAliveNetworkHandlerSelector(NettyNetworkHandlerFactory, serverListNew, context) - ) // We can move the factory to configuration but this is not necessary for now. + init { + if (configuration.loginCacheEnabled) { + eventChannel.parentScope(this).subscribeAlways<WtLogin.Login.LoginPacketResponse> { event -> + if (event is WtLogin.Login.LoginPacketResponse.Success) { + if (client.wLoginSigInfoInitialized) { + saveSecrets(AccountSecretsImpl(client)) + } + } + } + } } - @JvmField - val groupListModifyLock = Mutex() + /////////////////////////// accounts secrets end - // internally visible only - fun getGroupByUin(uin: Long): Group { - return getGroupByUinOrNull(uin) - ?: throw NoSuchElementException("Group ${Mirai.calculateGroupCodeByGroupUin(uin)} not found") - } - - fun getGroupByUinOrNull(uin: Long): Group? { - return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin } - } - - - suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect( - timeoutMillis: Long = 5000, - retry: Int = 2 - ): E = network.run { sendAndExpect(timeoutMillis, retry) } - - suspend inline fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E = - network.run { sendAndExpect(timeoutMillis, retry) } - - /** - * 获取 获取群公告 所需的 bkn 参数 - * */ - val bkn: Int - get() = client.wLoginSigInfo.sKey.data - .fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() } - .and(Int.MAX_VALUE) - override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) } - override val strangers: ContactList<Stranger> = ContactList() }