diff --git a/mirai-console/backend/mirai-console/compatibility-validation/jvm/api/jvm.api b/mirai-console/backend/mirai-console/compatibility-validation/jvm/api/jvm.api index 855a8f1b5..4467a910d 100644 --- a/mirai-console/backend/mirai-console/compatibility-validation/jvm/api/jvm.api +++ b/mirai-console/backend/mirai-console/compatibility-validation/jvm/api/jvm.api @@ -384,7 +384,7 @@ public abstract interface class net/mamoe/mirai/console/command/CommandSenderOnM public abstract class net/mamoe/mirai/console/command/CompositeCommand : net/mamoe/mirai/console/command/AbstractCommand, net/mamoe/mirai/console/command/Command, net/mamoe/mirai/console/command/descriptor/CommandArgumentContextAware { public fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext;)V public synthetic fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getContext ()Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext; + public fun getContext ()Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext; public final fun getOverloads ()Ljava/util/List; public fun getUsage ()Ljava/lang/String; } @@ -1107,6 +1107,8 @@ public abstract class net/mamoe/mirai/console/command/java/JCompositeCommand : n public fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;)V public fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;)V public synthetic fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + protected final fun addArgumentContext (Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext;)V + public final fun getContext ()Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext; public final fun getDescription ()Ljava/lang/String; public final fun getPermission ()Lnet/mamoe/mirai/console/permission/Permission; public final fun getPrefixOptional ()Z @@ -1139,6 +1141,7 @@ public abstract class net/mamoe/mirai/console/command/java/JSimpleCommand : net/ public fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;)V public fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;)V public synthetic fun (Lnet/mamoe/mirai/console/command/CommandOwner;Ljava/lang/String;[Ljava/lang/String;Lnet/mamoe/mirai/console/permission/Permission;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + protected final fun addArgumentContext (Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext;)V public fun getContext ()Lnet/mamoe/mirai/console/command/descriptor/CommandArgumentContext; public fun getDescription ()Ljava/lang/String; public fun getPermission ()Lnet/mamoe/mirai/console/permission/Permission; diff --git a/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt b/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt index f14ac0bae..023e13843 100644 --- a/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt +++ b/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt @@ -21,6 +21,7 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationTarget.FUNCTION + /** * 复合指令. 指令注册时候会通过反射构造指令解析器. * @@ -68,8 +69,8 @@ import kotlin.annotation.AnnotationTarget.FUNCTION * * @SubCommand * suspend fun CommandContext.repeat() { - * // 使用 CommandContext 作为参数, - * sendMessage("/manage list 被调用了") + * // 使用 CommandContext 作为参数,可以获得触发指令的原消息链 originalMessage,其中包含 MessageMetadata。 + * sender.sendMessage(originalMessage) * } * * // 支持 Image 类型, 需在聊天中执行此指令. @@ -111,9 +112,9 @@ public abstract class CompositeCommand( } /** - * [CommandValueArgumentParser] 的环境 - */ - public final override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext + * 智能参数解析环境 + */ // open since 2.12 + public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext /** * 标记一个函数为子指令, 当 [value] 为空时使用函数名. diff --git a/mirai-console/backend/mirai-console/src/command/java/JCompositeCommand.kt b/mirai-console/backend/mirai-console/src/command/java/JCompositeCommand.kt index bbeb28bb6..f9dfdac14 100644 --- a/mirai-console/backend/mirai-console/src/command/java/JCompositeCommand.kt +++ b/mirai-console/backend/mirai-console/src/command/java/JCompositeCommand.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.command.java @@ -13,8 +13,10 @@ import net.mamoe.mirai.console.command.BuiltInCommands import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandOwner import net.mamoe.mirai.console.command.CompositeCommand +import net.mamoe.mirai.console.command.descriptor.CommandArgumentContext import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext +import net.mamoe.mirai.console.command.descriptor.plus import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER @@ -24,7 +26,7 @@ import net.mamoe.mirai.console.permission.Permission * 复合指令. 指令注册时候会通过反射构造指令解析器. * * 示例: - * ``` + * ```java * public final class MyCompositeCommand extends CompositeCommand { * public static final MyCompositeCommand INSTANCE = new MyCompositeCommand(); * @@ -52,12 +54,18 @@ import net.mamoe.mirai.console.permission.Permission * result = ExceptionsKt.stackTraceToString(e); * } * - * sender.sendMessage("结果: " + result) + * sender.sendMessage("结果: " + result); * } * * @SubCommand * public void list(CommandSender sender) { // 执行 "/manage list" 时调用这个方法 - * sender.sendMessage("/manage list 被调用了") + * sender.sendMessage("/manage list 被调用了"); + * } + * + * @SubCommand + * public void repeat(CommandContext context) { + * // 使用 CommandContext 作为参数,可以获得触发指令的原消息链 originalMessage,其中包含 MessageMetadata。 + * context.getSender().sendMessage(context.getOriginalMessage()); * } * * // 支持 Image 类型, 需在聊天中执行此指令. @@ -91,4 +99,18 @@ public abstract class JCompositeCommand public final override var prefixOptional: Boolean = false protected set + /** + * 智能参数解析环境 + * @since 2.12 + */ + public final override var context: CommandArgumentContext = super.context + private set + + /** + * 增加智能参数解析环境 + * @since 2.12 + */ + protected open fun addArgumentContext(context: CommandArgumentContext) { + this.context += context + } } \ No newline at end of file diff --git a/mirai-console/backend/mirai-console/src/command/java/JSimpleCommand.kt b/mirai-console/backend/mirai-console/src/command/java/JSimpleCommand.kt index 8d98ee863..73abf14af 100644 --- a/mirai-console/backend/mirai-console/src/command/java/JSimpleCommand.kt +++ b/mirai-console/backend/mirai-console/src/command/java/JSimpleCommand.kt @@ -1,19 +1,21 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.command.java +import net.mamoe.mirai.console.command.CommandContext import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandOwner import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.command.descriptor.CommandArgumentContext import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors +import net.mamoe.mirai.console.command.descriptor.plus import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER @@ -25,21 +27,23 @@ import net.mamoe.mirai.console.permission.Permission * public final class MySimpleCommand extends JSimpleCommand { * public static final MySimpleCommand INSTANCE = new MySimpleCommand(); * private MySimpleCommand() { - * super(MyPlugin.INSTANCE, "tell") + * super(MyPlugin.INSTANCE, "tell"); * // 可选设置如下属性 - * setDescription("这是一个测试指令") - * setUsage("/tell ") // 如不设置则自动根据带有 @Handler 的方法生成 - * setPermission(CommandPermission.Operator.INSTANCE) - * setPrefixOptional(true) + * setDescription("这是一个测试指令"); + * setUsage("/tell "); // 如不设置则自动根据带有 @Handler 的方法生成 + * setPermission(CommandPermission.Operator.INSTANCE); + * setPrefixOptional(true); * } * * @Handler * public void onCommand(CommandSender sender, User target, String message) { - * target.sendMessage(message) + * target.sendMessage(message); * } * } * ``` * + * 其中 `CommandSender` 也可以替换为 `CommandContext`,可通过 [CommandContext.originalMessage] 获得触发指令的原消息链。 + * * @see SimpleCommand * @see [CommandManager.executeCommand] */ @@ -57,6 +61,18 @@ public abstract class JSimpleCommand @JvmOverloads constructor( @ExperimentalCommandDescriptors public override var prefixOptional: Boolean = super.prefixOptional protected set + + /** + * 指令参数环境. + */ public override var context: CommandArgumentContext = super.context protected set + + /** + * 增加智能参数解析环境 + * @since 2.12 + */ + protected fun addArgumentContext(context: CommandArgumentContext) { + this.context += context + } } \ No newline at end of file diff --git a/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt b/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt new file mode 100644 index 000000000..199fb23e6 --- /dev/null +++ b/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:OptIn(ExperimentalCommandDescriptors::class) + +package net.mamoe.mirai.console.command + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.console.Testing +import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException +import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser +import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors +import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext +import net.mamoe.mirai.console.command.java.JCompositeCommand +import net.mamoe.mirai.console.command.java.JSimpleCommand +import net.mamoe.mirai.console.internal.data.classifierAsKClass +import net.mamoe.mirai.message.data.Image +import net.mamoe.mirai.message.data.MessageContent +import net.mamoe.mirai.message.data.PlainText +import net.mamoe.mirai.message.data.messageChainOf +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import kotlin.test.assertEquals + +internal class CommandValueArgumentContextTest : AbstractCommandTest() { + + inner class CustomBooleanParser : CommandValueArgumentParser { + @Throws(CommandArgumentParserException::class) + override fun parse(raw: String, sender: CommandSender): Boolean { + return raw == "TRUE!" + } + + @Throws(CommandArgumentParserException::class) + override fun parse(raw: MessageContent, sender: CommandSender): Boolean { + // 将一个图片认为是 'true' + return if (raw is Image && raw.imageId == "{A7CBB529-43A2-127C-E426-59D29BAA8515}.jpg") { + true + } else super.parse(raw, sender) + } + } + + inner class JavaComposite : JCompositeCommand(mockPlugin, "main") { + init { + addArgumentContext( + buildCommandArgumentContext { java.lang.Boolean.TYPE with CustomBooleanParser() } + ) + } + + @SubCommand("name") + fun foo(context: CommandContext, arg: String, b: Boolean) { + Testing.ok(b) + } + } + + inner class JavaSimple : JSimpleCommand(mockPlugin, "main") { + init { + addArgumentContext( + buildCommandArgumentContext { java.lang.Boolean.TYPE with CustomBooleanParser() } + ) + } + + @Handler + fun foo(context: CommandContext, arg: String, b: Boolean) { + Testing.ok(b) + } + } + + @TestFactory + fun test(): List { + return listOf( + JavaComposite() to listOf( + messageChainOf(PlainText("/main"), PlainText("name"), PlainText("aaa"), PlainText("TRUE!")), + messageChainOf( + PlainText("/main"), + PlainText("name"), + PlainText("aaa"), + Image("{A7CBB529-43A2-127C-E426-59D29BAA8515}.jpg") + ) + ), JavaSimple() to listOf( + messageChainOf(PlainText("/main"), PlainText("aaa"), PlainText("TRUE!")), + messageChainOf( + PlainText("/main"), + PlainText("aaa"), + Image("{A7CBB529-43A2-127C-E426-59D29BAA8515}.jpg") + ) + ) + ).flatMap { (instance, cmds) -> + cmds.map { message -> + DynamicTest.dynamicTest(instance::class.supertypes.first().classifierAsKClass().simpleName) { + runBlocking { + instance.withRegistration { + assertEquals( + true, + Testing.withTesting { + assertSuccess( + CommandManager.executeCommand(sender, message, checkPermission = false) + ) + } + ) + } + } + } + } + } + } +} \ No newline at end of file