mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 02:12:37 +08:00
Terminal (#179)
* Rename ConsolePure to ConsoleTerminal * Fix the way to close console with Ctrl+C * Fix windows pipeline error. Fix EndOfFileException * Add ConsoleExperimentalApi * Collect imports * Review - Change old CLI main deprecation level to ERROR - Update documents - Update tasks from pure to terminal * Fix error in closing console. * Fix terminal closing and Ctrl+C closing. * Add console shut-downing status * Don't invokeOnCompletion when console shut-downing * Fix Input interrupt * Ensure active unless console is shut downing * Change MiraiConsole.isShutDowning to `!job.isActive` * Code Review - Update Message on MiraiConsolePureLoader.kt - Change MiraiConsole.isShutDowning to MiraiConsole.isActive - Change MiraiConsole.shutdown to ConsoleInternalApi * run catching * Fix console input * Update shutdown * Fix module * Revert 5199395 * Typo
This commit is contained in:
parent
f990b7ce8c
commit
8fe2506e75
4
.github/workflows/bintray.yml
vendored
4
.github/workflows/bintray.yml
vendored
@ -32,6 +32,6 @@ jobs:
|
||||
run: ./gradlew :mirai-console:fillBuildConstants -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console:bintrayUpload
|
||||
run: ./gradlew :mirai-console:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console-pure:bintrayUpload
|
||||
run: ./gradlew :mirai-console-pure:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console-terminal:bintrayUpload
|
||||
run: ./gradlew :mirai-console-terminal:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
|
||||
|
4
.github/workflows/shadow.yml
vendored
4
.github/workflows/shadow.yml
vendored
@ -28,8 +28,8 @@ jobs:
|
||||
run: ./gradlew build # if test's failed, don't publish
|
||||
- name: Gradle :mirai-console:githubUpload
|
||||
run: ./gradlew :mirai-console:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
||||
- name: Gradle :mirai-console-pure:githubUpload
|
||||
run: ./gradlew :mirai-console-pure:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
||||
- name: Gradle :mirai-console-terminal:githubUpload
|
||||
run: ./gradlew :mirai-console-terminal:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
||||
|
||||
|
||||
# - name: Upload artifact
|
||||
|
@ -28,12 +28,11 @@ console 由后端和前端一起工作. 使用时必须选择一个前端.
|
||||
|
||||
前端:
|
||||
|
||||
- `mirai-console-pure`: console 的轻量命令行前端.
|
||||
- `mirai-console-terminal`: console 的 Unix 终端界面前端.
|
||||
- `mirai-console-graphical`: console 的 JavaFX 图形化界面前端. (开发中)
|
||||
- `mirai-console-terminal`: console 的 Unix 终端界面前端. (开发中)
|
||||
|
||||
|
||||
**注意:`mirai-console` 后端和 pure 前端正在进行完全的重构, 所有 API 都不具有稳定性**
|
||||
**注意:`mirai-console` 后端和 terminal 前端正在进行完全的重构, 所有 API 都不具有稳定性**
|
||||
|
||||
### 使用
|
||||
|
||||
@ -56,7 +55,7 @@ dependencies {
|
||||
implementation("net.mamoe:mirai-core:$CORE_VERSION") // mirai-core 的 API
|
||||
implementation("net.mamoe:mirai-console:$CONSOLE_VERSION") // 后端
|
||||
|
||||
testImplementation("net.mamoe:mirai-console-pure:$CONSOLE_VERSION") // 前端, 用于启动测试
|
||||
testImplementation("net.mamoe:mirai-console-terminal:$CONSOLE_VERSION") // 前端, 用于启动测试
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -18,6 +18,7 @@ import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.extensions.BotConfigurationAlterer
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
|
||||
@ -146,6 +147,10 @@ public interface MiraiConsole : CoroutineScope {
|
||||
else -> null!!
|
||||
}
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi("This is a low-level API and might be removed in the future.")
|
||||
public val isActive: Boolean
|
||||
get() = job.isActive
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,9 @@ internal object BuiltInJvmPluginLoaderImpl :
|
||||
|
||||
override fun disable(plugin: JvmPlugin) {
|
||||
if (!plugin.isEnabled) return
|
||||
ensureActive()
|
||||
|
||||
if (MiraiConsole.isActive)
|
||||
ensureActive()
|
||||
|
||||
if (plugin is JvmPluginInternal) {
|
||||
plugin.internalOnDisable()
|
||||
|
@ -151,6 +151,7 @@ internal abstract class JvmPluginInternal(
|
||||
)
|
||||
)
|
||||
.also {
|
||||
if (!MiraiConsole.isActive) return@also
|
||||
BuiltInJvmPluginLoaderImpl.coroutineContext[Job]!!.invokeOnCompletion {
|
||||
this.cancel()
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ object Versions {
|
||||
const val core = "1.3.0"
|
||||
const val console = "1.0-RC-dev-2"
|
||||
const val consoleGraphical = "0.0.7"
|
||||
const val consoleTerminal = "0.1.0"
|
||||
const val consolePure = console
|
||||
const val consoleTerminal = console
|
||||
|
||||
const val kotlinCompiler = "1.4.10"
|
||||
const val kotlinStdlib = kotlinCompiler
|
||||
|
16
docs/Run.md
16
docs/Run.md
@ -19,16 +19,16 @@ https://github.com/LXY1226/MiraiOK
|
||||
- mirai-console 任一前端
|
||||
- 相关依赖
|
||||
|
||||
只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 pure 前端可用。
|
||||
只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 terminal 前端可用。
|
||||
|
||||
### 启动 mirai-console-pure 前端
|
||||
### 启动 mirai-console-terminal 前端
|
||||
|
||||
mirai 在版本发布时会同时发布打包依赖的 Shadow JAR,存放在 [mirai-repo]。
|
||||
|
||||
1. 在 [mirai-repo] 下载如下三个模块的最新版本文件并放到一个文件夹内 (如 `libs`):
|
||||
- mirai-core-qqandroid
|
||||
- mirai-console
|
||||
- mirai-console-pure
|
||||
- mirai-console-terminal
|
||||
|
||||
2. 创建一个新的文件, 名为 `start-mirai-console.bat`/`start-mirai-console.ps1`/`start-mirai-console.sh`
|
||||
|
||||
@ -36,26 +36,26 @@ Windows CMD:
|
||||
```shell script
|
||||
@echo off
|
||||
title Mirai Console
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader %*
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader %*
|
||||
pause
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
```shell script
|
||||
$Host.UI.RawUI.WindowTitle = "Mirai Console"
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader $args
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader $args
|
||||
pause
|
||||
```
|
||||
|
||||
Linux:
|
||||
```shell script
|
||||
#!/usr/bin/env bash
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader $*
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader $*
|
||||
```
|
||||
|
||||
然后就可以开始使用 mirai-console 了
|
||||
|
||||
### mirai-console-pure 前端参数
|
||||
使用 `./start-mirai-console --help` 查看 mirai-console-pure 支持的启动参数
|
||||
### mirai-console-terminal 前端参数
|
||||
使用 `./start-mirai-console --help` 查看 mirai-console-terminal 支持的启动参数
|
||||
|
||||
[mirai-repo]: https://github.com/project-mirai/mirai-repo/tree/master/shadow
|
||||
|
@ -66,10 +66,10 @@ ext.apply {
|
||||
this.set("shadowJar", x)
|
||||
}
|
||||
|
||||
version = Versions.consolePure
|
||||
version = Versions.consoleTerminal
|
||||
|
||||
description = "Console Pure CLI frontend for mirai"
|
||||
description = "Console Terminal CLI frontend for mirai"
|
||||
|
||||
setupPublishing("mirai-console-pure", bintrayPkgName = "mirai-console-pure")
|
||||
setupPublishing("mirai-console-terminal", bintrayPkgName = "mirai-console-terminal")
|
||||
|
||||
// endregion
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
|
||||
import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
|
||||
|
||||
@Deprecated(
|
||||
message = "Please use MiraiConsoleTerminalLoader",
|
||||
level = DeprecationLevel.ERROR,
|
||||
replaceWith = ReplaceWith(
|
||||
"MiraiConsoleTerminalLoader",
|
||||
"net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader"
|
||||
)
|
||||
)
|
||||
object MiraiConsolePureLoader {
|
||||
@Deprecated(
|
||||
message = "for binary compatibility",
|
||||
level = DeprecationLevel.ERROR
|
||||
)
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
System.err.println("WARNING: Mirai Console Pure已经更名为 Mirai Console Terminal")
|
||||
System.err.println("请使用新的入口点 net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader")
|
||||
MiraiConsoleTerminalLoader.main(args)
|
||||
}
|
||||
}
|
@ -5,9 +5,10 @@
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.OutputStream
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import org.fusesource.jansi.Ansi
|
||||
import org.jline.reader.EndOfFileException
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
|
||||
internal object ConsoleInputImpl : ConsoleInput {
|
||||
private val format = DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||
internal val thread = Executors.newSingleThreadExecutor { task ->
|
||||
Thread(task, "Mirai Console Input Thread").also {
|
||||
it.isDaemon = false
|
||||
}
|
||||
}
|
||||
internal var executingCoroutine: CancellableContinuation<String>? = null
|
||||
|
||||
|
||||
override suspend fun requestInput(hint: String): String {
|
||||
return suspendCancellableCoroutine { coroutine ->
|
||||
if (thread.isShutdown || thread.isTerminated) {
|
||||
coroutine.resumeWithException(EndOfFileException())
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
executingCoroutine = coroutine
|
||||
kotlin.runCatching {
|
||||
thread.submit {
|
||||
kotlin.runCatching {
|
||||
lineReader.readLine(
|
||||
if (hint.isNotEmpty()) {
|
||||
lineReader.printAbove(
|
||||
Ansi.ansi()
|
||||
.fgCyan()
|
||||
.a(
|
||||
LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())
|
||||
.format(format)
|
||||
)
|
||||
.a(" ")
|
||||
.fgMagenta().a(hint)
|
||||
.reset()
|
||||
.toString()
|
||||
)
|
||||
"$hint > "
|
||||
} else "> "
|
||||
)
|
||||
}.let { result ->
|
||||
executingCoroutine = null
|
||||
coroutine.resumeWith(result)
|
||||
}
|
||||
}
|
||||
}.onFailure { error ->
|
||||
executingCoroutine = null
|
||||
kotlin.runCatching { coroutine.resumeWithException(EndOfFileException(error)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,13 @@
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@ -23,10 +24,10 @@ package net.mamoe.mirai.console.pure
|
||||
AnnotationTarget.CONSTRUCTOR
|
||||
)
|
||||
@MustBeDocumented
|
||||
annotation class ConsolePureExperimentalApi
|
||||
annotation class ConsoleTerminalExperimentalApi
|
||||
|
||||
@ConsolePureExperimentalApi
|
||||
public object ConsolePureSettings {
|
||||
@ConsoleTerminalExperimentalApi
|
||||
public object ConsoleTerminalSettings {
|
||||
@JvmField
|
||||
var setupAnsi: Boolean = System.getProperty("os.name")
|
||||
.toLowerCase()
|
@ -5,13 +5,14 @@
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
@ -20,18 +21,33 @@ import net.mamoe.mirai.console.command.CommandExecuteStatus
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.terminal.noconsole.NoConsole
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.console.util.requestInput
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import org.jline.reader.EndOfFileException
|
||||
import org.jline.reader.UserInterruptException
|
||||
|
||||
val consoleLogger by lazy { DefaultLogger("console") }
|
||||
|
||||
@OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
|
||||
@OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class)
|
||||
internal fun startupConsoleThread() {
|
||||
if (ConsolePureSettings.noConsole) return
|
||||
if (terminal is NoConsole) return
|
||||
|
||||
MiraiConsole.launch(CoroutineName("Input")) {
|
||||
MiraiConsole.launch(CoroutineName("Input Cancelling Daemon")) {
|
||||
while (true) {
|
||||
delay(2000)
|
||||
}
|
||||
}.invokeOnCompletion {
|
||||
runCatching<Unit> {
|
||||
terminal.close()
|
||||
ConsoleInputImpl.thread.shutdownNow()
|
||||
runCatching {
|
||||
ConsoleInputImpl.executingCoroutine?.cancel(EndOfFileException())
|
||||
}
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
}
|
||||
MiraiConsole.launch(CoroutineName("Console Command")) {
|
||||
while (true) {
|
||||
try {
|
||||
val next = MiraiConsole.requestInput("").let {
|
||||
@ -65,17 +81,14 @@ internal fun startupConsoleThread() {
|
||||
} catch (e: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: UserInterruptException) {
|
||||
MiraiConsole.cancel()
|
||||
BuiltInCommands.StopCommand.run { ConsoleCommandSender.handle() }
|
||||
return@launch
|
||||
} catch (eof: EndOfFileException) {
|
||||
consoleLogger.warning("Closing input service...")
|
||||
return@launch
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MiraiConsole.job.invokeOnCompletion {
|
||||
runCatching {
|
||||
terminal.close()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
@file:Suppress(
|
||||
@ -17,13 +18,16 @@
|
||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
||||
"EXPOSED_SUPER_CLASS"
|
||||
)
|
||||
@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class, ConsolePureExperimentalApi::class)
|
||||
@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class, ConsoleTerminalExperimentalApi::class)
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
||||
@ -32,10 +36,10 @@ import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
||||
import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
|
||||
import net.mamoe.mirai.console.terminal.ConsoleInputImpl.requestInput
|
||||
import net.mamoe.mirai.console.terminal.noconsole.AllEmptyLineReader
|
||||
import net.mamoe.mirai.console.terminal.noconsole.NoConsole
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.pure.noconsole.AllEmptyLineReader
|
||||
import net.mamoe.mirai.console.pure.noconsole.NoConsole
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.console.util.NamedSupervisorJob
|
||||
@ -46,31 +50,28 @@ import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.impl.completer.NullCompleter
|
||||
import org.jline.terminal.Terminal
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import org.jline.terminal.impl.AbstractWindowsTerminal
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* mirai-console-pure 后端实现
|
||||
* mirai-console-terminal 后端实现
|
||||
*
|
||||
* @see MiraiConsolePureLoader CLI 入口点
|
||||
* @see MiraiConsoleTerminalLoader CLI 入口点
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
class MiraiConsoleImplementationPure
|
||||
class MiraiConsoleImplementationTerminal
|
||||
@JvmOverloads constructor(
|
||||
override val rootPath: Path = Paths.get(".").toAbsolutePath(),
|
||||
override val builtInPluginLoaders: List<Lazy<PluginLoader<*, *>>> = listOf(lazy { JvmPluginLoader }),
|
||||
override val frontEndDescription: MiraiConsoleFrontEndDescription = ConsoleFrontEndDescImpl,
|
||||
override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl = ConsoleCommandSenderImplPure,
|
||||
override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl = ConsoleCommandSenderImplTerminal,
|
||||
override val dataStorageForJvmPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val configStorageForJvmPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
|
||||
override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(
|
||||
NamedSupervisorJob("MiraiConsoleImplementationPure") +
|
||||
NamedSupervisorJob("MiraiConsoleImplementationTerminal") +
|
||||
CoroutineExceptionHandler { coroutineContext, throwable ->
|
||||
if (throwable is CancellationException) {
|
||||
return@CoroutineExceptionHandler
|
||||
@ -94,30 +95,9 @@ class MiraiConsoleImplementationPure
|
||||
}
|
||||
}
|
||||
|
||||
private object ConsoleInputImpl : ConsoleInput {
|
||||
private val format = DateTimeFormatter.ofPattern("HH:mm:ss")
|
||||
|
||||
override suspend fun requestInput(hint: String): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
lineReader.readLine(
|
||||
if (hint.isNotEmpty()) {
|
||||
lineReader.printAbove(
|
||||
Ansi.ansi()
|
||||
.fgCyan().a(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()).format(format))
|
||||
.a(" ")
|
||||
.fgMagenta().a(hint)
|
||||
.reset()
|
||||
.toString()
|
||||
)
|
||||
"$hint > "
|
||||
} else "> "
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val lineReader: LineReader by lazy {
|
||||
if (ConsolePureSettings.noConsole) return@lazy AllEmptyLineReader
|
||||
val terminal = terminal
|
||||
if (terminal is NoConsole) return@lazy AllEmptyLineReader
|
||||
|
||||
LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
@ -126,7 +106,7 @@ val lineReader: LineReader by lazy {
|
||||
}
|
||||
|
||||
val terminal: Terminal = run {
|
||||
if (ConsolePureSettings.noConsole) return@run NoConsole
|
||||
if (ConsoleTerminalSettings.noConsole) return@run NoConsole
|
||||
|
||||
val dumb = System.getProperty("java.class.path")
|
||||
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
|
||||
@ -134,7 +114,33 @@ val terminal: Terminal = run {
|
||||
runCatching {
|
||||
TerminalBuilder.builder()
|
||||
.dumb(dumb)
|
||||
.paused(true)
|
||||
.build()
|
||||
.let { terminal ->
|
||||
if (terminal is AbstractWindowsTerminal) {
|
||||
val pumpField = runCatching {
|
||||
AbstractWindowsTerminal::class.java.getDeclaredField("pump").also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
}.onFailure { err ->
|
||||
err.printStackTrace()
|
||||
return@let terminal.also { it.resume() }
|
||||
}.getOrThrow()
|
||||
var response = terminal
|
||||
terminal.setOnClose {
|
||||
response = NoConsole
|
||||
}
|
||||
terminal.resume()
|
||||
val pumpThread = pumpField[terminal] as? Thread ?: return@let NoConsole
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
while (pumpThread.state == Thread.State.NEW);
|
||||
Thread.sleep(1000)
|
||||
terminal.setOnClose(null)
|
||||
return@let response
|
||||
}
|
||||
terminal.resume()
|
||||
terminal
|
||||
}
|
||||
}.recoverCatching {
|
||||
TerminalBuilder.builder()
|
||||
.jansi(true)
|
||||
@ -147,7 +153,7 @@ val terminal: Terminal = run {
|
||||
}
|
||||
|
||||
private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
|
||||
override val name: String get() = "Pure"
|
||||
override val name: String get() = "Terminal"
|
||||
override val vendor: String get() = "Mamoe Technologies"
|
||||
override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
@file:Suppress(
|
||||
@ -15,9 +16,9 @@
|
||||
"INVISIBLE_GETTER",
|
||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
||||
)
|
||||
@file:OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
|
||||
@file:OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class)
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
package net.mamoe.mirai.console.terminal
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -26,20 +27,22 @@ import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
||||
import net.mamoe.mirai.console.pure.noconsole.SystemOutputPrintStream
|
||||
import net.mamoe.mirai.console.terminal.noconsole.SystemOutputPrintStream
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
import java.io.FileDescriptor
|
||||
import java.io.FileOutputStream
|
||||
import java.io.PrintStream
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* mirai-console-pure CLI 入口点
|
||||
* mirai-console-terminal CLI 入口点
|
||||
*/
|
||||
object MiraiConsolePureLoader {
|
||||
object MiraiConsoleTerminalLoader {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
parse(args, exitProcess = true)
|
||||
@ -53,10 +56,10 @@ object MiraiConsolePureLoader {
|
||||
}
|
||||
}
|
||||
|
||||
@ConsolePureExperimentalApi
|
||||
@ConsoleTerminalExperimentalApi
|
||||
fun printHelpMessage() {
|
||||
val help = listOf(
|
||||
"" to "Mirai-Console[Pure FrontEnd] v" + kotlin.runCatching {
|
||||
"" to "Mirai-Console[Terminal FrontEnd] v" + kotlin.runCatching {
|
||||
net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
||||
}.getOrElse { "<unknown>" },
|
||||
"" to "",
|
||||
@ -96,7 +99,7 @@ object MiraiConsolePureLoader {
|
||||
}
|
||||
}
|
||||
|
||||
@ConsolePureExperimentalApi
|
||||
@ConsoleTerminalExperimentalApi
|
||||
fun parse(args: Array<String>, exitProcess: Boolean = false) {
|
||||
val iterator = args.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
@ -107,19 +110,19 @@ object MiraiConsolePureLoader {
|
||||
return
|
||||
}
|
||||
"--no-console" -> {
|
||||
ConsolePureSettings.noConsole = true
|
||||
ConsoleTerminalSettings.noConsole = true
|
||||
}
|
||||
"--dont-setup-terminal-ansi" -> {
|
||||
ConsolePureSettings.setupAnsi = false
|
||||
ConsoleTerminalSettings.setupAnsi = false
|
||||
}
|
||||
"--no-ansi" -> {
|
||||
ConsolePureSettings.noAnsi = true
|
||||
ConsolePureSettings.setupAnsi = false
|
||||
ConsoleTerminalSettings.noAnsi = true
|
||||
ConsoleTerminalSettings.setupAnsi = false
|
||||
}
|
||||
"--reading-replacement" -> {
|
||||
ConsolePureSettings.noConsoleSafeReading = true
|
||||
ConsoleTerminalSettings.noConsoleSafeReading = true
|
||||
if (iterator.hasNext()) {
|
||||
ConsolePureSettings.noConsoleReadingReplacement = iterator.next()
|
||||
ConsoleTerminalSettings.noConsoleReadingReplacement = iterator.next()
|
||||
} else {
|
||||
println("Bad option `--reading-replacement`")
|
||||
println("Usage: --reading-replacement <string>")
|
||||
@ -129,7 +132,7 @@ object MiraiConsolePureLoader {
|
||||
}
|
||||
}
|
||||
"--safe-reading" -> {
|
||||
ConsolePureSettings.noConsoleSafeReading = true
|
||||
ConsoleTerminalSettings.noConsoleSafeReading = true
|
||||
}
|
||||
else -> {
|
||||
println("Unknown option `$option`")
|
||||
@ -140,13 +143,13 @@ object MiraiConsolePureLoader {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ConsolePureSettings.noConsole)
|
||||
if (ConsoleTerminalSettings.noConsole)
|
||||
SystemOutputPrintStream // Setup Output Channel
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@ConsoleExperimentalApi
|
||||
fun startAsDaemon(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
|
||||
fun startAsDaemon(instance: MiraiConsoleImplementationTerminal = MiraiConsoleImplementationTerminal()) {
|
||||
instance.start()
|
||||
overrideSTD()
|
||||
startupConsoleThread()
|
||||
@ -160,7 +163,7 @@ internal object ConsoleDataHolder : AutoSavePluginDataHolder,
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
override val dataHolderName: String
|
||||
get() = "Pure"
|
||||
get() = "Terminal"
|
||||
}
|
||||
|
||||
internal fun overrideSTD() {
|
||||
@ -181,12 +184,16 @@ internal fun overrideSTD() {
|
||||
}
|
||||
|
||||
|
||||
internal object ConsoleCommandSenderImplPure : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
||||
internal object ConsoleCommandSenderImplTerminal : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
||||
override suspend fun sendMessage(message: String) {
|
||||
kotlin.runCatching {
|
||||
lineReader.printAbove(message)
|
||||
}.onFailure {
|
||||
consoleLogger.error("Exception while ConsoleCommandSenderImplPure.sendMessage", it)
|
||||
}.onFailure { exception ->
|
||||
// If failed. It means JLine Terminal not working...
|
||||
PrintStream(FileOutputStream(FileDescriptor.err)).use {
|
||||
it.println("Exception while ConsoleCommandSenderImplTerminal.sendMessage")
|
||||
exception.printStackTrace(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,12 @@
|
||||
/*
|
||||
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
||||
*/
|
||||
@file:OptIn(ConsolePureExperimentalApi::class)
|
||||
@file:OptIn(ConsoleTerminalExperimentalApi::class)
|
||||
|
||||
package net.mamoe.mirai.console.pure.noconsole
|
||||
package net.mamoe.mirai.console.terminal.noconsole
|
||||
|
||||
import net.mamoe.mirai.console.pure.ConsolePureExperimentalApi
|
||||
import net.mamoe.mirai.console.pure.ConsolePureSettings
|
||||
import net.mamoe.mirai.console.terminal.ConsoleTerminalExperimentalApi
|
||||
import net.mamoe.mirai.console.terminal.ConsoleTerminalSettings
|
||||
import org.jline.keymap.KeyMap
|
||||
import org.jline.reader.*
|
||||
import org.jline.terminal.Attributes
|
||||
@ -69,8 +69,8 @@ internal object AllIgnoredOutputStream : OutputStream() {
|
||||
}
|
||||
|
||||
internal val SystemOutputPrintStream by lazy {
|
||||
@OptIn(ConsolePureExperimentalApi::class)
|
||||
if (ConsolePureSettings.setupAnsi) {
|
||||
@OptIn(ConsoleTerminalExperimentalApi::class)
|
||||
if (ConsoleTerminalSettings.setupAnsi) {
|
||||
org.fusesource.jansi.AnsiConsole.systemInstall()
|
||||
}
|
||||
System.out
|
||||
@ -81,16 +81,16 @@ internal object AllEmptyLineReader : LineReader {
|
||||
|
||||
override fun printAbove(str: String?) {
|
||||
if (str == null) return
|
||||
@OptIn(ConsolePureExperimentalApi::class)
|
||||
if (ConsolePureSettings.noAnsi) {
|
||||
@OptIn(ConsoleTerminalExperimentalApi::class)
|
||||
if (ConsoleTerminalSettings.noAnsi) {
|
||||
SystemOutputPrintStream.println(ANSI_REGEX.replace(str, ""))
|
||||
} else SystemOutputPrintStream.println(str)
|
||||
}
|
||||
|
||||
@OptIn(ConsolePureExperimentalApi::class)
|
||||
@OptIn(ConsoleTerminalExperimentalApi::class)
|
||||
override fun readLine(): String =
|
||||
if (ConsolePureSettings.noConsoleSafeReading) ConsolePureSettings.noConsoleReadingReplacement
|
||||
else error("Unsupported Reading line when console front-end closed.")
|
||||
if (ConsoleTerminalSettings.noConsoleSafeReading) ConsoleTerminalSettings.noConsoleReadingReplacement
|
||||
else throw EndOfFileException("Unsupported Reading line when console front-end closed.")
|
||||
|
||||
// region
|
||||
private fun <T> ignored(): T = error("Ignored")
|
@ -19,7 +19,7 @@ fun includeProject(projectPath: String, path: String? = null) {
|
||||
|
||||
includeProject(":mirai-console", "backend/mirai-console")
|
||||
includeProject(":mirai-console.codegen", "backend/codegen")
|
||||
includeProject(":mirai-console-pure", "frontend/mirai-console-pure")
|
||||
includeProject(":mirai-console-terminal", "frontend/mirai-console-terminal")
|
||||
includeProject(":mirai-console-compiler-common", "tools/compiler-common")
|
||||
includeProject(":mirai-console-intellij", "tools/intellij-plugin")
|
||||
includeProject(":mirai-console-gradle", "tools/gradle-plugin")
|
||||
|
@ -2,11 +2,11 @@ import net.mamoe.mirai.alsoLogin
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.load
|
||||
import net.mamoe.mirai.console.pure.MiraiConsolePureLoader
|
||||
import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
|
||||
import org.example.myplugin.MyPluginMain
|
||||
|
||||
suspend fun main() {
|
||||
MiraiConsolePureLoader.startAsDaemon()
|
||||
MiraiConsoleTerminalLoader.startAsDaemon()
|
||||
|
||||
MyPluginMain.load() // 主动加载插件, Console 会调用 MyPluginMain.onLoad
|
||||
MyPluginMain.enable() // 主动启用插件, Console 会调用 MyPluginMain.onEnable
|
||||
|
Loading…
Reference in New Issue
Block a user