Use MessageChain for arguments for executing commands

This commit is contained in:
Him188 2020-09-04 11:44:56 +08:00
parent be9b0293bd
commit 4566403ece
10 changed files with 52 additions and 46 deletions

View File

@ -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<out Any>)
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<out Any>): Unit =
public suspend inline fun Command.onCommand(sender: CommandSender, args: MessageChain): Unit =
sender.run { onCommand(args) }
/**

View File

@ -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<out Any>?
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<out Any>
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<out Any>
public override val args: MessageChain
) : CommandExecuteResult() {
/** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */
public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION

View File

@ -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<out Any>) {
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<out Any>) {
protected override suspend fun CommandSender.onDefault(rawArgs: MessageChain) {
sendMessage(usage)
}

View File

@ -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<out Any>)
public abstract override suspend fun CommandSender.onCommand(args: MessageChain)
}

View File

@ -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<out Any>) {
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<out Any>) {
internal override suspend fun CommandSender.onDefault(rawArgs: MessageChain) {
sendMessage(usage)
}

View File

@ -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<out Any>) {
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<out Any>) // overrides bridge
public fun onCommand(sender: CommandSender, args: MessageChain) // overrides bridge
}

View File

@ -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<out Any>)
public abstract fun onCommand(sender: CommandSender, args: MessageChain)
public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
public final override suspend fun CommandSender.onCommand(args: MessageChain) {
withContext(Dispatchers.IO) { onCommand(this@onCommand, args) }
}
}

View File

@ -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
)

View File

@ -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<out Any>)
abstract suspend fun CommandSender.onDefault(rawArgs: MessageChain)
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
DefaultSubCommandDescriptor(
"",
permission,
onCommand = { sender: CommandSender, args: Array<out Any> ->
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<out Any>) -> 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<out Any>,
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<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: MessageChain, offset: Int): Array<out Any>? {
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<out Any>): 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 <T> Array<T>.contentEqualsOffset(other: Array<out Any>, length: Int): Boolean {
internal fun <T> Array<T>.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<String> = split(' ').filterNot { it.isBlank() }.toTypedArray()
internal fun Any.flattenCommandComponents(): ArrayList<Any> {
val list = ArrayList<Any>()
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 <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =

View File

@ -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 <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Arr
@Throws(CommandExecutionException::class)
internal suspend fun CommandSender.executeCommandInternal(
command: Command,
args: Array<out Any>,
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)
}