[core] Code improvement

This commit is contained in:
Karlatemp 2023-02-15 23:49:55 +08:00
parent 2ab424c989
commit bf50e8a770
No known key found for this signature in database
GPG Key ID: BA173CA2B9956C59
3 changed files with 70 additions and 42 deletions

View File

@ -18,51 +18,71 @@ import net.mamoe.mirai.internal.network.QRCodeLoginData
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.internal.utils.MiraiProtocolInternal.Companion.asInternal
import net.mamoe.mirai.utils.LoginSolver
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.toUHexString
internal interface QRCodeLoginProcessor {
suspend fun process(handler: NetworkHandler, client: QQAndroidClient): QRCodeLoginData
suspend fun process(handler: NetworkHandler, client: QQAndroidClient): QRCodeLoginData = error("Not implemented")
/**
* Allocate a special processor for once login request
*/
fun prepareProcess(handler: NetworkHandler, client: QQAndroidClient): QRCodeLoginProcessor =
error("Not implemented")
companion object : ComponentKey<QRCodeLoginProcessor> {
internal val NOOP = object : QRCodeLoginProcessor {
override suspend fun process(handler: NetworkHandler, client: QQAndroidClient): QRCodeLoginData {
error("NOOP")
}
}
internal val NOOP = object : QRCodeLoginProcessor {}
//TODO: these exception should throw in network instead here.
fun parse(ssoContext: SsoProcessorContext, logger: MiraiLogger): QRCodeLoginProcessor {
if (!ssoContext.bot.configuration.qrCodeLogin) return NOOP
check(ssoContext.bot.configuration.protocol == BotConfiguration.MiraiProtocol.ANDROID_WATCH) {
if (!ssoContext.bot.configuration.doQRCodeLogin) return NOOP
check(ssoContext.bot.configuration.protocol.asInternal.canDoQRCodeLogin) {
"The login protocol must be ANDROID_WATCH while enabling qrcode login." +
"Set it by `bot.configuration.protocol = BotConfiguration.MiraiProtocol.ANDROID_WATCH`."
}
val loginSolver = ssoContext.bot.configuration.loginSolver
?: throw IllegalStateException("No LoginSolver found while enabling qrcode login. " +
"Please provide by BotConfiguration.loginSolver. " +
"For example use `BotFactory.newBot(...) { loginSolver = yourLoginSolver}` in Kotlin, " +
"use `BotFactory.newBot(..., new BotConfiguration() {{ setLoginSolver(yourLoginSolver) }})` in Java.")
val qrCodeLoginListener = loginSolver.qrCodeLoginListener
?: throw IllegalStateException("No QRCodeLoginListener provided in LoginSolver while enabling qrcode login.")
return QRCodeLoginProcessorImpl(qrCodeLoginListener, logger)
return QRCodeLoginProcessorPreLoaded(ssoContext, logger)
}
}
}
internal class QRCodeLoginProcessorPreLoaded(
private val ssoContext: SsoProcessorContext,
private val logger: MiraiLogger,
) : QRCodeLoginProcessor {
override fun prepareProcess(handler: NetworkHandler, client: QQAndroidClient): QRCodeLoginProcessor {
val loginSolver = ssoContext.bot.configuration.loginSolver
?: throw IllegalStateException(
"No LoginSolver found while enabling qrcode login. " +
"Please provide by BotConfiguration.loginSolver. " +
"For example use `BotFactory.newBot(...) { loginSolver = yourLoginSolver}` in Kotlin, " +
"use `BotFactory.newBot(..., new BotConfiguration() {{ setLoginSolver(yourLoginSolver) }})` in Java."
)
val qrCodeLoginListener = loginSolver.createQRCodeLoginListener(client.bot)
return QRCodeLoginProcessorImpl(qrCodeLoginListener, logger)
}
}
internal class QRCodeLoginProcessorImpl(
private val listener: LoginSolver.QRCodeLoginListener,
private val qrCodeLoginListener: LoginSolver.QRCodeLoginListener,
private val logger: MiraiLogger,
) : QRCodeLoginProcessor {
private val lock = Mutex(false)
private var state by atomic(LoginSolver.QRCodeLoginListener.State.DEFAULT)
private var state = atomic(LoginSolver.QRCodeLoginListener.State.DEFAULT)
private suspend fun requestQRCode(handler: NetworkHandler, client: QQAndroidClient) : WtLogin.TransEmp.TransEmpResponse.FetchQRCode {
private suspend fun requestQRCode(
handler: NetworkHandler,
client: QQAndroidClient
): WtLogin.TransEmp.TransEmpResponse.FetchQRCode {
logger.debug { "requesting qrcode." }
val resp = handler.sendAndExpect(WtLogin.TransEmp.FetchQRCode(client), attempts = 1)
check(resp is WtLogin.TransEmp.TransEmpResponse.FetchQRCode) { "Cannot fetch qrcode, resp=$resp" }
listener.onFetchQRCode(handler.context.bot, resp.imageData)
qrCodeLoginListener.onFetchQRCode(handler.context.bot, resp.imageData)
return resp
}
@ -70,19 +90,20 @@ internal class QRCodeLoginProcessorImpl(
handler: NetworkHandler,
client: QQAndroidClient,
sig: ByteArray
) : WtLogin.TransEmp.TransEmpResponse {
): WtLogin.TransEmp.TransEmpResponse {
logger.debug { "querying qrcode state. sig=${sig.toUHexString()}" }
val resp = handler.sendAndExpect(WtLogin.TransEmp.QueryQRCodeStatus(client, sig), attempts = 1, timeout = 500)
check(
resp is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus ||
resp is WtLogin.TransEmp.TransEmpResponse.QRCodeConfirmed
resp is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus || resp is WtLogin.TransEmp.TransEmpResponse.QRCodeConfirmed
) { "Cannot query qrcode status, resp=$resp" }
lock.withLock {
val currState = resp.mapProtocolState()
if (currState != state) {
state = currState
val currentState = state.value
val newState = resp.mapProtocolState()
if (currentState != newState && state.compareAndSet(currentState, newState)) {
logger.debug { "qrcode state changed: $state" }
listener.onStatusChanged(handler.context.bot, state)
qrCodeLoginListener.onStatusChanged(handler.context.bot, newState)
}
}
return resp
@ -92,19 +113,21 @@ internal class QRCodeLoginProcessorImpl(
main@ while (true) { // TODO: add new bot config property to set times of fetching qrcode
val qrCodeData = requestQRCode(handler, client)
state@ while (true) {
when(val status = queryQRCodeStatus(handler, client, qrCodeData.sig)) {
when (val status = queryQRCodeStatus(handler, client, qrCodeData.sig)) {
is WtLogin.TransEmp.TransEmpResponse.QRCodeConfirmed -> {
return status.data
}
is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus -> when(status.state) {
is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus -> when (status.state) {
WtLogin.TransEmp.TransEmpResponse.QRCodeStatus.State.TIMEOUT,
WtLogin.TransEmp.TransEmpResponse.QRCodeStatus.State.CANCELLED -> {
break@state
}
else -> { } // WAITING_FOR_SCAN or WAITING_FOR_CONFIRM
else -> {} // WAITING_FOR_SCAN or WAITING_FOR_CONFIRM
}
// status is FetchQRCode, which is unreachable.
else -> { error("query qrcode status packet should not be FetchQRCode.") }
else -> {
error("query qrcode status packet should not be FetchQRCode.")
}
}
delay(5000)
}
@ -113,7 +136,7 @@ internal class QRCodeLoginProcessorImpl(
private fun WtLogin.TransEmp.TransEmpResponse.mapProtocolState(): LoginSolver.QRCodeLoginListener.State {
return when (this) {
is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus -> when(this.state) {
is WtLogin.TransEmp.TransEmpResponse.QRCodeStatus -> when (this.state) {
WtLogin.TransEmp.TransEmpResponse.QRCodeStatus.State.WAITING_FOR_SCAN ->
LoginSolver.QRCodeLoginListener.State.WAITING_FOR_SCAN

View File

@ -165,7 +165,7 @@ internal class SsoProcessorImpl(
val qrCodeLoginProcessor = ssoContext.bot.components[QRCodeLoginProcessor]
if (qrCodeLoginProcessor !== QRCodeLoginProcessor.NOOP) {
val qrcodeLoginData = qrCodeLoginProcessor.process(handler, client)
val qrcodeLoginData = qrCodeLoginProcessor.prepareProcess(handler, client).process(handler, client)
SlowLoginImpl(handler, LoginType.QRCode(qrcodeLoginData)).doLogin()
} else {
SlowLoginImpl(handler, LoginType.Password).doLogin()
@ -229,7 +229,7 @@ internal class SsoProcessorImpl(
private inner class SlowLoginImpl(
handler: NetworkHandler,
private val type: LoginType
private val loginType: LoginType
) : LoginStrategy(handler) {
private fun loginSolverNotNull(): LoginSolver {
@ -270,14 +270,15 @@ internal class SsoProcessorImpl(
override suspend fun doLogin() = withExceptionCollector {
fun QQAndroidClient.getWtLogin9Packet(allowSlider: Boolean, type: LoginType) = when(type) {
is LoginType.Password -> WtLogin9.Password(this, allowSlider)
is LoginType.QRCode -> WtLogin9.QRCode(this, type.qrCodeLoginData)
@Suppress("FunctionName")
fun SSOWtLogin9(allowSlider: Boolean) = when (loginType) {
is LoginType.Password -> WtLogin9.Password(client, allowSlider)
is LoginType.QRCode -> WtLogin9.QRCode(client, loginType.qrCodeLoginData)
}
var allowSlider = sliderSupported || bot.configuration.protocol == MiraiProtocol.ANDROID_PHONE
var response: LoginPacketResponse = client.getWtLogin9Packet(allowSlider, type).sendAndExpect()
var response: LoginPacketResponse = SSOWtLogin9(allowSlider).sendAndExpect()
mainloop@ while (true) {
when (response) {
@ -297,7 +298,7 @@ internal class SsoProcessorImpl(
check(result is DeviceVerificationResultImpl)
response = when (result) {
is UrlDeviceVerificationResult -> {
client.getWtLogin9Packet(allowSlider, type).sendAndExpect()
SSOWtLogin9(allowSlider).sendAndExpect()
}
is SmsDeviceVerificationResult -> {
@ -324,7 +325,7 @@ internal class SsoProcessorImpl(
collectThrow(error)
}
response = if (ticket == null) {
client.getWtLogin9Packet(allowSlider, type).sendAndExpect()
SSOWtLogin9(allowSlider).sendAndExpect()
} else {
WtLogin2.SubmitSliderCaptcha(client, ticket).sendAndExpect()
}

View File

@ -25,6 +25,7 @@ internal class MiraiProtocolInternal(
@JvmField internal val sign: String,
@JvmField internal val buildTime: Long,
@JvmField internal val ssoVersion: Int,
@JvmField internal val canDoQRCodeLogin: Boolean = false,
) {
internal companion object {
internal val protocols = EnumMap<MiraiProtocol, MiraiProtocolInternal>(MiraiProtocol::class)
@ -70,6 +71,7 @@ internal class MiraiProtocolInternal(
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
1559564731L,
5,
canDoQRCodeLogin = true,
)
protocols[MiraiProtocol.IPAD] = MiraiProtocolInternal(
"com.tencent.minihd.qq",
@ -96,5 +98,7 @@ internal class MiraiProtocolInternal(
7,
)
}
inline val MiraiProtocol.asInternal: MiraiProtocolInternal get() = get(this)
}
}