From 5db71cd299e94f3abab59b1236d2aa7e00aab0cd Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Thu, 24 Dec 2020 00:34:13 +0800 Subject: [PATCH] Disable SliderCaptcha. #745 --- .../kotlin/network/LoginFailedException.kt | 5 ++ .../commonMain/kotlin/utils/LoginSolver.kt | 5 ++ .../src/commonMain/kotlin/QQAndroidBot.kt | 2 +- .../kotlin/network/BotNetworkHandler.kt | 2 +- .../network/QQAndroidBotNetworkHandler.kt | 46 +++++++++++++++---- .../network/protocol/packet/login/WtLogin.kt | 9 ++-- 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt b/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt index 98c2e60af..7d638159b 100644 --- a/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt +++ b/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt @@ -61,6 +61,11 @@ public class NoStandardInputForCaptchaException @MiraiInternalApi constructor( @MiraiExperimentalApi("Will be removed when SMS login is supported") public class UnsupportedSMSLoginException(message: String?) : LoginFailedException(true, message) +/** + * 无法完成滑块验证 + */ +public class NotSupportedSliderCaptchaException(message: String?) : LoginFailedException(true, message) + /** * 非 mirai 实现的异常 */ diff --git a/mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt b/mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt index 8a097e707..6bdc9ddf0 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt @@ -45,6 +45,10 @@ public abstract class LoginSolver { */ public abstract suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? + // TODO: 2020-12-24 滑动验证码支持 + @MiraiInternalApi + public open val isSliderCaptchaSupport: Boolean get() = false + /** * 处理滑动验证码. * 返回 null 以表示无法处理验证码, 将会刷新验证码或重试登录. @@ -89,6 +93,7 @@ public abstract class LoginSolver { public fun getDefault(): LoginSolver = Default ?: error("LoginSolver is not provided by default on your platform. Please specify by BotConfiguration.loginSolver") } + } /** diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index c6a13908d..0fc8e6f92 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -93,7 +93,7 @@ internal class QQAndroidBot constructor( @Throws(LoginFailedException::class) // only override suspend fun relogin(cause: Throwable?) { client.useNextServers { host, port -> - network.closeEverythingAndRelogin(host, port, cause) + network.closeEverythingAndRelogin(host, port, cause, 0) } } diff --git a/mirai-core/src/commonMain/kotlin/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/BotNetworkHandler.kt index 77569974f..9c674b33a 100644 --- a/mirai-core/src/commonMain/kotlin/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/BotNetworkHandler.kt @@ -73,7 +73,7 @@ internal abstract class BotNetworkHandler : CoroutineScope { */ @Suppress("SpellCheckingInspection") @MiraiInternalApi - abstract suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable? = null) + abstract suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable? = null, step: Int) /** * 初始化获取好友列表等值. diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt index 01ed0d005..0a0ec2b3b 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidBotNetworkHandler.kt @@ -40,10 +40,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.utils.* import net.mamoe.mirai.internal.utils.io.readPacketExact -import net.mamoe.mirai.network.ForceOfflineException -import net.mamoe.mirai.network.RetryLaterException -import net.mamoe.mirai.network.UnsupportedSMSLoginException -import net.mamoe.mirai.network.WrongPasswordException +import net.mamoe.mirai.network.* import net.mamoe.mirai.utils.* import java.util.concurrent.ConcurrentLinkedQueue import kotlin.coroutines.CoroutineContext @@ -114,10 +111,14 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo }.also { heartbeatJob = it } } - - override suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable?) { + // @param step + // 0 -> 初始状态, 其他函数调用应永远传入 0 + // 1 -> 代表滑块验证已禁用 + override suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable?, step: Int) { heartbeatJob?.cancel(CancellationException("relogin", cause)) heartbeatJob?.join() + _packetReceiverJob?.cancel(CancellationException("relogin", cause)) + _packetReceiverJob?.join() if (::channel.isInitialized) { // if (channel.isOpen) { // kotlin.runCatching { @@ -161,14 +162,20 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo return this } + val isSliderCaptchaSupport = bot.configuration.loginSolver?.isSliderCaptchaSupport ?: false + val allowSlider = isSliderCaptchaSupport + || bot.configuration.protocol == BotConfiguration.MiraiProtocol.ANDROID_PHONE + || step == 0 + fun loginSolverNotNull() = bot.configuration.loginSolver.notnull() - var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect() + var response: WtLogin.Login.LoginPacketResponse = + WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect() mainloop@ while (true) { when (response) { is WtLogin.Login.LoginPacketResponse.UnsafeLogin -> { loginSolverNotNull().onSolveUnsafeDeviceLoginVerify(bot, response.url) - response = WtLogin.Login.SubCommand9(bot.client).sendAndExpect() + response = WtLogin.Login.SubCommand9(bot.client, allowSlider).sendAndExpect() } is WtLogin.Login.LoginPacketResponse.Captcha -> when (response) { @@ -183,6 +190,21 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo continue@mainloop } is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> { + if (!isSliderCaptchaSupport) { + if (step == 0) { + return closeEverythingAndRelogin(host, port, cause, 1) + } + throw NotSupportedSliderCaptchaException( + buildString { + append("Mirai 无法完成滑块验证.") + if (allowSlider) { + append(" 使用协议 ") + append(bot.configuration.protocol) + append(" 强制要求滑块验证, 请更换协议后重试") + } + } + ) + } val ticket = loginSolverNotNull().onSolveSliderCaptcha(bot, response.url).orEmpty() response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect() continue@mainloop @@ -193,7 +215,13 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo if (response.message.contains("0x9a")) { //Error(title=登录失败, message=请你稍后重试。(0x9a), errorInfo=) throw RetryLaterException() } - throw WrongPasswordException(response.toString()) + val msg = response.toString() + throw WrongPasswordException(buildString(capacity = msg.length) { + append(msg) + if (msg.contains("当前上网环境异常")) { // Error(title=禁止登录, message=当前上网环境异常,请更换网络环境或在常用设备上登录或稍后再试。, errorInfo=) + append(", tips=若频繁出现, 请尝试开启设备锁") + } + }) } is WtLogin.Login.LoginPacketResponse.DeviceLockLogin -> { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index ee67b8247..ab7ed0fc0 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -123,12 +123,13 @@ internal class WtLogin { private const val appId = 16L operator fun invoke( - client: QQAndroidClient + client: QQAndroidClient, + allowSlider: Boolean ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeShort(9) // subCommand - writeShort(0x18) // count of TLVs, probably ignored by server? + writeShort(if (allowSlider) 0x18 else 0x17) // count of TLVs, probably ignored by server? //writeShort(LoginType.PASSWORD.value.toShort()) t18(appId, client.appClientVersion, client.uin) @@ -231,7 +232,9 @@ internal class WtLogin { t187(client.device.macAddress) t188(client.device.androidId) t194(client.device.imsiMd5) - t191() + if (allowSlider) { + t191() + } /* t201(N = byteArrayOf())*/