From 0dbbbbb51dc0e90da99de9050976df8b51e58fc5 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 4 Mar 2020 22:57:36 +0800 Subject: [PATCH 1/8] Fix loadClass --- gradle/wrapper/gradle-wrapper.properties | 4 +- mirai-console/build.gradle.kts | 2 +- .../mirai/console/core/MiraiCoreLoader.kt | 119 +++++++++--------- 3 files changed, 66 insertions(+), 59 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 64f0fd9b7..a47f646fb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -#Thu Feb 27 13:09:44 CST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +#Wed Mar 04 22:27:09 CST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts index 8f7b468d5..f55755310 100644 --- a/mirai-console/build.gradle.kts +++ b/mirai-console/build.gradle.kts @@ -37,7 +37,7 @@ val miraiVersion: String by rootProject.ext dependencies { compileOnly("net.mamoe:mirai-core-jvm:$miraiVersion") - compileOnly("net.mamoe:mirai-core-qqandroid-jvm:$miraiVersion") + // compileOnly("net.mamoe:mirai-core-qqandroid-jvm:$miraiVersion") api(kotlin("serialization")) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt index a07eeec54..cae6aa608 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt @@ -1,3 +1,12 @@ +/* + * 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.core import io.ktor.client.HttpClient @@ -6,20 +15,21 @@ import io.ktor.client.request.get import io.ktor.client.request.url import io.ktor.client.statement.HttpResponse import io.ktor.http.URLProtocol +import io.ktor.util.KtorExperimentalAPI import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.jvm.javaio.copyTo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import net.mamoe.mirai.console.MiraiConsole - import java.io.File import java.net.URLClassLoader import kotlin.math.pow import kotlin.system.exitProcess -@Suppress("EXPERIMENTAL_API_USAGE") -val Http: HttpClient get() = HttpClient(CIO) +@UseExperimental(KtorExperimentalAPI::class) +val Http: HttpClient + get() = HttpClient(CIO) object MiraiCoreLoader { val coresPath by lazy { @@ -30,7 +40,7 @@ object MiraiCoreLoader { } } - private fun getProtocolLib():File?{ + private fun getProtocolLib(): File? { this.coresPath.listFiles()?.forEach { file -> if (file != null && file.extension == "jar" && file.name.contains("qqandroid")) { return file @@ -39,7 +49,7 @@ object MiraiCoreLoader { return null } - private fun getCore():File?{ + private fun getCore(): File? { this.coresPath.listFiles()?.forEach { file -> if (file != null && file.extension == "jar" && file.name.contains("core") && (!file.name.contains("qqandroid"))) { return file @@ -49,14 +59,14 @@ object MiraiCoreLoader { } - operator fun invoke():String{ + operator fun invoke(): String { MiraiConsole.logger("Fetching Newest Core Version .. ") val newest = runBlocking { getNewestVersion() } val current = getCurrentVersion() MiraiConsole.logger("Local Version: $current | Newest Version: $newest") - if(current != newest){ + if (current != newest) { MiraiConsole.logger("Updating from V$current -> V$newest, this is a force update") cleanCoreAndLib() runBlocking { @@ -77,21 +87,21 @@ object MiraiCoreLoader { private suspend fun getNewestVersion(): String { try { return """>([0-9])*\.([0-9])*\.([0-9])*/""".toRegex().findAll( - Http.get { - url { - protocol = URLProtocol.HTTPS - host = "jcenter.bintray.com" - path("net/mamoe/mirai-core-qqandroid-jvm/") + Http.get { + url { + protocol = URLProtocol.HTTPS + host = "jcenter.bintray.com" + path("net/mamoe/mirai-core-qqandroid-jvm/") + } } - } - ).asSequence() + ).asSequence() .map { it.value.drop(1).dropLast(1) } .maxBy { it.split('.').foldRightIndexed(0) { index: Int, s: String, acc: Int -> acc + 100.0.pow(index).toInt() + (s.toIntOrNull() ?: 0) } }!! - }catch (e:Exception){ + } catch (e: Exception) { MiraiConsole.logger("Failed to fetch newest Core version, please seek for help") e.printStackTrace() MiraiConsole.logger("Failed to fetch newest Core version, please seek for help") @@ -103,18 +113,18 @@ object MiraiCoreLoader { * 使用Protocol Lib判断当前版本 * 如果没有 会返回0.0.0 * */ - private fun getCurrentVersion():String{ - val file = getProtocolLib() - if(file == null || getCore() == null)return "0.0.0" + private fun getCurrentVersion(): String { + val file = getProtocolLib() + if (file == null || getCore() == null) return "0.0.0" val numberVersion = """([0-9])*\.([0-9])*\.([0-9])*""".toRegex().find(file.name)?.value - if(numberVersion != null) { + if (numberVersion != null) { return numberVersion + file.name.substringAfter(numberVersion).substringBefore(".jar") } return "0.0.0" } - private fun cleanCoreAndLib(){ + private fun cleanCoreAndLib() { this.coresPath.listFiles()?.forEach { if (it != null && it.extension == "jar") { it.delete() @@ -123,34 +133,36 @@ object MiraiCoreLoader { } - - val lib_jcenter = "https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar" - val lib_aliyun = "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar" + val lib_jcenter = + "https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar" + val lib_aliyun = + "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar" val core_jcenter = "https://jcenter.bintray.com/net/mamoe/mirai-core-jvm/{version}/:mirai-core-jvm-{version}.jar" - val core_aliyun = "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar" + val core_aliyun = + "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar" - private suspend fun downloadCoreAndLib(version:String){ - var fileStream = File(coresPath.absolutePath + "/" + "mirai-core-qqandroid-jvm-$version.jar").also{ + private suspend fun downloadCoreAndLib(version: String) { + var fileStream = File(coresPath.absolutePath + "/" + "mirai-core-qqandroid-jvm-$version.jar").also { withContext(Dispatchers.IO) { it.createNewFile() } }.outputStream() - suspend fun downloadRequest(url:String, version:String):ByteReadChannel{ - return Http.get(){ - this.url(url.replace("{version}",version)) + suspend fun downloadRequest(url: String, version: String): ByteReadChannel { + return Http.get() { + this.url(url.replace("{version}", version)) }.content } - var stream = try{ + var stream = try { MiraiConsole.logger("Downloading newest Protocol lib from Aliyun") - downloadRequest(lib_aliyun,version) - }catch (ignored:Exception){ - try{ + downloadRequest(lib_aliyun, version) + } catch (ignored: Exception) { + try { MiraiConsole.logger("Downloading newest Protocol lib from JCenter") - downloadRequest(lib_jcenter,version) - }catch (e:Exception){ + downloadRequest(lib_jcenter, version) + } catch (e: Exception) { MiraiConsole.logger("Failed to download Protocol lib, please seeking for help") e.printStackTrace() MiraiConsole.logger("Failed to download Protocol lib, please seeking for help") @@ -163,21 +175,21 @@ object MiraiCoreLoader { fileStream.flush() } - fileStream = File(coresPath.absolutePath + "/" + "mirai-core-jvm-$version.jar").also{ + fileStream = File(coresPath.absolutePath + "/" + "mirai-core-jvm-$version.jar").also { withContext(Dispatchers.IO) { it.createNewFile() } }.outputStream() - stream = try{ + stream = try { MiraiConsole.logger("Downloading newest Mirai Core from Aliyun") - downloadRequest(core_aliyun,version) - }catch (ignored:Exception){ - try{ + downloadRequest(core_aliyun, version) + } catch (ignored: Exception) { + try { MiraiConsole.logger("Downloading newest Mirai Core from JCenter") - downloadRequest(core_jcenter,version) - }catch (e:Exception){ + downloadRequest(core_jcenter, version) + } catch (e: Exception) { MiraiConsole.logger("Failed to download Mirai Core, please seeking for help") e.printStackTrace() MiraiConsole.logger("Failed to download Mirai Core, please seeking for help") @@ -192,24 +204,19 @@ object MiraiCoreLoader { } - private fun loadCoreAndLib(){ + private fun loadCoreAndLib() { try { - MiraiConsole.logger("Core:" + getCore()) - MiraiConsole.logger("Protocol:" + getProtocolLib()) + MiraiConsole.logger("Core: " + getCore()) + MiraiConsole.logger("Protocol: " + getProtocolLib()) - MiraiCoreClassLoader( - (getCore()!!), - this.javaClass.classLoader + + val classloader = URLClassLoader( + arrayOf(getCore()!!.toURI().toURL(), getProtocolLib()!!.toURI().toURL()), + Thread.currentThread().contextClassLoader ) - .loadClass("net.mamoe.mirai.BotFactory") - - - MiraiCoreClassLoader( - (getProtocolLib()!!), - this.javaClass.classLoader - ) - .loadClass("net.mamoe.mirai.qqandroid.QQAndroid") + println(classloader.loadClass("net.mamoe.mirai.BotFactory")) + println(classloader.loadClass("net.mamoe.mirai.qqandroid.QQAndroid")) } catch (e: ClassNotFoundException) { MiraiConsole.logger("Failed to load core, please seek for help") From b6fb0f06ef9beeda1700107a350db2a025f3662b Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 08:20:04 +0800 Subject: [PATCH 2/8] Fix loadClass --- .../net/mamoe/mirai/console/MiraiConsole.kt | 5 ++--- .../mirai/console/core/MiraiCoreLoader.kt | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index 303cb493c..ea4863bb8 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import net.mamoe.mirai.Bot +import net.mamoe.mirai.BotFactory import net.mamoe.mirai.console.MiraiConsole.CommandProcessor.processNextCommandLine import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandSender @@ -19,11 +20,8 @@ import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.command.DefaultCommands import net.mamoe.mirai.console.core.MiraiCoreLoader 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 { @@ -93,6 +91,7 @@ object MiraiConsole { MiraiCoreLoader() + println(BotFactory::class) /* 加载ECDH */ try { ECDH() diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt index cae6aa608..13542cb69 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt @@ -207,16 +207,27 @@ object MiraiCoreLoader { private fun loadCoreAndLib() { try { - MiraiConsole.logger("Core: " + getCore()) - MiraiConsole.logger("Protocol: " + getProtocolLib()) + val coreFile = getCore()!! + val protocolFile = getProtocolLib()!! + + MiraiConsole.logger("Core: $coreFile") + MiraiConsole.logger("Protocol: $protocolFile") val classloader = URLClassLoader( - arrayOf(getCore()!!.toURI().toURL(), getProtocolLib()!!.toURI().toURL()), - Thread.currentThread().contextClassLoader + arrayOf(coreFile.toURI().toURL(), protocolFile.toURI().toURL()), + this.javaClass.classLoader ) + ClassLoader.getSystemClassLoader() + // this.javaClass.classLoader. println(classloader.loadClass("net.mamoe.mirai.BotFactory")) println(classloader.loadClass("net.mamoe.mirai.qqandroid.QQAndroid")) + println(classloader.loadClass("net.mamoe.mirai.utils.cryptor.ECDHJvmKt")) + + val a = classloader.loadClass("net.mamoe.mirai.qqandroid.QQAndroid").kotlin.objectInstance!! + println(a::class.java) + + println(Class.forName("net.mamoe.mirai.qqandroid.QQAndroid")) } catch (e: ClassNotFoundException) { MiraiConsole.logger("Failed to load core, please seek for help") From 446bd67738c22f1dfbd5a000add22481d1f5b6c6 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 08:27:18 +0800 Subject: [PATCH 3/8] Remove kotlinx.io --- mirai-console/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts index f55755310..bf72ff17d 100644 --- a/mirai-console/build.gradle.kts +++ b/mirai-console/build.gradle.kts @@ -57,7 +57,6 @@ dependencies { api(kotlinx("serialization-runtime", serializationVersion)) api(kotlinx("coroutines-io", coroutinesIoVersion)) api(kotlinx("coroutines-io-jvm", coroutinesIoVersion)) - api(kotlinx("io-jvm", coroutinesIoVersion)) api("org.bouncycastle:bcprov-jdk15on:1.64") From 6dc4a24d8cf56da2d221551995087765c8d455c4 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 08:27:27 +0800 Subject: [PATCH 4/8] Use `runCatching` --- .../net/mamoe/mirai/console/core/MiraiCoreLoader.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt index 13542cb69..8a410c0ad 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt @@ -155,14 +155,14 @@ object MiraiCoreLoader { }.content } - var stream = try { + var stream = kotlin.runCatching { MiraiConsole.logger("Downloading newest Protocol lib from Aliyun") downloadRequest(lib_aliyun, version) - } catch (ignored: Exception) { - try { + }.getOrElse { + kotlin.runCatching { MiraiConsole.logger("Downloading newest Protocol lib from JCenter") downloadRequest(lib_jcenter, version) - } catch (e: Exception) { + }.getOrElse { e -> MiraiConsole.logger("Failed to download Protocol lib, please seeking for help") e.printStackTrace() MiraiConsole.logger("Failed to download Protocol lib, please seeking for help") From 602a95136065c8eba43f18a72452ba71081ee180 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 08:35:02 +0800 Subject: [PATCH 5/8] Simplify dependencies --- mirai-console/build.gradle.kts | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts index bf72ff17d..f36b99db9 100644 --- a/mirai-console/build.gradle.kts +++ b/mirai-console/build.gradle.kts @@ -39,8 +39,6 @@ dependencies { compileOnly("net.mamoe:mirai-core-jvm:$miraiVersion") // compileOnly("net.mamoe:mirai-core-qqandroid-jvm:$miraiVersion") - api(kotlin("serialization")) - api(group = "com.alibaba", name = "fastjson", version = "1.2.62") api(group = "org.yaml", name = "snakeyaml", version = "1.25") @@ -50,26 +48,20 @@ dependencies { api(kotlin("stdlib", kotlinVersion)) api(kotlin("serialization", kotlinVersion)) - api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion") - api(kotlinx("coroutines-io", coroutinesIoVersion)) - api(kotlinx("coroutines-core", coroutinesVersion)) - api(ktor("client-core-jvm", ktorVersion)) - api(kotlinx("serialization-runtime", serializationVersion)) - api(kotlinx("coroutines-io", coroutinesIoVersion)) + api(kotlin("reflect", kotlinVersion)) + api(kotlinx("coroutines-io-jvm", coroutinesIoVersion)) + api(kotlinx("coroutines-core", coroutinesVersion)) + api(kotlinx("serialization-runtime", serializationVersion)) + api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion") api("org.bouncycastle:bcprov-jdk15on:1.64") - api(kotlin("reflect", kotlinVersion)) - api(kotlin("serialization", kotlinVersion)) - api(kotlinx("coroutines-core-common", coroutinesVersion)) - api(kotlinx("serialization-runtime-common", serializationVersion)) - api(ktor("http-cio", ktorVersion)) - api(ktor("http", ktorVersion)) + api(ktor("http-jvm", ktorVersion)) + api(ktor("io-jvm", ktorVersion)) api(ktor("client-core-jvm", ktorVersion)) api(ktor("client-cio", ktorVersion)) - api(ktor("client-core", ktorVersion)) api(ktor("network", ktorVersion)) } From 7fa1e2631d50d2400abd216f5e62454f05bee5ed Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 08:58:07 +0800 Subject: [PATCH 6/8] Misc improvements --- .../net/mamoe/mirai/console/MiraiConsole.kt | 6 + .../mamoe/mirai/console/plugins/PluginBase.kt | 305 ++---------------- .../mirai/console/plugins/PluginManager.kt | 248 ++++++++++++++ 3 files changed, 280 insertions(+), 279 deletions(-) create mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index ea4863bb8..d18a33da2 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -179,6 +179,12 @@ object MiraiConsole { frontEnd.pushLog(identity, "$identityStr: $any") } } + + operator fun invoke(identityStr: String, identity: Long, e: Exception? = null) { + if (e != null) { + frontEnd.pushLog(identity, "$identityStr: ${e.stackTrace}") + } + } } } 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 index b29e1a998..c8bec6dc1 100644 --- 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 @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("MemberVisibilityCanBePrivate", "unused") + package net.mamoe.mirai.console.plugins import kotlinx.coroutines.* @@ -15,39 +17,28 @@ import net.mamoe.mirai.console.command.Command import net.mamoe.mirai.console.command.CommandSender 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) +abstract class PluginBase +@JvmOverloads constructor(coroutineContext: CoroutineContext = EmptyCoroutineContext) : CoroutineScope { private val supervisorJob = SupervisorJob() final override val coroutineContext: CoroutineContext = coroutineContext + supervisorJob /** - * 插件被分配的data folder, 如果插件改名了 data folder 也会变 请注意 + * 插件被分配的数据目录。数据目录会与插件名称同名。 */ val dataFolder: File by lazy { - File(_getDataFolder()).also { + File(PluginManager.pluginsPath + "/" + PluginManager.lastPluginName).also { it.mkdir() } } - private fun _getDataFolder():String{ - return if(inited){ - PluginManager.pluginsPath + "/" + pluginName - }else{ - PluginManager.pluginsPath + "/" + PluginManager.lastPluginName//for init - } - } - /** * 当一个插件被加载时调用 */ @@ -95,28 +86,19 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope { this.onDisable() } - internal var pluginName:String = "" - - private var inited = false - internal fun init() { - this.onLoad() - inited = true - } - - val pluginManager = PluginManager + internal var pluginName: String = "" val logger: MiraiLogger by lazy { - SimpleLogger("Plugin ${pluginName}") { _, message, e -> + SimpleLogger("Plugin $pluginName") { _, message, e -> MiraiConsole.logger("[${pluginName}]", 0, message) if (e != null) { - MiraiConsole.logger("[${pluginName}]", 0, e.toString()) - e.printStackTrace() + MiraiConsole.logger("[${pluginName}]", 0, e) } } } /** - * 加载一个插件jar, resources中的东西 + * 加载 resources 中的文件 */ fun getResources(fileName: String): InputStream? { return try { @@ -130,16 +112,15 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope { } /** - * 加载一个插件jar, resources中的Config - * 这个Config是read-only的 + * 加载 resource 中的 [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]) + return Config.load(getResources(fileName) ?: error("No such file: $fileName"), fileName.substringAfter('.')) } - } class PluginDescription( @@ -159,37 +140,37 @@ class PluginDescription( companion object { fun readFromContent(content_: String): PluginDescription { - with(Config.load(content_,"yml")){ + with(Config.load(content_, "yml")) { try { return PluginDescription( name = this.getString("name"), - author = try{ + author = kotlin.runCatching { this.getString("author") - }catch (e:Exception){ + }.getOrElse { "unknown" }, - basePath = try{ + basePath = kotlin.runCatching { this.getString("path") - }catch (e:Exception){ + }.getOrElse { this.getString("main") }, - version = try{ + version = kotlin.runCatching { this.getString("version") - }catch (e:Exception){ + }.getOrElse { "unknown" }, - info = try{ + info = kotlin.runCatching { this.getString("info") - }catch (e:Exception){ + }.getOrElse { "unknown" }, - depends = try{ + depends = kotlin.runCatching { this.getStringList("depends") - }catch (e:Exception){ - listOf() + }.getOrElse { + listOf() } ) - }catch (e:Exception){ + } catch (e: Exception) { error("Failed to read Plugin.YML") } } @@ -199,237 +180,3 @@ class PluginDescription( internal class PluginClassLoader(file: File, parent: ClassLoader) : URLClassLoader(arrayOf(file.toURI().toURL()), parent) - -object PluginManager { - - internal var lastPluginName: String = "" - - 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, sender: CommandSender,args: List) { - nameToPluginBaseMap.values.forEach { - it.onCommand(command,sender, args) - } - } - - - fun getPluginDescriptions(base:PluginBase):PluginDescription{ - nameToPluginBaseMap.forEach{ (s, pluginBase) -> - if(pluginBase == base){ - return pluginDescriptions[s]!! - } - } - error("can not find plugin description") - } - - fun getPluginDataFolder(){ - } - - 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 { - try { - 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 - }catch (e:Exception){ - logger.info(e.message) - } - } - } - } - - 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 { - lastPluginName = description.name - 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.pluginName = description.name - plugin.init() - 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/plugins/PluginManager.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt new file mode 100644 index 000000000..171364693 --- /dev/null +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -0,0 +1,248 @@ +/* + * 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.CancellationException +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.command.Command +import net.mamoe.mirai.console.command.CommandSender +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.util.jar.JarFile + + +object PluginManager { + + @Volatile + internal var lastPluginName: String = "" + + internal val pluginsPath = (System.getProperty("user.dir") + "/plugins/").replace("//", "/").also { + File(it).mkdirs() + } + + private val logger = SimpleLogger("Plugin Manager") { _, message, e -> + MiraiConsole.logger("[Plugin Manager]", 0, message) + MiraiConsole.logger("[Plugin Manager]", 0, e) + } + + //已完成加载的 + private val nameToPluginBaseMap: MutableMap = mutableMapOf() + private val pluginDescriptions: MutableMap = mutableMapOf() + + fun onCommand(command: Command, sender: CommandSender, args: List) { + nameToPluginBaseMap.values.forEach { + it.onCommand(command, sender, args) + } + } + + + fun getPluginDescription(base: PluginBase): PluginDescription { + nameToPluginBaseMap.forEach { (s, pluginBase) -> + if (pluginBase == base) { + return pluginDescriptions[s]!! + } + } + error("can not find plugin description") + } + + 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 { + try { + 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 + } catch (e: Exception) { + logger.info(e.message) + } + } + } + } + + 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 { + lastPluginName = description.name + 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.pluginName = description.name + true + } catch (e: ClassCastException) { + logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ") + false + } + } catch (e: ClassNotFoundException) { + logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath) + logger.error(e) + 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) ?: 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 + } +} From 34bece66228babce5c06790759feb23e9858c99d Mon Sep 17 00:00:00 2001 From: PeratX <1215714524@qq.com> Date: Thu, 5 Mar 2020 09:29:13 +0800 Subject: [PATCH 7/8] Graphical: executable JAR --- mirai-console-graphical/build.gradle.kts | 9 ++++++++- .../net/mamoe/mirai/console/graphical/MiraiGraphical.kt | 7 +++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mirai-console-graphical/build.gradle.kts b/mirai-console-graphical/build.gradle.kts index d8656417b..46d73f6c8 100644 --- a/mirai-console-graphical/build.gradle.kts +++ b/mirai-console-graphical/build.gradle.kts @@ -13,6 +13,13 @@ javafx { apply(plugin = "com.github.johnrengelman.shadow") +tasks.withType() { + manifest { + attributes["Main-Class"] = "net.mamoe.mirai.console.graphical.MiraiGraphicalLoader" + } +} + + val kotlinVersion: String by rootProject.ext val atomicFuVersion: String by rootProject.ext val coroutinesVersion: String by rootProject.ext @@ -41,4 +48,4 @@ dependencies { 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 index 51e61f081..8d1f7375c 100644 --- 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 @@ -17,8 +17,11 @@ import tornadofx.App import tornadofx.find import tornadofx.launch -fun main(args: Array) { - launch(args) +object MiraiGraphicalLoader { + @JvmStatic + fun main(args: Array) { + launch(args) + } } class MiraiGraphicalUI : App(Decorator::class, PrimaryStyleSheet::class) { From 62001cc1a87d12dcf2d30d69ed0c1d9ae27f04cd Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 5 Mar 2020 09:45:54 +0800 Subject: [PATCH 8/8] Review: misc improvements --- gradle.properties | 2 +- .../controller/MiraiGraphicalUIController.kt | 4 +- .../mirai/console/MiraiConsoleTerminalUI.kt | 114 ++++++-------- mirai-console/build.gradle.kts | 18 ++- .../net/mamoe/mirai/console/MiraiConsole.kt | 21 +-- .../mamoe/mirai/console/command/Command.kt | 145 ++++-------------- .../mirai/console/command/CommandManager.kt | 70 +++++++++ .../mirai/console/command/DefaultCommands.kt | 32 ++-- .../mirai/console/core/MiraiCoreLoader.kt | 50 +++--- .../mirai/console/plugins/ConfigSection.kt | 70 +++++---- .../mirai/console/plugins/PluginManager.kt | 3 +- .../mirai/console/pure/MiraiConsoleUIPure.kt | 4 +- .../mamoe/mirai/console/utils/BotHelper.kt | 19 +-- 13 files changed, 264 insertions(+), 288 deletions(-) create mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt diff --git a/gradle.properties b/gradle.properties index 513e76952..d86a7b009 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ kotlin.code.style=official # config miraiVersion=0.24.1 -mirai_console_version=0.3.1 +miraiConsoleVersion=0.3.1 kotlin.incremental.multiplatform=true kotlin.parallel.tasks.in.project=true # kotlin 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 index d39948f49..03abb928c 100644 --- 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 @@ -3,7 +3,6 @@ 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 @@ -11,6 +10,7 @@ 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.plugins.PluginManager import net.mamoe.mirai.console.utils.MiraiConsoleUI import net.mamoe.mirai.utils.LoginSolver import tornadofx.* @@ -75,7 +75,7 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { override fun createLoginSolver(): LoginSolver = loginSolver private fun getPluginsFromConsole(): ObservableList = - MiraiConsole.pluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable() + PluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable() } diff --git a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt index 142eedee6..891e5003a 100644 --- a/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt +++ b/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt @@ -1,3 +1,5 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + package net.mamoe.mirai.console import com.googlecode.lanterna.SGR @@ -8,7 +10,6 @@ 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.* @@ -16,7 +17,6 @@ import kotlinx.coroutines.io.ByteWriteChannel import kotlinx.coroutines.io.close import kotlinx.coroutines.io.jvm.nio.copyTo import kotlinx.coroutines.io.reader -import kotlinx.io.core.IoBuffer import kotlinx.io.core.use import net.mamoe.mirai.Bot import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.cleanPage @@ -51,49 +51,26 @@ import kotlin.system.exitProcess * */ -fun String.actualLength(): Int { - var x = 0 - this.forEach { - if (it.isChineseChar()) { - x += 2 - } else { - x += 1 - } - } - return x -} +val String.actualLength: Int get() = this.sumBy { if (it.isChineseChar) 2 else 1 } + 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 + return this.sumBy { if (it.isChineseChar) 2 else 1 }.coerceAtMost(widthMax).coerceAtLeast(2) } -fun Char.isChineseChar(): Boolean { - return this.toString().isChineseChar() -} +val Char.isChineseChar: Boolean + get() { + return this.toString().isChineseChar + } -fun String.isChineseChar(): Boolean { - return this.matches(Regex("[\u4e00-\u9fa5]")) -} +val String.isChineseChar: Boolean + get() { + return this.matches(Regex("[\u4e00-\u9fa5]")) + } object MiraiConsoleTerminalUI : MiraiConsoleUI { - val cacheLogSize = 50 + const val cacheLogSize = 50 var mainTitle = "Mirai Console v0.01 Core v0.15" override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) { @@ -122,7 +99,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { @Volatile var requesting = false - var requestResult: String? = null + private var requestResult: String? = null override suspend fun requestInput(): String { requesting = true while (requesting) { @@ -132,7 +109,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } - suspend fun provideInput(input: String) { + private suspend fun provideInput(input: String) { if (requesting) { requestResult = input requesting = false @@ -164,17 +141,13 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } } - var toLog = "" + lateinit var toLog: String tempFile.inputStream().use { val img = ImageIO.read(it) - if (img == null) { - toLog += "无法创建字符图片. 请查看文件\n" - } else { - toLog += img.createCharImg((terminal.terminalSize.columns / 1.5).toInt()) - } + toLog += img?.createCharImg((terminal.terminalSize.columns / 1.5).toInt()) ?: "无法创建字符图片. 请查看文件\n" } pushLog(0, "$toLog[Login Solver]请输验证码. ${tempFile.absolutePath}") - return requestInput()!! + return requestInput() .takeUnless { it.isEmpty() || it.length != 4 } .also { pushLog(0, "[Login Solver]正在提交[$it]中...") @@ -206,11 +179,11 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } } - val log = ConcurrentHashMap>().also { + private val log = ConcurrentHashMap>().also { it[0L] = LimitLinkedQueue(cacheLogSize) } - val botAdminCount = ConcurrentHashMap() + private val botAdminCount = ConcurrentHashMap() private val screens = mutableListOf(0L) private var currentScreenId = 0 @@ -219,7 +192,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { lateinit var terminal: Terminal lateinit var textGraphics: TextGraphics - var hasStart = false + private var hasStart = false private lateinit var internalPrinter: PrintStream fun start() { if (hasStart) { @@ -277,11 +250,12 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { */ var lastJob: Job? = null - terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize -> + terminal.addResizeListener { _: Terminal, _: TerminalSize -> lastJob = GlobalScope.launch { try { delay(300) if (lastJob == coroutineContext[Job]) { + @Suppress("BlockingMethodInNonBlockingContext") terminal.clearScreen() //inited = false update() @@ -292,7 +266,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { pushLog(0, "[UI ERROR] ${e.message}") } } - }) + } if (terminal !is SwingTerminalFrame) { System.setOut(PrintStream(object : OutputStream() { @@ -322,7 +296,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { thread { while (true) { try { - var keyStroke: KeyStroke = terminal.readInput() + val keyStroke: KeyStroke = terminal.readInput() when (keyStroke.keyType) { KeyType.ArrowLeft -> { @@ -411,7 +385,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { textGraphics.foregroundColor = TextColor.ANSI.WHITE textGraphics.backgroundColor = TextColor.ANSI.GREEN - textGraphics.putString((width - mainTitle.actualLength()) / 2, 1, mainTitle, SGR.BOLD) + 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)) @@ -426,15 +400,15 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { val leftName = getScreenName(getLeftScreenId()) // clearRows(2) - textGraphics.putString((width - title.actualLength()) / 2 - "$leftName << ".length, 2, "$leftName << ") + 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.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") + textGraphics.putString((width + title.actualLength) / 2 + 1, 2, ">> $rightName") } fun drawMainFrame( @@ -447,7 +421,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { clearRows(4) textGraphics.putString(2, 4, "|Online Bots: $onlineBotCount") textGraphics.putString( - width - 2 - "Powered By Mamoe Technologies|".actualLength(), + width - 2 - "Powered By Mamoe Technologies|".actualLength, 4, "Powered By Mamoe Technologies|" ) @@ -463,7 +437,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { 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|") + textGraphics.putString(width - 2 - "Add admins via commands|".actualLength, 4, "Add admins via commands|") } @@ -472,7 +446,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { fun drawLog(string: String, flush: Boolean = true) { val maxHeight = terminal.terminalSize.rows - 4 - val heightNeed = (string.actualLength() / (terminal.terminalSize.columns - 6)) + 1 + val heightNeed = (string.actualLength / (terminal.terminalSize.columns - 6)) + 1 if (heightNeed - 1 > maxHeight) { pushLog(0, "[UI ERROR]: 您的屏幕太小, 有一条超长LOG无法显示") return//拒绝打印 @@ -481,7 +455,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { cleanPage()//翻页 } if (string.contains("\n")) { - string.split("\n").forEach { + string.split("\n").forEach { _ -> drawLog(string, false) } } else { @@ -491,7 +465,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { if (x == "") { break } - val toWrite = if (x.actualLength() > width) { + val toWrite = if (x.actualLength > width) { val index = x.getSubStringIndexByActualLength(width) x.substring(0, index).also { x = if (index < x.length) { @@ -539,7 +513,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { var vara = 0 val toPrint = mutableListOf() toDraw.forEach { - val heightNeed = (it.actualLength() / (terminal.terminalSize.columns - 6)) + 1 + val heightNeed = (it.actualLength / (terminal.terminalSize.columns - 6)) + 1 vara += heightNeed if (currentHeight + vara < terminal.terminalSize.rows - 4) { logsToDraw++ @@ -558,8 +532,8 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } - var commandBuilder = StringBuilder() - fun redrawCommand() { + private var commandBuilder = StringBuilder() + private fun redrawCommand() { val height = terminal.terminalSize.rows val width = terminal.terminalSize.columns clearRows(height - 3) @@ -594,7 +568,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } private fun deleteCommandChar() { - if (!commandBuilder.isEmpty()) { + if (commandBuilder.isNotEmpty()) { commandBuilder = StringBuilder(commandBuilder.toString().substring(0, commandBuilder.length - 1)) } val height = terminal.terminalSize.rows @@ -606,7 +580,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } - var lastEmpty: Job? = null + private var lastEmpty: Job? = null private fun emptyCommand() { commandBuilder = StringBuilder() if (terminal is SwingTerminal) { @@ -617,7 +591,9 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { try { delay(100) if (lastEmpty == coroutineContext[Job]) { - terminal.clearScreen() + withContext(Dispatchers.IO) { + terminal.clearScreen() + } //inited = false update() redrawCommand() @@ -630,7 +606,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { } } - fun update() { + private fun update() { when (screens[currentScreenId]) { 0L -> { drawMainFrame(screens.size - 1) @@ -658,7 +634,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI { class LimitLinkedQueue( - val limit: Int = 50 + private val limit: Int = 50 ) : ConcurrentLinkedDeque() { override fun push(e: T) { if (size >= limit) { diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts index f36b99db9..e9621b718 100644 --- a/mirai-console/build.gradle.kts +++ b/mirai-console/build.gradle.kts @@ -26,7 +26,7 @@ fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$v fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version" -tasks.withType() { +tasks.withType { manifest { attributes["Main-Class"] = "net.mamoe.mirai.console.pure.MiraiConsolePureLoader" } @@ -35,6 +35,16 @@ tasks.withType() { val miraiVersion: String by rootProject.ext +kotlin { + sourceSets { + all { + languageSettings.enableLanguageFeature("InlineClasses") + + languageSettings.useExperimentalAnnotation("kotlin.Experimental") + languageSettings.useExperimentalAnnotation("kotlin.OptIn") + } + } +} dependencies { compileOnly("net.mamoe:mirai-core-jvm:$miraiVersion") // compileOnly("net.mamoe:mirai-core-qqandroid-jvm:$miraiVersion") @@ -65,8 +75,8 @@ dependencies { api(ktor("network", ktorVersion)) } -val mirai_console_version: String by project.ext -version = mirai_console_version +val miraiConsoleVersion: String by project.ext +version = miraiConsoleVersion description = "Console with plugin support for mirai" bintray { @@ -109,7 +119,7 @@ publishing { groupId = rootProject.group.toString() artifactId = "mirai-console" - version = mirai_console_version + version = miraiConsoleVersion pom.withXml { val root = asNode() diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index d18a33da2..044e74efd 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -12,7 +12,6 @@ package net.mamoe.mirai.console import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import net.mamoe.mirai.Bot -import net.mamoe.mirai.BotFactory import net.mamoe.mirai.console.MiraiConsole.CommandProcessor.processNextCommandLine import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandSender @@ -38,20 +37,10 @@ object MiraiConsole { * */ val bots get() = Bot.instances - fun getBotByUIN(uin: Long): Bot? { - bots.forEach { - if (it.get()?.uin == uin) { - return it.get() - } - } - return null + fun getBotOrNull(uin: Long): Bot? { + return bots.asSequence().mapNotNull { it.get() }.firstOrNull { it.uin == uin } } - /** - * PluginManager - */ - val pluginManager: PluginManager get() = PluginManager - /** * 与前端交互所使用的Logger */ @@ -89,9 +78,8 @@ object MiraiConsole { logger("Mirai为开源项目,请自觉遵守开源项目协议") logger("Powered by Mamoe Technologies and contributors") - MiraiCoreLoader() + MiraiCoreLoader.loadCore() - println(BotFactory::class) /* 加载ECDH */ try { ECDH() @@ -102,7 +90,7 @@ object MiraiConsole { /* 依次启用功能 */ DefaultCommands() - pluginManager.loadPlugins() + PluginManager.loadPlugins() CommandProcessor.start() /* 通知启动完成 */ @@ -150,6 +138,7 @@ object MiraiConsole { fun runConsoleCommandBlocking(command: String) = runBlocking { runConsoleCommand(command) } + @Suppress("unused") fun runCommandBlocking(sender: CommandSender, command: String) = runBlocking { runCommand(sender, command) } private suspend fun processNextCommandLine() { 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 index da08ff2da..b178e1aa8 100644 --- 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 @@ -7,82 +7,18 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("NOTHING_TO_INLINE") + 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.console.plugins.PluginBase import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.sendMessage -import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.utils.MiraiExperimentalAPI - -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, sender, args) - } else { - sender.sendMessage(this.usage) - } - } catch (e: Exception) { - sender.sendMessage("在运行指令时出现了未知错误") - e.printStackTrace() - } finally { - (sender as CommandSenderImpl).flushMessage() - } - } - return true - } - -} interface CommandSender { /** @@ -92,7 +28,7 @@ interface CommandSender { suspend fun sendMessage(message: String) /** - * 写入要发送的内容 所有内容最后会被以一条发出, 不管成功与否 + * 写入要发送的内容 所有内容最后会被以一条发出 */ fun appendMessage(message: String) @@ -139,21 +75,6 @@ open class ContactCommandSender(val contact: Contact) : CommandSenderImpl() { } } -/** - * 弃用中 - * */ -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 @@ -161,9 +82,18 @@ interface Command { val usage: String suspend fun onCommand(sender: CommandSender, args: List): Boolean - fun register() } +inline fun Command.register() = CommandManager.register(this) + + +fun registerCommand(builder: CommandBuilder.() -> Unit): Command { + return CommandBuilder().apply(builder).register() +} + + +// for java +@Suppress("unused") abstract class BlockingCommand( override val name: String, override val alias: List = listOf(), @@ -172,7 +102,7 @@ abstract class BlockingCommand( ) : Command { /** * 最高优先级监听器 - * 如果 return `false` 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听 + * 如果 return `false`, 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听 * */ final override suspend fun onCommand(sender: CommandSender, args: List): Boolean { return withContext(Dispatchers.IO) { @@ -181,10 +111,6 @@ abstract class BlockingCommand( } abstract fun onCommandBlocking(sender: CommandSender, args: List): Boolean - - override fun register() { - CommandManager.register(this) - } } class AnonymousCommand internal constructor( @@ -197,10 +123,6 @@ class AnonymousCommand internal constructor( override suspend fun onCommand(sender: CommandSender, args: List): Boolean { return onCommand.invoke(sender, args) } - - override fun register() { - CommandManager.register(this) - } } class CommandBuilder internal constructor() { @@ -208,30 +130,25 @@ class CommandBuilder internal constructor() { var alias: List? = null var description: String = "" var usage: String = "use /help for help" - var onCommand: (suspend CommandSender.(args: List) -> Boolean)? = null + internal 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() } +private fun CommandBuilder.register(): AnonymousCommand { + if (name == null || onCommand == null) { + error("CommandBuilder not complete") } -} - -fun registerCommand(builder: CommandBuilder.() -> Unit): Command { - return CommandBuilder().apply(builder).register() -} - + if (alias == null) { + alias = listOf() + } + return AnonymousCommand( + name!!, + alias!!, + description, + usage, + onCommand!! + ).also { it.register() } +} \ No newline at end of file diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt new file mode 100644 index 000000000..437489ee2 --- /dev/null +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt @@ -0,0 +1,70 @@ +/* + * 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 + */ + +@file:Suppress("unused") + +package net.mamoe.mirai.console.command + +import net.mamoe.mirai.console.plugins.PluginManager + +object CommandManager { + private val registeredCommand: MutableMap = mutableMapOf() + + val commands: Collection get() = 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) { + (command.alias.asSequence() + command.name).forEach { + registeredCommand.remove(it) + } // label compilation failed + } + + fun unregister(commandName: String): Boolean { + return registeredCommand.remove(commandName) != null + } + + /* + * 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.drop(1) + registeredCommand[commandHead]?.run { + try { + if (onCommand(sender, blocks.drop(1))) { + PluginManager.onCommand(this, sender, args) + } else { + sender.sendMessage(this.usage) + } + } catch (e: Exception) { + sender.sendMessage("在运行指令时出现了未知错误") + e.printStackTrace() + } finally { + (sender as CommandSenderImpl).flushMessage() + } + } + return true + } + +} \ No newline at end of file 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 index 3fcefb14e..37d5bdc55 100644 --- 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 @@ -14,7 +14,7 @@ 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.managers import net.mamoe.mirai.console.utils.removeManager import net.mamoe.mirai.contact.sendMessage import net.mamoe.mirai.event.subscribeMessages @@ -49,7 +49,7 @@ object DefaultCommands { MiraiConsole.logger("[Bot Manager]", 0, it[1] + " 不是一个Bot的ID") return@onCommand false } - val bot = MiraiConsole.getBotByUIN(botId) + val bot = MiraiConsole.getBotOrNull(botId) if (bot == null) { MiraiConsole.logger("[Bot Manager]", 0, "$botId 没有在Console中登陆") return@onCommand false @@ -88,7 +88,7 @@ object DefaultCommands { MiraiConsole.logger("[Bot Manager]", 0, it[2] + "移除成功") } "list" -> { - bot.getManagers().forEach { + bot.managers.forEach { MiraiConsole.logger("[Bot Manager]", 0, " -> $it") } } @@ -106,7 +106,7 @@ object DefaultCommands { return@onCommand false } if (it.size < 2) { - MiraiConsole.logger("\"/login qqnumber qqpassword \" to login a bot") + MiraiConsole.logger("\"/login qq password \" to login a bot") MiraiConsole.logger("\"/login qq号 qq密码 \" 来登录一个BOT") return@onCommand false } @@ -138,11 +138,11 @@ object DefaultCommands { } bot.login() bot.subscribeMessages { - this.startsWith("/") { + startsWith("/") { message -> if (bot.checkManager(this.sender.id)) { val sender = ContactCommandSender(this.subject) MiraiConsole.CommandProcessor.runCommand( - sender, it + sender, message ) } } @@ -159,13 +159,13 @@ object DefaultCommands { registerCommand { name = "status" description = "获取状态" - onCommand { - when (it.size) { + onCommand { args -> + when (args.size) { 0 -> { sendMessage("当前有" + MiraiConsole.bots.size + "个BOT在线") } 1 -> { - val bot = it[0] + val bot = args[0] var find = false MiraiConsole.bots.forEach { if (it.get()?.uin.toString().contains(bot)) { @@ -196,13 +196,13 @@ object DefaultCommands { return@onCommand false } val bot: Bot? = if (it.size == 2) { - if (MiraiConsole.bots.size == 0) { + if (MiraiConsole.bots.isEmpty()) { MiraiConsole.logger("还没有BOT登录") return@onCommand false } MiraiConsole.bots[0].get() } else { - MiraiConsole.getBotByUIN(it[0].toLong()) + MiraiConsole.getBotOrNull(it[0].toLong()) } if (bot == null) { MiraiConsole.logger("没有找到BOT") @@ -228,11 +228,11 @@ object DefaultCommands { alias = listOf("plugin") description = "获取插件列表" onCommand { - PluginManager.getAllPluginDescriptions().let { - it.forEach { + PluginManager.getAllPluginDescriptions().let { descriptions -> + descriptions.forEach { appendMessage("\t" + it.name + " v" + it.version + " by" + it.author + " " + it.info) } - appendMessage("加载了" + it.size + "个插件") + appendMessage("加载了" + descriptions.size + "个插件") true } } @@ -243,10 +243,10 @@ object DefaultCommands { alias = listOf("commands", "help", "helps") description = "获取指令列表" onCommand { - CommandManager.getCommands().let { + CommandManager.commands.let { commands -> var size = 0 appendMessage("")//\n - it.toSet().forEach { + commands.forEach { ++size appendMessage("-> " + it.name + " :" + it.description) } diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt index 8a410c0ad..aaefb328b 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/core/MiraiCoreLoader.kt @@ -7,15 +7,15 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.console.core import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO import io.ktor.client.request.get -import io.ktor.client.request.url import io.ktor.client.statement.HttpResponse import io.ktor.http.URLProtocol -import io.ktor.util.KtorExperimentalAPI import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.jvm.javaio.copyTo import kotlinx.coroutines.Dispatchers @@ -27,12 +27,11 @@ import java.net.URLClassLoader import kotlin.math.pow import kotlin.system.exitProcess -@UseExperimental(KtorExperimentalAPI::class) val Http: HttpClient get() = HttpClient(CIO) object MiraiCoreLoader { - val coresPath by lazy { + private val coresPath by lazy { File(System.getProperty("user.dir") + "/core/").also { if (!it.exists()) { it.mkdirs() @@ -59,7 +58,7 @@ object MiraiCoreLoader { } - operator fun invoke(): String { + fun loadCore(): String { MiraiConsole.logger("Fetching Newest Core Version .. ") val newest = runBlocking { getNewestVersion() @@ -82,7 +81,7 @@ object MiraiCoreLoader { /** - * 使用Protocol Lib判断最新版本 + * 判断最新版本 * */ private suspend fun getNewestVersion(): String { try { @@ -110,9 +109,9 @@ object MiraiCoreLoader { } /** - * 使用Protocol Lib判断当前版本 - * 如果没有 会返回0.0.0 - * */ + * 判断当前版本 + * 默认返回 "0.0.0" + */ private fun getCurrentVersion(): String { val file = getProtocolLib() if (file == null || getCore() == null) return "0.0.0" @@ -133,14 +132,18 @@ object MiraiCoreLoader { } - val lib_jcenter = - "https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar" - val lib_aliyun = - "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar" + @Suppress("SpellCheckingInspection") + private object Links { + internal const val libJcenter = + "https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar" + internal const val libAliyun = + "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar" - val core_jcenter = "https://jcenter.bintray.com/net/mamoe/mirai-core-jvm/{version}/:mirai-core-jvm-{version}.jar" - val core_aliyun = - "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar" + internal const val coreJcenter = + "https://jcenter.bintray.com/net/mamoe/mirai-core-jvm/{version}/:mirai-core-jvm-{version}.jar" + internal const val coreAliyun = + "https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar" + } private suspend fun downloadCoreAndLib(version: String) { var fileStream = File(coresPath.absolutePath + "/" + "mirai-core-qqandroid-jvm-$version.jar").also { @@ -150,18 +153,16 @@ object MiraiCoreLoader { }.outputStream() suspend fun downloadRequest(url: String, version: String): ByteReadChannel { - return Http.get() { - this.url(url.replace("{version}", version)) - }.content + return Http.get(url.replace("{version}", version)).content } var stream = kotlin.runCatching { MiraiConsole.logger("Downloading newest Protocol lib from Aliyun") - downloadRequest(lib_aliyun, version) + downloadRequest(Links.libAliyun, version) }.getOrElse { kotlin.runCatching { MiraiConsole.logger("Downloading newest Protocol lib from JCenter") - downloadRequest(lib_jcenter, version) + downloadRequest(Links.libJcenter, version) }.getOrElse { e -> MiraiConsole.logger("Failed to download Protocol lib, please seeking for help") e.printStackTrace() @@ -184,11 +185,11 @@ object MiraiCoreLoader { stream = try { MiraiConsole.logger("Downloading newest Mirai Core from Aliyun") - downloadRequest(core_aliyun, version) + downloadRequest(Links.coreAliyun, version) } catch (ignored: Exception) { try { MiraiConsole.logger("Downloading newest Mirai Core from JCenter") - downloadRequest(core_jcenter, version) + downloadRequest(Links.coreJcenter, version) } catch (e: Exception) { MiraiConsole.logger("Failed to download Mirai Core, please seeking for help") e.printStackTrace() @@ -240,8 +241,5 @@ object MiraiCoreLoader { } -internal class MiraiCoreClassLoader(file: File, parent: ClassLoader) : - URLClassLoader(arrayOf(file.toURI().toURL()), parent) - 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 index dffc93092..995c8fd1b 100644 --- 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 @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("MemberVisibilityCanBePrivate") + package net.mamoe.mirai.console.plugins import com.alibaba.fastjson.JSON @@ -17,12 +19,14 @@ import com.moandjiezana.toml.Toml import com.moandjiezana.toml.TomlWriter import kotlinx.serialization.Serializable import kotlinx.serialization.UnstableDefault +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.encodeToString import org.yaml.snakeyaml.Yaml import java.io.File import java.io.InputStream import java.util.* import java.util.concurrent.ConcurrentHashMap +import kotlin.NoSuchElementException import kotlin.properties.ReadWriteProperty import kotlin.reflect.KClass import kotlin.reflect.KProperty @@ -134,16 +138,16 @@ inline operator fun Config.setValue(thisRef: Any?, property: K } /* 带有默认值的代理 */ +@Suppress("unused") inline fun Config.withDefault( - noinline defaultValue: () -> T + crossinline 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 + return defaultValue() } override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { @@ -153,6 +157,7 @@ inline fun Config.withDefault( } /* 带有默认值且如果为空会写入的代理 */ +@Suppress("unused") inline fun Config.withDefaultWrite( noinline defaultValue: () -> T ): WithDefaultWriteLoader { @@ -191,7 +196,7 @@ class WithDefaultWriteLoader( return object : ReadWriteProperty { override fun getValue(thisRef: Any, property: KProperty<*>): T { if (config.exist(property.name)) {//unsafe - return config._smartCast(property.name, _class) + return config.smartCastInternal(property.name, _class) } return defaultValue } @@ -203,12 +208,14 @@ class WithDefaultWriteLoader( } } -inline fun Config.smartCast(property: KProperty<*>): T { - return _smartCast(property.name, T::class) +@PublishedApi +internal inline fun Config.smartCast(property: KProperty<*>): T { + return smartCastInternal(property.name, T::class) } +@PublishedApi @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") -fun Config._smartCast(propertyName: String, _class: KClass): T { +internal fun Config.smartCastInternal(propertyName: String, _class: KClass): T { return when (_class) { String::class -> this.getString(propertyName) Int::class -> this.getInt(propertyName) @@ -251,70 +258,72 @@ fun Config._smartCast(propertyName: String, _class: KClass): T { interface ConfigSection : Config, MutableMap { override fun getConfigSection(key: String): ConfigSection { - val content = get(key) ?: error("ConfigSection does not contain $key ") + val content = get(key) ?: throw NoSuchElementException(key) if (content is ConfigSection) { return content } + @Suppress("UNCHECKED_CAST") return ConfigSectionDelegation( Collections.synchronizedMap( - (get(key) ?: error("ConfigSection does not contain $key ")) as LinkedHashMap + (get(key) ?: throw NoSuchElementException(key)) as LinkedHashMap ) ) } override fun getString(key: String): String { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString() + return (get(key) ?: throw NoSuchElementException(key)).toString() } override fun getInt(key: String): Int { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toInt() + return (get(key) ?: throw NoSuchElementException(key)).toString().toInt() } override fun getFloat(key: String): Float { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toFloat() + return (get(key) ?: throw NoSuchElementException(key)).toString().toFloat() } override fun getBoolean(key: String): Boolean { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toBoolean() + return (get(key) ?: throw NoSuchElementException(key)).toString().toBoolean() } override fun getDouble(key: String): Double { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toDouble() + return (get(key) ?: throw NoSuchElementException(key)).toString().toDouble() } override fun getLong(key: String): Long { - return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toLong() + return (get(key) ?: throw NoSuchElementException(key)).toString().toLong() } override fun getList(key: String): List<*> { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>) + return ((get(key) ?: throw NoSuchElementException(key)) as List<*>) } override fun getStringList(key: String): List { - return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString() } + return ((get(key) ?: throw NoSuchElementException(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() } + return ((get(key) ?: throw NoSuchElementException(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() } + return ((get(key) ?: throw NoSuchElementException(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() } + return ((get(key) ?: throw NoSuchElementException(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() } + return ((get(key) ?: throw NoSuchElementException(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 { + return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { if (it is ConfigSection) { it } else { + @Suppress("UNCHECKED_CAST") ConfigSectionDelegation( Collections.synchronizedMap( it as MutableMap @@ -334,7 +343,7 @@ interface ConfigSection : Config, MutableMap { } @Serializable -open class ConfigSectionImpl() : ConcurrentHashMap(), +open class ConfigSectionImpl : ConcurrentHashMap(), ConfigSection { override fun set(key: String, value: Any) { super.put(key, value) @@ -370,7 +379,7 @@ open class ConfigSectionDelegation( private val delegate: MutableMap ) : ConfigSection, MutableMap by delegate { override fun set(key: String, value: Any) { - delegate.put(key, value) + delegate[key] = value } override fun contains(key: String): Boolean { @@ -394,6 +403,7 @@ interface FileConfig : Config { } +@MiraiInternalAPI abstract class FileConfigImpl internal constructor( private val rawContent: String ) : FileConfig, @@ -402,6 +412,7 @@ abstract class FileConfigImpl internal constructor( internal var file: File? = null + @Suppress("unused") constructor(file: File) : this(file.readText()) { this.file = file } @@ -425,7 +436,7 @@ abstract class FileConfigImpl internal constructor( override fun remove(key: String): Any? = content.remove(key) override fun save() { - if (isReadOnly()) { + if (isReadOnly) { error("Config is readonly") } if (!((file?.exists())!!)) { @@ -434,7 +445,7 @@ abstract class FileConfigImpl internal constructor( file?.writeText(serialize(content)) } - fun isReadOnly() = file == null + val isReadOnly: Boolean get() = file == null override fun contains(key: String): Boolean { return content.contains(key) @@ -454,6 +465,7 @@ abstract class FileConfigImpl internal constructor( } +@UseExperimental(MiraiInternalAPI::class) class JsonConfig internal constructor( content: String ) : FileConfigImpl(content) { @@ -466,7 +478,7 @@ class JsonConfig internal constructor( if (content.isEmpty() || content.isBlank() || content == "{}") { return ConfigSectionImpl() } - return JSON.parseObject( + return JSON.parseObject( content, object : TypeReference() {}, Feature.OrderedField @@ -479,6 +491,7 @@ class JsonConfig internal constructor( } } +@UseExperimental(MiraiInternalAPI::class) class YamlConfig internal constructor(content: String) : FileConfigImpl(content) { constructor(file: File) : this(file.readText()) { this.file = file @@ -490,7 +503,7 @@ class YamlConfig internal constructor(content: String) : FileConfigImpl(content) } return ConfigSectionDelegation( Collections.synchronizedMap( - Yaml().load>(content) as LinkedHashMap + Yaml().load(content) as LinkedHashMap ) ) } @@ -501,6 +514,7 @@ class YamlConfig internal constructor(content: String) : FileConfigImpl(content) } +@UseExperimental(MiraiInternalAPI::class) class TomlConfig internal constructor(content: String) : FileConfigImpl(content) { constructor(file: File) : this(file.readText()) { this.file = file diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index 171364693..d8208139f 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("unused", "unused") + package net.mamoe.mirai.console.plugins import kotlinx.coroutines.CancellationException @@ -22,7 +24,6 @@ import java.util.jar.JarFile object PluginManager { - @Volatile internal var lastPluginName: String = "" 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 index ddedb1866..48ef089c5 100644 --- 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 @@ -18,8 +18,8 @@ import net.mamoe.mirai.utils.LoginSolver import kotlin.concurrent.thread class MiraiConsoleUIPure : MiraiConsoleUI { - var requesting = false - var requestStr = "" + private var requesting = false + private var requestStr = "" init { thread { 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 index 2443d024e..2b383e011 100644 --- 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 @@ -18,32 +18,33 @@ import net.mamoe.mirai.console.plugins.withDefaultWriteSave import net.mamoe.mirai.console.utils.BotManagers.BOT_MANAGERS import java.io.File -object BotManagers { +internal object BotManagers { val config = File("${MiraiConsole.path}/bot.yml").loadAsConfig() val BOT_MANAGERS: ConfigSection by config.withDefaultWriteSave { ConfigSectionImpl() } } -fun Bot.addManager(long: Long) { +internal 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) { +internal 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()) -} +internal val Bot.managers: List + get() { + 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) +internal fun Bot.checkManager(long: Long): Boolean { + return this.managers.contains(long) }