Skip to content

Commit 545e5bb

Browse files
StageGuardHim188
andauthored
[core] Provide isFirstLogin and getReLoginCause in BotAuthInfo (#2664)
* [core] provide `isFirstLogin` and `getReLoginCause` in `BotAuthInfo` * [core] provide `reAuthCause` as sealed class * [core] add test * Update mirai-core-api/src/commonMain/kotlin/auth/BotAuthorization.kt 我觉得这个描述不太好,因为这里是 `authorize` 过程而不是 `login` 过程 Co-authored-by: Him188 <[email protected]> * [core] optimize docs and rename * import * revert linebreak --------- Co-authored-by: Him188 <[email protected]>
1 parent 8ff64d4 commit 545e5bb

File tree

9 files changed

+384
-5
lines changed

9 files changed

+384
-5
lines changed

mirai-core-api/compatibility-validation/android/api/android.api

+38
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,48 @@ public final class net/mamoe/mirai/_MiraiInstance {
172172
public static final fun set (Lnet/mamoe/mirai/IMirai;)V
173173
}
174174

175+
public abstract class net/mamoe/mirai/auth/AuthReason {
176+
public abstract fun getBot ()Lnet/mamoe/mirai/Bot;
177+
public abstract fun getMessage ()Ljava/lang/String;
178+
}
179+
180+
public final class net/mamoe/mirai/auth/AuthReason$FastLoginError : net/mamoe/mirai/auth/AuthReason {
181+
public fun getBot ()Lnet/mamoe/mirai/Bot;
182+
public fun getMessage ()Ljava/lang/String;
183+
}
184+
185+
public final class net/mamoe/mirai/auth/AuthReason$ForceOffline : net/mamoe/mirai/auth/AuthReason {
186+
public fun getBot ()Lnet/mamoe/mirai/Bot;
187+
public fun getMessage ()Ljava/lang/String;
188+
}
189+
190+
public final class net/mamoe/mirai/auth/AuthReason$FreshLogin : net/mamoe/mirai/auth/AuthReason {
191+
public fun getBot ()Lnet/mamoe/mirai/Bot;
192+
public fun getMessage ()Ljava/lang/String;
193+
}
194+
195+
public final class net/mamoe/mirai/auth/AuthReason$MsfOffline : net/mamoe/mirai/auth/AuthReason {
196+
public fun getBot ()Lnet/mamoe/mirai/Bot;
197+
public fun getMessage ()Ljava/lang/String;
198+
}
199+
200+
public final class net/mamoe/mirai/auth/AuthReason$NetworkError : net/mamoe/mirai/auth/AuthReason {
201+
public fun getBot ()Lnet/mamoe/mirai/Bot;
202+
public fun getMessage ()Ljava/lang/String;
203+
}
204+
205+
public final class net/mamoe/mirai/auth/AuthReason$Unknown : net/mamoe/mirai/auth/AuthReason {
206+
public fun getBot ()Lnet/mamoe/mirai/Bot;
207+
public final fun getCause ()Ljava/lang/Throwable;
208+
public fun getMessage ()Ljava/lang/String;
209+
}
210+
175211
public abstract interface class net/mamoe/mirai/auth/BotAuthInfo {
176212
public abstract fun getConfiguration ()Lnet/mamoe/mirai/utils/BotConfiguration;
177213
public abstract fun getDeviceInfo ()Lnet/mamoe/mirai/utils/DeviceInfo;
178214
public abstract fun getId ()J
215+
public abstract fun getReason ()Lnet/mamoe/mirai/auth/AuthReason;
216+
public abstract fun isFirstLogin ()Z
179217
}
180218

181219
public abstract interface class net/mamoe/mirai/auth/BotAuthResult {

mirai-core-api/compatibility-validation/jvm/api/jvm.api

+38
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,48 @@ public final class net/mamoe/mirai/_MiraiInstance {
172172
public static final fun set (Lnet/mamoe/mirai/IMirai;)V
173173
}
174174

175+
public abstract class net/mamoe/mirai/auth/AuthReason {
176+
public abstract fun getBot ()Lnet/mamoe/mirai/Bot;
177+
public abstract fun getMessage ()Ljava/lang/String;
178+
}
179+
180+
public final class net/mamoe/mirai/auth/AuthReason$FastLoginError : net/mamoe/mirai/auth/AuthReason {
181+
public fun getBot ()Lnet/mamoe/mirai/Bot;
182+
public fun getMessage ()Ljava/lang/String;
183+
}
184+
185+
public final class net/mamoe/mirai/auth/AuthReason$ForceOffline : net/mamoe/mirai/auth/AuthReason {
186+
public fun getBot ()Lnet/mamoe/mirai/Bot;
187+
public fun getMessage ()Ljava/lang/String;
188+
}
189+
190+
public final class net/mamoe/mirai/auth/AuthReason$FreshLogin : net/mamoe/mirai/auth/AuthReason {
191+
public fun getBot ()Lnet/mamoe/mirai/Bot;
192+
public fun getMessage ()Ljava/lang/String;
193+
}
194+
195+
public final class net/mamoe/mirai/auth/AuthReason$MsfOffline : net/mamoe/mirai/auth/AuthReason {
196+
public fun getBot ()Lnet/mamoe/mirai/Bot;
197+
public fun getMessage ()Ljava/lang/String;
198+
}
199+
200+
public final class net/mamoe/mirai/auth/AuthReason$NetworkError : net/mamoe/mirai/auth/AuthReason {
201+
public fun getBot ()Lnet/mamoe/mirai/Bot;
202+
public fun getMessage ()Ljava/lang/String;
203+
}
204+
205+
public final class net/mamoe/mirai/auth/AuthReason$Unknown : net/mamoe/mirai/auth/AuthReason {
206+
public fun getBot ()Lnet/mamoe/mirai/Bot;
207+
public final fun getCause ()Ljava/lang/Throwable;
208+
public fun getMessage ()Ljava/lang/String;
209+
}
210+
175211
public abstract interface class net/mamoe/mirai/auth/BotAuthInfo {
176212
public abstract fun getConfiguration ()Lnet/mamoe/mirai/utils/BotConfiguration;
177213
public abstract fun getDeviceInfo ()Lnet/mamoe/mirai/utils/DeviceInfo;
178214
public abstract fun getId ()J
215+
public abstract fun getReason ()Lnet/mamoe/mirai/auth/AuthReason;
216+
public abstract fun isFirstLogin ()Z
179217
}
180218

181219
public abstract interface class net/mamoe/mirai/auth/BotAuthResult {

mirai-core-api/src/commonMain/kotlin/auth/BotAuthorization.kt

+110
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
package net.mamoe.mirai.auth
1111

12+
import net.mamoe.mirai.Bot
1213
import net.mamoe.mirai.BotFactory
1314
import net.mamoe.mirai.Mirai
15+
import net.mamoe.mirai.event.events.BotOfflineEvent
1416
import net.mamoe.mirai.network.LoginFailedException
1517
import net.mamoe.mirai.network.RetryLaterException
1618
import net.mamoe.mirai.utils.*
@@ -102,6 +104,114 @@ public interface BotAuthInfo {
102104
public val id: Long
103105
public val deviceInfo: DeviceInfo
104106
public val configuration: BotConfiguration
107+
108+
/**
109+
* 是否是首次登录
110+
*
111+
* 首次登录指的是首次调用 [Bot.login] 进行登录,直到登录成功的过程。
112+
*
113+
* 若在首次登录过程中多次进入[认证流程][BotAuthorization.authorize],则这流程些均被视为首次登录。
114+
*
115+
* @see Bot.login
116+
* @see BotAuthorization.authorize
117+
*/
118+
public val isFirstLogin: Boolean
119+
120+
/**
121+
* 导致进入[认证流程][BotAuthorization.authorize]的原因。
122+
*/
123+
public val reason: AuthReason
124+
}
125+
126+
/**
127+
* 导致进行[认证流程][BotAuthorization.authorize]的原因
128+
*/
129+
public sealed class AuthReason {
130+
public abstract val bot: Bot
131+
public abstract val message: String?
132+
133+
/**
134+
* Bot 全新[登录][Bot.login]
135+
*
136+
* 全新登录指登录前本地没有任何当前 Bot 的登录缓存信息而进行的登录。
137+
*
138+
* 全新登录时将会进入[认证流程][BotAuthorization.authorize]。
139+
*
140+
* @see Bot.login
141+
* @see FastLoginError
142+
*/
143+
public class FreshLogin @MiraiInternalApi constructor(
144+
override val bot: Bot,
145+
override val message: String?
146+
) : AuthReason()
147+
148+
/**
149+
* Bot 被挤下线
150+
*
151+
* 当 Bot 账号在其他客户端使用相同(或相似)协议登录时,Bot 会下线,
152+
* 被挤下线后当前的登录会话将失效。
153+
*
154+
* 当 [BotConfiguration.autoReconnectOnForceOffline] 为 `true` 时,
155+
* Bot 会尝试重新登录,并会以此原因进入[认证流程][BotAuthorization.authorize]。
156+
*
157+
* @see BotConfiguration.autoReconnectOnForceOffline
158+
* @see BotOfflineEvent.Force
159+
*/
160+
public class ForceOffline @MiraiInternalApi constructor(
161+
override val bot: Bot,
162+
override val message: String?
163+
) : AuthReason()
164+
165+
/**
166+
* Bot 被服务器断开
167+
*
168+
* 因其他原因导致 Bot 被服务器断开。这些原因包括账号被封禁、被其他客户端手动下线等,
169+
* 被服务器断开下线后当前的登录会话将失效。
170+
*
171+
* Bot 会尝试重新登录,并会以此原因进入[认证流程][BotAuthorization.authorize]。
172+
*
173+
* @see BotOfflineEvent.MsfOffline
174+
*/
175+
public class MsfOffline @MiraiInternalApi constructor(
176+
override val bot: Bot,
177+
override val message: String?
178+
) : AuthReason()
179+
180+
/**
181+
* 由网络原因引起的掉线
182+
*
183+
* 一般情况下,Bot 被服务器断开后会尝试重新登录。
184+
*
185+
* 由网络问题引起的掉线不一定会使当前的登录会话失效,
186+
* 仅登录会话失效时 Bot 会以此原因进入[认证流程][BotAuthorization.authorize]。
187+
*/
188+
public class NetworkError @MiraiInternalApi constructor(
189+
override val bot: Bot,
190+
override val message: String?
191+
) : AuthReason()
192+
193+
/**
194+
* 快速登录失败
195+
*
196+
* Bot 账号首次 [登录][Bot.login] 成功后,会保存登录缓存信息用于下次登录。
197+
*
198+
* 下次登录时,Bot 会首先使用登录缓存信息尝试快速登录,
199+
* 若快速登录失败,则会以此原因进入[认证流程][BotAuthorization.authorize]。
200+
*
201+
* @see BotAuthorization.authorize
202+
* @see Bot.login
203+
*/
204+
public class FastLoginError @MiraiInternalApi constructor(
205+
override val bot: Bot,
206+
override val message: String?
207+
) : AuthReason()
208+
209+
public class Unknown @MiraiInternalApi constructor(
210+
override val bot: Bot,
211+
public val cause: Throwable?
212+
) : AuthReason() {
213+
override val message: String? = cause?.message
214+
}
105215
}
106216

107217
@NotStableForInheritance

mirai-core/src/commonMain/kotlin/QQAndroidBot.kt

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package net.mamoe.mirai.internal
1212
import kotlinx.atomicfu.atomic
1313
import kotlinx.coroutines.*
1414
import net.mamoe.mirai.Bot
15+
import net.mamoe.mirai.auth.AuthReason
1516
import net.mamoe.mirai.event.broadcast
1617
import net.mamoe.mirai.event.events.BotOfflineEvent
1718
import net.mamoe.mirai.event.events.BotOnlineEvent
@@ -141,14 +142,18 @@ internal open class QQAndroidBot constructor(
141142
cause is ForceOfflineException -> {
142143
eventDispatcher.broadcastAsync(BotOfflineEvent.Force(bot, cause.title, cause.message))
143144
}
145+
144146
cause is StatSvc.ReqMSFOffline.MsfOfflineToken -> {
145147
eventDispatcher.broadcastAsync(BotOfflineEvent.MsfOffline(bot, cause))
146148
}
149+
147150
cause is NetworkException && cause.recoverable -> {
148151
eventDispatcher.broadcastAsync(BotOfflineEvent.Dropped(bot, cause))
149152
}
153+
150154
cause is BotClosedByEvent -> {
151155
}
156+
152157
else -> {
153158
// any other unexpected exceptions considered as an error
154159

@@ -165,6 +170,17 @@ internal open class QQAndroidBot constructor(
165170
}
166171
}
167172
},
173+
StateChangedObserver("ReLoginCauseCatcher", State.OK, State.CLOSED) { new ->
174+
get(SsoProcessor).authReason = when (val cause = new.getCause()) {
175+
is ForceOfflineException -> AuthReason.ForceOffline(bot, cause.message)
176+
is StatSvc.ReqMSFOffline.MsfOfflineToken -> AuthReason.MsfOffline(bot, cause.message)
177+
is NetworkException -> AuthReason.NetworkError(bot, cause.message)
178+
else -> AuthReason.Unknown(bot, cause)
179+
}
180+
},
181+
StateChangedObserver("FirstLoginObserver", State.OK) {
182+
get(SsoProcessor).isFirstLogin = false
183+
}
168184
).safe(logger.subLogger("StateObserver")) + LoggingStateObserver.createLoggingIfEnabled()
169185
}
170186

mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt

+20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.network.components
1111

1212
import kotlinx.atomicfu.AtomicRef
1313
import kotlinx.atomicfu.atomic
14+
import net.mamoe.mirai.auth.AuthReason
1415
import net.mamoe.mirai.auth.BotAuthInfo
1516
import net.mamoe.mirai.auth.BotAuthorization
1617
import net.mamoe.mirai.internal.network.Packet
@@ -58,6 +59,9 @@ internal interface SsoProcessor {
5859
val firstLoginSucceed: Boolean get() = firstLoginResult?.success ?: false
5960
val registerResp: StatSvc.Register.Response?
6061

62+
var isFirstLogin: Boolean
63+
var authReason: AuthReason
64+
6165
/**
6266
* Do login. Throws [LoginFailedException] if failed
6367
*/
@@ -147,13 +151,22 @@ internal open class SsoProcessorImpl(
147151
override val ssoSession: SsoSession get() = client
148152
private val components get() = ssoContext.bot.components
149153

154+
override var isFirstLogin: Boolean = true
155+
override var authReason: AuthReason by lateinitMutableProperty {
156+
AuthReason.FreshLogin(ssoContext.bot, null)
157+
}
158+
150159
private val botAuthInfo = object : BotAuthInfo {
151160
override val id: Long
152161
get() = ssoContext.bot.id
153162
override val deviceInfo: DeviceInfo
154163
get() = ssoContext.device
155164
override val configuration: BotConfiguration
156165
get() = ssoContext.bot.configuration
166+
override val isFirstLogin: Boolean
167+
get() = this@SsoProcessorImpl.isFirstLogin
168+
override val reason: AuthReason
169+
get() = this@SsoProcessorImpl.authReason
157170
}
158171

159172
protected open suspend fun doSlowLogin(
@@ -215,6 +228,11 @@ internal open class SsoProcessorImpl(
215228
kotlin.runCatching {
216229
doFastLogin(handler)
217230
}.onFailure { e ->
231+
// first fast-login exception should also be considered as re-auth cause.
232+
if (isFirstLogin) {
233+
authReason = AuthReason.FastLoginError(ssoContext.bot, e.message)
234+
}
235+
218236
initAndStartAuthControl()
219237
authControl!!.exceptionCollector.collect(e)
220238

@@ -224,6 +242,8 @@ internal open class SsoProcessorImpl(
224242
loginSuccess()
225243

226244
return
245+
} else if (isFirstLogin) {
246+
authReason = AuthReason.FreshLogin(ssoContext.bot, null)
227247
}
228248
}
229249

mirai-core/src/commonTest/kotlin/network/auth/AbstractBotAuthTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal abstract class AbstractBotAuthTest : AbstractCommonNHTestWithSelector()
2626
overrideComponents[SsoProcessor] = SsoProcessorImpl(overrideComponents[SsoProcessorContext])
2727
}
2828

29-
protected fun setAuthorization(authorize: (session: BotAuthSession, info: BotAuthInfo) -> BotAuthResult) {
29+
protected fun setAuthorization(authorize: suspend (session: BotAuthSession, info: BotAuthInfo) -> BotAuthResult) {
3030
// Run a real SsoProcessor, just without sending packets
3131
bot.account.authorization = object : BotAuthorization {
3232
override suspend fun authorize(session: BotAuthSession, info: BotAuthInfo): BotAuthResult {

0 commit comments

Comments
 (0)