From c6ae8e32c2efb865b6692a1785077606522b19cf Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 27 Feb 2020 13:30:15 +0800 Subject: [PATCH] Separate `mirai-console` series from main repository --- mirai-console-graphical/README.md | 6 - mirai-console-graphical/build.gradle.kts | 45 -- .../mirai/console/graphical/MiraiGraphical.kt | 36 - .../controller/MiraiGraphicalUIController.kt | 94 --- .../mirai/console/graphical/model/BotModel.kt | 19 - .../console/graphical/model/ConsoleInfo.kt | 17 - .../console/graphical/model/PluginModel.kt | 20 - .../graphical/model/VerificationCodeModel.kt | 17 - .../styleSheet/LoginViewStyleSheet.kt | 47 -- .../graphical/styleSheet/PrimaryStyleSheet.kt | 21 - .../console/graphical/util/JFoenixAdaptor.kt | 48 -- .../mirai/console/graphical/view/Decorator.kt | 9 - .../mirai/console/graphical/view/LoginView.kt | 50 -- .../console/graphical/view/PluginsView.kt | 33 - .../console/graphical/view/PrimaryView.kt | 99 --- .../console/graphical/view/SettingsView.kt | 36 - .../view/VerificationCodeFragment.kt | 19 - .../src/main/resources/character.png | Bin 30438 -> 0 bytes .../src/main/resources/logo.png | Bin 4752 -> 0 bytes mirai-console-terminal/README.md | 6 - mirai-console-terminal/build.gradle.kts | 44 -- .../console/MiraiConsoleTerminalLoader.kt | 31 - .../mirai/console/MiraiConsoleTerminalUI.kt | 662 ------------------ mirai-console/README.MD | 88 --- mirai-console/build.gradle.kts | 46 -- .../net/mamoe/mirai/console/MiraiConsole.kt | 218 ------ .../mamoe/mirai/console/command/Command.kt | 242 ------- .../mirai/console/command/DefaultCommands.kt | 265 ------- .../mirai/console/plugins/ConfigSection.kt | 525 -------------- .../mamoe/mirai/console/plugins/PluginBase.kt | 410 ----------- .../console/pure/MiraiConsolePureLoader.kt | 16 - .../mirai/console/pure/MiraiConsoleUIPure.kt | 81 --- .../mamoe/mirai/console/utils/BotHelper.kt | 41 -- .../mirai/console/utils/MiraiConsoleUI.kt | 75 -- settings.gradle | 3 - 35 files changed, 3369 deletions(-) delete mode 100644 mirai-console-graphical/README.md delete mode 100644 mirai-console-graphical/build.gradle.kts delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt delete mode 100644 mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt delete mode 100644 mirai-console-graphical/src/main/resources/character.png delete mode 100644 mirai-console-graphical/src/main/resources/logo.png delete mode 100644 mirai-console-terminal/README.md delete mode 100644 mirai-console-terminal/build.gradle.kts delete mode 100644 mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt delete mode 100644 mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt delete mode 100644 mirai-console/README.MD delete mode 100644 mirai-console/build.gradle.kts delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt diff --git a/mirai-console-graphical/README.md b/mirai-console-graphical/README.md deleted file mode 100644 index cc26c6c15..000000000 --- a/mirai-console-graphical/README.md +++ /dev/null @@ -1,6 +0,0 @@ -### Mirai Console Graphical -支持windows/mac -有正式UI界面实现的CONSOLE -优点: 适合新手/完全不懂编程的/界面美丽 -缺点: 不能在linux服务器运行 -所使用插件系统与terminal版本一致 可以来回切换 \ No newline at end of file diff --git a/mirai-console-graphical/build.gradle.kts b/mirai-console-graphical/build.gradle.kts deleted file mode 100644 index 557c0ea79..000000000 --- a/mirai-console-graphical/build.gradle.kts +++ /dev/null @@ -1,45 +0,0 @@ -plugins { - id("kotlinx-serialization") - id("org.openjfx.javafxplugin") version "0.0.8" - id("kotlin") - id("java") -} - -javafx { - version = "13.0.2" - modules = listOf("javafx.controls") - //mainClassName = "Application" -} - -apply(plugin = "com.github.johnrengelman.shadow") - -val kotlinVersion: String by rootProject.ext -val atomicFuVersion: String by rootProject.ext -val coroutinesVersion: String by rootProject.ext -val kotlinXIoVersion: String by rootProject.ext -val coroutinesIoVersion: String by rootProject.ext - -val klockVersion: String by rootProject.ext -val ktorVersion: String by rootProject.ext - -val serializationVersion: String by rootProject.ext - -fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version" - -fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version" - -dependencies { - api(project(":mirai-core")) - api(project(":mirai-core-qqandroid")) - api(project(":mirai-api-http")) - api(project(":mirai-console")) - runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main")) - api(group = "no.tornado", name = "tornadofx", version = "1.7.19") - api(group = "com.jfoenix", name = "jfoenix", version = "9.0.8") - api("org.bouncycastle:bcprov-jdk15on:1.64") - // classpath is not set correctly by IDE -} - -tasks.withType { - kotlinOptions.jvmTarget = "1.8" -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt deleted file mode 100644 index 51e61f081..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.console.graphical - -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController -import net.mamoe.mirai.console.graphical.styleSheet.PrimaryStyleSheet -import net.mamoe.mirai.console.graphical.view.Decorator -import tornadofx.App -import tornadofx.find -import tornadofx.launch - -fun main(args: Array) { - launch(args) -} - -class MiraiGraphicalUI : App(Decorator::class, PrimaryStyleSheet::class) { - - override fun init() { - super.init() - - MiraiConsole.start(find()) - } - - override fun stop() { - super.stop() - MiraiConsole.stop() - } -} diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt deleted file mode 100644 index 159b62e92..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt +++ /dev/null @@ -1,94 +0,0 @@ -package net.mamoe.mirai.console.graphical.controller - -import javafx.application.Platform -import javafx.collections.ObservableList -import javafx.stage.Modality -import kotlinx.io.core.IoBuffer -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.graphical.model.BotModel -import net.mamoe.mirai.console.graphical.model.ConsoleInfo -import net.mamoe.mirai.console.graphical.model.PluginModel -import net.mamoe.mirai.console.graphical.model.VerificationCodeModel -import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment -import net.mamoe.mirai.console.utils.MiraiConsoleUI -import net.mamoe.mirai.utils.LoginSolver -import tornadofx.* - -class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { - - private val loginSolver = GraphicalLoginSolver() - private val cache = mutableMapOf() - val mainLog = observableListOf() - - - val botList = observableListOf() - val pluginList: ObservableList by lazy(::getPluginsFromConsole) - - val consoleInfo = ConsoleInfo() - - fun login(qq: String, psd: String) { - MiraiConsole.CommandProcessor.runConsoleCommandBlocking("/login $qq $psd") - } - - fun sendCommand(command: String) = MiraiConsole.CommandProcessor.runConsoleCommandBlocking(command) - - override fun pushLog(identity: Long, message: String) = Platform.runLater { - when (identity) { - 0L -> mainLog.add(message) - else -> cache[identity]?.logHistory?.add(message) - } - } - - override fun prePushBot(identity: Long) = Platform.runLater { - BotModel(identity).also { - cache[identity] = it - botList.add(it) - } - } - - override fun pushBot(bot: Bot) = Platform.runLater { - cache[bot.uin]?.bot = bot - } - - override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) { - Platform.runLater { - consoleInfo.consoleVersion = consoleVersion - consoleInfo.consoleBuild = consoleBuild - consoleInfo.coreVersion = coreVersion - } - } - - override suspend fun requestInput(question: String): String { - val model = VerificationCodeModel() - find(Scope(model)).openModal( - modality = Modality.APPLICATION_MODAL, - resizable = false - ) - return model.code.value - } - - override fun pushBotAdminStatus(identity: Long, admins: List) = Platform.runLater { - cache[identity]?.admins?.setAll(admins) - } - - override fun createLoginSolver(): LoginSolver = loginSolver - - private fun getPluginsFromConsole(): ObservableList = - MiraiConsole.pluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable() - -} - -class GraphicalLoginSolver : LoginSolver() { - override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt deleted file mode 100644 index 542f8b146..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.mamoe.mirai.console.graphical.model - -import javafx.beans.property.SimpleObjectProperty -import net.mamoe.mirai.Bot -import tornadofx.* - -class BotModel(val uin: Long) { - val botProperty = SimpleObjectProperty(null) - var bot: Bot by botProperty - - val logHistory = observableListOf() - val admins = observableListOf() -} - -class BotViewModel(botModel: BotModel? = null) : ItemViewModel(botModel) { - val bot = bind(BotModel::botProperty) - val logHistory = bind(BotModel::logHistory) - val admins = bind(BotModel::admins) -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt deleted file mode 100644 index 9c43ecc1e..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.mamoe.mirai.console.graphical.model - -import javafx.beans.property.SimpleStringProperty -import tornadofx.setValue -import tornadofx.getValue - -class ConsoleInfo { - - val consoleVersionProperty = SimpleStringProperty() - var consoleVersion by consoleVersionProperty - - val consoleBuildProperty = SimpleStringProperty() - var consoleBuild by consoleBuildProperty - - val coreVersionProperty = SimpleStringProperty() - var coreVersion by coreVersionProperty -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt deleted file mode 100644 index e1323901f..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.mamoe.mirai.console.graphical.model - -import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject -import javafx.beans.property.SimpleBooleanProperty -import javafx.beans.property.SimpleStringProperty -import net.mamoe.mirai.console.plugins.PluginDescription -import tornadofx.getValue -import tornadofx.setValue - -class PluginModel( - val name: String, - val version: String, - val author: String, - val description: String -) : RecursiveTreeObject() { - constructor(plugin: PluginDescription):this(plugin.name, plugin.version, plugin.author, plugin.info) - - val enabledProperty = SimpleBooleanProperty(this, "enabledProperty") - var enabled by enabledProperty -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt deleted file mode 100644 index 5582cfa8c..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.mamoe.mirai.console.graphical.model - -import javafx.beans.property.SimpleStringProperty -import tornadofx.ItemViewModel -import tornadofx.getValue -import tornadofx.setValue - -class VerificationCode { - val codeProperty = SimpleStringProperty("") - var code: String by codeProperty -} - -class VerificationCodeModel(code: VerificationCode) : ItemViewModel(code) { - constructor(): this(VerificationCode()) - - val code = bind(VerificationCode::codeProperty) -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt deleted file mode 100644 index 6e0b1f69c..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt +++ /dev/null @@ -1,47 +0,0 @@ -package net.mamoe.mirai.console.graphical.styleSheet - -import javafx.scene.Cursor -import javafx.scene.effect.BlurType -import javafx.scene.effect.DropShadow -import javafx.scene.paint.Color -import javafx.scene.text.FontWeight -import tornadofx.* - -class LoginViewStyleSheet : Stylesheet() { - - companion object { - val vBox by csselement("VBox") - } - - init { - - vBox { - maxWidth = 500.px - maxHeight = 500.px - - backgroundColor += c("39c5BB", 0.3) - backgroundRadius += box(15.px) - - padding = box(50.px, 100.px) - spacing = 25.px - - borderRadius += box(15.px) - effect = DropShadow(BlurType.THREE_PASS_BOX, Color.GRAY, 10.0, 0.0, 15.0, 15.0) - } - - textField { - prefHeight = 30.px - textFill = Color.BLACK - fontWeight = FontWeight.BOLD - } - - button { - backgroundColor += c("00BCD4", 0.8) - padding = box(10.px, 0.px) - prefWidth = 500.px - textFill = Color.WHITE - fontWeight = FontWeight.BOLD - cursor = Cursor.HAND - } - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt deleted file mode 100644 index 8f47b7a39..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt +++ /dev/null @@ -1,21 +0,0 @@ -package net.mamoe.mirai.console.graphical.styleSheet - -import tornadofx.* - -class PrimaryStyleSheet : Stylesheet() { - companion object { - val jfxTitle by cssclass("jfx-decorator-buttons-container") - val container by cssclass("jfx-decorator-content-container") - } - - init { - jfxTitle { - backgroundColor += c("00BCD4") - } - - container { - borderColor += box(c("00BCD4")) - borderWidth += box(0.px, 4.px, 4.px, 4.px) - } - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt deleted file mode 100644 index 61dcde2ae..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt +++ /dev/null @@ -1,48 +0,0 @@ -package net.mamoe.mirai.console.graphical.util - -import com.jfoenix.controls.* -import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject -import javafx.beans.value.ObservableValue -import javafx.collections.ObservableList -import javafx.event.EventTarget -import javafx.scene.Node -import javafx.scene.control.* -import tornadofx.SortedFilteredList -import tornadofx.attachTo -import tornadofx.bind - -internal fun EventTarget.jfxTabPane(op: TabPane.() -> Unit = {}) = JFXTabPane().attachTo(this, op) - -internal fun EventTarget.jfxButton(text: String = "", graphic: Node? = null, op: Button.() -> Unit = {}) = - JFXButton(text).attachTo(this, op) { - if (graphic != null) it.graphic = graphic - } - -fun EventTarget.jfxTextfield(value: String? = null, op: JFXTextField.() -> Unit = {}) = JFXTextField().attachTo(this, op) { - if (value != null) it.text = value -} - -fun EventTarget.jfxTextfield(property: ObservableValue, op: JFXTextField.() -> Unit = {}) = jfxTextfield().apply { - bind(property) - op(this) -} - -fun EventTarget.jfxPasswordfield(value: String? = null, op: JFXPasswordField.() -> Unit = {}) = JFXPasswordField().attachTo(this, op) { - if (value != null) it.text = value -} - -fun EventTarget.jfxPasswordfield(property: ObservableValue, op: JFXPasswordField.() -> Unit = {}) = jfxPasswordfield().apply { - bind(property) - op(this) -} - -internal fun EventTarget.jfxListView(values: ObservableList? = null, op: ListView.() -> Unit = {}) = - JFXListView().attachTo(this, op) { - if (values != null) { - if (values is SortedFilteredList) values.bindTo(it) - else it.items = values - } - } - -fun ?> EventTarget.jfxTreeTableView(items: ObservableList? = null, op: JFXTreeTableView.() -> Unit = {}) - = JFXTreeTableView(RecursiveTreeItem(items, RecursiveTreeObject::getChildren)).attachTo(this, op) \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt deleted file mode 100644 index 9ecff5fda..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import com.jfoenix.controls.JFXDecorator -import tornadofx.View - -class Decorator: View() { - - override val root = JFXDecorator(primaryStage, find().root) -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt deleted file mode 100644 index 46b9d59dc..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt +++ /dev/null @@ -1,50 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import javafx.beans.property.SimpleStringProperty -import javafx.geometry.Pos -import javafx.scene.image.Image -import kotlinx.coroutines.runBlocking -import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController -import net.mamoe.mirai.console.graphical.styleSheet.LoginViewStyleSheet -import net.mamoe.mirai.console.graphical.util.jfxButton -import net.mamoe.mirai.console.graphical.util.jfxPasswordfield -import net.mamoe.mirai.console.graphical.util.jfxTextfield -import tornadofx.* - -class LoginView : View("CNM") { - - private val controller = find() - private val qq = SimpleStringProperty("") - private val psd = SimpleStringProperty("") - - override val root = borderpane { - - addStylesheet(LoginViewStyleSheet::class) - - center = vbox { - - imageview(Image(LoginView::class.java.classLoader.getResourceAsStream("character.png"))) { - alignment = Pos.CENTER - } - - jfxTextfield(qq) { - promptText = "QQ" - isLabelFloat = true - } - - jfxPasswordfield(psd) { - promptText = "Password" - isLabelFloat = true - } - - jfxButton("Login").action { - runAsync { - runBlocking { controller.login(qq.value, psd.value) } - }.ui { - qq.value = "" - psd.value = "" - } - } - } - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt deleted file mode 100644 index 7351b2480..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import com.jfoenix.controls.JFXTreeTableColumn -import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController -import net.mamoe.mirai.console.graphical.model.PluginModel -import net.mamoe.mirai.console.graphical.util.jfxTreeTableView -import tornadofx.View - -class PluginsView : View() { - - private val controller = find() - val plugins = controller.pluginList - - override val root = jfxTreeTableView(plugins) { - columns.addAll( - JFXTreeTableColumn("插件名").apply { - prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1)) - }, - JFXTreeTableColumn("版本").apply { - prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1)) - }, - JFXTreeTableColumn("作者").apply { - prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1)) - }, - JFXTreeTableColumn("介绍").apply { - prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.6)) - }, - JFXTreeTableColumn("操作").apply { - prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.08)) - } - ) - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt deleted file mode 100644 index a0760cd6b..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt +++ /dev/null @@ -1,99 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import com.jfoenix.controls.* -import javafx.collections.ObservableList -import javafx.scene.control.Tab -import javafx.scene.control.TabPane -import javafx.scene.image.Image -import javafx.scene.input.KeyCode -import kotlinx.coroutines.runBlocking -import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController -import net.mamoe.mirai.console.graphical.model.BotModel -import net.mamoe.mirai.console.graphical.util.jfxListView -import net.mamoe.mirai.console.graphical.util.jfxTabPane -import tornadofx.* - -class PrimaryView : View() { - - private val controller = find() - - override val root = borderpane { - - prefWidth = 1000.0 - prefHeight = 650.0 - - left = vbox { - - imageview(Image(PrimaryView::class.java.classLoader.getResourceAsStream("logo.png"))) - - // bot list - jfxListView(controller.botList) { - fitToParentSize() - - setCellFactory { - object : JFXListCell() { - init { - onDoubleClick { - (center as TabPane).logTab( - text = item.uin.toString(), - logs = item.logHistory - ).select() - } - } - - override fun updateItem(item: BotModel?, empty: Boolean) { - super.updateItem(item, empty) - if (item != null && !empty) { - graphic = null - text = item.uin.toString() - } else { - graphic = null - text = "" - } - } - } - } - } - - // command input - textfield { - setOnKeyPressed { - if (it.code == KeyCode.ENTER) { - runAsync { - runBlocking { controller.sendCommand(text) } - }.ui { text = "" } - } - } - } - } - - center = jfxTabPane { - - tab("Login").content = find().root - - tab("Plugins").content = find().root - - tab("Settings").content = find().root - - logTab("Main", controller.mainLog) - } - } -} - -private fun TabPane.logTab( - text: String? = null, - logs: ObservableList, - op: Tab.() -> Unit = {} -)= tab(text) { - listview(logs) { - - fitToParentSize() - cellFormat { - graphic = label(it) { - maxWidthProperty().bind(this@listview.widthProperty()) - isWrapText = true - } - } - } - also(op) -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt deleted file mode 100644 index aa194d712..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt +++ /dev/null @@ -1,36 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController -import net.mamoe.mirai.console.graphical.util.jfxButton -import net.mamoe.mirai.console.graphical.util.jfxTextfield -import tornadofx.* - -class SettingsView : View() { - - private val controller = find() - - override val root = form { - - fieldset { - field { - jfxButton("撤掉") { } - jfxButton("保存") { } - } - } - - fieldset("插件目录") { - field { - jfxTextfield("...") { isEditable = false } - jfxButton("打开目录") - } - } - - fieldset("最大日志容量") { - field { - jfxTextfield("...") { - - } - } - } - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt b/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt deleted file mode 100644 index 7e386ba8a..000000000 --- a/mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.mamoe.mirai.console.graphical.view - -import javafx.scene.Parent -import tornadofx.* - -class VerificationCodeFragment : Fragment() { - - override val root = vbox { - //TODO: 显示验证码 - - form { - fieldset { - field("验证码") { - textfield() - } - } - } - } -} \ No newline at end of file diff --git a/mirai-console-graphical/src/main/resources/character.png b/mirai-console-graphical/src/main/resources/character.png deleted file mode 100644 index b55153f1ec3c344df01a0cbde83fa12319d413a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30438 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 e;r|QY5hTy*X-9PTK0dzDB)$5-@qW=ezF(R1& diff --git a/mirai-console-terminal/README.md b/mirai-console-terminal/README.md deleted file mode 100644 index bd7980bfe..000000000 --- a/mirai-console-terminal/README.md +++ /dev/null @@ -1,6 +0,0 @@ -### Mirai Console Terminal -支持windows/mac/linux -在terminal环境下的Console, 由控制台富文本实现简易UI -优点: 可以在linux环境下运行/简洁使用效率高 -缺点: 需要有略微的terminal知识 -所使用插件系统与graphical版本一致 可以来回切换 \ No newline at end of file diff --git a/mirai-console-terminal/build.gradle.kts b/mirai-console-terminal/build.gradle.kts deleted file mode 100644 index f95dd4cdb..000000000 --- a/mirai-console-terminal/build.gradle.kts +++ /dev/null @@ -1,44 +0,0 @@ -plugins { - id("kotlinx-serialization") - id("kotlin") - id("java") -} - - -apply(plugin = "com.github.johnrengelman.shadow") - - -tasks.withType() { - manifest { - attributes["Main-Class"] = "net.mamoe.mirai.console.MiraiConsoleTerminalLoader" - } -} - -val kotlinVersion: String by rootProject.ext -val atomicFuVersion: String by rootProject.ext -val coroutinesVersion: String by rootProject.ext -val kotlinXIoVersion: String by rootProject.ext -val coroutinesIoVersion: String by rootProject.ext - -val klockVersion: String by rootProject.ext -val ktorVersion: String by rootProject.ext - -val serializationVersion: String by rootProject.ext - -fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version" - -fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version" - - - -dependencies { - api(project(":mirai-core")) - api(project(":mirai-core-qqandroid")) - api(project(":mirai-api-http")) - api(project(":mirai-console")) - runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main")) - runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) - api(group = "com.googlecode.lanterna", name = "lanterna", version = "3.0.2") - api("org.bouncycastle:bcprov-jdk15on:1.64") - // classpath is not set correctly by IDE -} \ No newline at end of file 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 deleted file mode 100644 index 9da5cf122..000000000 --- a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt +++ /dev/null @@ -1,31 +0,0 @@ -package net.mamoe.mirai.console - -import net.mamoe.mirai.console.pure.MiraiConsoleUIPure -import kotlin.concurrent.thread - -class MiraiConsoleTerminalLoader { - companion object { - @JvmStatic - fun main(args: Array) { - if (args.contains("pure") || args.contains("-pure") || System.getProperty( - "os.name", - "" - ).toLowerCase().contains("windows") - ) { - println("[MiraiConsoleTerminalLoader]: 将以Pure[兼容模式]启动Console") - MiraiConsole.start(MiraiConsoleUIPure()) - } else { - MiraiConsoleTerminalUI.start() - thread { - MiraiConsole.start( - MiraiConsoleTerminalUI - ) - } - } - Runtime.getRuntime().addShutdownHook(thread(start = false) { - MiraiConsole.stop() - MiraiConsoleTerminalUI.exit() - }) - } - } -} \ No newline at end of file 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 deleted file mode 100644 index ebefb9e1a..000000000 --- a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt +++ /dev/null @@ -1,662 +0,0 @@ -package net.mamoe.mirai.console - -import com.googlecode.lanterna.SGR -import com.googlecode.lanterna.TerminalSize -import com.googlecode.lanterna.TextColor -import com.googlecode.lanterna.graphics.TextGraphics -import com.googlecode.lanterna.input.KeyStroke -import com.googlecode.lanterna.input.KeyType -import com.googlecode.lanterna.terminal.DefaultTerminalFactory -import com.googlecode.lanterna.terminal.Terminal -import com.googlecode.lanterna.terminal.TerminalResizeListener -import com.googlecode.lanterna.terminal.swing.SwingTerminal -import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame -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.console.utils.MiraiConsoleUI -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.createCharImg -import net.mamoe.mirai.utils.writeChannel -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 javax.imageio.ImageIO -import kotlin.concurrent.thread -import kotlin.system.exitProcess - -/** - * 此文件不推荐任何人看 - * 可能导致 - * 1:心肌梗死 - * 2:呼吸困难 - * 3:想要重写但是发现改任何一个看似不合理的地方都会崩 - * - * @author NaturalHG - * - */ - -fun String.actualLength(): Int { - var x = 0 - this.forEach { - if (it.isChineseChar()) { - x += 2 - } else { - x += 1 - } - } - return x -} - -fun String.getSubStringIndexByActualLength(widthMax: Int): Int { - var index = 0 - var currentLength = 0 - this.forEach { - if (it.isChineseChar()) { - currentLength += 2 - } else { - currentLength += 1 - } - if (currentLength > widthMax) { - return@forEach - } - ++index - } - if (index < 2) { - index = 2 - } - return index -} - -fun Char.isChineseChar(): Boolean { - return this.toString().isChineseChar() -} - -fun String.isChineseChar(): Boolean { - return this.matches(Regex("[\u4e00-\u9fa5]")) -} - - -object MiraiConsoleTerminalUI : MiraiConsoleUI { - val cacheLogSize = 50 - var mainTitle = "Mirai Console v0.01 Core v0.15" - - override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) { - mainTitle = "Mirai Console(Terminal) $consoleVersion $consoleBuild Core $coreVersion" - } - - override fun pushLog(identity: Long, message: String) { - log[identity]!!.push(message) - if (identity == screens[currentScreenId]) { - drawLog(message) - } - } - - override fun prePushBot(identity: Long) { - log[identity] = LimitLinkedQueue(cacheLogSize) - } - - override fun pushBot(bot: Bot) { - 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) { - delay(100)//不然会卡死 迷惑吧 - } - return requestResult!! - } - - - suspend fun provideInput(input: String) { - if (requesting) { - requestResult = input - requesting = false - } else { - MiraiConsole.CommandProcessor.runConsoleCommand(commandBuilder.toString()) - } - } - - - override fun pushBotAdminStatus(identity: Long, admins: List) { - 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]") - } - } - - var toLog = "" - tempFile.inputStream().use { - val img = ImageIO.read(it) - if (img == null) { - toLog += "无法创建字符图片. 请查看文件\n" - } else { - toLog += img.createCharImg((terminal.terminalSize.columns / 1.5).toInt()) - } - } - pushLog(0, "$toLog[Login Solver]请输验证码. ${tempFile.absolutePath}") - 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>().also { - it[0L] = LimitLinkedQueue(cacheLogSize) - } - - val botAdminCount = ConcurrentHashMap() - - private val screens = mutableListOf(0L) - private var currentScreenId = 0 - - - lateinit var terminal: Terminal - lateinit var textGraphics: TextGraphics - - var hasStart = false - private lateinit var internalPrinter: PrintStream - fun start() { - if (hasStart) { - return - } - - internalPrinter = System.out - - - hasStart = true - val defaultTerminalFactory = DefaultTerminalFactory(internalPrinter, System.`in`, Charset.defaultCharset()) - try { - terminal = defaultTerminalFactory.createTerminal() - terminal.enterPrivateMode() - terminal.clearScreen() - terminal.setCursorVisible(false) - } catch (e: Exception) { - try { - terminal = SwingTerminalFrame("Mirai Console") - terminal.enterPrivateMode() - terminal.clearScreen() - terminal.setCursorVisible(false) - } catch (e: Exception) { - error("can not create terminal") - } - } - textGraphics = terminal.newTextGraphics() - - /* - var lastRedrawTime = 0L - var lastNewWidth = 0 - var lastNewHeight = 0 - - terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize -> - try { - if (lastNewHeight == newSize.rows - && - lastNewWidth == newSize.columns - ) { - return@TerminalResizeListener - } - lastNewHeight = newSize.rows - lastNewWidth = newSize.columns - terminal.clearScreen() - if(terminal !is SwingTerminalFrame) { - Thread.sleep(300) - } - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - }catch (ignored:Exception){ - - } - }) - - */ - var lastJob: Job? = null - terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize -> - lastJob = GlobalScope.launch { - try { - delay(300) - if (lastJob == coroutineContext[Job]) { - terminal.clearScreen() - //inited = false - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - }) - - if (terminal !is SwingTerminalFrame) { - System.setOut(PrintStream(object : OutputStream() { - var builder = java.lang.StringBuilder() - override fun write(b: Int) { - with(b.toChar()) { - if (this == '\n') { - pushLog(0, builder.toString()) - builder = java.lang.StringBuilder() - } else { - builder.append(this) - } - } - } - })) - } - - System.setErr(System.out) - - try { - update() - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - - val charList = listOf(',', '.', '/', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '=', '+', '!', ' ') - thread { - while (true) { - try { - var keyStroke: KeyStroke = terminal.readInput() - - when (keyStroke.keyType) { - KeyType.ArrowLeft -> { - currentScreenId = - getLeftScreenId() - clearRows(2) - cleanPage() - update() - } - KeyType.ArrowRight -> { - currentScreenId = - getRightScreenId() - clearRows(2) - cleanPage() - update() - } - KeyType.Enter -> { - runBlocking { - provideInput(commandBuilder.toString()) - } - emptyCommand() - } - KeyType.Escape -> { - exit() - } - else -> { - if (keyStroke.character != null) { - if (keyStroke.character.toInt() == 8) { - deleteCommandChar() - } - if (keyStroke.character.isLetterOrDigit() || charList.contains(keyStroke.character)) { - addCommandChar(keyStroke.character) - } - } - } - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - } - } - - private fun getLeftScreenId(): Int { - var newId = currentScreenId - 1 - if (newId < 0) { - newId = screens.size - 1 - } - return newId - } - - private fun getRightScreenId(): Int { - var newId = 1 + currentScreenId - if (newId >= screens.size) { - newId = 0 - } - return newId - } - - private fun getScreenName(id: Int): String { - return when (screens[id]) { - 0L -> { - "Console Screen" - } - else -> { - "Bot: ${screens[id]}" - } - } - } - - - fun clearRows(row: Int) { - textGraphics.putString( - 0, row, " ".repeat( - terminal.terminalSize.columns - ) - ) - } - - fun drawFrame( - title: String - ) { - val width = terminal.terminalSize.columns - val height = terminal.terminalSize.rows - terminal.setBackgroundColor(TextColor.ANSI.DEFAULT) - - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.GREEN - textGraphics.putString((width - mainTitle.actualLength()) / 2, 1, mainTitle, SGR.BOLD) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString(2, 3, "-".repeat(width - 4)) - textGraphics.putString(2, 5, "-".repeat(width - 4)) - textGraphics.putString(2, height - 4, "-".repeat(width - 4)) - textGraphics.putString(2, height - 3, "|>>>") - textGraphics.putString(width - 3, height - 3, "|") - textGraphics.putString(2, height - 2, "-".repeat(width - 4)) - - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - val leftName = - getScreenName(getLeftScreenId()) - // clearRows(2) - textGraphics.putString((width - title.actualLength()) / 2 - "$leftName << ".length, 2, "$leftName << ") - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.YELLOW - textGraphics.putString((width - title.actualLength()) / 2, 2, title, SGR.BOLD) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - val rightName = - getScreenName(getRightScreenId()) - textGraphics.putString((width + title.actualLength()) / 2 + 1, 2, ">> $rightName") - } - - fun drawMainFrame( - onlineBotCount: Number - ) { - drawFrame("Console Screen") - val width = terminal.terminalSize.columns - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - clearRows(4) - textGraphics.putString(2, 4, "|Online Bots: $onlineBotCount") - textGraphics.putString( - width - 2 - "Powered By Mamoe Technologies|".actualLength(), - 4, - "Powered By Mamoe Technologies|" - ) - } - - fun drawBotFrame( - qq: Long, - adminCount: Number - ) { - drawFrame("Bot: $qq") - val width = terminal.terminalSize.columns - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - clearRows(4) - textGraphics.putString(2, 4, "|Admins: $adminCount") - textGraphics.putString(width - 2 - "Add admins via commands|".actualLength(), 4, "Add admins via commands|") - } - - - object LoggerDrawer { - var currentHeight = 6 - - fun drawLog(string: String, flush: Boolean = true) { - val maxHeight = terminal.terminalSize.rows - 4 - val heightNeed = (string.actualLength() / (terminal.terminalSize.columns - 6)) + 1 - if (heightNeed - 1 > maxHeight) { - pushLog(0, "[UI ERROR]: 您的屏幕太小, 有一条超长LOG无法显示") - return//拒绝打印 - } - if (currentHeight + heightNeed > maxHeight) { - cleanPage()//翻页 - } - if (string.contains("\n")) { - string.split("\n").forEach { - drawLog(string, false) - } - } else { - val width = terminal.terminalSize.columns - 6 - var x = string - while (true) { - if (x == "") { - break - } - val toWrite = if (x.actualLength() > width) { - val index = x.getSubStringIndexByActualLength(width) - x.substring(0, index).also { - x = if (index < x.length) { - x.substring(index) - } else { - "" - } - } - } else { - x.also { - x = "" - } - } - try { - textGraphics.foregroundColor = TextColor.ANSI.GREEN - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString( - 3, - currentHeight, toWrite, SGR.ITALIC - ) - } catch (ignored: Exception) { - // - } - ++currentHeight - } - } - if (flush && terminal is SwingTerminalFrame) { - terminal.flush() - } - } - - - fun cleanPage() { - for (index in 6 until terminal.terminalSize.rows - 4) { - clearRows(index) - } - currentHeight = 6 - } - - - fun redrawLogs(toDraw: Queue) { - //this.cleanPage() - currentHeight = 6 - var logsToDraw = 0 - var vara = 0 - val toPrint = mutableListOf() - toDraw.forEach { - val heightNeed = (it.actualLength() / (terminal.terminalSize.columns - 6)) + 1 - vara += heightNeed - if (currentHeight + vara < terminal.terminalSize.rows - 4) { - logsToDraw++ - toPrint.add(it) - } else { - return@forEach - } - } - toPrint.reversed().forEach { - drawLog(it, false) - } - if (terminal is SwingTerminalFrame) { - terminal.flush() - } - } - } - - - var commandBuilder = StringBuilder() - fun redrawCommand() { - val height = terminal.terminalSize.rows - val width = terminal.terminalSize.columns - clearRows(height - 3) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString(2, height - 3, "|>>>") - textGraphics.putString(width - 3, height - 3, "|") - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.BLACK - textGraphics.putString(7, height - 3, commandBuilder.toString()) - if (terminal is SwingTerminalFrame) { - terminal.flush() - } - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - } - - private fun addCommandChar( - c: Char - ) { - if (!requesting && commandBuilder.isEmpty() && c != '/') { - addCommandChar('/') - } - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.BLACK - val height = terminal.terminalSize.rows - commandBuilder.append(c) - if (terminal is SwingTerminalFrame) { - redrawCommand() - } else { - textGraphics.putString(6 + commandBuilder.length, height - 3, c.toString()) - } - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - } - - private fun deleteCommandChar() { - if (!commandBuilder.isEmpty()) { - commandBuilder = StringBuilder(commandBuilder.toString().substring(0, commandBuilder.length - 1)) - } - val height = terminal.terminalSize.rows - if (terminal is SwingTerminalFrame) { - redrawCommand() - } else { - textGraphics.putString(7 + commandBuilder.length, height - 3, " ") - } - } - - - var lastEmpty: Job? = null - private fun emptyCommand() { - commandBuilder = StringBuilder() - if (terminal is SwingTerminal) { - redrawCommand() - terminal.flush() - } else { - lastEmpty = GlobalScope.launch { - try { - delay(100) - if (lastEmpty == coroutineContext[Job]) { - terminal.clearScreen() - //inited = false - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - } - } - - fun update() { - when (screens[currentScreenId]) { - 0L -> { - drawMainFrame(screens.size - 1) - } - else -> { - drawBotFrame( - screens[currentScreenId], - 0 - ) - } - } - redrawLogs(log[screens[currentScreenId]]!!) - } - - fun exit() { - try { - terminal.exitPrivateMode() - terminal.close() - exitProcess(0) - } catch (ignored: Exception) { - - } - } -} - - -class LimitLinkedQueue( - val limit: Int = 50 -) : ConcurrentLinkedDeque() { - override fun push(e: T) { - if (size >= limit) { - this.pollLast() - } - return super.push(e) - } -} diff --git a/mirai-console/README.MD b/mirai-console/README.MD deleted file mode 100644 index 060fe54b9..000000000 --- a/mirai-console/README.MD +++ /dev/null @@ -1,88 +0,0 @@ -# Mirai Console -你可以在全平台运行Mirai高效率机器人框架 -### Mirai Console提供了6个版本以满足各种需要 -#### 所有版本的Mirai Console API相同 插件系统相同 - -| 名字 | 介绍 | -| --- | --- | -| Mirai-Console-Pure | 最纯净版, CLI环境, 通过标准输入与标准输出 交互 | -| Mirai-Console-Terminal | (UNIX)Terminal环境 提供简洁的富文本控制台 | -| Mirai-Console-Android | 安卓APP (TODO) | -| Mirai-Console-Graphical | JavaFX的图形化界面 (.jar/.exe/.dmg) | -| Mirai-Console-WebPanel | Web Panel操作(TODO) | -| Mirai-Console-Ios | IOS APP (TODO) | - - - -### 如何选择版本 -1: Mirai-Console-Pure 兼容性最高, 在其他都表现不佳的时候请使用
-2: 以系统区分 -```kotlin - return when(operatingSystem){ - WINDOWS -> listOf("Graphical","WebPanel","Pure") - MAC_OS -> listOf("Graphical","Terminal","WebPanel","Pure") - LINUX -> listOf("Terminal","Pure") - ANDROID -> listOf("Android","Pure","WebPanel") - IOS -> listOf("Ios") - else -> listOf("Pure") - } -``` -3: 以策略区分 -```kotlin - return when(task){ - 体验 -> listOf("Graphical","Terminal","WebPanel","Android","Pure") - 测试插件 -> listOf("Pure") - 调试插件 -> byOperatingSystem() - 稳定挂机 -> listOf("Terminal","Pure") - else -> listOf("Pure") - } -``` - - -#### More Importantly, Mirai Console support Plugins, tells the bot what to do -#### Mirai Console 支持插件系统, 你可以自己开发或使用公开的插件来逻辑化机器人, 如群管 -
- -#### download 下载 -#### how to get/write plugins 如何获取/写插件 -
-
- -### how to use(如何使用) -#### how to run Mirai Console -

    -
  • download mirai-console.jar
  • -
  • open command line/terminal
  • -
  • create a folder and put mirai-console.jar in
  • -
  • cd that folder
  • -
  • "java -jar mirai-console.jar"
  • -
