Jelajahi Sumber

Mirai Console (Terminal)V0.01

jiahua.liu 6 tahun lalu
induk
melakukan
e971fd769d

+ 53 - 5
mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt

@@ -12,22 +12,24 @@ import com.googlecode.lanterna.terminal.TerminalResizeListener
 import com.googlecode.lanterna.terminal.swing.SwingTerminal
 import com.googlecode.lanterna.terminal.swing.SwingTerminalFontConfiguration
 import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
+import kotlinx.coroutines.io.close
+import kotlinx.io.core.IoBuffer
+import kotlinx.io.core.use
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.cleanPage
 import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.drawLog
 import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.redrawLogs
+import net.mamoe.mirai.utils.LoginSolver
+import net.mamoe.mirai.utils.writeChannel
 import java.awt.Font
+import java.io.File
 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
 
@@ -97,6 +99,52 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
         botAdminCount[identity] = admins.size
     }
 
+    override fun createLoginSolver(): LoginSolver {
+        return object : LoginSolver() {
+            override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
+                val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
+                withContext(Dispatchers.IO) {
+                    tempFile.createNewFile()
+                    pushLog(0, "[Login Solver]需要图片验证码登录, 验证码为 4 字母")
+                    try {
+                        tempFile.writeChannel().apply { writeFully(data); close() }
+                        pushLog(0, "将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
+                    } catch (e: Exception) {
+                        error("[Login Solver]验证码无法保存[Error0001]")
+                    }
+                }
+                pushLog(0, "[Login Solver]请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
+                return requestInput("[Login Solver]请输入 4 位字母验证码. 若要更换验证码, 请直接回车")!!.takeUnless { it.isEmpty() || it.length != 4 }
+                    .also {
+                        pushLog(0, "[Login Solver]正在提交[$it]中...")
+                    }
+            }
+
+            override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
+                pushLog(0, "[Login Solver]需要滑动验证码")
+                pushLog(0, "[Login Solver]请在任意浏览器中打开以下链接并完成验证码. ")
+                pushLog(0, "[Login Solver]完成后请输入任意字符 ")
+                pushLog(0, url)
+                return requestInput("[Login Solver]完成后请输入任意字符").also {
+                    pushLog(0, "[Login Solver]正在提交中")
+                }
+            }
+
+
+            override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
+                pushLog(0, "[Login Solver]需要进行账户安全认证")
+                pushLog(0, "[Login Solver]该账户有[设备锁]/[不常用登陆地点]/[不常用设备登陆]的问题")
+                pushLog(0, "[Login Solver]完成以下账号认证即可成功登陆|理论本认证在mirai每个账户中最多出现1次")
+                pushLog(0, "[Login Solver]请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符")
+                pushLog(0, "[Login Solver]这步操作将在后续的版本中优化")
+                pushLog(0, url)
+                return requestInput("[Login Solver]完成后请输入任意字符").also {
+                    pushLog(0, "[Login Solver]正在提交中...")
+                }
+            }
+
+        }
+    }
 
     val log = ConcurrentHashMap<Long, LimitLinkedQueue<String>>().also {
         it[0L] = LimitLinkedQueue(cacheLogSize)

+ 1 - 13
mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt

@@ -123,19 +123,7 @@ object MiraiConsole {
                         runBlocking {
                             frontEnd.prePushBot(qqNumber)
                             val bot = Bot(qqNumber, qqPassword) {
-                                this.loginSolver = DefaultLoginSolver(object : LoginSolverInputReader {
-                                    override suspend fun read(question: String): String? {
-                                        return frontEnd.requestInput(question)
-                                    }
-                                },
-                                    SimpleLogger("Login Helper") { _, message, e ->
-                                        logger("[Login Helper]", qqNumber, message)
-                                        if (e != null) {
-                                            logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ
-                                            e.printStackTrace()
-                                        }
-                                    }
-                                )
+                                this.loginSolver = frontEnd.createLoginSolver()
                                 this.botLoggerSupplier = {
                                     SimpleLogger("BOT $qqNumber]") { _, message, e ->
                                         logger("[BOT $qqNumber]", qqNumber, message)

+ 6 - 0
mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleUI.kt

@@ -1,6 +1,7 @@
 package net.mamoe.mirai.console
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.utils.LoginSolver
 
 /**
  * 只需要实现一个这个 传入MiraiConsole 就可以绑定UI层与Console层
@@ -57,4 +58,9 @@ interface MiraiConsoleUI {
         admins: List<Long>
     )
 
+    /**
+     * 由UI层创建一个LoginSolver
+     */
+    fun createLoginSolver(): LoginSolver
+
 }

+ 1 - 1
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt

@@ -118,7 +118,7 @@ class DefaultLoginSolver(
 }
 
 // Copied from Ktor CIO
-private fun File.writeChannel(
+public fun File.writeChannel(
     coroutineContext: CoroutineContext = Dispatchers.IO
 ): ByteWriteChannel = GlobalScope.reader(CoroutineName("file-writer") + coroutineContext, autoFlush = true) {
     @Suppress("BlockingMethodInNonBlockingContext")