mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Fix builtin commands and frontend-pure command processing
This commit is contained in:
parent
58eac01cad
commit
56a01c23fe
@ -9,13 +9,20 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.alsoLogin
|
import net.mamoe.mirai.alsoLogin
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.job
|
import net.mamoe.mirai.console.job
|
||||||
import net.mamoe.mirai.console.stacktraceString
|
import net.mamoe.mirai.console.stacktraceString
|
||||||
import net.mamoe.mirai.event.selectMessagesUnit
|
import net.mamoe.mirai.event.selectMessagesUnit
|
||||||
|
import net.mamoe.mirai.utils.DirectoryLogger
|
||||||
|
import net.mamoe.mirai.utils.weeksToMillis
|
||||||
|
import java.io.File
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@ -24,15 +31,44 @@ import kotlin.system.exitProcess
|
|||||||
*/
|
*/
|
||||||
fun MiraiConsole.addBot(id: Long, password: String): Bot {
|
fun MiraiConsole.addBot(id: Long, password: String): Bot {
|
||||||
return Bot(id, password) {
|
return Bot(id, password) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重定向 [网络日志][networkLoggerSupplier] 到指定目录. 若目录不存在将会自动创建 ([File.mkdirs])
|
||||||
|
* @see DirectoryLogger
|
||||||
|
* @see redirectNetworkLogToDirectory
|
||||||
|
*/
|
||||||
|
fun redirectNetworkLogToDirectory(
|
||||||
|
dir: File = File("logs"),
|
||||||
|
retain: Long = 1.weeksToMillis,
|
||||||
|
identity: (bot: Bot) -> String = { "Net ${it.id}" }
|
||||||
|
) {
|
||||||
|
require(!dir.isFile) { "dir must not be a file" }
|
||||||
|
dir.mkdirs()
|
||||||
|
networkLoggerSupplier = { DirectoryLogger(identity(it), dir, retain) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun redirectBotLogToDirectory(
|
||||||
|
dir: File = File("logs"),
|
||||||
|
retain: Long = 1.weeksToMillis,
|
||||||
|
identity: (bot: Bot) -> String = { "Net ${it.id}" }
|
||||||
|
) {
|
||||||
|
require(!dir.isFile) { "dir must not be a file" }
|
||||||
|
dir.mkdirs()
|
||||||
|
botLoggerSupplier = { DirectoryLogger(identity(it), dir, retain) }
|
||||||
|
}
|
||||||
|
|
||||||
fileBasedDeviceInfo()
|
fileBasedDeviceInfo()
|
||||||
this.loginSolver = this@addBot.frontEnd.createLoginSolver()
|
this.loginSolver = this@addBot.frontEnd.createLoginSolver()
|
||||||
redirectNetworkLogToDirectory()
|
redirectNetworkLogToDirectory()
|
||||||
redirectBotLogToDirectory()
|
// redirectBotLogToDirectory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BuiltInCommand : Command
|
@Suppress("EXPOSED_SUPER_INTERFACE")
|
||||||
|
interface BuiltInCommand : Command, BuiltInCommandInternal
|
||||||
|
|
||||||
|
// for identification
|
||||||
|
internal interface BuiltInCommandInternal : Command
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object BuiltInCommands {
|
object BuiltInCommands {
|
||||||
@ -50,7 +86,7 @@ object BuiltInCommands {
|
|||||||
object Help : SimpleCommand(
|
object Help : SimpleCommand(
|
||||||
ConsoleCommandOwner, "help",
|
ConsoleCommandOwner, "help",
|
||||||
description = "Gets help about the console."
|
description = "Gets help about the console."
|
||||||
) {
|
), BuiltInCommand {
|
||||||
init {
|
init {
|
||||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||||
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
|
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
|
||||||
@ -67,18 +103,23 @@ object BuiltInCommands {
|
|||||||
object Stop : SimpleCommand(
|
object Stop : SimpleCommand(
|
||||||
ConsoleCommandOwner, "stop", "shutdown", "exit",
|
ConsoleCommandOwner, "stop", "shutdown", "exit",
|
||||||
description = "Stop the whole world."
|
description = "Stop the whole world."
|
||||||
) {
|
), BuiltInCommand {
|
||||||
init {
|
init {
|
||||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||||
|
if (!MiraiConsole.isActive) {
|
||||||
|
return@thread
|
||||||
|
}
|
||||||
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
|
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val closingLock = Mutex()
|
||||||
|
|
||||||
@Handler
|
@Handler
|
||||||
suspend fun CommandSender.handle() {
|
suspend fun CommandSender.handle(): Unit = closingLock.withLock {
|
||||||
sendMessage("Stopping mirai-console")
|
sendMessage("Stopping mirai-console")
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
MiraiConsole.job.cancel()
|
MiraiConsole.job.cancelAndJoin()
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { sendMessage("mirai-console stopped successfully.") },
|
onSuccess = { sendMessage("mirai-console stopped successfully.") },
|
||||||
onFailure = {
|
onFailure = {
|
||||||
@ -93,16 +134,16 @@ object BuiltInCommands {
|
|||||||
object Login : SimpleCommand(
|
object Login : SimpleCommand(
|
||||||
ConsoleCommandOwner, "login",
|
ConsoleCommandOwner, "login",
|
||||||
description = "Log in a bot account."
|
description = "Log in a bot account."
|
||||||
) {
|
), BuiltInCommand {
|
||||||
@Handler
|
@Handler
|
||||||
suspend fun CommandSender.handle(id: Long, password: String) {
|
suspend fun CommandSender.handle(id: Long, password: String) {
|
||||||
sendMessage(
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
MiraiConsole.addBot(id, password).alsoLogin()
|
MiraiConsole.addBot(id, password).alsoLogin()
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { "${it.nick} ($id) Login succeed" },
|
onSuccess = { sendMessage("${it.nick} ($id) Login succeed") },
|
||||||
onFailure = { throwable ->
|
onFailure = { throwable ->
|
||||||
"Login failed: ${throwable.localizedMessage ?: throwable.message}" +
|
sendMessage("Login failed: ${throwable.localizedMessage ?: throwable.message ?: throwable.toString()}" +
|
||||||
if (this is MessageEventContextAware<*>) {
|
if (this is MessageEventContextAware<*>) {
|
||||||
this.fromEvent.selectMessagesUnit {
|
this.fromEvent.selectMessagesUnit {
|
||||||
"stacktrace" reply {
|
"stacktrace" reply {
|
||||||
@ -110,10 +151,11 @@ object BuiltInCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"test"
|
"test"
|
||||||
} else ""
|
} else "")
|
||||||
|
|
||||||
|
throw throwable
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ abstract class SimpleCommand @JvmOverloads constructor(
|
|||||||
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
|
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
|
||||||
CommandParserContextAware {
|
CommandParserContextAware {
|
||||||
|
|
||||||
|
override val usage: String
|
||||||
|
get() = super.usage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标注指令处理器
|
* 标注指令处理器
|
||||||
*/
|
*/
|
||||||
@ -47,7 +50,7 @@ abstract class SimpleCommand @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
|
@Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
|
||||||
final override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = error("shouldn't be reached")
|
override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = sendMessage(usage)
|
||||||
|
|
||||||
final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
|
||||||
subCommands.single().parseAndExecute(this, args, false)
|
subCommands.single().parseAndExecute(this, args, false)
|
||||||
|
@ -54,7 +54,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
|
|||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
internal var _usage: String = "<not yet initialized>"
|
internal var _usage: String = "<not yet initialized>"
|
||||||
|
|
||||||
final override val usage: String // initialized by subCommand reflection
|
override val usage: String // initialized by subCommand reflection
|
||||||
get() = _usage
|
get() = _usage
|
||||||
|
|
||||||
abstract suspend fun CommandSender.onDefault(rawArgs: Array<out Any>)
|
abstract suspend fun CommandSender.onDefault(rawArgs: Array<out Any>)
|
||||||
@ -125,9 +125,10 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
|
|||||||
argsWithSubCommandNameNotRemoved: Array<out Any>,
|
argsWithSubCommandNameNotRemoved: Array<out Any>,
|
||||||
removeSubName: Boolean
|
removeSubName: Boolean
|
||||||
) {
|
) {
|
||||||
if (!onCommand(
|
val args = parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) names.size else 0)
|
||||||
|
if (args == null || !onCommand(
|
||||||
sender,
|
sender,
|
||||||
parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) names.size else 0)
|
args
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
sender.sendMessage(usage)
|
sender.sendMessage(usage)
|
||||||
@ -136,8 +137,10 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
|
|||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
|
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
|
||||||
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): Array<out Any> {
|
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): Array<out Any>? {
|
||||||
require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size - offset}" }
|
if (rawArgs.size < offset + this.params.size)
|
||||||
|
return null
|
||||||
|
//require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size - offset}" }
|
||||||
|
|
||||||
return Array(this.params.size) { index ->
|
return Array(this.params.size) { index ->
|
||||||
val param = params[index]
|
val param = params[index]
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
const val core = "1.1-EA"
|
const val core = "1.0.3"
|
||||||
const val console = "1.0-dev-1"
|
const val console = "1.0-dev-1"
|
||||||
const val consoleGraphical = "0.0.7"
|
const val consoleGraphical = "0.0.7"
|
||||||
const val consoleTerminal = "0.1.0"
|
const val consoleTerminal = "0.1.0"
|
||||||
|
@ -32,6 +32,7 @@ dependencies {
|
|||||||
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
|
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
|
||||||
compileAndRuntime(kotlin("stdlib")) // embedded by core
|
compileAndRuntime(kotlin("stdlib")) // embedded by core
|
||||||
|
|
||||||
|
runtimeOnly("net.mamoe:mirai-core-qqandroid:${Versions.core}")
|
||||||
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
|
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
|
||||||
testApi(project(":mirai-console"))
|
testApi(project(":mirai-console"))
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,12 @@ package net.mamoe.mirai.console.pure
|
|||||||
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.command.CommandExecuteStatus
|
import net.mamoe.mirai.console.command.*
|
||||||
import net.mamoe.mirai.console.command.CommandPrefix
|
|
||||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
|
||||||
import net.mamoe.mirai.console.command.executeCommandDetailed
|
|
||||||
import net.mamoe.mirai.console.job
|
import net.mamoe.mirai.console.job
|
||||||
import net.mamoe.mirai.console.pure.MiraiConsolePure.Companion.start
|
import net.mamoe.mirai.console.pure.MiraiConsolePure.Companion.start
|
||||||
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
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.message.data.content
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
@ -50,14 +48,19 @@ internal fun startup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun startConsoleThread() {
|
internal fun startConsoleThread() {
|
||||||
thread(name = "Console", isDaemon = false) {
|
thread(name = "Console Input") {
|
||||||
val consoleLogger = DefaultLogger("Console")
|
val consoleLogger = DefaultLogger("Console")
|
||||||
|
try {
|
||||||
kotlinx.coroutines.runBlocking {
|
kotlinx.coroutines.runBlocking {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val next = MiraiConsoleFrontEndPure.requestInput("").let {
|
val next = MiraiConsoleFrontEndPure.requestInput("").let {
|
||||||
if (it.startsWith(CommandPrefix)) {
|
when {
|
||||||
|
it.startsWith(CommandPrefix) -> {
|
||||||
it
|
it
|
||||||
} else CommandPrefix + it
|
}
|
||||||
|
it == "?" -> CommandPrefix + BuiltInCommands.Help.primaryName
|
||||||
|
else -> CommandPrefix + it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (next.isBlank()) {
|
if (next.isBlank()) {
|
||||||
continue
|
continue
|
||||||
@ -68,6 +71,7 @@ internal fun startConsoleThread() {
|
|||||||
CommandExecuteStatus.SUCCESSFUL -> {
|
CommandExecuteStatus.SUCCESSFUL -> {
|
||||||
}
|
}
|
||||||
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
||||||
|
result.exception?.printStackTrace()
|
||||||
}
|
}
|
||||||
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
||||||
consoleLogger.warning("Unknown command: ${result.commandName}")
|
consoleLogger.warning("Unknown command: ${result.commandName}")
|
||||||
@ -75,18 +79,32 @@ internal fun startConsoleThread() {
|
|||||||
CommandExecuteStatus.PERMISSION_DENIED -> {
|
CommandExecuteStatus.PERMISSION_DENIED -> {
|
||||||
consoleLogger.warning("Permission denied.")
|
consoleLogger.warning("Permission denied.")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
return@thread
|
||||||
|
}
|
||||||
}.let { thread ->
|
}.let { thread ->
|
||||||
MiraiConsole.job.invokeOnCompletion {
|
MiraiConsole.job.invokeOnCompletion {
|
||||||
|
runCatching {
|
||||||
thread.interrupt()
|
thread.interrupt()
|
||||||
|
}.exceptionOrNull()?.printStackTrace()
|
||||||
|
runCatching {
|
||||||
|
ConsoleUtils.terminal.close()
|
||||||
|
}.exceptionOrNull()?.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object ConsoleCommandSenderImpl : ConsoleCommandSender() {
|
internal object ConsoleCommandSenderImpl : ConsoleCommandSender() {
|
||||||
override suspend fun sendMessage(message: Message) {
|
override suspend fun sendMessage(message: Message) {
|
||||||
|
kotlin.runCatching {
|
||||||
ConsoleUtils.lineReader.printAbove(message.contentToString())
|
ConsoleUtils.lineReader.printAbove(message.contentToString())
|
||||||
|
}.onFailure {
|
||||||
|
println(message.content)
|
||||||
|
it.printStackTrace()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user