- -
    -
  • 下载mirai-console.jar
  • -
  • 打开终端
  • -
  • 在任何地方创建一个文件夹, 并放入mirai-console.jar
  • -
  • 在终端中打开该文件夹"cd"
  • -
  • 输入"java -jar mirai-console.jar"
  • -
- -#### how to add plugins -
    -
  • After first time of running mirai console
  • -
  • /plugins/folder will be created next to mirai-console.jar
  • -
  • put plugin(.jar) into /plugins/
  • -
  • restart mirai console
  • -
  • checking logger and check if the plugin is loaded successfully
  • -
  • if the plugin has it own Config file, it normally appears in /plugins/{pluginName}/
  • -
- -
    -
  • 在首次运行mirai console后
  • -
  • mirai-console.jar 的同级会出现/plugins/文件夹
  • -
  • 将插件(.jar)放入/plugins/文件夹
  • -
  • 重启mirai console
  • -
  • 在开启后检查日志, 是否成功加载
  • -
  • 如该插件有配置文件, 配置文件一般会创建在/plugins/插件名字/ 文件夹下
  • -
- - diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts deleted file mode 100644 index ce59efa26..000000000 --- a/mirai-console/build.gradle.kts +++ /dev/null @@ -1,46 +0,0 @@ -plugins { - id("kotlinx-serialization") - id("kotlin") - id("java") -} - - -apply(plugin = "com.github.johnrengelman.shadow") - -val kotlinVersion: String by rootProject.ext -val atomicFuVersion: String by rootProject.ext -val coroutinesVersion: String by rootProject.ext -val kotlinXIoVersion: String by rootProject.ext -val coroutinesIoVersion: String by rootProject.ext - -val klockVersion: String by rootProject.ext -val ktorVersion: String by rootProject.ext - -val serializationVersion: String by rootProject.ext - -fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version" - -fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version" - -tasks.withType() { - manifest { - attributes["Main-Class"] = "net.mamoe.mirai.console.pure.MiraiConsolePureLoader" - } -} - - -dependencies { - api(project(":mirai-core")) - api(project(":mirai-core-qqandroid")) - // api(project(":mirai-api-http")) - runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main")) - runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) - api(kotlin("serialization")) - api(group = "com.alibaba", name = "fastjson", version = "1.2.62") - api(group = "org.yaml", name = "snakeyaml", version = "1.25") - api(group = "com.moandjiezana.toml", name = "toml4j", version = "0.7.2") - api("org.bouncycastle:bcprov-jdk15on:1.64") - - implementation("no.tornado:tornadofx:1.7.19") - // classpath is not set correctly by IDE -} \ No newline at end of file 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 deleted file mode 100644 index 4f634f5bc..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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.console - -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsole.CommandProcessor.processNextCommandLine -import net.mamoe.mirai.console.command.CommandManager -import net.mamoe.mirai.console.command.CommandSender -import net.mamoe.mirai.console.command.ConsoleCommandSender -import net.mamoe.mirai.console.command.DefaultCommands -import net.mamoe.mirai.console.plugins.PluginManager -import net.mamoe.mirai.console.plugins.loadAsConfig -import net.mamoe.mirai.console.plugins.withDefaultWrite -import net.mamoe.mirai.console.utils.MiraiConsoleUI -import net.mamoe.mirai.utils.cryptor.ECDH -import java.io.File - - -object MiraiConsole { - /** - * 发布的版本号 统一修改位置 - */ - const val version = "0.1.0" - const val coreVersion = "v0.18.0" - const val build = "Alpha" - - - /** - * 获取从Console登陆上的Bot, Bots - * */ - val bots get() = Bot.instances - - fun getBotByUIN(uin: Long): Bot? { - bots.forEach { - if (it.get()?.uin == uin) { - return it.get() - } - } - return null - } - - /** - * PluginManager - */ - val pluginManager: PluginManager get() = PluginManager - - /** - * 与前端交互所使用的Logger - */ - var logger = UIPushLogger - - /** - * Console运行路径 - */ - var path: String = System.getProperty("user.dir") - - /** - * Console前端接口 - */ - lateinit var frontEnd: MiraiConsoleUI - - - /** - * 启动Console - */ - var start = false - - fun start( - frontEnd: MiraiConsoleUI - ) { - if (start) { - return - } - start = true - - /* 加载ECDH */ - try { - ECDH() - } catch (ignored: Exception) { - } - //Security.removeProvider("BC") - - - /* 初始化前端 */ - this.frontEnd = frontEnd - frontEnd.pushVersion(version, build, coreVersion) - logger("Mirai-console [$version $build | core version $coreVersion] is still in testing stage, major features are available") - logger("Mirai-console now running under $path") - logger("Get news in github: https://github.com/mamoe/mirai") - logger("Mirai为开源项目,请自觉遵守开源项目协议") - logger("Powered by Mamoe Technologies and contributors") - - /* 依次启用功能 */ - DefaultCommands() - HTTPAPIAdaptar() - pluginManager.loadPlugins() - CommandProcessor.start() - - /* 通知启动完成 */ - logger("Mirai-console 启动完成") - logger("\"/login qqnumber qqpassword \" to login a bot") - logger("\"/login qq号 qq密码 \" 来登录一个BOT") - } - - fun stop() { - PluginManager.disableAllPlugins() - try { - bots.forEach { - it.get()?.close() - } - } catch (ignored: Exception) { - - } - } - - - object CommandProcessor : Job by { - GlobalScope.launch(start = CoroutineStart.LAZY) { - processNextCommandLine() - } - }() { - - internal class FullCommand( - val sender: CommandSender, - val commandStr: String - ) - - private val commandChannel: Channel = Channel() - - suspend fun runConsoleCommand(command: String) { - commandChannel.send( - FullCommand(ConsoleCommandSender, command) - ) - } - - suspend fun runCommand(sender: CommandSender, command: String) { - commandChannel.send( - FullCommand(sender, command) - ) - } - - fun runConsoleCommandBlocking(command: String) = runBlocking { runConsoleCommand(command) } - - fun runCommandBlocking(sender: CommandSender, command: String) = runBlocking { runCommand(sender, command) } - - private suspend fun processNextCommandLine() { - for (command in commandChannel) { - var commandStr = command.commandStr - if (!commandStr.startsWith("/")) { - commandStr = "/$commandStr" - } - if (!CommandManager.runCommand(command.sender, commandStr)) { - command.sender.sendMessage("未知指令 $commandStr") - } - } - } - } - - object UIPushLogger { - operator fun invoke(any: Any? = null) { - invoke( - "[Mirai$version $build]", - 0L, - any - ) - } - - operator fun invoke(identityStr: String, identity: Long, any: Any? = null) { - if (any != null) { - frontEnd.pushLog(identity, "$identityStr: $any") - } - } - } - -} - -object MiraiProperties { - var config = File("${MiraiConsole.path}/mirai.properties").loadAsConfig() - - var HTTP_API_ENABLE: Boolean by config.withDefaultWrite { true } - var HTTP_API_PORT: Int by config.withDefaultWrite { 8080 } - /* - var HTTP_API_AUTH_KEY: String by config.withDefaultWriteSave { - "InitKey" + generateSessionKey() - }*/ -} - -object HTTPAPIAdaptar { - operator fun invoke() { - /* - if (MiraiProperties.HTTP_API_ENABLE) { - if (MiraiProperties.HTTP_API_AUTH_KEY.startsWith("InitKey")) { - MiraiConsole.logger("请尽快更改初始生成的HTTP API AUTHKEY") - } - MiraiConsole.logger("正在启动HTTPAPI; 端口=" + MiraiProperties.HTTP_API_PORT) - MiraiHttpAPIServer.logger = SimpleLogger("HTTP API") { _, message, e -> - MiraiConsole.logger("[Mirai HTTP API]", 0, message) - } - MiraiHttpAPIServer.start( - MiraiProperties.HTTP_API_PORT, - MiraiProperties.HTTP_API_AUTH_KEY - ) - MiraiConsole.logger("HTTPAPI启动完成; 端口= " + MiraiProperties.HTTP_API_PORT) - }*/ - } -} - - - diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt deleted file mode 100644 index 17bc58c21..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ /dev/null @@ -1,242 +0,0 @@ -/* - * 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.console.command - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.plugins.PluginManager -import net.mamoe.mirai.contact.Contact -import net.mamoe.mirai.contact.Group -import net.mamoe.mirai.contact.sendMessage -import net.mamoe.mirai.message.GroupMessage -import net.mamoe.mirai.message.MessageReceipt -import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.quote -import net.mamoe.mirai.message.data.toMessage -import net.mamoe.mirai.utils.MiraiExperimentalAPI -import java.lang.StringBuilder - -object CommandManager { - private val registeredCommand: MutableMap = mutableMapOf() - - fun getCommands(): Collection { - return registeredCommand.values - } - - - fun register(command: Command) { - val allNames = mutableListOf(command.name).also { it.addAll(command.alias) } - allNames.forEach { - if (registeredCommand.containsKey(it)) { - error("Command Name(or Alias) $it is already registered, consider if same functional plugin was installed") - } - } - allNames.forEach { - registeredCommand[it] = command - } - } - - fun unregister(command: Command) { - val allNames = mutableListOf(command.name).also { it.addAll(command.alias) } - allNames.forEach { - registeredCommand.remove(it) - } - } - - fun unregister(commandName: String) { - registeredCommand.remove(commandName) - } - - /* - * Index: MiraiConsole - * */ - internal suspend fun runCommand(sender: CommandSender, fullCommand: String): Boolean { - val blocks = fullCommand.split(" ") - val commandHead = blocks[0].replace("/", "") - if (!registeredCommand.containsKey(commandHead)) { - return false - } - val args = blocks.subList(1, blocks.size) - registeredCommand[commandHead]?.run { - try { - if (onCommand( - sender, - blocks.subList(1, blocks.size) - ) - ) { - PluginManager.onCommand(this, args) - } else { - sender.sendMessage(this.usage) - } - } catch (e: Exception) { - sender.sendMessage("在运行指令时出现了未知错误") - e.printStackTrace() - } finally { - (sender as CommandSenderImpl).flushMessage() - } - } - return true - } - -} - -interface CommandSender { - /** - * 立刻发送一条Message - */ - suspend fun sendMessage(messageChain: MessageChain) - - suspend fun sendMessage(message: String) - /** - * 写入要发送的内容 所有内容最后会被以一条发出, 不管成功与否 - */ - fun appendMessage(message: String) - - fun sendMessageBlocking(messageChain: MessageChain) = runBlocking { sendMessage(messageChain) } - fun sendMessageBlocking(message: String) = runBlocking { sendMessage(message) } -} - -abstract class CommandSenderImpl : CommandSender { - internal val builder = StringBuilder() - - override fun appendMessage(message: String) { - builder.append(message).append("\n") - } - - internal open suspend fun flushMessage() { - if (!builder.isEmpty()) { - sendMessage(builder.toString().removeSuffix("\n")) - } - } -} - -object ConsoleCommandSender : CommandSenderImpl() { - override suspend fun sendMessage(messageChain: MessageChain) { - MiraiConsole.logger("[Command]", 0, messageChain.toString()) - } - - override suspend fun sendMessage(message: String) { - MiraiConsole.logger("[Command]", 0, message) - } - - override suspend fun flushMessage() { - super.flushMessage() - builder.clear() - } -} - -open class ContactCommandSender(val contact: Contact) : CommandSenderImpl() { - override suspend fun sendMessage(messageChain: MessageChain) { - contact.sendMessage(messageChain) - } - - override suspend fun sendMessage(message: String) { - contact.sendMessage(message) - } -} - -/** - * 弃用中 - * */ -class GroupCommandSender(val toQuote: GroupMessage, contact: Contact) : ContactCommandSender(contact) { - @MiraiExperimentalAPI - override suspend fun sendMessage(message: String) { - toQuote.quoteReply(message) - } - - @MiraiExperimentalAPI - override suspend fun sendMessage(messageChain: MessageChain) { - toQuote.quoteReply(messageChain) - } -} - -interface Command { - val name: String - val alias: List - val description: String - val usage: String - - suspend fun onCommand(sender: CommandSender, args: List): Boolean - fun register() -} - -abstract class BlockingCommand( - override val name: String, - override val alias: List = listOf(), - override val description: String = "", - override val usage: String = "" -) : Command { - /** - * 最高优先级监听器 - * 如果 return `false` 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听 - * */ - final override suspend fun onCommand(sender: CommandSender, args: List): Boolean { - return withContext(Dispatchers.IO) { - onCommandBlocking(sender, args) - } - } - - abstract fun onCommandBlocking(sender: CommandSender, args: List): Boolean - - override fun register() { - CommandManager.register(this) - } -} - -class AnonymousCommand internal constructor( - override val name: String, - override val alias: List, - override val description: String, - override val usage: String = "", - val onCommand: suspend CommandSender.(args: List) -> Boolean -) : Command { - override suspend fun onCommand(sender: CommandSender, args: List): Boolean { - return onCommand.invoke(sender, args) - } - - override fun register() { - CommandManager.register(this) - } -} - -class CommandBuilder internal constructor() { - var name: String? = null - var alias: List? = null - var description: String = "" - var usage: String = "use /help for help" - var onCommand: (suspend CommandSender.(args: List) -> Boolean)? = null - - fun onCommand(commandProcess: suspend CommandSender.(args: List) -> Boolean) { - onCommand = commandProcess - } - - fun register(): Command { - if (name == null || onCommand == null) { - error("CommandBuilder not complete") - } - if (alias == null) { - alias = listOf() - } - return AnonymousCommand( - name!!, - alias!!, - description, - usage, - onCommand!! - ).also { it.register() } - } -} - -fun registerCommand(builder: CommandBuilder.() -> Unit): Command { - return CommandBuilder().apply(builder).register() -} - diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt deleted file mode 100644 index 651eb423b..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt +++ /dev/null @@ -1,265 +0,0 @@ -package net.mamoe.mirai.console.command - -import kotlinx.coroutines.runBlocking -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.plugins.PluginManager -import net.mamoe.mirai.console.utils.addManager -import net.mamoe.mirai.console.utils.checkManager -import net.mamoe.mirai.console.utils.getManagers -import net.mamoe.mirai.console.utils.removeManager -import net.mamoe.mirai.contact.sendMessage -import net.mamoe.mirai.event.subscribeMessages -import net.mamoe.mirai.message.GroupMessage -import net.mamoe.mirai.utils.SimpleLogger -import java.lang.StringBuilder -import java.util.NoSuchElementException - - -/** - * Some defaults commands are recommend to be replaced by plugin provided commands - */ - -object DefaultCommands { - operator fun invoke() { - registerCommand { - name = "manager" - description = "Add a manager" - onCommand { it -> - if (this !is ConsoleCommandSender) { - sendMessage("请在后台使用该指令") - return@onCommand false - } - if (it.size < 2) { - MiraiConsole.logger("[Bot Manager]", 0, "/manager add [bot ID] [Manager ID]") - MiraiConsole.logger("[Bot Manager]", 0, "/manager remove [bot ID] [Manager ID]") - MiraiConsole.logger("[Bot Manager]", 0, "/manager list [bot ID]") - return@onCommand true - } - val botId = try { - it[1].toLong() - } catch (e: Exception) { - MiraiConsole.logger("[Bot Manager]", 0, it[1] + " 不是一个Bot的ID") - return@onCommand false - } - val bot = MiraiConsole.getBotByUIN(botId) - if (bot == null) { - MiraiConsole.logger("[Bot Manager]", 0, "$botId 没有在Console中登陆") - return@onCommand false - } - when (it[0]) { - "add" -> { - if (it.size < 3) { - MiraiConsole.logger("[Bot Manager]", 0, "/manager add [bot ID] [Manager ID]") - return@onCommand true - } - val adminID = try { - it[2].toLong() - } catch (e: Exception) { - MiraiConsole.logger("[Bot Manager]", 0, it[2] + " 不是一个ID") - return@onCommand false - } - bot.addManager(adminID) - MiraiConsole.logger("[Bot Manager]", 0, it[2] + "增加成功") - } - "remove" -> { - if (it.size < 3) { - MiraiConsole.logger("[Bot Manager]", 0, "/manager remove [bot ID] [Manager ID]") - return@onCommand true - } - val adminID = try { - it[2].toLong() - } catch (e: Exception) { - MiraiConsole.logger("[Bot Manager]", 0, it[1] + " 不是一个ID") - return@onCommand false - } - if (!bot.checkManager(adminID)) { - MiraiConsole.logger("[Bot Manager]", 0, it[2] + "本身不是一个Manager") - return@onCommand true - } - bot.removeManager(adminID) - MiraiConsole.logger("[Bot Manager]", 0, it[2] + "移除成功") - } - "list" -> { - bot.getManagers().forEach { - MiraiConsole.logger("[Bot Manager]", 0, " -> $it") - } - } - } - return@onCommand true - } - } - - registerCommand { - name = "login" - description = "机器人登陆" - onCommand { - if (this !is ConsoleCommandSender) { - sendMessage("请在后台使用该指令") - return@onCommand false - } - if (it.size < 2) { - MiraiConsole.logger("\"/login qqnumber qqpassword \" to login a bot") - MiraiConsole.logger("\"/login qq号 qq密码 \" 来登录一个BOT") - return@onCommand false - } - val qqNumber = it[0].toLong() - val qqPassword = it[1] - MiraiConsole.logger("[Bot Login]", 0, "login...") - try { - MiraiConsole.frontEnd.prePushBot(qqNumber) - val bot = Bot(qqNumber, qqPassword) { - this.loginSolver = MiraiConsole.frontEnd.createLoginSolver() - this.botLoggerSupplier = { - SimpleLogger("BOT $qqNumber]") { _, message, e -> - MiraiConsole.logger("[BOT $qqNumber]", qqNumber, message) - if (e != null) { - MiraiConsole.logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ - e.printStackTrace() - } - } - } - this.networkLoggerSupplier = { - SimpleLogger("BOT $qqNumber") { _, message, e -> - MiraiConsole.logger("[NETWORK]", qqNumber, message)//因为在一页 所以可以不打QQ - if (e != null) { - MiraiConsole.logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ - e.printStackTrace() - } - } - } - } - bot.login() - bot.subscribeMessages { - this.startsWith("/") { - if (bot.checkManager(this.sender.id)) { - val sender = ContactCommandSender(this.subject) - MiraiConsole.CommandProcessor.runCommand( - sender, it - ) - } - } - } - sendMessage("$qqNumber login successes") - MiraiConsole.frontEnd.pushBot(bot) - } catch (e: Exception) { - sendMessage("$qqNumber login failed -> " + e.message) - } - true - } - } - - registerCommand { - name = "status" - description = "获取状态" - onCommand { - when (it.size) { - 0 -> { - sendMessage("当前有" + MiraiConsole.bots.size + "个BOT在线") - } - 1 -> { - val bot = it[0] - var find = false - MiraiConsole.bots.forEach { - if (it.get()?.uin.toString().contains(bot)) { - find = true - appendMessage("" + it.get()?.uin + ": 在线中; 好友数量:" + it.get()?.qqs?.size + "; 群组数量:" + it.get()?.groups?.size) - } - } - if (!find) { - sendMessage("没有找到BOT$bot") - } - } - } - true - } - } - - - registerCommand { - name = "say" - description = "聊天功能演示" - onCommand { - if (it.size < 2) { - MiraiConsole.logger("say [好友qq号或者群号] [文本消息] //将默认使用第一个BOT") - MiraiConsole.logger("say [bot号] [好友qq号或者群号] [文本消息]") - return@onCommand false - } - val bot: Bot? = if (it.size == 2) { - if (MiraiConsole.bots.size == 0) { - MiraiConsole.logger("还没有BOT登录") - return@onCommand false - } - MiraiConsole.bots[0].get() - } else { - MiraiConsole.getBotByUIN(it[0].toLong()) - } - if (bot == null) { - MiraiConsole.logger("没有找到BOT") - return@onCommand false - } - val target = it[it.size - 2].toLong() - val message = it[it.size - 1] - try { - val contact = bot[target] - runBlocking { - contact.sendMessage(message) - MiraiConsole.logger("消息已推送") - } - } catch (e: NoSuchElementException) { - MiraiConsole.logger("没有找到群或好友 号码为${target}") - return@onCommand false - } - true - } - } - - - registerCommand { - name = "plugins" - alias = listOf("plugin") - description = "获取插件列表" - onCommand { - PluginManager.getAllPluginDescriptions().let { - it.forEach { - appendMessage("\t" + it.name + " v" + it.version + " by" + it.author + " " + it.info) - } - appendMessage("加载了" + it.size + "个插件") - true - } - } - } - - registerCommand { - name = "command" - alias = listOf("commands", "help", "helps") - description = "获取指令列表" - onCommand { - CommandManager.getCommands().let { - var size = 0 - appendMessage("")//\n - it.toSet().forEach { - ++size - appendMessage("-> " + it.name + " :" + it.description) - } - appendMessage("""共有${size}条指令""") - } - true - } - } - - registerCommand { - name = "about" - description = "About Mirai-Console" - onCommand { - appendMessage("v${MiraiConsole.version} ${MiraiConsole.build} is still in testing stage, major features are available") - appendMessage("now running under ${MiraiConsole.path}") - appendMessage("在Github中获取项目最新进展: https://github.com/mamoe/mirai") - appendMessage("Mirai为开源项目,请自觉遵守开源项目协议") - appendMessage("Powered by Mamoe Technologies and contributors") - true - } - } - - } -} \ No newline at end of file diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt deleted file mode 100644 index ed52dae92..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt +++ /dev/null @@ -1,525 +0,0 @@ -/* - * 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.console.plugins - -import com.alibaba.fastjson.JSON -import com.alibaba.fastjson.JSONObject -import com.alibaba.fastjson.TypeReference -import com.alibaba.fastjson.parser.Feature -import com.moandjiezana.toml.Toml -import com.moandjiezana.toml.TomlWriter -import kotlinx.serialization.Serializable -import kotlinx.serialization.UnstableDefault -import net.mamoe.mirai.utils.io.encodeToString -import org.yaml.snakeyaml.Yaml -import tornadofx.c -import java.io.File -import java.io.InputStream -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import kotlin.collections.LinkedHashMap -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.full.isSubclassOf - - -/** - * TODO: support all config types - * only JSON is now supported - * - */ - -interface Config { - fun getConfigSection(key: String): ConfigSection - fun getString(key: String): String - fun getInt(key: String): Int - fun getFloat(key: String): Float - fun getDouble(key: String): Double - fun getLong(key: String): Long - fun getBoolean(key: String): Boolean - fun getList(key: String): List<*> - fun getStringList(key: String): List - fun getIntList(key: String): List - fun getFloatList(key: String): List - fun getDoubleList(key: String): List - fun getLongList(key: String): List - fun getConfigSectionList(key: String): List - operator fun set(key: String, value: Any) - operator fun get(key: String): Any? - operator fun contains(key: String): Boolean - fun exist(key: String): Boolean - fun setIfAbsent(key: String, value: Any) - fun asMap(): Map - fun save() - - companion object { - fun load(fileName: String): Config { - return load( - File( - fileName.replace( - "//", - "/" - ) - ) - ) - } - - /** - * create a read-write config - * */ - fun load(file: File): Config { - if (!file.exists()) { - file.createNewFile() - } - return when (file.extension.toLowerCase()) { - "json" -> JsonConfig(file) - "yml" -> YamlConfig(file) - "yaml" -> YamlConfig(file) - "mirai" -> YamlConfig(file) - "ini" -> TomlConfig(file) - "toml" -> TomlConfig(file) - "properties" -> TomlConfig(file) - "property" -> TomlConfig(file) - "data" -> TomlConfig(file) - else -> error("Unsupported file config type ${file.extension.toLowerCase()}") - } - } - - /** - * create a read-only config - */ - fun load(content: String, type: String): Config { - return when (type.toLowerCase()) { - "json" -> JsonConfig(content) - "yml" -> YamlConfig(content) - "yaml" -> YamlConfig(content) - "mirai" -> YamlConfig(content) - "ini" -> TomlConfig(content) - "toml" -> TomlConfig(content) - "properties" -> TomlConfig(content) - "property" -> TomlConfig(content) - "data" -> TomlConfig(content) - else -> error("Unsupported file config type $content") - } - } - - /** - * create a read-only config - */ - fun load(inputStream: InputStream, type: String): Config { - return load(inputStream.readBytes().encodeToString(), type) - } - - } -} - - -fun File.loadAsConfig(): Config { - return Config.load(this) -} - -/* 最简单的代理 */ -inline operator fun Config.getValue(thisRef: Any?, property: KProperty<*>): T { - return smartCast(property) -} - -inline operator fun Config.setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this[property.name] = value -} - -/* 带有默认值的代理 */ -inline fun Config.withDefault( - noinline defaultValue: () -> T -): ReadWriteProperty { - val default by lazy { defaultValue.invoke() } - return object : ReadWriteProperty { - override fun getValue(thisRef: Any, property: KProperty<*>): T { - if (this@withDefault.exist(property.name)) {//unsafe - return this@withDefault.smartCast(property) - } - return default - } - - override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { - this@withDefault[property.name] = value - } - } -} - -/* 带有默认值且如果为空会写入的代理 */ -inline fun Config.withDefaultWrite( - noinline defaultValue: () -> T -): WithDefaultWriteLoader { - return WithDefaultWriteLoader( - T::class, - this, - defaultValue, - false - ) -} - -/* 带有默认值且如果为空会写入保存的代理 */ -inline fun Config.withDefaultWriteSave( - noinline defaultValue: () -> T -): WithDefaultWriteLoader { - return WithDefaultWriteLoader(T::class, this, defaultValue, true) -} - -class WithDefaultWriteLoader( - private val _class: KClass, - private val config: Config, - private val defaultValue: () -> T, - private val save: Boolean -) { - operator fun provideDelegate( - thisRef: Any, - prop: KProperty<*> - ): ReadWriteProperty { - val defaultValue by lazy { defaultValue.invoke() } - if (!config.contains(prop.name)) { - config[prop.name] = defaultValue - if (save) { - config.save() - } - } - return object : ReadWriteProperty { - override fun getValue(thisRef: Any, property: KProperty<*>): T { - if (config.exist(property.name)) {//unsafe - return config._smartCast(property.name, _class) - } - return defaultValue - } - - override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { - config[property.name] = value - } - } - } -} - -inline fun Config.smartCast(property: KProperty<*>): T { - return _smartCast(property.name, T::class) -} - -@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") -fun Config._smartCast(propertyName: String, _class: KClass): T { - return when (_class) { - String::class -> this.getString(propertyName) - Int::class -> this.getInt(propertyName) - Float::class -> this.getFloat(propertyName) - Double::class -> this.getDouble(propertyName) - Long::class -> this.getLong(propertyName) - Boolean::class -> this.getBoolean(propertyName) - else -> when { - _class.isSubclassOf(ConfigSection::class) -> this.getConfigSection(propertyName) - _class == List::class || _class == MutableList::class -> { - val list = this.getList(propertyName) - return if (list.isEmpty()) { - list - } else { - when (list[0]!!::class) { - String::class -> getStringList(propertyName) - Int::class -> getIntList(propertyName) - Float::class -> getFloatList(propertyName) - Double::class -> getDoubleList(propertyName) - Long::class -> getLongList(propertyName) - //不去支持getConfigSectionList(propertyName) - // LinkedHashMap::class -> getConfigSectionList(propertyName)//faster approach - else -> { - //if(list[0]!! is ConfigSection || list[0]!! is Map<*,*>){ - // getConfigSectionList(propertyName) - //}else { - error("unsupported type" + list[0]!!::class) - //} - } - } - } as T - } - else -> { - error("unsupported type") - } - } - } as T -} - - -interface ConfigSection : Config, MutableMap { - override fun getConfigSection(key: String): ConfigSection { - val content = get(key) ?: error("ConfigSection does not contain $key ") - if (content is ConfigSection) { - return content - } - return ConfigSectionDelegation( - Collections.synchronizedMap( - (get(key) ?: error("ConfigSection does not contain $key ")) as LinkedHashMap - ) - ) - } - - override fun getString(key: String): String { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString() - } - - override fun getInt(key: String): Int { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toInt() - } - - override fun getFloat(key: String): Float { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toFloat() - } - - override fun getBoolean(key: String): Boolean { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toBoolean() - } - - override fun getDouble(key: String): Double { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toDouble() - } - - override fun getLong(key: String): Long { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toLong() - } - - override fun getList(key: String): List<*> { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>) - } - - override fun getStringList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString() } - } - - override fun getIntList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toInt() } - } - - override fun getFloatList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toFloat() } - } - - override fun getDoubleList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toDouble() } - } - - override fun getLongList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toLong() } - } - - override fun getConfigSectionList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { - if (it is ConfigSection) { - it - } else { - ConfigSectionDelegation( - Collections.synchronizedMap( - it as MutableMap - ) - ) - } - } - } - - override fun exist(key: String): Boolean { - return get(key) != null - } - - override fun setIfAbsent(key: String, value: Any) { - if (!exist(key)) set(key, value) - } -} - -@Serializable -open class ConfigSectionImpl() : ConcurrentHashMap(), - ConfigSection { - override fun set(key: String, value: Any) { - super.put(key, value) - } - - override operator fun get(key: String): Any? { - return super.get(key) - } - - @Suppress("RedundantOverride") - override fun contains(key: String): Boolean { - return super.contains(key) - } - - override fun exist(key: String): Boolean { - return containsKey(key) - } - - override fun asMap(): Map { - return this - } - - override fun save() { - - } - - override fun setIfAbsent(key: String, value: Any) { - this.putIfAbsent(key, value)//atomic - } -} - -open class ConfigSectionDelegation( - private val delegate: MutableMap -) : ConfigSection, MutableMap by delegate { - override fun set(key: String, value: Any) { - delegate.put(key, value) - } - - override fun contains(key: String): Boolean { - return delegate.containsKey(key) - } - - override fun asMap(): Map { - return delegate - } - - override fun save() { - - } -} - - -interface FileConfig : Config { - fun deserialize(content: String): ConfigSection - - fun serialize(config: ConfigSection): String -} - - -abstract class FileConfigImpl internal constructor( - private val rawContent: String -) : FileConfig, - ConfigSection { - - internal var file: File? = null - - - constructor(file: File) : this(file.readText()) { - this.file = file - } - - - private val content by lazy { - deserialize(rawContent) - } - - - override val size: Int get() = content.size - override val entries: MutableSet> get() = content.entries - override val keys: MutableSet get() = content.keys - override val values: MutableCollection get() = content.values - override fun containsKey(key: String): Boolean = content.containsKey(key) - override fun containsValue(value: Any): Boolean = content.containsValue(value) - override fun put(key: String, value: Any): Any? = content.put(key, value) - override fun isEmpty(): Boolean = content.isEmpty() - override fun putAll(from: Map) = content.putAll(from) - override fun clear() = content.clear() - override fun remove(key: String): Any? = content.remove(key) - - override fun save() { - if (isReadOnly()) { - error("Config is readonly") - } - if (!((file?.exists())!!)) { - file?.createNewFile() - } - file?.writeText(serialize(content)) - } - - fun isReadOnly() = file == null - - override fun contains(key: String): Boolean { - return content.contains(key) - } - - override fun get(key: String): Any? { - return content[key] - } - - override fun set(key: String, value: Any) { - content[key] = value - } - - override fun asMap(): Map { - return content.asMap() - } - -} - -class JsonConfig internal constructor( - content: String -) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - @UnstableDefault - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank() || content == "{}") { - return ConfigSectionImpl() - } - return JSON.parseObject( - content, - object : TypeReference() {}, - Feature.OrderedField - ) - } - - @UnstableDefault - override fun serialize(config: ConfigSection): String { - return JSONObject.toJSONString(config) - } -} - -class YamlConfig internal constructor(content: String) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank()) { - return ConfigSectionImpl() - } - return ConfigSectionDelegation( - Collections.synchronizedMap( - Yaml().load>(content) as LinkedHashMap - ) - ) - } - - override fun serialize(config: ConfigSection): String { - return Yaml().dumpAsMap(config) - } - -} - -class TomlConfig internal constructor(content: String) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank()) { - return ConfigSectionImpl() - } - return ConfigSectionDelegation( - Collections.synchronizedMap( - Toml().read(content).toMap() - ) - ) - } - - override fun serialize(config: ConfigSection): String { - return TomlWriter().write(config) - } -} \ No newline at end of file diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt deleted file mode 100644 index e5e35c34e..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt +++ /dev/null @@ -1,410 +0,0 @@ -/* - * 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.console.plugins - -import kotlinx.coroutines.* -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.command.Command -import net.mamoe.mirai.utils.DefaultLogger -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.SimpleLogger -import net.mamoe.mirai.utils.io.encodeToString -import java.io.File -import java.io.InputStream -import java.net.URL -import java.net.URLClassLoader -import java.util.jar.JarFile -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext - - -abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope { - constructor() : this(EmptyCoroutineContext) - - private val supervisorJob = SupervisorJob() - final override val coroutineContext: CoroutineContext = coroutineContext + supervisorJob - - /** - * 插件被分配的data folder, 如果插件改名了 data folder 也会变 请注意 - */ - val dataFolder: File by lazy { - File(PluginManager.pluginsPath + pluginDescription.name).also { it.mkdir() } - } - - /** - * 当一个插件被加载时调用 - */ - open fun onLoad() { - - } - - /** - * 当所有插件全部被加载后被调用 - */ - open fun onEnable() { - - } - - /** - * 当插件关闭前被调用 - */ - open fun onDisable() { - - } - - /** - * 当任意指令被使用 - */ - open fun onCommand(command: Command, args: List) { - - } - - - internal fun enable() { - this.onEnable() - } - - /** - * 加载一个data folder中的Config - * 这个config是read-write的 - */ - fun loadConfig(fileName: String): Config { - return Config.load(dataFolder.absolutePath + fileName) - } - - @JvmOverloads - internal fun disable(throwable: CancellationException? = null) { - this.coroutineContext[Job]!!.cancelChildren(throwable) - this.onDisable() - } - - private lateinit var pluginDescription: PluginDescription - - internal fun init(pluginDescription: PluginDescription) { - this.pluginDescription = pluginDescription - this.onLoad() - } - - val pluginManager = PluginManager - - val logger: MiraiLogger by lazy { - SimpleLogger("Plugin ${pluginDescription.name}") { _, message, e -> - MiraiConsole.logger("[${pluginDescription.name}]", 0, message) - if (e != null) { - MiraiConsole.logger("[${pluginDescription.name}]", 0, e.toString()) - e.printStackTrace() - } - } - } - - /** - * 加载一个插件jar, resources中的东西 - */ - fun getResources(fileName: String): InputStream? { - return try { - this.javaClass.classLoader.getResourceAsStream(fileName) - } catch (e: Exception) { - PluginManager.getFileInJarByName( - this.pluginDescription.name, - fileName - ) - } - } - - /** - * 加载一个插件jar, resources中的Config - * 这个Config是read-only的 - */ - fun getResourcesConfig(fileName: String): Config { - if (fileName.contains(".")) { - error("Unknown Config Type") - } - return Config.load(getResources(fileName) ?: error("Config Not Found"), fileName.split(".")[1]) - } - -} - -class PluginDescription( - val name: String, - val author: String, - val basePath: String, - val version: String, - val info: String, - val depends: List,//插件的依赖 - internal var loaded: Boolean = false, - internal var noCircularDepend: Boolean = true -) { - - override fun toString(): String { - return "name: $name\nauthor: $author\npath: $basePath\nver: $version\ninfo: $info\ndepends: $depends" - } - - companion object { - fun readFromContent(content_: String): PluginDescription { - val content = content_.split("\n") - - var name = "Plugin" - var author = "Unknown" - var basePath = "net.mamoe.mirai.PluginMain" - var info = "Unknown" - var version = "1.0.0" - val depends = mutableListOf(); - - content.forEach { - val line = it.trim() - val lowercaseLine = line.toLowerCase() - if (it.contains(":")) { - when { - lowercaseLine.startsWith("name") -> { - name = line.substringAfter(":").trim() - } - lowercaseLine.startsWith("author") -> { - author = line.substringAfter(":").trim() - } - lowercaseLine.startsWith("info") || lowercaseLine.startsWith("information") -> { - info = line.substringAfter(":").trim() - } - lowercaseLine.startsWith("main") || lowercaseLine.startsWith("path") || lowercaseLine.startsWith( - "basepath" - ) -> { - basePath = line.substringAfter(":").trim() - } - lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> { - version = line.substringAfter(":").trim() - } - } - } else if (line.startsWith("-")) { - depends.add(line.substringAfter("-").trim()) - } - } - return PluginDescription( - name, - author, - basePath, - version, - info, - depends - ) - } - } -} - -internal class PluginClassLoader(file: File, parent: ClassLoader) : URLClassLoader(arrayOf(file.toURI().toURL()), parent) - -object PluginManager { - internal val pluginsPath = System.getProperty("user.dir") + "/plugins/".replace("//", "/").also { - File(it).mkdirs() - } - - val logger = SimpleLogger("Plugin Manager") { _, message, e -> - MiraiConsole.logger("[Plugin Manager]", 0, message) - } - - //已完成加载的 - private val nameToPluginBaseMap: MutableMap = mutableMapOf() - private val pluginDescriptions: MutableMap = mutableMapOf() - - fun onCommand(command: Command, args: List) { - nameToPluginBaseMap.values.forEach { - it.onCommand(command, args) - } - } - - fun getAllPluginDescriptions(): Collection { - return pluginDescriptions.values - } - - /** - * 尝试加载全部插件 - */ - fun loadPlugins() { - val pluginsFound: MutableMap = mutableMapOf() - val pluginsLocation: MutableMap = mutableMapOf() - - logger.info("""开始加载${pluginsPath}下的插件""") - - File(pluginsPath).listFiles()?.forEach { file -> - if (file != null && file.extension == "jar") { - val jar = JarFile(file) - val pluginYml = - jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull() - if (pluginYml == null) { - logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin") - } else { - val description = - PluginDescription.readFromContent( - URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use { - it.readBytes().encodeToString() - }) - pluginsFound[description.name] = description - pluginsLocation[description.name] = file - } - } - } - - fun checkNoCircularDepends( - target: PluginDescription, - needDepends: List, - existDepends: MutableList - ) { - - if (!target.noCircularDepend) { - return - } - - existDepends.add(target.name) - - if (needDepends.any { existDepends.contains(it) }) { - target.noCircularDepend = false - } - - existDepends.addAll(needDepends) - - needDepends.forEach { - if (pluginsFound.containsKey(it)) { - checkNoCircularDepends(pluginsFound[it]!!, pluginsFound[it]!!.depends, existDepends) - } - } - } - - - pluginsFound.values.forEach { - checkNoCircularDepends(it, it.depends, mutableListOf()) - } - - //load - - - fun loadPlugin(description: PluginDescription): Boolean { - if (!description.noCircularDepend) { - logger.error("Failed to load plugin " + description.name + " because it has circular dependency") - return false - } - - //load depends first - description.depends.forEach { dependent -> - if (!pluginsFound.containsKey(dependent)) { - logger.error("Failed to load plugin " + description.name + " because it need " + dependent + " as dependency") - return false - } - val depend = pluginsFound[dependent]!! - //还没有加载 - if (!depend.loaded && !loadPlugin(pluginsFound[dependent]!!)) { - logger.error("Failed to load plugin " + description.name + " because " + dependent + " as dependency failed to load") - return false - } - } - //在这里所有的depends都已经加载了 - - - //real load - logger.info("loading plugin " + description.name) - - try { - val pluginClass = try { - PluginClassLoader( - (pluginsLocation[description.name]!!), - this.javaClass.classLoader - ) - .loadClass(description.basePath) - } catch (e: ClassNotFoundException) { - logger.info("failed to find Main: " + description.basePath + " checking if it's kotlin's path") - PluginClassLoader( - (pluginsLocation[description.name]!!), - this.javaClass.classLoader - ) - .loadClass("${description.basePath}Kt") - } - return try { - val subClass = pluginClass.asSubclass(PluginBase::class.java) - val plugin: PluginBase = subClass.kotlin.objectInstance ?: subClass.getDeclaredConstructor().newInstance() - description.loaded = true - logger.info("successfully loaded plugin " + description.name + " version " + description.version + " by " + description.author) - logger.info(description.info) - - nameToPluginBaseMap[description.name] = plugin - pluginDescriptions[description.name] = description - plugin.init(description) - true - } catch (e: ClassCastException) { - logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ") - false - } - } catch (e: ClassNotFoundException) { - e.printStackTrace() - logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath) - return false - } - } - - pluginsFound.values.forEach { - loadPlugin(it) - } - - nameToPluginBaseMap.values.forEach { - it.enable() - } - - logger.info("""加载了${nameToPluginBaseMap.size}个插件""") - - } - - - @JvmOverloads - fun disableAllPlugins(throwable: CancellationException? = null) { - nameToPluginBaseMap.values.forEach { - it.disable(throwable) - } - } - - /** - * 根据插件名字找Jar的文件 - * null => 没找到 - */ - fun getJarPath(pluginName: String): File? { - File(pluginsPath).listFiles()?.forEach { file -> - if (file != null && file.extension == "jar") { - val jar = JarFile(file) - val pluginYml = - jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull() - if (pluginYml != null) { - val description = - PluginDescription.readFromContent( - URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use { - it.readBytes().encodeToString() - }) - if (description.name.toLowerCase() == pluginName.toLowerCase()) { - return file - } - } - } - } - return null - } - - - /** - * 根据插件名字找Jar中的文件 - * null => 没找到 - */ - fun getFileInJarByName(pluginName: String, toFind: String): InputStream? { - val jarFile = getJarPath(pluginName) - if (jarFile == null) { - return null - } - val jar = JarFile(jarFile) - val toFindFile = - jar.entries().asSequence().filter { it.name == toFind }.firstOrNull() ?: return null - return URL("jar:file:" + jarFile.absoluteFile + "!/" + toFindFile.name).openConnection().inputStream - } -} - - - diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt deleted file mode 100644 index deac0fce0..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.mamoe.mirai.console.pure - -import net.mamoe.mirai.console.MiraiConsole -import kotlin.concurrent.thread - -class MiraiConsolePureLoader { - companion object { - @JvmStatic - fun main(args: Array) { - MiraiConsole.start(MiraiConsoleUIPure()) - Runtime.getRuntime().addShutdownHook(thread(start = false) { - MiraiConsole.stop() - }) - } - } -} \ No newline at end of file diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt deleted file mode 100644 index 388f5551e..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.console.pure - -import kotlinx.coroutines.delay -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.utils.MiraiConsoleUI -import net.mamoe.mirai.utils.DefaultLoginSolver -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.LoginSolverInputReader -import kotlin.concurrent.thread - -class MiraiConsoleUIPure : MiraiConsoleUI { - var requesting = false - var requestStr = "" - - init { - thread { - while (true) { - val input = readLine() ?: return@thread - if (requesting) { - requestStr = input - requesting = false - } else { - MiraiConsole.CommandProcessor.runConsoleCommandBlocking(input) - } - } - } - } - - override fun pushLog(identity: Long, message: String) { - println(message) - } - - override fun prePushBot(identity: Long) { - - } - - override fun pushBot(bot: Bot) { - - } - - override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) { - - } - - override suspend fun requestInput(question: String): String { - requesting = true - while (true) { - delay(50) - if (!requesting) { - return requestStr - } - } - } - - override fun pushBotAdminStatus(identity: Long, admins: List) { - - } - - override fun createLoginSolver(): LoginSolver { - return DefaultLoginSolver( - reader = object : LoginSolverInputReader { - override suspend fun read(question: String): String? { - return requestInput(question) - } - } - ) - } - -} - - diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt deleted file mode 100644 index 51e9082c3..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.mamoe.mirai.console.utils - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.MiraiProperties -import net.mamoe.mirai.console.utils.BotManagers.BOT_MANAGERS -import net.mamoe.mirai.console.plugins.ConfigSection -import net.mamoe.mirai.console.plugins.ConfigSectionImpl -import net.mamoe.mirai.console.plugins.loadAsConfig -import net.mamoe.mirai.console.plugins.withDefaultWriteSave -import java.io.File - -object BotManagers { - val config = File("${MiraiConsole.path}/bot.yml").loadAsConfig() - val BOT_MANAGERS: ConfigSection by config.withDefaultWriteSave { ConfigSectionImpl() } -} - -fun Bot.addManager(long: Long) { - BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf()) - BOT_MANAGERS[this.uin.toString()] = - (BOT_MANAGERS.getLongList(this.uin.toString()) as MutableList).apply { add(long) } - BotManagers.config.save() -} - -fun Bot.removeManager(long: Long) { - BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf()) - BOT_MANAGERS[this.uin.toString()] = - (BOT_MANAGERS.getLongList(this.uin.toString()) as MutableList).apply { add(long) } - BotManagers.config.save() -} - -fun Bot.getManagers(): List { - BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf()) - return BOT_MANAGERS.getLongList(this.uin.toString()) -} - -fun Bot.checkManager(long: Long): Boolean { - return this.getManagers().contains(long) -} - - diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt deleted file mode 100644 index a82d8a0bc..000000000 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.console.utils - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.utils.LoginSolver - -/** - * 只需要实现一个这个 传入MiraiConsole 就可以绑定UI层与Console层 - * 注意线程 - */ - -interface MiraiConsoleUI { - /** - * 让UI层展示一条log - * - * identity:log所属的screen, Main=0; Bot=Bot.uin - */ - fun pushLog( - identity: Long, - message: String - ) - - /** - * 让UI层准备接受新增的一个BOT - */ - fun prePushBot( - identity: Long - ) - - /** - * 让UI层接受一个新的bot - * */ - fun pushBot( - bot: Bot - ) - - - fun pushVersion( - consoleVersion: String, - consoleBuild: String, - coreVersion: String - ) - - /** - * 让UI层提供一个Input - * 这个Input 不 等于 Command - * - */ - suspend fun requestInput( - question: String - ): String - - - /** - * 让UI层更新BOT管理员的数据 - */ - fun pushBotAdminStatus( - identity: Long, - admins: List - ) - - /** - * 由UI层创建一个LoginSolver - */ - fun createLoginSolver(): LoginSolver - -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 03306c551..5287bbbcd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -42,9 +42,6 @@ include(':mirai-core') //include(':mirai-core-timpc') include(':mirai-core-qqandroid') -include(':mirai-japt') -include(':mirai-console') -include(':mirai-console-terminal') //include(':mirai-api') include(':mirai-api-http') include(':mirai-demos:mirai-demo-1')