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 {
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))

View File

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

View File

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

View File

@ -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 {

View File

@ -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