From d0fb1383a0c0bba6a88086a86d404d6ca9e5886e Mon Sep 17 00:00:00 2001
From: "jiahua.liu" <n@mamoe.net>
Date: Sun, 16 Feb 2020 17:25:30 +0800
Subject: [PATCH] Mirai Console V0.01

---
 .../console/MiraiConsoleTerminalLoader.kt     |  8 ++-
 .../mirai/console/MiraiConsoleTerminalUI.kt   | 57 ++++++++++++++-----
 .../net/mamoe/mirai/console/MiraiConsole.kt   | 33 +++++++++--
 .../net/mamoe/mirai/console/MiraiConsoleUI.kt | 12 +++-
 .../mamoe/mirai/utils/BotConfigurationJvm.kt  | 23 ++++++--
 5 files changed, 107 insertions(+), 26 deletions(-)

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<String>) {
             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<Long>) {
         botAdminCount[identity] = admins.size
     }
 
 
-    val log = mutableMapOf<Long, Queue<String>>().also {
+    val log = ConcurrentHashMap<Long, LimitLinkedQueue<String>>().also {
         it[0L] = LimitLinkedQueue(cacheLogSize)
     }
-    val botAdminCount = mutableMapOf<Long, Int>()
+    val botAdminCount = ConcurrentHashMap<Long, Int>()
 
     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<T>(
     val limit: Int = 50
-) : ConcurrentLinkedQueue<T>() {
-    override fun offer(e: T): Boolean {
+) : ConcurrentLinkedDeque<T>() {
+    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<Long>
     )
+
 }
\ 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("正在提交中...")
         }
     }