This commit is contained in:
Him188 2020-06-01 20:38:31 +08:00
parent 2dcd8ffea0
commit 2e8df632cd
5 changed files with 44 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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