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 f9b568763..49d664c51 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 @@ -11,17 +11,15 @@ package net.mamoe.mirai.console.command +import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.description.CommandParam -import net.mamoe.mirai.console.command.description.CommandParserContext -import net.mamoe.mirai.console.command.description.EmptyCommandParserContext -import net.mamoe.mirai.console.command.description.plus -import net.mamoe.mirai.console.plugins.MyArg +import net.mamoe.mirai.console.command.hasAnnotation import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.SingleMessage +import java.lang.Exception import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KClass -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.* /** * 指令 @@ -30,6 +28,9 @@ import kotlin.reflect.full.findAnnotation */ interface Command { val names: Array + + fun getPrimaryName():String = names[0] + val usage: String val description: String val permission: CommandPermission @@ -57,6 +58,10 @@ abstract class CompositeCommand @JvmOverloads constructor( override val prefixOptional: Boolean = false, overrideContext: CommandParserContext = EmptyCommandParserContext ) : Command { + + class IllegalParameterException(message:String): Exception(message) + + override val names: Array = names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).toTypedArray() val context: CommandParserContext = CommandParserContext.Builtins + overrideContext @@ -70,7 +75,7 @@ abstract class CompositeCommand @JvmOverloads constructor( /** 标记一个函数为子指令 */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) - annotation class SubCommand(val name: String) + annotation class SubCommand(vararg val name: String) /** 指令描述 */ @Retention(AnnotationRetention.RUNTIME) @@ -102,14 +107,54 @@ abstract class CompositeCommand @JvmOverloads constructor( internal val subCommands: Array by lazy { this::class.declaredFunctions.filter { it.hasAnnotation() }.map { function -> + + val overridePermission = function.findAnnotation()//optional + + val subDescription = function.findAnnotation()?.description + if(subDescription == null && owner is PluginCommandOwner){ + (owner as PluginCommandOwner).plugin.logger.warning("A description for sub commend is recommend for command " + this.getPrimaryName()) + } + + if((function.returnType.classifier as? KClass<*>)?.isSubclassOf(Boolean::class) != true){ + throw IllegalParameterException("Return Type of SubCommand must be Boolean") + } + + val parameter = function.parameters + if ( + parameter.isEmpty() || + parameter[0].isVararg || + ((parameter[0].type.classifier as? KClass<*>)?.isSubclassOf(CommandSender::class)!=true) + ){ + throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.getPrimaryName() + " should be ") + } + + val commandName = function.findAnnotation()!!.name.map { + if(!it.isValidSubName()){ + error("SubName $it is not valid") + } + it + }.toTypedArray() + + //map parameter + val parms = parameter.subList(1,parameter.size).map { + if(it.isVararg){ + throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.getPrimaryName() + " should not be var arg") + } + if(it.isOptional){ + throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.getPrimaryName() + " should not be var optional") + } + CommandParam( + it.findAnnotation()?.name?:it.name?:"unknown", + (parameter[0].type.classifier as? KClass<*>)?: throw IllegalParameterException("unsolved type reference from param " + it.name + " in " + function.name + " from " + this.getPrimaryName())) + }.toTypedArray() + SubCommandDescriptor( - arrayOf(function.name), - arrayOf(CommandParam("p", MyArg::class)), - "", - CommandPermission.Default, + commandName, + parms, + subDescription?:"unknown", + overridePermission?.permission?.getInstance() ?: permission, onCommand = block { sender: CommandSender, args: Array -> - println("subname finally gor args: ${args.joinToString()}") - true + function.callSuspend(sender,*args) as Boolean } ) }.toTypedArray() @@ -234,3 +279,7 @@ internal fun Any.flattenCommandComponents(): ArrayList { internal inline fun KAnnotatedElement.hasAnnotation(): Boolean = findAnnotation() != null + +internal inline fun KClass.getInstance():T { + return this.objectInstance ?: this.createInstance() +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt index 63facd067..15d6dd4dc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt @@ -13,7 +13,7 @@ sealed class CommandOwner object TestCommandOwner : CommandOwner() -abstract class PluginCommandOwner(plugin: PluginBase) : CommandOwner() +abstract class PluginCommandOwner(val plugin: PluginBase) : CommandOwner() // 由前端实现 internal abstract class ConsoleCommandOwner : CommandOwner()