mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-20 19:09:12 +08:00
LoginSolver swing (#257)
* LoginSolver, gui support * make `internal`,`object`, and rename.
This commit is contained in:
parent
03cffb897c
commit
0ede49896e
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Desktop
|
||||||
|
import java.awt.event.KeyEvent
|
||||||
|
import java.awt.event.KeyListener
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
import javax.swing.JFrame
|
||||||
|
import javax.swing.JTextField
|
||||||
|
|
||||||
|
internal class WindowInitialzier(private val initializer: WindowInitialzier.(JFrame) -> Unit) {
|
||||||
|
private lateinit var frame0: JFrame
|
||||||
|
val frame: JFrame get() = frame0
|
||||||
|
fun java.awt.Component.append() {
|
||||||
|
frame.add(this, BorderLayout.NORTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun java.awt.Component.last() {
|
||||||
|
frame.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun init(frame: JFrame) {
|
||||||
|
this.frame0 = frame;
|
||||||
|
initializer(frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun openWindow(title: String = "", initializer: WindowInitialzier.(JFrame) -> Unit = {}): String {
|
||||||
|
return openWindow(title, WindowInitialzier(initializer))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun openWindow(title: String = "", initializer: WindowInitialzier = WindowInitialzier {}): String {
|
||||||
|
val frame = JFrame()
|
||||||
|
val value = JTextField()
|
||||||
|
val def = CompletableDeferred<String>()
|
||||||
|
value.addKeyListener(object : KeyListener {
|
||||||
|
override fun keyTyped(e: KeyEvent?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun keyPressed(e: KeyEvent?) {
|
||||||
|
when (e!!.keyCode) {
|
||||||
|
27, 10 -> {
|
||||||
|
def.complete(value.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun keyReleased(e: KeyEvent?) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
frame.layout = BorderLayout(10, 5)
|
||||||
|
frame.add(value, BorderLayout.SOUTH)
|
||||||
|
initializer.init(frame)
|
||||||
|
|
||||||
|
frame.pack()
|
||||||
|
frame.defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE
|
||||||
|
frame.addWindowListener(object : WindowAdapter() {
|
||||||
|
override fun windowClosing(e: WindowEvent?) {
|
||||||
|
def.complete(value.text)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
frame.setLocationRelativeTo(null)
|
||||||
|
frame.title = title
|
||||||
|
frame.isVisible = true
|
||||||
|
|
||||||
|
val result = def.await()
|
||||||
|
frame.dispose()
|
||||||
|
return result
|
||||||
|
}
|
@ -22,19 +22,46 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.io.core.use
|
import kotlinx.io.core.use
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.network.BotNetworkHandler
|
import java.awt.Desktop
|
||||||
import java.awt.Image
|
import java.awt.Image
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
|
|
||||||
actual typealias Throws = kotlin.jvm.Throws
|
actual typealias Throws = kotlin.jvm.Throws
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
@MiraiExperimentalAPI
|
||||||
class DefaultLoginSolver(
|
class DefaultLoginSolver(
|
||||||
|
val input: suspend () -> String,
|
||||||
|
val overrideLogger: MiraiLogger? = null
|
||||||
|
) : LoginSolver() {
|
||||||
|
private val degelate: LoginSolver
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Desktop.isDesktopSupported()) {
|
||||||
|
degelate = SwingSolver
|
||||||
|
} else {
|
||||||
|
degelate = DefaultLoginSolverImpl(input, overrideLogger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? {
|
||||||
|
return degelate.onSolvePicCaptcha(bot, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
|
||||||
|
return degelate.onSolveSliderCaptcha(bot, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
|
||||||
|
return degelate.onSolveUnsafeDeviceLoginVerify(bot, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalAPI
|
||||||
|
class DefaultLoginSolverImpl(
|
||||||
private val input: suspend () -> String,
|
private val input: suspend () -> String,
|
||||||
private val overrideLogger: MiraiLogger? = null
|
private val overrideLogger: MiraiLogger? = null
|
||||||
) : LoginSolver() {
|
) : LoginSolver() {
|
||||||
@ -106,7 +133,14 @@ actual abstract class LoginSolver {
|
|||||||
actual companion object {
|
actual companion object {
|
||||||
actual val Default: LoginSolver
|
actual val Default: LoginSolver
|
||||||
@OptIn(MiraiExperimentalAPI::class)
|
@OptIn(MiraiExperimentalAPI::class)
|
||||||
get() = DefaultLoginSolver(input = { withContext(Dispatchers.IO) { readLine() } ?: error("No standard input") })
|
get() {
|
||||||
|
if (Desktop.isDesktopSupported()) {
|
||||||
|
return SwingSolver
|
||||||
|
}
|
||||||
|
return DefaultLoginSolverImpl(input = {
|
||||||
|
withContext(Dispatchers.IO) { readLine() } ?: error("No standard input")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +183,8 @@ private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Doub
|
|||||||
return (r * 30 + g * 59 + b * 11 + 50) / 100
|
return (r * 30 + g * 59 + b * 11 + 50) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
fun grayCompare(g1: Int, g2: Int): Boolean = kotlin.math.min(g1, g2).toDouble() / kotlin.math.max(g1, g2) >= ignoreRate
|
fun grayCompare(g1: Int, g2: Int): Boolean =
|
||||||
|
kotlin.math.min(g1, g2).toDouble() / kotlin.math.max(g1, g2) >= ignoreRate
|
||||||
|
|
||||||
val background = gray(image.getRGB(0, 0))
|
val background = gray(image.getRGB(0, 0))
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import java.awt.Desktop
|
||||||
|
import java.net.URI
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
import javax.swing.ImageIcon
|
||||||
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JTextField
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalAPI
|
||||||
|
object SwingSolver : LoginSolver() {
|
||||||
|
override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? {
|
||||||
|
return openWindow("Mirai PicCaptcha(${bot.id})") {
|
||||||
|
val image = ImageIO.read(data.inputStream())
|
||||||
|
JLabel(ImageIcon(image)).append()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
|
||||||
|
return openWindow("Mirai SliderCaptcha(${bot.id})") {
|
||||||
|
JLabel("需要滑动验证码, 完成后请关闭该窗口").append()
|
||||||
|
Desktop.getDesktop().browse(URI(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
|
||||||
|
return openWindow("Mirai UnsafeDeviceLoginVerify(${bot.id})") {
|
||||||
|
JLabel("需要进行账户安全认证").last()
|
||||||
|
JLabel("该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题").last()
|
||||||
|
JLabel("完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次").last()
|
||||||
|
JTextField(url).append()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user