mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Public API stabilization:
- Redesign MiraiConsoleFrontEndDescription - Redesign MiraiConsoleImplementation - Rework path-relevant properties - Add docs
This commit is contained in:
parent
bcd93ab34e
commit
218fb2bdcc
@ -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<PluginLoader<*, *>>
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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"
|
||||
}
|
@ -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()
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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()
|
@ -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")
|
||||
}
|
||||
|
@ -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<PluginLoader<*, *>> 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<PluginLoader<*, *>> 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.")
|
||||
|
@ -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 <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
@ -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() {
|
||||
|
@ -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<PluginLoader<*, *>> = 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 {
|
||||
|
@ -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) }
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -21,18 +21,35 @@ import java.io.File
|
||||
*
|
||||
* 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护.
|
||||
*
|
||||
* ### 内建加载器
|
||||
* - [JarPluginLoader] Jar 插件加载器
|
||||
*
|
||||
* ### 扩展加载器
|
||||
* 插件被允许扩展一个加载器。 可通过 [PluginManager.registerPluginLoader]
|
||||
*
|
||||
* @see JarPluginLoader Jar 插件加载器
|
||||
* @see PluginManager.registerPluginLoader 注册一个扩展的插件加载器
|
||||
*/
|
||||
public interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
/**
|
||||
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次
|
||||
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表.
|
||||
*
|
||||
* 在 console 启动时, [PluginManager] 会获取所有 [PluginDescription], 分析依赖关系, 确认插件加载顺序.
|
||||
*
|
||||
* **实现细节:** 此函数只*应该*在 console 启动时被调用一次. 但取决于前端实现不同, 或可能由于被一些插件需要, 此函数也可能会被多次调用.
|
||||
*/
|
||||
public fun listPlugins(): List<D>
|
||||
|
||||
/**
|
||||
* 获取此插件的描述
|
||||
* 获取此插件的描述.
|
||||
*
|
||||
* **实现细节**: 此函数只允许抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误.
|
||||
*
|
||||
* 若在 console 启动并加载所有插件的过程中, 本函数抛出异常, 则会放弃此插件的加载, 并影响依赖它的其他插件.
|
||||
*
|
||||
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等).
|
||||
*
|
||||
* @see PluginDescription 插件描述
|
||||
*/
|
||||
@get:JvmName("getPluginDescription")
|
||||
@get:Throws(PluginLoadException::class)
|
||||
@ -41,11 +58,19 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
/**
|
||||
* 加载一个插件 (实例), 但不 [启用][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<P : Plugin, D : PluginDescription> : PluginLoa
|
||||
}
|
||||
|
||||
/**
|
||||
* [FilePluginLoader] 的默认基础实现
|
||||
* [FilePluginLoader] 的默认基础实现.
|
||||
*
|
||||
* @see FilePluginLoader
|
||||
*/
|
||||
public abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
|
||||
public override val fileSuffix: String
|
||||
) : FilePluginLoader<P, D> {
|
||||
private fun pluginsFilesSequence(): Sequence<File> =
|
||||
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]
|
||||
|
@ -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<Plugin>
|
||||
|
||||
/**
|
||||
* 内建的插件加载器列表. 由 [MiraiConsole] 初始化.
|
||||
*
|
||||
* @return 不可变的 list.
|
||||
* @return 只读列表
|
||||
*/
|
||||
public val builtInLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
/**
|
||||
* 由插件创建的 [PluginLoader]
|
||||
*
|
||||
* @return 只读列表
|
||||
*/
|
||||
public val pluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
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)
|
||||
}
|
||||
/**
|
||||
* @see PluginManager.pluginsDataPath
|
||||
*/
|
||||
@get:JvmSynthetic
|
||||
public inline val PluginManager.pluginsDataFolder: File
|
||||
get() = pluginsDataPath.toFile()
|
@ -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(
|
||||
/** 依赖插件名 */
|
||||
|
@ -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) }
|
||||
}
|
||||
// don't move into INSTANCE, Compilation error
|
||||
@JvmSynthetic
|
||||
public suspend inline fun MiraiConsole.requestInput(hint: String): String = ConsoleInput.requestInput(hint)
|
@ -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<PluginLoader<*, *>> = 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
|
||||
|
@ -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.")
|
||||
|
@ -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<String, MiraiLogger>()
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<PluginLoader<*, *>> = 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
|
||||
}
|
@ -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<String>?) {
|
||||
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()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user