Him188 5 năm trước cách đây
mục cha
commit
2e8df632cd

+ 1 - 1
mirai-core-qqandroid/build.gradle.kts

@@ -94,7 +94,7 @@ kotlin {
         val jvmMain by getting {
             dependencies {
                 runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
-                //    api(kotlinx("coroutines-debug", "1.3.5"))
+                //    api(kotlinx("coroutines-debug", Versions.Kotlin.coroutines))
                 api("moe.him188:jcekt:${Versions.jcekt}")
                 api(kotlinx("serialization-runtime", Versions.Kotlin.serialization))
                 //api(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))

+ 36 - 17
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/BotImpl.kt

@@ -7,13 +7,18 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR", "OverridingDeprecatedMember", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+@file:Suppress(
+    "EXPERIMENTAL_API_USAGE",
+    "DEPRECATION_ERROR",
+    "OverridingDeprecatedMember",
+    "INVISIBLE_REFERENCE",
+    "INVISIBLE_MEMBER"
+)
 
 package net.mamoe.mirai.qqandroid
 
 import kotlinx.coroutines.*
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.closeAndJoin
 import net.mamoe.mirai.event.Listener
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.BotOfflineEvent
@@ -61,6 +66,10 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
             if (event.bot != this@BotImpl) {
                 return@subscribeAlways
             }
+            if (!event.bot.isActive) {
+                // bot closed
+                return@subscribeAlways
+            }
             if (!::_network.isInitialized) {
                 // bot 还未登录就被 close
                 return@subscribeAlways
@@ -79,6 +88,7 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
                     }
                     bot.logger.info { "Connection dropped by server or lost, retrying login" }
 
+                    var failed = false
                     val time = measureTime {
                         tailrec suspend fun reconnect() {
                             retryCatching<Unit>(
@@ -95,25 +105,29 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
                                     @OptIn(ThisApiMustBeUsedInWithConnectionLockBlock::class)
                                     relogin((event as? BotOfflineEvent.Dropped)?.cause)
                                 }
-                                BotReloginEvent(bot, (event as? BotOfflineEvent.Dropped)?.cause).broadcast()
+                                launch { BotReloginEvent(bot, (event as? BotOfflineEvent.Dropped)?.cause).broadcast() }
                                 return
                             }.getOrElse {
                                 if (it is LoginFailedException && !it.killBot) {
-                                    logger.info { "Cannot reconnect" }
+                                    logger.info { "Cannot reconnect." }
                                     logger.warning(it)
                                     logger.info { "Retrying in 3s..." }
                                     delay(3000)
                                     return@getOrElse
                                 }
-                                logger.info { "Cannot reconnect" }
-                                throw it
+                                logger.info { "Cannot reconnect due to fatal error." }
+                                bot.cancel(CancellationException("Cannot reconnect due to fatal error.", it))
+                                failed = true
+                                return
                             }
                             reconnect()
                         }
                         reconnect()
                     }
 
-                    logger.info { "Reconnected successfully in ${time.asHumanReadable}" }
+                    if (!failed) {
+                        logger.info { "Reconnected successfully in ${time.asHumanReadable}" }
+                    }
                 }
                 is BotOfflineEvent.Active -> {
                     val cause = event.cause
@@ -122,12 +136,12 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
                     } else {
                         " with exception: " + cause.message
                     }
-                    bot.logger.info { "Bot is closed manually$msg" }
-                    closeAndJoin(CancellationException(event.toString()))
+                    bot.logger.info { "Bot is closed manually: $msg" }
+                    bot.cancel(CancellationException(event.toString()))
                 }
                 is BotOfflineEvent.Force -> {
                     bot.logger.info { "Connection occupied by another android device: ${event.message}" }
-                    closeAndJoin(ForceOfflineException(event.toString()))
+                    bot.cancel(ForceOfflineException(event.toString()))
                 }
             }
         }
@@ -217,6 +231,8 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
 
     init {
         coroutineContext[Job]!!.invokeOnCompletion { throwable ->
+            logger.info { "Bot cancelled" + throwable?.message?.let { ": $it" }.orEmpty() }
+
             kotlin.runCatching {
                 network.close(throwable)
             }
@@ -237,14 +253,17 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
             // already cancelled
             return
         }
-        this.launch {
-            BotOfflineEvent.Active(this@BotImpl, cause).broadcast()
+        GlobalScope.launch {
+            runCatching { BotOfflineEvent.Active(this@BotImpl, cause).broadcast() }.exceptionOrNull()
+                ?.let { logger.error(it) }
         }
-        logger.info { "Bot cancelled" + cause?.message?.let { ": $it" }.orEmpty() }
-        if (cause == null) {
-            supervisorJob.cancel()
-        } else {
-            supervisorJob.cancel(CancellationException("Bot closed", cause))
+
+        if (supervisorJob.isActive) {
+            if (cause == null) {
+                supervisorJob.cancel()
+            } else {
+                supervisorJob.cancel(CancellationException("Bot closed", cause))
+            }
         }
     }
 }

+ 0 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt

@@ -123,7 +123,6 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
             channel.close()
         }
         channel = PlatformSocket()
-        // TODO: 2020/2/14 连接多个服务器, #52
 
         while (isActive) {
             try {

+ 4 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt

@@ -137,7 +137,10 @@ internal open class QQAndroidClient(
                 return@retryCatching
             }.getOrElse {
                 bot.client.serverList.remove(pair)
-                bot.logger.warning(it)
+                if (it !is LoginFailedException) {
+                    // 不要重复打印.
+                    bot.logger.warning(it)
+                }
                 throw it
             }
         }.getOrElse {

+ 3 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt

@@ -50,7 +50,7 @@ suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() }
 abstract class Bot internal constructor(
     val configuration: BotConfiguration
 ) : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI, ContactOrBot {
-    final override val coroutineContext: CoroutineContext =
+    final override val coroutineContext: CoroutineContext = // for id
         configuration.parentCoroutineContext
             .plus(SupervisorJob(configuration.parentCoroutineContext[Job]))
             .plus(configuration.parentCoroutineContext[CoroutineExceptionHandler]
@@ -58,6 +58,8 @@ abstract class Bot internal constructor(
                     logger.error("An exception was thrown under a coroutine of Bot", e)
                 }
             )
+            .plus(CoroutineName("Mirai Bot"))
+
 
     companion object {
         @JvmField