From 4566403ececbc474b3b7974c8d8854f0b38225c8 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 4 Sep 2020 11:44:56 +0800 Subject: [PATCH] Use MessageChain for arguments for executing commands --- .../mamoe/mirai/console/command/Command.kt | 5 ++- .../console/command/CommandExecuteResult.kt | 7 ++-- .../mirai/console/command/CompositeCommand.kt | 5 ++- .../mamoe/mirai/console/command/RawCommand.kt | 6 +-- .../mirai/console/command/SimpleCommand.kt | 5 ++- .../mirai/console/command/java/JCommand.kt | 5 ++- .../mirai/console/command/java/JRawCommand.kt | 12 +++--- .../internal/command/CommandManagerImpl.kt | 4 +- .../command/CompositeCommandInternal.kt | 41 +++++++++---------- .../console/internal/command/internal.kt | 8 ++-- 10 files changed, 52 insertions(+), 46 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 133dbdd12..2dcca4945 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -16,6 +16,7 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.java.JCommand import net.mamoe.mirai.console.internal.command.isValidSubName +import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.SingleMessage /** @@ -71,7 +72,7 @@ public interface Command { * @see CommandManager.executeCommand 查看更多信息 */ @JvmBlockingBridge - public suspend fun CommandSender.onCommand(args: Array) + public suspend fun CommandSender.onCommand(args: MessageChain) public companion object { /** @@ -84,7 +85,7 @@ public interface Command { } @JvmSynthetic -public suspend inline fun Command.onCommand(sender: CommandSender, args: Array): Unit = +public suspend inline fun Command.onCommand(sender: CommandSender, args: MessageChain): Unit = sender.run { onCommand(args) } /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt index 8da372d5d..f2195ae3b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandExecuteResult.CommandExecuteStatus import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.MessageChain import kotlin.contracts.contract /** @@ -34,7 +35,7 @@ public sealed class CommandExecuteResult { public abstract val commandName: String? /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public abstract val args: Array? + public abstract val args: MessageChain? // abstract val to allow smart casting @@ -45,7 +46,7 @@ public sealed class CommandExecuteResult { /** 尝试执行的指令名 */ public override val commandName: String, /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public override val args: Array + public override val args: MessageChain ) : CommandExecuteResult() { /** 指令执行时发生的错误, 总是 `null` */ public override val exception: Nothing? get() = null @@ -63,7 +64,7 @@ public sealed class CommandExecuteResult { /** 尝试执行的指令名 */ public override val commandName: String, /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ - public override val args: Array + public override val args: MessageChain ) : CommandExecuteResult() { /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt index 3573258a0..da0008e7c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt @@ -21,6 +21,7 @@ import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import net.mamoe.mirai.message.data.MessageChain import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationTarget.FUNCTION import kotlin.reflect.KClass @@ -116,14 +117,14 @@ public abstract class CompositeCommand( @Target(AnnotationTarget.VALUE_PARAMETER) protected annotation class Name(val value: String) - public final override suspend fun CommandSender.onCommand(args: Array) { + public final override suspend fun CommandSender.onCommand(args: MessageChain) { matchSubCommand(args)?.parseAndExecute(this, args, true) ?: kotlin.run { defaultSubCommand.onCommand(this, args) } } - protected override suspend fun CommandSender.onDefault(rawArgs: Array) { + protected override suspend fun CommandSender.onDefault(rawArgs: MessageChain) { sendMessage(usage) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt index e340aa8eb..57a436a4f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt @@ -14,7 +14,7 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.java.JRawCommand -import net.mamoe.mirai.message.data.SingleMessage +import net.mamoe.mirai.message.data.MessageChain /** * 无参数解析, 接收原生参数的指令. @@ -47,11 +47,11 @@ public abstract class RawCommand( /** * 在指令被执行时调用. * - * @param args 指令参数. 数组元素类型可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割. + * @param args 指令参数. * * @see CommandManager.execute 查看更多信息 */ - public abstract override suspend fun CommandSender.onCommand(args: Array) + public abstract override suspend fun CommandSender.onCommand(args: MessageChain) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt index 2529b701c..12c4a16a2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt @@ -22,6 +22,7 @@ import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.java.JSimpleCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver +import net.mamoe.mirai.message.data.MessageChain /** * 简单的, 支持参数自动解析的指令. @@ -71,7 +72,7 @@ public abstract class SimpleCommand( */ public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext - public final override suspend fun CommandSender.onCommand(args: Array) { + public final override suspend fun CommandSender.onCommand(args: MessageChain) { subCommands.single().parseAndExecute(this, args, false) } @@ -81,7 +82,7 @@ public abstract class SimpleCommand( } @Deprecated("prohibited", level = DeprecationLevel.HIDDEN) - internal override suspend fun CommandSender.onDefault(rawArgs: Array) { + internal override suspend fun CommandSender.onDefault(rawArgs: MessageChain) { sendMessage(usage) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt index 98b9a1ffc..38d3d2c0f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCommand.kt @@ -15,6 +15,7 @@ import net.mamoe.mirai.console.command.Command import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandSender +import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.SingleMessage /** @@ -25,7 +26,7 @@ import net.mamoe.mirai.message.data.SingleMessage * @see Command */ public interface JCommand : Command { - public override suspend fun CommandSender.onCommand(args: Array) { + public override suspend fun CommandSender.onCommand(args: MessageChain) { withContext(Dispatchers.IO) { onCommand(this@onCommand, args) } } @@ -36,5 +37,5 @@ public interface JCommand : Command { * * @see CommandManager.executeCommand 查看更多信息 */ - public fun onCommand(sender: CommandSender, args: Array) // overrides bridge + public fun onCommand(sender: CommandSender, args: MessageChain) // overrides bridge } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt index 0c84e3700..d3b3fb87a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt @@ -11,10 +11,10 @@ package net.mamoe.mirai.console.command.java import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import net.mamoe.mirai.console.command.Command -import net.mamoe.mirai.console.command.CommandOwner -import net.mamoe.mirai.console.command.CommandPermission -import net.mamoe.mirai.console.command.CommandSender +import net.mamoe.mirai.console.command.* +import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.SingleMessage /** * 供 Java 用户继承 @@ -76,9 +76,9 @@ public abstract class JRawCommand( */ @Suppress("INAPPLICABLE_JVM_NAME") @JvmName("onCommand") - public abstract fun onCommand(sender: CommandSender, args: Array) + public abstract fun onCommand(sender: CommandSender, args: MessageChain) - public final override suspend fun CommandSender.onCommand(args: Array) { + public final override suspend fun CommandSender.onCommand(args: MessageChain) { withContext(Dispatchers.IO) { onCommand(this@onCommand, args) } } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt index 78b3820da..7fde28a03 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt @@ -147,7 +147,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by Coroutine ): CommandExecuteResult { return sender.executeCommandInternal( this, - arguments.flattenCommandComponents().toTypedArray(), + arguments.flattenCommandComponents(), primaryName, checkPermission ) @@ -160,7 +160,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by Coroutine ): CommandExecuteResult { return sender.executeCommandInternal( this, - arguments.flattenCommandComponents().toTypedArray(), + arguments.flattenCommandComponents(), primaryName, checkPermission ) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index f0f789a56..5aa9a1486 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -15,8 +15,10 @@ import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContextAware +import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.SingleMessage +import net.mamoe.mirai.message.data.buildMessageChain import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KClass import kotlin.reflect.KFunction @@ -65,13 +67,13 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( return _usage } - abstract suspend fun CommandSender.onDefault(rawArgs: Array) + abstract suspend fun CommandSender.onDefault(rawArgs: MessageChain) internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { DefaultSubCommandDescriptor( "", permission, - onCommand = { sender: CommandSender, args: Array -> + onCommand = { sender: CommandSender, args: MessageChain -> sender.onDefault(args) } ) @@ -116,7 +118,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( internal class DefaultSubCommandDescriptor( val description: String, val permission: CommandPermission, - val onCommand: suspend (sender: CommandSender, rawArgs: Array) -> Unit + val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit ) internal inner class SubCommandDescriptor( @@ -130,7 +132,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( val usage: String = createUsage(this@AbstractReflectionCommand) internal suspend fun parseAndExecute( sender: CommandSender, - argsWithSubCommandNameNotRemoved: Array, + argsWithSubCommandNameNotRemoved: MessageChain, removeSubName: Boolean ) { val args = parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) names.size else 0) @@ -145,7 +147,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( @JvmField internal val bakedSubNames: Array> = names.map { it.bakeSubName() }.toTypedArray() - private fun parseArgs(sender: CommandSender, rawArgs: Array, offset: Int): Array? { + private fun parseArgs(sender: CommandSender, rawArgs: MessageChain, offset: Int): Array? { 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}" } @@ -154,9 +156,8 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( val param = params[index] val rawArg = rawArgs[offset + index] when (rawArg) { - is String -> context[param.type]?.parse(rawArg, sender) - is SingleMessage -> context[param.type]?.parse(rawArg, sender) - else -> throw IllegalArgumentException("Illegal argument type: ${rawArg::class.qualifiedName}") + is PlainText -> context[param.type]?.parse(rawArg.content, sender) + else -> context[param.type]?.parse(rawArg, sender) } ?: error("Cannot find a parser for $rawArg") } } @@ -165,7 +166,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( /** * @param rawArgs 元素类型必须为 [SingleMessage] 或 [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException] */ - internal fun matchSubCommand(rawArgs: Array): SubCommandDescriptor? { + internal fun matchSubCommand(rawArgs: MessageChain): SubCommandDescriptor? { val maxCount = rawArgs.size var cur = 0 bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) -> @@ -180,7 +181,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( } } -internal fun Array.contentEqualsOffset(other: Array, length: Int): Boolean { +internal fun Array.contentEqualsOffset(other: MessageChain, length: Int): Boolean { repeat(length) { index -> if (!other[index].toString().equals(this[index].toString(), ignoreCase = true)) { return false @@ -193,18 +194,16 @@ internal val ILLEGAL_SUB_NAME_CHARS = "\\/!@#$%^&*()_+-={}[];':\",.<>?`~".toChar internal fun String.isValidSubName(): Boolean = ILLEGAL_SUB_NAME_CHARS.none { it in this } internal fun String.bakeSubName(): Array = split(' ').filterNot { it.isBlank() }.toTypedArray() -internal fun Any.flattenCommandComponents(): ArrayList { - val list = ArrayList() - when (this) { - is PlainText -> this.content.splitToSequence(' ').filterNot { it.isBlank() } - .forEach { list.add(it) } - is CharSequence -> this.splitToSequence(' ').filterNot { it.isBlank() }.forEach { list.add(it) } - is SingleMessage -> list.add(this) - is Array<*> -> this.forEach { if (it != null) list.addAll(it.flattenCommandComponents()) } - is Iterable<*> -> this.forEach { if (it != null) list.addAll(it.flattenCommandComponents()) } - else -> list.add(this.toString()) +internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain { + when (this@flattenCommandComponents) { + is PlainText -> this@flattenCommandComponents.content.splitToSequence(' ').filterNot { it.isBlank() } + .forEach { +it } + is CharSequence -> this@flattenCommandComponents.splitToSequence(' ').filterNot { it.isBlank() }.forEach { +it } + is SingleMessage -> +(this@flattenCommandComponents) + is Array<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) } + is Iterable<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) } + else -> add(this@flattenCommandComponents.toString()) } - return list } internal inline fun KAnnotatedElement.hasAnnotation(): Boolean = diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt index 60d03f8d9..cd8ef90a9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt @@ -12,6 +12,8 @@ package net.mamoe.mirai.console.internal.command import net.mamoe.mirai.console.command.* import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.asMessageChain import kotlin.math.max import kotlin.math.min @@ -144,7 +146,7 @@ internal inline fun List.dropToTypedArray(n: Int): Array = Arr @Throws(CommandExecutionException::class) internal suspend fun CommandSender.executeCommandInternal( command: Command, - args: Array, + args: MessageChain, commandName: String, checkPermission: Boolean ): CommandExecuteResult { @@ -182,7 +184,7 @@ internal suspend fun CommandSender.executeCommandInternal( ): CommandExecuteResult { val command = CommandManagerImpl.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName) - val args = messages.flattenCommandComponents().dropToTypedArray(1) + val args = messages.flattenCommandComponents() - return executeCommandInternal(command, args, commandName, checkPermission) + return executeCommandInternal(command, args.drop(1).asMessageChain(), commandName, checkPermission) }