diff --git a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt index 4b0428601..33ca3c002 100644 --- a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt +++ b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt @@ -7,9 +7,11 @@ class MiraiConsoleTerminalLoader { @JvmStatic fun main(args: Array) { MiraiConsoleTerminalUI.start() - MiraiConsole.start( - MiraiConsoleTerminalUI - ) + thread { + MiraiConsole.start( + MiraiConsoleTerminalUI + ) + } Runtime.getRuntime().addShutdownHook(thread(start = false) { MiraiConsole.stop() }) diff --git a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt index f87ec4501..421f48436 100644 --- a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt +++ b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt @@ -25,6 +25,8 @@ import java.io.OutputStream import java.io.PrintStream import java.nio.charset.Charset import java.util.* +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentLinkedDeque import java.util.concurrent.ConcurrentLinkedQueue import kotlin.concurrent.thread import kotlin.system.exitProcess @@ -44,7 +46,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { val cacheLogSize = 50 override fun pushLog(identity: Long, message: String) { - log[identity]!!.offer(message) + log[identity]!!.push(message) if (identity == screens[currentScreenId]) { drawLog(message) } @@ -52,23 +54,49 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { override fun prePushBot(identity: Long) { log[identity] = LimitLinkedQueue(cacheLogSize) - botAdminCount[identity] = 0 - screens.add(identity) } override fun pushBot(bot: Bot) { - //nothing to do + botAdminCount[bot.uin] = 0 + screens.add(bot.uin) + drawFrame(this.getScreenName(currentScreenId)) + if (terminal is SwingTerminalFrame) { + terminal.flush() + } } + var requesting = false + var requestResult: String? = null + override suspend fun requestInput(question: String): String { + requesting = true + while (requesting) { + Thread.sleep(100)//不然会卡死 迷惑吧 + } + return requestResult!! + } + + + fun provideInput(input: String) { + if (requesting) { + requestResult = input + requesting = false + } else { + MiraiConsole.CommandListener.commandChannel.offer( + commandBuilder.toString() + ) + } + } + + override fun pushBotAdminStatus(identity: Long, admins: List) { botAdminCount[identity] = admins.size } - val log = mutableMapOf>().also { + val log = ConcurrentHashMap>().also { it[0L] = LimitLinkedQueue(cacheLogSize) } - val botAdminCount = mutableMapOf() + val botAdminCount = ConcurrentHashMap() private val screens = mutableListOf(0L) private var currentScreenId = 0 @@ -202,9 +230,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { update() } KeyType.Enter -> { - MiraiConsole.CommandListener.commandChannel.offer( - commandBuilder.toString() - ) + provideInput(commandBuilder.toString()) emptyCommand() } KeyType.Escape -> { @@ -333,6 +359,9 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { fun drawLog(string: String, flush: Boolean = true) { val maxHeight = terminal.terminalSize.rows - 4 val heightNeed = (string.length / (terminal.terminalSize.columns - 6)) + 1 + if (heightNeed - 1 > maxHeight) { + return//拒绝打印 + } if (currentHeight + heightNeed > maxHeight) { cleanPage() } @@ -421,7 +450,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { private fun addCommandChar( c: Char ) { - if (commandBuilder.isEmpty() && c != '/') { + if (!requesting && commandBuilder.isEmpty() && c != '/') { addCommandChar('/') } textGraphics.foregroundColor = TextColor.ANSI.WHITE @@ -488,11 +517,11 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { class LimitLinkedQueue( val limit: Int = 50 -) : ConcurrentLinkedQueue() { - override fun offer(e: T): Boolean { +) : ConcurrentLinkedDeque() { + override fun push(e: T) { if (size >= limit) { - poll() + this.pollLast() } - return super.offer(e) + return super.push(e) } } diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index 6c211cac5..5e19969ac 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -11,7 +11,6 @@ package net.mamoe.mirai.console import kotlinx.coroutines.runBlocking import net.mamoe.mirai.Bot -import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.api.http.MiraiHttpAPIServer import net.mamoe.mirai.api.http.generateSessionKey import net.mamoe.mirai.console.plugins.PluginManager @@ -19,7 +18,7 @@ import net.mamoe.mirai.console.plugins.loadAsConfig import net.mamoe.mirai.console.plugins.withDefaultWrite import net.mamoe.mirai.console.plugins.withDefaultWriteSave import net.mamoe.mirai.contact.sendMessage -import net.mamoe.mirai.utils.SimpleLogger +import net.mamoe.mirai.utils.* import java.io.File import java.util.* import java.util.concurrent.LinkedBlockingQueue @@ -119,12 +118,39 @@ object MiraiConsole { logger("[Bot Login]", 0, "login...") try { runBlocking { - Bot(qqNumber, qqPassword).alsoLogin() + frontEnd.prePushBot(qqNumber) + val bot = Bot(qqNumber, qqPassword) { + this.loginSolver = DefaultLoginSolver(object : LoginSolverInputReader { + override suspend fun read(question: String): String? { + return frontEnd.requestInput(question) + } + }) + this.botLoggerSupplier = { + SimpleLogger("BOT $qqNumber") { _, message, e -> + logger("[BOT $qqNumber]", qqNumber, message) + if (e != null) { + logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ + e.printStackTrace() + } + } + } + this.networkLoggerSupplier = { + SimpleLogger("BOT $qqNumber") { _, message, e -> + logger("[NETWORK]", qqNumber, message)//因为在一页 所以可以不打QQ + if (e != null) { + logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ + e.printStackTrace() + } + } + } + } + bot.login() logger( "[Bot Login]", 0, "$qqNumber login successes" ) + frontEnd.pushBot(bot) } } catch (e: Exception) { logger( @@ -132,7 +158,6 @@ object MiraiConsole { 0, "$qqNumber login failed -> " + e.message ) - e.printStackTrace() } true } diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleUI.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleUI.kt index f2db5f336..4f38dc332 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleUI.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleUI.kt @@ -10,7 +10,7 @@ import net.mamoe.mirai.Bot interface MiraiConsoleUI { /** * 让UI层展示一条log - * identityString: log前面的prefix + * * identity:log所属的screen, Main=0; Bot=Bot.uin */ fun pushLog( @@ -32,6 +32,15 @@ interface MiraiConsoleUI { bot: Bot ) + /** + * 让UI层提供一个Input + * 这个Input 不 等于 Command + * + */ + suspend fun requestInput( + question: String + ): String + /** * 让UI层更新BOT管理员的数据 */ @@ -39,4 +48,5 @@ interface MiraiConsoleUI { identity: Long, admins: List ) + } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt index f76ed73fa..84dbec29e 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt @@ -39,7 +39,22 @@ import kotlin.coroutines.EmptyCoroutineContext actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver() -internal class DefaultLoginSolver : LoginSolver() { +interface LoginSolverInputReader{ + suspend fun read(question:String):String? + + suspend operator fun invoke(question: String):String?{ + return read(question) + } +} +class DefaultLoginSolverInputReader: LoginSolverInputReader{ + override suspend fun read(question: String): String? { + return readLine() + } +} + +class DefaultLoginSolver( + val reader: LoginSolverInputReader = DefaultLoginSolverInputReader() +) : LoginSolver() { override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock { val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } withContext(Dispatchers.IO) { @@ -62,7 +77,7 @@ internal class DefaultLoginSolver : LoginSolver() { } } bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车") - return readLine()!!.takeUnless { it.isEmpty() || it.length != 4 }.also { + return reader("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")!!.takeUnless { it.isEmpty() || it.length != 4 }.also { bot.logger.info("正在提交[$it]中...") } } @@ -72,7 +87,7 @@ internal class DefaultLoginSolver : LoginSolver() { bot.logger.info("请在任意浏览器中打开以下链接并完成验证码. ") bot.logger.info("完成后请输入任意字符 ") bot.logger.info(url) - return readLine().also { + return reader("完成后请输入任意字符").also { bot.logger.info("正在提交中...") } } @@ -84,7 +99,7 @@ internal class DefaultLoginSolver : LoginSolver() { bot.logger.info("请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符") bot.logger.info("这步操作将在后续的版本中优化") bot.logger.info(url) - return readLine().also { + return reader("完成后请输入任意字符").also { bot.logger.info("正在提交中...") } }