mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-15 21:10:14 +08:00
Implement MiraiConsole bootstrap and plugin loading
This commit is contained in:
parent
442d7ee0ce
commit
01fd1b3b6e
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION")
|
@file:Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION")
|
||||||
|
@file:OptIn(ConsoleInternalAPI::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console
|
package net.mamoe.mirai.console
|
||||||
|
|
||||||
@ -16,18 +17,22 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.io.charsets.Charset
|
import kotlinx.io.charsets.Charset
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandOwner
|
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugin.PluginManager
|
||||||
import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
|
import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
|
||||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||||
|
import net.mamoe.mirai.console.setting.SettingStorage
|
||||||
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
|
||||||
|
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import net.mamoe.mirai.utils.info
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -80,14 +85,14 @@ internal object MiraiConsoleInitializer {
|
|||||||
/** 由前端调用 */
|
/** 由前端调用 */
|
||||||
internal fun init(instance: IMiraiConsole) {
|
internal fun init(instance: IMiraiConsole) {
|
||||||
this.instance = instance
|
this.instance = instance
|
||||||
MiraiConsoleInternal.initialize()
|
MiraiConsoleInternal.doStart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
|
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val buildDate: Date = Date(1592799753404L) // 2020-06-22 12:22:33
|
val buildDate: Date = Date(1592799753404L) // 2020-06-22 12:22:33
|
||||||
const val version: String = "0.5.1"
|
const val version: String = "1.0-M1"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,9 +112,10 @@ internal object MiraiConsoleInternal : CoroutineScope, IMiraiConsole, MiraiConso
|
|||||||
get() = instance.mainLogger
|
get() = instance.mainLogger
|
||||||
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
||||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
|
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
|
||||||
override val consoleCommandOwner: ConsoleCommandOwner get() = instance.consoleCommandOwner
|
|
||||||
override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender
|
override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender
|
||||||
|
|
||||||
|
override val settingStorage: SettingStorage get() = instance.settingStorage
|
||||||
|
|
||||||
init {
|
init {
|
||||||
DefaultLogger = { identity -> this.newLogger(identity) }
|
DefaultLogger = { identity -> this.newLogger(identity) }
|
||||||
}
|
}
|
||||||
@ -117,13 +123,23 @@ internal object MiraiConsoleInternal : CoroutineScope, IMiraiConsole, MiraiConso
|
|||||||
@ConsoleExperimentalAPI
|
@ConsoleExperimentalAPI
|
||||||
override fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
override fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
||||||
|
|
||||||
internal fun initialize() {
|
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." }
|
||||||
|
|
||||||
if (coroutineContext[Job] == null) {
|
if (coroutineContext[Job] == null) {
|
||||||
throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
|
throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
|
||||||
}
|
}
|
||||||
this.coroutineContext[Job]!!.invokeOnCompletion {
|
this.coroutineContext[Job]!!.invokeOnCompletion {
|
||||||
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
|
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainLogger.info { "Loading plugins..." }
|
||||||
|
PluginManager.loadEnablePlugins()
|
||||||
|
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
|
||||||
|
mainLogger.info { "mirai-console started successfully." }
|
||||||
// Only for initialize
|
// Only for initialize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,9 +171,9 @@ internal interface IMiraiConsole : CoroutineScope {
|
|||||||
*/
|
*/
|
||||||
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||||
|
|
||||||
internal val consoleCommandOwner: ConsoleCommandOwner
|
val consoleCommandSender: ConsoleCommandSender
|
||||||
|
|
||||||
internal val consoleCommandSender: ConsoleCommandSender
|
val settingStorage: SettingStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,15 +11,23 @@ package net.mamoe.mirai.console
|
|||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.utils.LoginSolver
|
import net.mamoe.mirai.utils.LoginSolver
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 只需要实现一个这个传入 MiraiConsole 就可以绑定 UI 层与 Console 层
|
* 只需要实现一个这个传入 MiraiConsole 就可以绑定 UI 层与 Console 层
|
||||||
* 需要保证线程安全
|
* 需要保证线程安全
|
||||||
*/
|
*/
|
||||||
@MiraiInternalAPI
|
|
||||||
interface MiraiConsoleFrontEnd {
|
interface MiraiConsoleFrontEnd {
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本
|
||||||
|
*/
|
||||||
|
val version: String
|
||||||
|
|
||||||
fun loggerFor(identity: String?): MiraiLogger
|
fun loggerFor(identity: String?): MiraiLogger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,4 +85,18 @@ abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
|
|||||||
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
||||||
|
|
||||||
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Not yet decided to make public API
|
||||||
|
internal class DeferredPluginLoader<P : Plugin, D : PluginDescription>(
|
||||||
|
initializer: () -> PluginLoader<P, D>
|
||||||
|
) : PluginLoader<P, D> {
|
||||||
|
private val instance by lazy(initializer)
|
||||||
|
|
||||||
|
override fun listPlugins(): List<D> = instance.listPlugins()
|
||||||
|
override val P.description: D get() = instance.run { description }
|
||||||
|
override fun load(description: D): P = instance.load(description)
|
||||||
|
override fun enable(plugin: P) = instance.enable(plugin)
|
||||||
|
override fun disable(plugin: P) = instance.disable(plugin)
|
||||||
|
}
|
||||||
|
@ -7,12 +7,13 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("NOTHING_TO_INLINE")
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugin
|
package net.mamoe.mirai.console.plugin
|
||||||
|
|
||||||
import kotlinx.atomicfu.locks.withLock
|
import kotlinx.atomicfu.locks.withLock
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
|
import net.mamoe.mirai.utils.info
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
@ -26,8 +27,10 @@ object PluginManager {
|
|||||||
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||||
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
|
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
|
||||||
|
|
||||||
|
@Suppress("ObjectPropertyName")
|
||||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||||
|
private val logger = MiraiConsole.newLogger("PluginManager")
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||||
@ -70,13 +73,33 @@ object PluginManager {
|
|||||||
// region LOADING
|
// region LOADING
|
||||||
|
|
||||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
||||||
// TODO: 2020/5/23 HANDLE INITIALIZATION EXCEPTION
|
return kotlin.runCatching {
|
||||||
return this.load(description).also { resolvedPlugins.add(it) }
|
this.load(description).also { resolvedPlugins.add(it) }
|
||||||
|
}.fold(
|
||||||
|
onSuccess = {
|
||||||
|
logger.info { "Successfully loaded plugin ${description.name}" }
|
||||||
|
it
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
logger.info { "Cannot load plugin ${description.name}" }
|
||||||
|
throw it
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginAndEnable(description: D) {
|
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.enablePlugin(plugin: Plugin) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
kotlin.runCatching {
|
||||||
return this.enable(loadPluginNoEnable(description.unwrap()))
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
this.enable(plugin as P)
|
||||||
|
}.fold(
|
||||||
|
onSuccess = {
|
||||||
|
logger.info { "Successfully enabled plugin ${plugin.description.name}" }
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
logger.info { "Cannot enable plugin ${plugin.description.name}" }
|
||||||
|
throw it
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,10 +116,15 @@ object PluginManager {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@Throws(PluginMissingDependencyException::class)
|
@Throws(PluginMissingDependencyException::class)
|
||||||
internal fun loadEnablePlugins() {
|
internal fun loadEnablePlugins() {
|
||||||
val all = loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second }
|
(loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second })
|
||||||
|
.sortByDependencies().loadAndEnableAllInOrder()
|
||||||
|
}
|
||||||
|
|
||||||
for ((loader, desc) in all.sortByDependencies()) {
|
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
|
||||||
loader.loadPluginAndEnable(desc)
|
return this.map { (loader, desc) ->
|
||||||
|
loader to loader.loadPluginNoEnable(desc)
|
||||||
|
}.forEach { (loader, plugin) ->
|
||||||
|
loader.enablePlugin(plugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +140,7 @@ object PluginManager {
|
|||||||
.onEach { (loader, descriptions) ->
|
.onEach { (loader, descriptions) ->
|
||||||
loader as PluginLoader<Plugin, PluginDescription>
|
loader as PluginLoader<Plugin, PluginDescription>
|
||||||
|
|
||||||
for (it in descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies()) {
|
descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies().loadAndEnableAllInOrder()
|
||||||
loader.loadPluginAndEnable(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.flatMap { it.second.asSequence() }
|
.flatMap { it.second.asSequence() }
|
||||||
|
|
||||||
@ -161,12 +187,7 @@ object PluginManager {
|
|||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
class PluginMissingDependencyException : PluginResolutionException {
|
class PluginMissingDependencyException(message: String?) : PluginResolutionException(message)
|
||||||
constructor() : super()
|
|
||||||
constructor(message: String?) : super(message)
|
|
||||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
|
||||||
constructor(cause: Throwable?) : super(cause)
|
|
||||||
}
|
|
||||||
|
|
||||||
open class PluginResolutionException : Exception {
|
open class PluginResolutionException : Exception {
|
||||||
constructor() : super()
|
constructor() : super()
|
||||||
|
@ -14,10 +14,12 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandOwner
|
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||||
|
import net.mamoe.mirai.console.setting.MemorySettingStorage
|
||||||
|
import net.mamoe.mirai.console.setting.SettingStorage
|
||||||
|
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import net.mamoe.mirai.utils.LoginSolver
|
import net.mamoe.mirai.utils.LoginSolver
|
||||||
@ -28,6 +30,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
@OptIn(ConsoleInternalAPI::class)
|
||||||
fun initTestEnvironment() {
|
fun initTestEnvironment() {
|
||||||
MiraiConsoleInitializer.init(object : IMiraiConsole {
|
MiraiConsoleInitializer.init(object : IMiraiConsole {
|
||||||
override val rootDir: File = createTempDir()
|
override val rootDir: File = createTempDir()
|
||||||
@ -39,10 +42,10 @@ fun initTestEnvironment() {
|
|||||||
}
|
}
|
||||||
override val mainLogger: MiraiLogger = DefaultLogger("main")
|
override val mainLogger: MiraiLogger = DefaultLogger("main")
|
||||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = listOf(JarPluginLoader)
|
override val builtInPluginLoaders: List<PluginLoader<*, *>> = listOf(JarPluginLoader)
|
||||||
override val consoleCommandOwner: ConsoleCommandOwner = object : ConsoleCommandOwner() {}
|
|
||||||
override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() {
|
override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() {
|
||||||
override suspend fun sendMessage(message: Message) = println(message)
|
override suspend fun sendMessage(message: Message) = println(message)
|
||||||
}
|
}
|
||||||
|
override val settingStorage: SettingStorage get() = MemorySettingStorage
|
||||||
override val coroutineContext: CoroutineContext = SupervisorJob()
|
override val coroutineContext: CoroutineContext = SupervisorJob()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test
|
|||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
object TestCompositeCommand : CompositeCommand(
|
object TestCompositeCommand : CompositeCommand(
|
||||||
ConsoleCommandOwner.instance,
|
ConsoleCommandOwner,
|
||||||
"testComposite", "tsC"
|
"testComposite", "tsC"
|
||||||
) {
|
) {
|
||||||
@SubCommand
|
@SubCommand
|
||||||
@ -44,7 +44,7 @@ object TestSimpleCommand : RawCommand(owner, "testSimple", "tsS") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val sender by lazy { ConsoleCommandSender.instance }
|
internal val sender by lazy { ConsoleCommandSender.instance }
|
||||||
internal val owner by lazy { ConsoleCommandOwner.instance }
|
internal val owner by lazy { ConsoleCommandOwner }
|
||||||
|
|
||||||
internal class TestCommand {
|
internal class TestCommand {
|
||||||
companion object {
|
companion object {
|
||||||
@ -61,7 +61,7 @@ internal class TestCommand {
|
|||||||
assertTrue(TestCompositeCommand.register())
|
assertTrue(TestCompositeCommand.register())
|
||||||
assertFalse(TestCompositeCommand.register())
|
assertFalse(TestCompositeCommand.register())
|
||||||
|
|
||||||
assertEquals(1, ConsoleCommandOwner.instance.registeredCommands.size)
|
assertEquals(1, ConsoleCommandOwner.registeredCommands.size)
|
||||||
|
|
||||||
assertEquals(1, InternalCommandManager.registeredCommands.size)
|
assertEquals(1, InternalCommandManager.registeredCommands.size)
|
||||||
assertEquals(2, InternalCommandManager.requiredPrefixCommandMap.size)
|
assertEquals(2, InternalCommandManager.requiredPrefixCommandMap.size)
|
||||||
@ -131,14 +131,16 @@ internal class TestCommand {
|
|||||||
fun `composite sub command resolution conflict`() {
|
fun `composite sub command resolution conflict`() {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val composite = object : CompositeCommand(
|
val composite = object : CompositeCommand(
|
||||||
ConsoleCommandOwner.instance,
|
ConsoleCommandOwner,
|
||||||
"tr"
|
"tr"
|
||||||
) {
|
) {
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
@SubCommand
|
@SubCommand
|
||||||
fun mute(seconds: Int) {
|
fun mute(seconds: Int) {
|
||||||
Testing.ok(1)
|
Testing.ok(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
@SubCommand
|
@SubCommand
|
||||||
fun mute(seconds: Int, arg2: Int) {
|
fun mute(seconds: Int, arg2: Int) {
|
||||||
Testing.ok(2)
|
Testing.ok(2)
|
||||||
@ -164,7 +166,7 @@ internal class TestCommand {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val composite = object : CompositeCommand(
|
val composite = object : CompositeCommand(
|
||||||
ConsoleCommandOwner.instance,
|
ConsoleCommandOwner,
|
||||||
"test",
|
"test",
|
||||||
overrideContext = CommandParserContext {
|
overrideContext = CommandParserContext {
|
||||||
add(object : CommandArgParser<MyClass> {
|
add(object : CommandArgParser<MyClass> {
|
||||||
|
@ -16,7 +16,7 @@ import org.jline.reader.impl.completer.NullCompleter
|
|||||||
import org.jline.terminal.Terminal
|
import org.jline.terminal.Terminal
|
||||||
import org.jline.terminal.TerminalBuilder
|
import org.jline.terminal.TerminalBuilder
|
||||||
|
|
||||||
object ConsoleUtils {
|
internal object ConsoleUtils {
|
||||||
|
|
||||||
val lineReader: LineReader
|
val lineReader: LineReader
|
||||||
val terminal: Terminal
|
val terminal: Terminal
|
||||||
@ -24,10 +24,10 @@ object ConsoleUtils {
|
|||||||
init {
|
init {
|
||||||
|
|
||||||
val dumb = System.getProperty("java.class.path")
|
val dumb = System.getProperty("java.class.path")
|
||||||
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null
|
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
|
||||||
|
|
||||||
terminal = TerminalBuilder.builder()
|
terminal = TerminalBuilder.builder()
|
||||||
.dumb(dumb)
|
.jansi(true)
|
||||||
.build()
|
.build()
|
||||||
lineReader = LineReaderBuilder.builder()
|
lineReader = LineReaderBuilder.builder()
|
||||||
.terminal(terminal)
|
.terminal(terminal)
|
||||||
|
@ -7,12 +7,26 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress(
|
||||||
|
"INVISIBLE_MEMBER",
|
||||||
|
"INVISIBLE_REFERENCE",
|
||||||
|
"CANNOT_OVERRIDE_INVISIBLE_MEMBER",
|
||||||
|
"INVISIBLE_SETTER",
|
||||||
|
"INVISIBLE_GETTER",
|
||||||
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
||||||
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
||||||
|
"EXPOSED_SUPER_CLASS"
|
||||||
|
)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
//import net.mamoe.mirai.console.command.CommandManager
|
//import net.mamoe.mirai.console.command.CommandManager
|
||||||
//import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
//import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.console.MiraiConsoleBuildConstants
|
||||||
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
|
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
|
||||||
|
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.utils.DefaultLoginSolver
|
import net.mamoe.mirai.utils.DefaultLoginSolver
|
||||||
import net.mamoe.mirai.utils.LoginSolver
|
import net.mamoe.mirai.utils.LoginSolver
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
@ -21,6 +35,7 @@ import org.fusesource.jansi.Ansi
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
private val ANSI_RESET = Ansi().reset().toString()
|
private val ANSI_RESET = Ansi().reset().toString()
|
||||||
|
|
||||||
@ -30,6 +45,13 @@ internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mirai-console-pure 前端实现
|
||||||
|
*
|
||||||
|
* @see MiraiConsolePure 后端实现
|
||||||
|
* @see MiraiConsolePureLoader CLI 入口点
|
||||||
|
*/
|
||||||
|
@ConsoleInternalAPI
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
|
object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
|
||||||
private val globalLogger = LoggerCreator("Mirai")
|
private val globalLogger = LoggerCreator("Mirai")
|
||||||
@ -53,7 +75,10 @@ object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
|
|||||||
val sdf by lazy {
|
val sdf by lazy {
|
||||||
SimpleDateFormat("HH:mm:ss")
|
SimpleDateFormat("HH:mm:ss")
|
||||||
}
|
}
|
||||||
|
override val name: String
|
||||||
|
get() = "Pure"
|
||||||
|
override val version: String
|
||||||
|
get() = MiraiConsoleBuildConstants.version
|
||||||
|
|
||||||
override fun loggerFor(identity: String?): MiraiLogger {
|
override fun loggerFor(identity: String?): MiraiLogger {
|
||||||
identity?.apply {
|
identity?.apply {
|
||||||
@ -74,7 +99,9 @@ object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
|
|||||||
.toString()
|
.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return ConsoleUtils.lineReader.readLine("> ")
|
return suspendCancellableCoroutine {
|
||||||
|
it.resume(ConsoleUtils.lineReader.readLine("> "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLoginSolver(): LoginSolver {
|
override fun createLoginSolver(): LoginSolver {
|
||||||
|
@ -15,32 +15,58 @@
|
|||||||
"INVISIBLE_SETTER",
|
"INVISIBLE_SETTER",
|
||||||
"INVISIBLE_GETTER",
|
"INVISIBLE_GETTER",
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPE_WARNING"
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
||||||
|
"EXPOSED_SUPER_CLASS"
|
||||||
)
|
)
|
||||||
|
@file:OptIn(ConsoleInternalAPI::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import net.mamoe.mirai.console.IMiraiConsole
|
import net.mamoe.mirai.console.IMiraiConsole
|
||||||
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
|
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
|
||||||
|
import net.mamoe.mirai.console.MiraiConsoleInitializer
|
||||||
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
|
import net.mamoe.mirai.console.plugin.DeferredPluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||||
|
import net.mamoe.mirai.console.setting.MultiFileSettingStorage
|
||||||
|
import net.mamoe.mirai.console.setting.SettingStorage
|
||||||
|
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
|
|
||||||
private val delegateScope = CoroutineScope(EmptyCoroutineContext)
|
/**
|
||||||
|
* mirai-console-pure 后端实现
|
||||||
object MiraiConsolePure : IMiraiConsole {
|
*
|
||||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = LinkedList()
|
* @see MiraiConsoleFrontEndPure 前端实现
|
||||||
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure
|
* @see MiraiConsolePureLoader CLI 入口点
|
||||||
override val mainLogger: MiraiLogger = DefaultLogger("Console")
|
*/
|
||||||
override val rootDir: File = File("./test/console").also {
|
class MiraiConsolePure @JvmOverloads constructor(
|
||||||
it.mkdirs()
|
override val rootDir: File = File("."),
|
||||||
|
override val builtInPluginLoaders: List<PluginLoader<*, *>> = listOf(DeferredPluginLoader { JarPluginLoader }),
|
||||||
|
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure,
|
||||||
|
override val mainLogger: MiraiLogger = frontEnd.loggerFor("Console"),
|
||||||
|
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
|
||||||
|
override val settingStorage: SettingStorage = MultiFileSettingStorage(rootDir)
|
||||||
|
) : IMiraiConsole, CoroutineScope by CoroutineScope(SupervisorJob()) {
|
||||||
|
init {
|
||||||
|
rootDir.mkdir()
|
||||||
|
require(rootDir.isDirectory) { "rootDir ${rootDir.absolutePath} is not a directory" }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
@JvmStatic
|
||||||
|
private var started: Boolean = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun MiraiConsolePure.start() = synchronized(this) {
|
||||||
|
check(!started) { "mirai-console is already started and can't be restarted." }
|
||||||
|
MiraiConsoleInitializer.init(MiraiConsolePure())
|
||||||
|
started = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
get() = delegateScope.coroutineContext
|
|
||||||
}
|
}
|
@ -16,17 +16,23 @@
|
|||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPE_WARNING"
|
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPE_WARNING"
|
||||||
)
|
)
|
||||||
|
@file:OptIn(ConsoleInternalAPI::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
import net.mamoe.mirai.console.MiraiConsoleInitializer
|
import kotlinx.coroutines.isActive
|
||||||
import net.mamoe.mirai.console.command.CommandExecuteStatus
|
import net.mamoe.mirai.console.command.CommandExecuteStatus
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
import net.mamoe.mirai.console.command.executeCommandDetailed
|
import net.mamoe.mirai.console.command.executeCommandDetailed
|
||||||
|
import net.mamoe.mirai.console.pure.MiraiConsolePure.Companion.start
|
||||||
|
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mirai-console-pure CLI 入口点
|
||||||
|
*/
|
||||||
object MiraiConsolePureLoader {
|
object MiraiConsolePureLoader {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>?) {
|
fun main(args: Array<String>?) {
|
||||||
@ -36,18 +42,21 @@ object MiraiConsolePureLoader {
|
|||||||
|
|
||||||
|
|
||||||
internal fun startup() {
|
internal fun startup() {
|
||||||
MiraiConsoleInitializer.init(MiraiConsolePure)
|
|
||||||
startConsoleThread()
|
startConsoleThread()
|
||||||
|
MiraiConsolePure().start()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun startConsoleThread() {
|
internal fun startConsoleThread() {
|
||||||
thread(name = "Console", isDaemon = false) {
|
thread(name = "Console", isDaemon = false) {
|
||||||
val consoleLogger = DefaultLogger("Console")
|
val consoleLogger = DefaultLogger("Console")
|
||||||
kotlinx.coroutines.runBlocking {
|
kotlinx.coroutines.runBlocking {
|
||||||
while (true) {
|
while (isActive) {
|
||||||
val next = MiraiConsoleFrontEndPure.requestInput("")
|
val next = MiraiConsoleFrontEndPure.requestInput("")
|
||||||
|
if (next.isBlank()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
consoleLogger.debug("INPUT> $next")
|
consoleLogger.debug("INPUT> $next")
|
||||||
val result = ConsoleCS.executeCommandDetailed(next)
|
val result = ConsoleCommandSenderImpl.executeCommandDetailed(next)
|
||||||
when (result.status) {
|
when (result.status) {
|
||||||
CommandExecuteStatus.SUCCESSFUL -> {
|
CommandExecuteStatus.SUCCESSFUL -> {
|
||||||
}
|
}
|
||||||
@ -65,7 +74,7 @@ internal fun startConsoleThread() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ConsoleCS : ConsoleCommandSender() {
|
internal object ConsoleCommandSenderImpl : ConsoleCommandSender() {
|
||||||
override suspend fun sendMessage(message: Message) {
|
override suspend fun sendMessage(message: Message) {
|
||||||
ConsoleUtils.lineReader.printAbove(message.contentToString())
|
ConsoleUtils.lineReader.printAbove(message.contentToString())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user