Improve LoginCommand logic, remove unsafe checks, and add tests

This commit is contained in:
Him188 2022-03-16 15:27:00 +00:00
parent b1d66c0d20
commit 366ea34fde
3 changed files with 162 additions and 16 deletions

View File

@ -50,6 +50,9 @@ import net.mamoe.mirai.console.util.*
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.message.nextMessageOrNull
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Either
import net.mamoe.mirai.utils.Either.Companion.flatMapNull
import net.mamoe.mirai.utils.Either.Companion.fold
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.secondsToMillis
import java.lang.management.ManagementFactory
@ -188,9 +191,12 @@ public object BuiltInCommands {
ConsoleCommandOwner, "login", "登录",
description = "登录一个账号",
), BuiltInCommandInternal {
private suspend fun Bot.doLogin() = kotlin.runCatching {
login(); this
}.onFailure { close() }.getOrThrow()
internal var doLogin: suspend Bot.() -> Bot = {
kotlin.runCatching {
login()
this
}.onFailure { close() }.getOrThrow()
} // workaround since LoginCommand is object
@Handler
@JvmOverloads
@ -204,24 +210,27 @@ public object BuiltInCommands {
return this
}
suspend fun getPassword(id: Long): Any? {
fun getPassword(id: Long): Either<ByteArray, String?> {
val config = DataScope.get<AutoLoginConfig>()
val acc = config.accounts.firstOrNull { it.account == id.toString() }
if (acc == null) {
sendMessage("Could not find '$id' in AutoLogin config. Please specify password.")
return null
}
val acc = config.accounts.firstOrNull { it.account == id.toString() } ?: return Either.right(null)
val strv = acc.password.value
return if (acc.password.kind == MD5) strv.autoHexToBytes() else strv
return if (acc.password.kind == MD5) Either.left(strv.autoHexToBytes()) else Either.right(strv)
}
val pwd: Any = password ?: getPassword(id) ?: return
val pwd = Either.right<ByteArray, String?>(password).flatMapNull { getPassword(id) }
kotlin.runCatching {
when (pwd) {
is String -> MiraiConsole.addBot(id, pwd) { setup(protocol) }.doLogin()
is ByteArray -> MiraiConsole.addBot(id, pwd) { setup(protocol) }.doLogin()
else -> throw AssertionError("Assertion failed, please report to https://github.com/mamoe/mirai-console/issues/new/choose, debug=${pwd.javaClass}")// Unreachable
}
pwd.fold(
onLeft = { pass ->
MiraiConsole.addBot(id, pass) { setup(protocol) }.doLogin()
},
onRight = { pass ->
if (pass == null) {
sendMessage("Could not find '$id' in AutoLogin config. Please specify password.")
return
}
MiraiConsole.addBot(id, pass) { setup(protocol) }.doLogin()
}
)
}.fold(
onSuccess = { scopeWith(ConsoleCommandSender).sendMessage("${it.nick} ($id) Login successful") },
onFailure = { throwable ->

View File

@ -0,0 +1,19 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScopeImpl
import net.mamoe.mirai.console.internal.data.builtins.DataScope
import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest
internal abstract class AbstractCommandTest : AbstractConsoleInstanceTest() {
val dataScope get() = DataScope as ConsoleDataScopeImpl
val consoleSender get() = ConsoleCommandSender
}

View File

@ -0,0 +1,118 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.console.command
import kotlinx.coroutines.CompletableDeferred
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.console.command.BuiltInCommands.LoginCommand
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind
import net.mamoe.mirai.utils.md5
import net.mamoe.mirai.utils.toUHexString
import org.junit.jupiter.api.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
@OptIn(ExperimentalCommandDescriptors::class)
@JvmBlockingBridge
internal class LoginCommandTest : AbstractCommandTest() {
@Test
suspend fun `login with provided password`() {
val myId = 123L
val myPwd = "password001"
val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
LoginCommand.doLogin = {
val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
cont.complete(bot)
bot
}
LoginCommand.execute(consoleSender, "$myId $myPwd")
}
val account = bot.account
assertContentEquals(myPwd.md5(), account.passwordMd5)
assertEquals(myId, account.id)
}
@Test
suspend fun `login with saved plain password`() {
val myId = 123L
val myPwd = "password001"
dataScope.set(AutoLoginConfig().apply {
accounts.add(
Account(
account = myId.toString(),
password = Account.Password(PasswordKind.PLAIN, myPwd)
)
)
})
val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
LoginCommand.doLogin = {
val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
cont.complete(bot)
bot
}
LoginCommand.execute(consoleSender, "$myId")
}
val account = bot.account
assertContentEquals(myPwd.md5(), account.passwordMd5)
assertEquals(myId, account.id)
}
@Test
suspend fun `login with saved md5 password`() {
val myId = 123L
val myPwd = "password001"
dataScope.set(AutoLoginConfig().apply {
accounts.add(
Account(
account = myId.toString(),
password = Account.Password(PasswordKind.MD5, myPwd.md5().toUHexString(""))
)
)
})
val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
LoginCommand.doLogin = {
val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
cont.complete(bot)
bot
}
LoginCommand.execute(consoleSender, "$myId")
}
val account = bot.account
assertContentEquals(myPwd.md5(), account.passwordMd5)
assertEquals(myId, account.id)
}
}
@BuilderInference
internal suspend inline fun <T> awaitDeferred(
@BuilderInference
crossinline block: suspend (CompletableDeferred<T>) -> Unit
): T {
val deferred = CompletableDeferred<T>()
block(deferred)
return deferred.await()
}