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()
 }