diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index fc01c86b7..36a27d7d7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console +import com.vdurmont.semver4j.Semver import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import net.mamoe.mirai.Bot @@ -24,8 +25,8 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.MiraiLogger -import java.io.File -import java.util.* +import java.nio.file.Path +import java.time.Instant /** @@ -36,19 +37,20 @@ import java.util.* */ public interface MiraiConsole : CoroutineScope { /** - * Console 运行路径 + * Console 运行根目录, 由前端决定确切路径. + * + * 所有子模块都会在这个目录之下创建子目录. */ - public val rootDir: File + public val rootPath: Path /** - * Console 前端接口 - */ - @ConsoleExperimentalAPI - public val frontEnd: MiraiConsoleFrontEnd - - /** - * 与前端交互所使用的 Logger + * Console 主日志. + * + * **实现细节**: 这个 [MiraiLogger] 的 [MiraiLogger.identity] 通常为 `main` + * + * **注意**: 插件不应该在任何时刻使用它. */ + @ConsoleInternalAPI public val mainLogger: MiraiLogger /** @@ -58,13 +60,22 @@ public interface MiraiConsole : CoroutineScope { */ public val builtInPluginLoaders: List> - public val buildDate: Date + /** + * 此 Console 后端构建时间 + */ + public val buildDate: Instant - public val version: String + /** + * 此 Console 后端版本号 + */ + public val version: Semver @ConsoleExperimentalAPI public val pluginCenter: PluginCenter + /** + * 创建一个 logger + */ @ConsoleExperimentalAPI public fun newLogger(identity: String?): MiraiLogger @@ -88,8 +99,9 @@ public interface MiraiConsole : CoroutineScope { public fun addBot(id: Long, password: String, configuration: BotConfiguration.() -> Unit = {}): Bot = Bot(id, password) { fileBasedDeviceInfo() - this.loginSolver = frontEnd.createLoginSolver() redirectNetworkLogToDirectory() + + this.loginSolver = MiraiConsoleImplementationBridge.createLoginSolver(id, this) configuration() } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt deleted file mode 100644 index a2cda5c49..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2019-2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.console - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.MiraiLogger - -/** - * 只需要实现一个这个传入 MiraiConsole 就可以绑定 UI 层与 Console 层 - * - * 需要保证线程安全 - */ -@ConsoleExperimentalAPI -@ConsoleFrontEndImplementation -public interface MiraiConsoleFrontEnd { - /** - * 名称 - */ - public val name: String - - /** - * 版本 - */ - public val version: String - - public fun loggerFor(identity: String?): MiraiLogger - - /** - * 让 UI 层接受一个新的bot - * */ - public fun pushBot( - bot: Bot - ) - - /** - * 让 UI 层提供一个输入, 相当于 [readLine] - */ - public suspend fun requestInput(hint: String): String - - /** - * 由 UI 层创建一个 [LoginSolver] - */ - public fun createLoginSolver(): LoginSolver -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEndDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEndDescription.kt new file mode 100644 index 000000000..d4e300a0f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEndDescription.kt @@ -0,0 +1,37 @@ +/* + * 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 com.vdurmont.semver4j.Semver + +/** + * 有关前端实现的信息 + */ +public interface MiraiConsoleFrontEndDescription { + /** + * 此前端实现的名称 + */ + public val name: String + + /** + * 此前端实现的提供者 + */ + public val vendor: String + + /** + * 此前端实现的名称 + */ + public val version: Semver + + /** + * 返回显示在 [MiraiConsole] 启动时的信息 + */ + public fun render(): String = "Frontend ${name}: version ${version}, provided by $vendor" +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt index 3fc50e57d..a3b62baa2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.console import kotlinx.atomicfu.locks.withLock import kotlinx.coroutines.CoroutineScope +import net.mamoe.mirai.Bot import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.data.PluginDataStorage @@ -20,8 +21,11 @@ import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import net.mamoe.mirai.console.util.ConsoleInput +import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.MiraiLogger -import java.io.File +import java.nio.file.Path import java.util.concurrent.locks.ReentrantLock import kotlin.annotation.AnnotationTarget.* import kotlin.coroutines.CoroutineContext @@ -51,15 +55,16 @@ public interface MiraiConsoleImplementation : CoroutineScope { public override val coroutineContext: CoroutineContext /** - * Console 运行路径 + * Console 运行根目录 + * @see MiraiConsole.rootPath */ - public val rootDir: File + public val rootPath: Path /** * Console 前端接口 */ @ConsoleExperimentalAPI - public val frontEnd: MiraiConsoleFrontEnd + public val frontEndDescription: MiraiConsoleFrontEndDescription /** * 与前端交互所使用的 Logger @@ -79,6 +84,28 @@ public interface MiraiConsoleImplementation : CoroutineScope { public val configStorageForJarPluginLoader: PluginDataStorage public val dataStorageForBuiltIns: PluginDataStorage + /** + * @see ConsoleInput 的实现 + */ + public val consoleInput: ConsoleInput + + /** + * 创建一个 [LoginSolver] + * + * **调用备注**: 此函数通常在构造 [Bot] 实例时调用. + * + * @param requesterBot 请求者 [Bot.id] + * @param configuration 请求者 [Bot.configuration] + * + * @see LoginSolver.Default + */ + public fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver + + /** + * 创建一个 logger + */ + public fun newLogger(identity: String?): MiraiLogger + public companion object { internal lateinit var instance: MiraiConsoleImplementation private val initLock = ReentrantLock() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index 4a47c22fc..a06b1725a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.command.CommandManagerImpl.register import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.message.nextMessageOrNull import net.mamoe.mirai.utils.secondsToMillis import kotlin.concurrent.thread @@ -79,6 +80,7 @@ public object BuiltInCommands { }.fold( onSuccess = { sendMessage("mirai-console stopped successfully.") }, onFailure = { + @OptIn(ConsoleInternalAPI::class) MiraiConsole.mainLogger.error(it) sendMessage(it.localizedMessage ?: it.message ?: it.toString()) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt index 0782c983a..aa43b31c2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt @@ -16,6 +16,7 @@ import net.mamoe.mirai.console.internal.data.MultiFilePluginDataStorageImpl import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import java.io.File +import java.nio.file.Path import kotlin.reflect.KClass /** @@ -99,7 +100,7 @@ public interface MultiFilePluginDataStorage : PluginDataStorage { /** * 存放 [PluginData] 的目录. */ - public val directory: File + public val directoryPath: Path public companion object { /** @@ -109,7 +110,11 @@ public interface MultiFilePluginDataStorage : PluginDataStorage { */ @JvmStatic @JvmName("create") - public operator fun invoke(directory: File): MultiFilePluginDataStorage = + public operator fun invoke(directory: Path): MultiFilePluginDataStorage = MultiFilePluginDataStorageImpl(directory) } } + +@get:JvmSynthetic +public inline val MultiFilePluginDataStorage.directory: File + get() = this.directoryPath.toFile() \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt index be26ca308..6c6aefb80 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt @@ -9,10 +9,13 @@ package net.mamoe.mirai.console.internal -import java.util.* +import com.vdurmont.semver4j.Semver +import java.time.Instant internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants) @JvmStatic - val buildDate: Date = Date(1597935352287L) // 2020-08-20 22:55:52 - const val version: String = "1.0-M2-1" + val buildDate: Instant = Instant.ofEpochMilli(1597935352287L) // 2020-08-20 22:55:52 + + @JvmStatic + val version: Semver = Semver("1.0-M2-1") } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index ab0de0ac4..c9bca140b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -11,12 +11,13 @@ package net.mamoe.mirai.console.internal +import com.vdurmont.semver4j.Semver import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import net.mamoe.mirai.Bot import net.mamoe.mirai.console.IllegalMiraiConsoleImplementationError import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.MiraiConsoleFrontEnd +import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription import net.mamoe.mirai.console.MiraiConsoleImplementation import net.mamoe.mirai.console.command.BuiltInCommands import net.mamoe.mirai.console.command.Command.Companion.primaryName @@ -30,12 +31,12 @@ import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter import net.mamoe.mirai.console.util.ConsoleExperimentalAPI -import net.mamoe.mirai.utils.DefaultLogger -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.info -import java.io.File +import net.mamoe.mirai.console.util.ConsoleInput +import net.mamoe.mirai.console.util.ConsoleInternalAPI +import net.mamoe.mirai.utils.* +import java.nio.file.Path import java.text.SimpleDateFormat -import java.util.* +import java.time.Instant import kotlin.coroutines.CoroutineContext /** @@ -45,36 +46,39 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI MiraiConsole { override val pluginCenter: PluginCenter get() = CuiPluginCenter - private val instance: MiraiConsoleImplementation get() = MiraiConsoleImplementation.instance - override val buildDate: Date get() = MiraiConsoleBuildConstants.buildDate - override val version: String get() = MiraiConsoleBuildConstants.version - override val rootDir: File get() = instance.rootDir - override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd + private val instance: MiraiConsoleImplementation by MiraiConsoleImplementation.Companion::instance + override val buildDate: Instant by MiraiConsoleBuildConstants::buildDate + override val version: Semver by MiraiConsoleBuildConstants::version + override val rootPath: Path by instance::rootPath + override val frontEndDescription: MiraiConsoleFrontEndDescription by instance::frontEndDescription - @ConsoleExperimentalAPI - override val mainLogger: MiraiLogger - get() = instance.mainLogger - override val coroutineContext: CoroutineContext get() = instance.coroutineContext - override val builtInPluginLoaders: List> get() = instance.builtInPluginLoaders - override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender + @OptIn(ConsoleInternalAPI::class) + override val mainLogger: MiraiLogger by instance::mainLogger + override val coroutineContext: CoroutineContext by instance::coroutineContext + override val builtInPluginLoaders: List> by instance::builtInPluginLoaders + override val consoleCommandSender: ConsoleCommandSender by instance::consoleCommandSender - override val dataStorageForJarPluginLoader: PluginDataStorage get() = instance.dataStorageForJarPluginLoader - override val configStorageForJarPluginLoader: PluginDataStorage get() = instance.configStorageForJarPluginLoader - override val dataStorageForBuiltIns: PluginDataStorage get() = instance.dataStorageForBuiltIns + override val dataStorageForJarPluginLoader: PluginDataStorage by instance::dataStorageForJarPluginLoader + override val configStorageForJarPluginLoader: PluginDataStorage by instance::configStorageForJarPluginLoader + override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns + override val consoleInput: ConsoleInput by instance::consoleInput + + override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver = + instance.createLoginSolver(requesterBot, configuration) init { - DefaultLogger = { identity -> this.newLogger(identity) } + DefaultLogger = this::newLogger } @ConsoleExperimentalAPI - override fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity) + override fun newLogger(identity: String?): MiraiLogger = instance.newLogger(identity) @OptIn(ConsoleExperimentalAPI::class) internal fun doStart() { val buildDateFormatted = SimpleDateFormat("yyyy-MM-dd").format(buildDate) mainLogger.info { "Starting mirai-console..." } mainLogger.info { "Backend: version $version, built on $buildDateFormatted." } - mainLogger.info { "Frontend ${frontEnd.name}: version $version." } + mainLogger.info { frontEndDescription.render() } if (coroutineContext[Job] == null) { throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt index cb2363b80..f732cfa0c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -14,14 +14,15 @@ import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.yamlkt.Yaml import java.io.File +import java.nio.file.Path import kotlin.reflect.KClass @Suppress("RedundantVisibilityModifier") // might be public in the future internal open class MultiFilePluginDataStorageImpl( - public final override val directory: File + public final override val directoryPath: Path ) : PluginDataStorage, MultiFilePluginDataStorage { init { - directory.mkdir() + directoryPath.mkdir() } public override fun load(holder: PluginDataHolder, dataClass: Class): T = @@ -53,17 +54,17 @@ internal open class MultiFilePluginDataStorageImpl( protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) { val name = findASerialName() - val dir = File(directory, holder.name) + val dir = directoryPath.resolve(holder.name) if (dir.isFile) { - error("Target directory ${dir.path} for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.") + error("Target directory $dir for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.") } dir.mkdir() - val file = File(directory, name) + val file = directoryPath.resolve(name) if (file.isDirectory) { error("Target file $file is occupied by a directory therefore data $qualifiedNameOrTip can't be saved.") } - return file + return file.toFile() } @ConsoleExperimentalAPI @@ -78,4 +79,8 @@ internal open class MultiFilePluginDataStorageImpl( file.writeText(Yaml.default.encodeToString(pluginData.updaterSerializer, Unit)) } } -} \ No newline at end of file +} + +internal fun Path.mkdir(): Boolean = this.toFile().mkdir() +internal val Path.isFile: Boolean get() = this.toFile().isFile +internal val Path.isDirectory: Boolean get() = this.toFile().isDirectory \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index d6a914a9d..e05cb3b9d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -13,6 +13,7 @@ import kotlinx.atomicfu.AtomicLong import kotlinx.atomicfu.locks.withLock import kotlinx.coroutines.* import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.ResourceContainer.Companion.asResourceContainer @@ -20,8 +21,8 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.safeLoader import net.mamoe.mirai.utils.MiraiLogger -import java.io.File import java.io.InputStream +import java.nio.file.Path import java.util.concurrent.locks.ReentrantLock import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -60,11 +61,8 @@ internal abstract class JvmPluginInternal( private var firstRun = true - final override val dataFolder: File by lazy { - File( - PluginManager.pluginsDataFolder, - description.name - ).apply { mkdir() } + final override val dataFolderPath: Path by lazy { + PluginManager.pluginsPath.resolve(description.name).apply { mkdir() } } internal fun internalOnDisable() { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt index 6cc295f1e..3ee5d44f3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt @@ -16,14 +16,15 @@ import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.internal.data.cast +import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.plugin.* import net.mamoe.mirai.utils.info -import java.io.File +import java.nio.file.Path import java.util.concurrent.locks.ReentrantLock internal object PluginManagerImpl : PluginManager { - override val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } - override val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() } + override val pluginsPath: Path = MiraiConsole.rootPath.resolve("plugins").apply { mkdir() } + override val pluginsDataPath = MiraiConsole.rootPath.resolve("data").apply { mkdir() } @Suppress("ObjectPropertyName") private val _pluginLoaders: MutableList> = mutableListOf() @@ -45,15 +46,15 @@ internal object PluginManagerImpl : PluginManager { ?.getDescription(this) ?: error("Plugin is unloaded") - override fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock { - if (_pluginLoaders.any { it::class == loader }) { + override fun PluginLoader<*, *>.register(): Boolean = loadersLock.withLock { + if (_pluginLoaders.any { it::class == this }) { return false } - _pluginLoaders.add(loader) + _pluginLoaders.add(this) } - override fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock { - _pluginLoaders.remove(loader) + override fun PluginLoader<*, *>.unregister() = loadersLock.withLock { + _pluginLoaders.remove(this) } init { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ConsoleInputImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ConsoleInputImpl.kt new file mode 100644 index 000000000..9fbe21ff9 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ConsoleInputImpl.kt @@ -0,0 +1,15 @@ +package net.mamoe.mirai.console.internal.util + +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.util.ConsoleInput +import net.mamoe.mirai.console.util.requestInput + +@Suppress("unused") +internal object ConsoleInputImpl : ConsoleInput { + private val inputLock = Mutex() + + override suspend fun requestInput(hint: String): String = + inputLock.withLock { MiraiConsole.requestInput(hint) } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Exceptions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Exceptions.kt new file mode 100644 index 000000000..8f816b63d --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Exceptions.kt @@ -0,0 +1,28 @@ +/* + * 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.plugin + +// TODO: 2020/8/22 Document PluginMissingDependencyException +public class PluginMissingDependencyException : PluginResolutionException { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} + +// TODO: 2020/8/22 Document PluginResolutionException +public open class PluginResolutionException : Exception { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt index 20f80e720..f99a3acfd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt @@ -14,11 +14,12 @@ package net.mamoe.mirai.console.plugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import java.io.File +import java.nio.file.Path /** * 表示一个 mirai-console 插件. * - * @see PluginDescription 插件描述 + * @see PluginDescription 插件描述, 需由 [PluginLoader] 帮助提供([PluginLoader.description]) * @see JvmPlugin Java, Kotlin 或其他 JVM 平台插件 * @see PluginFileExtensions 支持文件系统存储的扩展 * @@ -71,13 +72,33 @@ public interface PluginFileExtensions { /** * 数据目录 */ - public val dataFolder: File + public val dataFolderPath: Path /** - * 从数据目录获取一个文件, 若不存在则创建文件. + * 从数据目录获取一个文件. + * @see dataFolderPath */ @JvmDefault - public fun file(relativePath: String): File = File(dataFolder, relativePath).apply { createNewFile() } + public fun resolveDataFile(relativePath: String): File = dataFolderPath.resolve(relativePath).toFile() - // TODO: 2020/7/11 add `fun path(...): Path` ? + /** + * 从数据目录获取一个文件. + * @see dataFolderPath + */ + @JvmDefault + public fun resolveDataPath(relativePath: String): Path = dataFolderPath.resolve(relativePath) + + /** + * 从数据目录获取一个文件. + * @see dataFolderPath + */ + @JvmDefault + public fun resolveDataFile(relativePath: Path): File = dataFolderPath.resolve(relativePath).toFile() + + /** + * 从数据目录获取一个文件路径. + * @see dataFolderPath + */ + @JvmDefault + public fun resolveDataPath(relativePath: Path): Path = dataFolderPath.resolve(relativePath) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt index 979ecf75c..02f8ced21 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt @@ -21,18 +21,35 @@ import java.io.File * * 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护. * + * ### 内建加载器 + * - [JarPluginLoader] Jar 插件加载器 + * + * ### 扩展加载器 + * 插件被允许扩展一个加载器。 可通过 [PluginManager.registerPluginLoader] + * * @see JarPluginLoader Jar 插件加载器 + * @see PluginManager.registerPluginLoader 注册一个扩展的插件加载器 */ public interface PluginLoader

{ /** - * 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次 + * 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. + * + * 在 console 启动时, [PluginManager] 会获取所有 [PluginDescription], 分析依赖关系, 确认插件加载顺序. + * + * **实现细节:** 此函数只*应该*在 console 启动时被调用一次. 但取决于前端实现不同, 或可能由于被一些插件需要, 此函数也可能会被多次调用. */ public fun listPlugins(): List /** - * 获取此插件的描述 + * 获取此插件的描述. + * + * **实现细节**: 此函数只允许抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误. + * + * 若在 console 启动并加载所有插件的过程中, 本函数抛出异常, 则会放弃此插件的加载, 并影响依赖它的其他插件. * * @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等). + * + * @see PluginDescription 插件描述 */ @get:JvmName("getPluginDescription") @get:Throws(PluginLoadException::class) @@ -41,11 +58,19 @@ public interface PluginLoader

{ /** * 加载一个插件 (实例), 但不 [启用][enable] 它. 返回加载成功的主类实例 * + * **实现细节**: 此函数只允许抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误. + * 当异常发生时, 插件将会直接被放弃加载, 并影响依赖它的其他插件. + * * @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等). */ @Throws(PluginLoadException::class) public fun load(description: D): P + /** + * 启用这个插件. + * + * **实现约定**: 若插件已经启用, 抛出 + */ public fun enable(plugin: P) public fun disable(plugin: P) } @@ -62,7 +87,7 @@ public open class PluginLoadException : RuntimeException { } /** - * '/plugins' 目录中的插件的加载器. 每个加载器需绑定一个后缀. + * ['/plugins'][PluginManager.pluginsPath] 目录中的插件的加载器. 每个加载器需绑定一个后缀. * * @see AbstractFilePluginLoader 默认基础实现 * @see JarPluginLoader 内建的 Jar (JVM) 插件加载器. @@ -75,13 +100,16 @@ public interface FilePluginLoader

: PluginLoa } /** - * [FilePluginLoader] 的默认基础实现 + * [FilePluginLoader] 的默认基础实现. + * + * @see FilePluginLoader */ public abstract class AbstractFilePluginLoader

( public override val fileSuffix: String ) : FilePluginLoader { private fun pluginsFilesSequence(): Sequence = - PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) } + PluginManager.pluginsPath.toFile().walk() + .filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) } /** * 读取扫描到的后缀与 [fileSuffix] 相同的文件中的 [PluginDescription] diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt index d902bf7d3..d575e62c3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt @@ -14,41 +14,74 @@ package net.mamoe.mirai.console.plugin import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl import java.io.File +import java.nio.file.Path /** * 插件管理器. + * + * [PluginManager] 管理所有 [插件加载器][PluginLoader], 储存对所有插件的引用 ([plugins]), 但不直接与 [插件实例][Plugin] 交互. + * + * 有关 [插件加载][PluginLoader.load], [插件启用][PluginLoader.enable] 等操作都由 [PluginLoader] 完成. + * [PluginManager] 仅作为一个联系所有 [插件加载器][PluginLoader], 使它们互相合作的桥梁. + * + * 若要主动加载一个插件, 请获取相应插件的 [PluginLoader], 然后使用 [PluginLoader.enable] + * + * @see Plugin 插件 + * @see PluginLoader 插件加载器 */ public interface PluginManager { /** - * `$rootDir/plugins` + * 插件自身存放路径. 由前端决定具体路径. + * + * **实现细节**: 在 terminal 前端实现为 `$rootPath/plugins` + * + * @see pluginsFolder [File] 类型 */ - public val pluginsDir: File + public val pluginsPath: Path /** - * `$rootDir/data` + * 插件数据存放路径 + * + * **实现细节**: 在 terminal 前端实现为 `$rootPath/data` + * + * @see pluginsDataFolder [File] 类型 */ - public val pluginsDataFolder: File + public val pluginsDataPath: Path /** * 已加载的插件列表 + * + * @return 只读列表 */ public val plugins: List /** * 内建的插件加载器列表. 由 [MiraiConsole] 初始化. * - * @return 不可变的 list. + * @return 只读列表 */ public val builtInLoaders: List> /** * 由插件创建的 [PluginLoader] + * + * @return 只读列表 */ public val pluginLoaders: List> - public fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean + /** + * 注册一个扩展的插件加载器 + * + * @see PluginLoader 插件加载器 + */ + public fun PluginLoader<*, *>.register(): Boolean - public fun unregisterPluginLoader(loader: PluginLoader<*, *>): Boolean + /** + * 取消注册一个扩展的插件加载器 + * + * @see PluginLoader 插件加载器 + */ + public fun PluginLoader<*, *>.unregister(): Boolean /** * 获取插件的 [描述][PluginDescription], 通过 [PluginLoader.getDescription] @@ -56,26 +89,23 @@ public interface PluginManager { public val Plugin.description: PluginDescription public companion object INSTANCE : PluginManager by PluginManagerImpl { - override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description } + // due to Kotlin's bug + public override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description } + public override fun PluginLoader<*, *>.register(): Boolean = PluginManagerImpl.run { register() } + public override fun PluginLoader<*, *>.unregister(): Boolean = PluginManagerImpl.run { unregister() } } } -@JvmSynthetic -public inline fun PluginLoader<*, *>.register(): Boolean = PluginManager.registerPluginLoader(this) +/** + * @see PluginManager.pluginsPath + */ +@get:JvmSynthetic +public inline val PluginManager.pluginsFolder: File + get() = pluginsPath.toFile() -@JvmSynthetic -public inline fun PluginLoader<*, *>.unregister(): Boolean = PluginManager.unregisterPluginLoader(this) - -public class PluginMissingDependencyException : PluginResolutionException { - public constructor() : super() - public constructor(message: String?) : super(message) - public constructor(message: String?, cause: Throwable?) : super(message, cause) - public constructor(cause: Throwable?) : super(cause) -} - -public open class PluginResolutionException : Exception { - public constructor() : super() - public constructor(message: String?) : super(message) - public constructor(message: String?, cause: Throwable?) : super(message, cause) - public constructor(cause: Throwable?) : super(cause) -} \ No newline at end of file +/** + * @see PluginManager.pluginsDataPath + */ +@get:JvmSynthetic +public inline val PluginManager.pluginsDataFolder: File + get() = pluginsDataPath.toFile() \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt index 9029e8d15..7389c1bcc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt @@ -22,21 +22,53 @@ import java.io.File /** - * 插件描述 + * 插件描述. + * + * @see Plugin */ public interface PluginDescription { + /** + * 插件类型. 将会决定加载顺序 + * + * @see PluginKind + */ public val kind: PluginKind + /** + * 插件名称. + */ public val name: String + + /** + * 插件作者, 允许为空 + */ public val author: String + + /** + * 插件版本. + * + * 语法参考: ([语义化版本 2.0.0](https://semver.org/lang/zh-CN/)) + * + * @see Semver 语义化版本 + */ public val version: Semver + + /** + * 插件信息, 允许为空 + */ public val info: String - /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ + /** + * 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 + * + * @see PluginDependency + */ public val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> } -/** 插件类型 */ +/** + * 插件类型 + */ @Serializable(with = PluginKind.AsStringSerializer::class) public enum class PluginKind { /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ @@ -55,7 +87,11 @@ public enum class PluginKind { ) } -/** 插件的一个依赖的信息 */ +/** + * 插件的一个依赖的信息 + * + * @see PluginDescription.dependencies + */ @Serializable(with = PluginDependency.SmartSerializer::class) public data class PluginDependency( /** 依赖插件名 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt index eb40973bf..9662e908e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt @@ -13,10 +13,9 @@ package net.mamoe.mirai.console.util -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.internal.util.ConsoleInputImpl /** * Console 输入. 由于 console 接管了 [stdin][System. in], [readLine] 等操作需要在这里进行. @@ -28,16 +27,9 @@ public interface ConsoleInput { @JvmBlockingBridge public suspend fun requestInput(hint: String): String - public companion object INSTANCE : ConsoleInput by ConsoleInputImpl { - @JvmSynthetic - public suspend inline fun MiraiConsole.requestInput(hint: String): String = ConsoleInput.requestInput(hint) - } + public companion object INSTANCE : ConsoleInput by ConsoleInputImpl } -@Suppress("unused") -internal object ConsoleInputImpl : ConsoleInput { - private val inputLock = Mutex() - - override suspend fun requestInput(hint: String): String = - inputLock.withLock { MiraiConsole.frontEnd.requestInput(hint) } -} \ No newline at end of file +// don't move into INSTANCE, Compilation error +@JvmSynthetic +public suspend inline fun MiraiConsole.requestInput(hint: String): String = ConsoleInput.requestInput(hint) \ No newline at end of file diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt index 8dd4fc9ef..ed65725d3 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout -import net.mamoe.mirai.Bot import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.ConsoleCommandSender @@ -22,13 +21,15 @@ import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.plugin.DeferredPluginLoader import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.PlatformLogger -import java.io.File +import java.nio.file.Path import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume @@ -37,22 +38,34 @@ import kotlin.test.assertNotNull @OptIn(ConsoleInternalAPI::class) fun initTestEnvironment() { object : MiraiConsoleImplementation { - override val rootDir: File = createTempDir() - override val frontEnd: MiraiConsoleFrontEnd = object : MiraiConsoleFrontEnd { - override val name: String get() = "Test" - override val version: String get() = "1.0.0" - override fun loggerFor(identity: String?): MiraiLogger = PlatformLogger(identity) - override fun pushBot(bot: Bot) = println("pushBot: $bot") - override suspend fun requestInput(hint: String): String = readLine()!! - override fun createLoginSolver(): LoginSolver = LoginSolver.Default - } + override val rootPath: Path = createTempDir().toPath() + + @ConsoleExperimentalAPI + override val frontEndDescription: MiraiConsoleFrontEndDescription + get() = TODO("Not yet implemented") override val mainLogger: MiraiLogger = DefaultLogger("main") override val builtInPluginLoaders: List> = listOf(DeferredPluginLoader { JarPluginLoader }) override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() { override suspend fun sendMessage(message: Message) = println(message) } override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage() + override val configStorageForJarPluginLoader: PluginDataStorage + get() = TODO("Not yet implemented") override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage() + override val consoleInput: ConsoleInput = object : ConsoleInput { + override suspend fun requestInput(hint: String): String { + println(hint) + return readLine() ?: error("No stdin") + } + } + + override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver = + LoginSolver.Default + + override fun newLogger(identity: String?): MiraiLogger { + return DefaultLogger(identity) + } + override val coroutineContext: CoroutineContext = SupervisorJob() }.start() CommandManager diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt index eb8548fa8..6f706ac97 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt @@ -17,6 +17,7 @@ import net.mamoe.mirai.console.command.CommandExecuteStatus import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommandDetailed import net.mamoe.mirai.console.util.ConsoleInternalAPI +import net.mamoe.mirai.console.util.requestInput import net.mamoe.mirai.utils.DefaultLogger import org.fusesource.jansi.Ansi import java.util.* @@ -50,11 +51,9 @@ internal fun startupConsoleThread() { val consoleLogger = DefaultLogger("console") while (isActive) { try { - val next = MiraiConsoleFrontEndPure.requestInput("").let { + val next = MiraiConsole.requestInput("").let { when { - it.startsWith(CommandManager.commandPrefix) -> { - it - } + it.startsWith(CommandManager.commandPrefix) -> it it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName else -> CommandManager.commandPrefix + it } @@ -71,7 +70,7 @@ internal fun startupConsoleThread() { result.exception?.printStackTrace() } CommandExecuteStatus.COMMAND_NOT_FOUND -> { - consoleLogger.warning("Unknown command: ${result.commandName}") + consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助") } CommandExecuteStatus.PERMISSION_DENIED -> { consoleLogger.warning("Permission denied.") diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt index f6cf70256..d055cb51c 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt @@ -22,20 +22,13 @@ package net.mamoe.mirai.console.pure //import net.mamoe.mirai.console.command.CommandManager //import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd -import io.ktor.utils.io.concurrent.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsoleFrontEnd +import com.vdurmont.semver4j.Semver +import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription import net.mamoe.mirai.console.util.ConsoleInternalAPI -import net.mamoe.mirai.utils.DefaultLoginSolver -import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.PlatformLogger import org.fusesource.jansi.Ansi import java.text.SimpleDateFormat -import java.util.* -import java.util.concurrent.ConcurrentHashMap private val ANSI_RESET = Ansi().reset().toString() @@ -53,25 +46,7 @@ internal val LoggerCreator: (identity: String?) -> MiraiLogger = { */ @ConsoleInternalAPI @Suppress("unused") -object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd { - private val globalLogger = LoggerCreator("Mirai") - private val cachedLoggers = ConcurrentHashMap() - - // companion object { - // ANSI color codes - const val COLOR_RED = "\u001b[38;5;196m" - const val COLOR_CYAN = "\u001b[38;5;87m" - const val COLOR_GREEN = "\u001b[38;5;82m" - - // use a dark yellow(more like orange) instead of light one to save Solarized-light users - const val COLOR_YELLOW = "\u001b[38;5;220m" - const val COLOR_GREY = "\u001b[38;5;244m" - const val COLOR_BLUE = "\u001b[38;5;27m" - const val COLOR_NAVY = "\u001b[38;5;24m" // navy uniform blue - const val COLOR_PINK = "\u001b[38;5;207m" - const val COLOR_RESET = "\u001b[39;49m" - // } - +object MiraiConsoleFrontEndPure : MiraiConsoleFrontEndDescription { internal val sdf by ThreadLocal.withInitial { // SimpleDateFormat not thread safe. SimpleDateFormat("HH:mm:ss") @@ -81,32 +56,9 @@ object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd { return this.get() } - override val name: String - get() = "Pure" - override val version: String - get() = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version - - override fun loggerFor(identity: String?): MiraiLogger { - identity?.apply { - return cachedLoggers.computeIfAbsent(this, LoggerCreator) - } - return globalLogger - } - - override fun pushBot(bot: Bot) { - } - - override suspend fun requestInput(hint: String): String { - return ConsoleUtils.miraiLineReader(hint) - } - - override fun createLoginSolver(): LoginSolver { - return DefaultLoginSolver( - input = suspend { - requestInput("LOGIN> ") - } - ) - } + override val name: String get() = "Pure" + override val version: Semver get() = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version + override val vendor: String get() = "Mamoe Technologies" } diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt index b259e3a31..905f6b3e9 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt @@ -22,10 +22,12 @@ package net.mamoe.mirai.console.pure +import com.vdurmont.semver4j.Semver import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import net.mamoe.mirai.console.ConsoleFrontEndImplementation -import net.mamoe.mirai.console.MiraiConsoleFrontEnd +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription import net.mamoe.mirai.console.MiraiConsoleImplementation import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.data.MultiFilePluginDataStorage @@ -33,9 +35,15 @@ import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.plugin.DeferredPluginLoader import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader +import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput +import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.console.util.ConsoleInternalAPI +import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.DefaultLoginSolver +import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.MiraiLogger -import java.io.File +import java.nio.file.Path +import java.nio.file.Paths import java.util.* /** @@ -44,20 +52,43 @@ import java.util.* * @see MiraiConsoleFrontEndPure 前端实现 * @see MiraiConsolePureLoader CLI 入口点 */ -class MiraiConsoleImplementationPure +internal class MiraiConsoleImplementationPure @JvmOverloads constructor( - override val rootDir: File = File("."), + override val rootPath: Path = Paths.get("."), override val builtInPluginLoaders: List> = Collections.unmodifiableList( listOf(DeferredPluginLoader { JarPluginLoader }) ), - override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure, - override val mainLogger: MiraiLogger = frontEnd.loggerFor("main"), + override val frontEndDescription: MiraiConsoleFrontEndDescription = ConsoleFrontEndDescImpl, override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl, - override val dataStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(File(rootDir, "data")), - override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(File(rootDir, "data")) + override val dataStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")), + override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")), + override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")) ) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) { - init { - rootDir.mkdir() - require(rootDir.isDirectory) { "rootDir ${rootDir.absolutePath} is not a directory" } + override val mainLogger: MiraiLogger by lazy { + MiraiConsole.newLogger("main") } + override val consoleInput: ConsoleInput get() = ConsoleInputImpl + + override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver { + return DefaultLoginSolver(input = { requestInput("LOGIN> ") }) + } + + override fun newLogger(identity: String?): MiraiLogger = LoggerCreator(identity) + + init { + with(rootPath.toFile()) { + mkdir() + require(isDirectory) { "rootDir $absolutePath is not a directory" } + } + } +} + +private object ConsoleInputImpl : ConsoleInput { + override suspend fun requestInput(hint: String): String = ConsoleUtils.miraiLineReader(hint) +} + +private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription { + override val name: String get() = "Pure" + override val vendor: String get() = "Mamoe Technologies" + override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version } \ No newline at end of file diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt index e81c4342d..ae8740c3b 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt @@ -20,18 +20,9 @@ package net.mamoe.mirai.console.pure -import kotlinx.coroutines.runBlocking -import net.mamoe.mirai.alsoLogin -import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start -import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register -import net.mamoe.mirai.console.command.CommandPermission -import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.ConsoleCommandSender -import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.util.ConsoleInternalAPI -import net.mamoe.mirai.contact.Member -import net.mamoe.mirai.contact.nameCardOrNick import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.content import net.mamoe.mirai.utils.DefaultLogger @@ -44,29 +35,12 @@ object MiraiConsolePureLoader { @JvmStatic fun main(args: Array?) { startup() - YellowCommand.register() - runBlocking { MiraiConsole.addBot(1994701021, "Asd123456789asd").alsoLogin() } - } -} - -object YellowCommand : SimpleCommand( - net.mamoe.mirai.console.command.ConsoleCommandOwner, "睡", "sleep", - prefixOptional = true, - description = "睡一个人", - permission = CommandPermission.Any -) { - @Handler - suspend fun CommandSender.handle(target: Member) { - target.mute(1) - - sendMessage("${this.name} 睡了 ${target.nameCardOrNick}") } } internal fun startup() { - DefaultLogger = { MiraiConsoleFrontEndPure.loggerFor(it) } - overrideSTD() MiraiConsoleImplementationPure().start() + overrideSTD() startupConsoleThread() }