Documentations and several improvements

This commit is contained in:
Him188 2020-08-23 02:25:42 +08:00
parent 0a7097b354
commit 775b888273
17 changed files with 506 additions and 79 deletions

View File

@ -16,8 +16,9 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
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.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.CommandManagerImpl.register import net.mamoe.mirai.console.internal.command.CommandManagerImpl
import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.console.util.ConsoleInternalAPI
import net.mamoe.mirai.message.nextMessageOrNull import net.mamoe.mirai.message.nextMessageOrNull

View File

@ -12,7 +12,7 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.internal.command.isValidSubName import net.mamoe.mirai.console.internal.command.isValidSubName
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
@ -53,14 +53,17 @@ public interface Command {
public val prefixOptional: Boolean public val prefixOptional: Boolean
/** /**
* 指令拥有者, 对于插件的指令通常是 [PluginCommandOwner] * 指令拥有者.
* @see CommandOwner
*/ */
public val owner: CommandOwner public val owner: CommandOwner
/** /**
* 在指令被执行时调用.
*
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] [String]. 且已经以 ' ' 分割. * @param args 指令参数. 数组元素类型可能是 [SingleMessage] [String]. 且已经以 ' ' 分割.
* *
* @see CommandManager.execute * @see CommandManager.executeCommand 查看更多信息
*/ */
@JvmBlockingBridge @JvmBlockingBridge
public suspend fun CommandSender.onCommand(args: Array<out Any>) public suspend fun CommandSender.onCommand(args: Array<out Any>)
@ -81,16 +84,23 @@ public suspend inline fun Command.onCommand(sender: CommandSender, args: Array<o
/** /**
* [Command] 的基础实现 * [Command] 的基础实现
*
* @see SimpleCommand
* @see CompositeCommand
* @see RawCommand
*/ */
public abstract class AbstractCommand @JvmOverloads constructor( public abstract class AbstractCommand @JvmOverloads constructor(
public final override val owner: CommandOwner, /** 指令拥有者. */
public override val owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "<no description available>", description: String = "<no description available>",
public final override val permission: CommandPermission = CommandPermission.Default, /** 指令权限 */
public final override val prefixOptional: Boolean = false public override val permission: CommandPermission = CommandPermission.Default,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false
) : Command { ) : Command {
public final override val description: String = description.trimIndent() public override val description: String = description.trimIndent()
public final override val names: Array<out String> = public override val names: Array<out String> =
names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).also { list -> names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).also { list ->
list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") } list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") }
}.toTypedArray() }.toTypedArray()

View File

@ -18,6 +18,10 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
* [CommandManager.executeCommand] , [Command.onCommand] 抛出异常时包装的异常. * [CommandManager.executeCommand] , [Command.onCommand] 抛出异常时包装的异常.
*/ */
public class CommandExecutionException( public class CommandExecutionException(
/**
* 执行者
*/
public val sender: CommandSender,
/** /**
* 执行过程发生异常的指令 * 执行过程发生异常的指令
*/ */

View File

@ -16,9 +16,15 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.SingleMessage
/**
* 指令管理器
*/
public interface CommandManager { public interface CommandManager {
/** /**
* 获取已经注册了的属于这个 [CommandOwner] 的指令列表. * 获取已经注册了的属于这个 [CommandOwner] 的指令列表.
@ -79,6 +85,7 @@ public interface CommandManager {
* *
* @return 成功执行的指令, 在无匹配指令时返回 `null` * @return 成功执行的指令, 在无匹配指令时返回 `null`
* @throws CommandExecutionException [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出 * @throws CommandExecutionException [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
* @see executeCommand
*/ */
@JvmBlockingBridge @JvmBlockingBridge
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
@ -89,6 +96,7 @@ public interface CommandManager {
* *
* @return 成功执行的指令, 在无匹配指令时返回 `null` * @return 成功执行的指令, 在无匹配指令时返回 `null`
* @throws CommandExecutionException [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出 * @throws CommandExecutionException [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
* @see executeCommand
*/ */
@JvmBlockingBridge @JvmBlockingBridge
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
@ -97,6 +105,16 @@ public interface CommandManager {
/** /**
* 解析并执行一个指令, 获取详细的指令参数等信息 * 解析并执行一个指令, 获取详细的指令参数等信息
* *
* 执行过程中产生的异常将不会直接抛出, 而会包装为 [CommandExecuteResult.ExecutionFailed]
*
* ### 指令解析流程
* 1. [messages] 的第一个消息元素的 [内容][Message.contentToString] 被作为指令名, 在已注册指令列表中搜索. (包含 [Command.prefixOptional] 相关的处理)
* 2. 参数语法分析.
* 在当前的实现下, [messages] 被以空格和 [SingleMessage] 分割.
* "MessageChain("foo bar", [Image], " test")" 被分割为 "foo", "bar", [Image], "test".
* 注意: 字符串与消息元素之间不需要空格, 会被强制分割. "bar[mirai:image:]" 会被分割为 "bar" [Image] 类型的消息元素.
* 3. 参数解析. 各类型指令实现不同. 详见 [RawCommand], [CompositeCommand], [SimpleCommand]
*
* @param messages 接受 [String] [Message], 其他对象将会被 [Any.toString] * @param messages 接受 [String] [Message], 其他对象将会被 [Any.toString]
* *
* @return 执行结果 * @return 执行结果

View File

@ -18,6 +18,10 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
* 总是作为 [CommandExecutionException.cause]. * 总是作为 [CommandExecutionException.cause].
*/ */
public class CommandPermissionDeniedException( public class CommandPermissionDeniedException(
/**
* 执行者
*/
public val commandSender: CommandSender,
/** /**
* 执行过程发生异常的指令 * 执行过程发生异常的指令
*/ */

View File

@ -24,10 +24,34 @@ import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
/** /**
* 指令发送者 * 指令发送者.
*
* ### 获得指令发送者
* - [MessageEvent.toCommandSender]
* - [FriendMessageEvent.toCommandSender]
* - [GroupMessageEvent.toCommandSender]
* - [TempMessageEvent.toCommandSender]
*
* - [Member.asCommandSender]
* - [Friend.asCommandSender]
* - [User.asCommandSender]
*
* ### 子类型
*
* 当真实收到由用户执行的指令时:
* - 若用户在群内指令执行, 对应 [CommandSender] [MemberCommandSenderOnMessage]
* - 若用户在私聊环境内指令执行, 对应 [CommandSender] [FriendCommandSenderOnMessage]
* - 若用户在临时会话内指令执行, 对应 [CommandSender] [TempCommandSenderOnMessage]
*
* 当指令由其他插件主动执行时, 插件应使用 [toCommandSender] [asCommandSender], 因此
* - 若用户在群内指令执行, 对应 [CommandSender] [MemberCommandSender]
* - 若用户在私聊环境内指令执行, 对应 [CommandSender] [FriendCommandSender]
* - 若用户在临时会话内指令执行, 对应 [CommandSender] [TempCommandSender]
* *
* @see ConsoleCommandSender 控制台 * @see ConsoleCommandSender 控制台
* @see UserCommandSender [User] ([群成员][Member], [好友][Friend]) * @see UserCommandSender [User] ([群成员][Member], [好友][Friend])
* @see toCommandSender
* @see asCommandSender
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
public interface CommandSender { public interface CommandSender {
@ -109,10 +133,16 @@ public abstract class ConsoleCommandSender internal constructor() : CommandSende
} }
} }
@ConsoleExperimentalAPI
public fun FriendMessageEvent.toCommandSender(): FriendCommandSenderOnMessage = FriendCommandSenderOnMessage(this) public fun FriendMessageEvent.toCommandSender(): FriendCommandSenderOnMessage = FriendCommandSenderOnMessage(this)
@ConsoleExperimentalAPI
public fun GroupMessageEvent.toCommandSender(): MemberCommandSenderOnMessage = MemberCommandSenderOnMessage(this) public fun GroupMessageEvent.toCommandSender(): MemberCommandSenderOnMessage = MemberCommandSenderOnMessage(this)
@ConsoleExperimentalAPI
public fun TempMessageEvent.toCommandSender(): TempCommandSenderOnMessage = TempCommandSenderOnMessage(this) public fun TempMessageEvent.toCommandSender(): TempCommandSenderOnMessage = TempCommandSenderOnMessage(this)
@ConsoleExperimentalAPI
public fun MessageEvent.toCommandSender(): CommandSenderOnMessage<*> = when (this) { public fun MessageEvent.toCommandSender(): CommandSenderOnMessage<*> = when (this) {
is FriendMessageEvent -> toCommandSender() is FriendMessageEvent -> toCommandSender()
is GroupMessageEvent -> toCommandSender() is GroupMessageEvent -> toCommandSender()
@ -120,9 +150,13 @@ public fun MessageEvent.toCommandSender(): CommandSenderOnMessage<*> = when (thi
else -> throw IllegalArgumentException("unsupported MessageEvent: ${this::class.qualifiedNameOrTip}") else -> throw IllegalArgumentException("unsupported MessageEvent: ${this::class.qualifiedNameOrTip}")
} }
@ConsoleExperimentalAPI
public fun Member.asCommandSender(): MemberCommandSender = MemberCommandSender(this) public fun Member.asCommandSender(): MemberCommandSender = MemberCommandSender(this)
@ConsoleExperimentalAPI
public fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this) public fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this)
@ConsoleExperimentalAPI
public fun User.asCommandSender(): UserCommandSender { public fun User.asCommandSender(): UserCommandSender {
return when (this) { return when (this) {
is Friend -> this.asCommandSender() is Friend -> this.asCommandSender()

View File

@ -26,7 +26,51 @@ import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* 复合指令. * 复合指令. 指令注册时候会通过反射构造指令解析器.
*
* 示例:
* ```
* @OptIn(ConsoleExperimentalAPI::class)
* object MyCompositeCommand : CompositeCommand(
* MyPluginMain, "manage", // "manage" 是主指令名
* description = "示例指令", permission = MyCustomPermission,
* // prefixOptional = true // 还有更多参数可填, 此处忽略
* ) {
*
* // [参数智能解析]
* //
* // 在控制台执行 "/manage <群号>.<群员> <持续时间>",
* // 或在聊天群内发送 "/manage <@一个群员> <持续时间>",
* // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>",
* // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>"
* // 时调用这个函数
* @SubCommand
* suspend fun CommandSender.mute(target: Member, duration: Int) { // 通过 /manage mute <target> <duration> 调用
* sendMessage("/manage mute 被调用了, 参数为: $target, $duration")
*
* val result = kotlin.runCatching {
* target.mute(duration).toString()
* }.getOrElse {
* it.stackTraceToString()
* } // 失败时返回堆栈信息
*
* sendMessage("结果: $result")
* }
*
* @SubCommand
* suspend fun CommandSender.list() { // 执行 "/manage list" 时调用这个函数
* sendMessage("/manage list 被调用了")
* }
*
* // 支持 Image 类型, 需在聊天中执行此指令.
* @SubCommand
* suspend fun CommandSender.test(image: Image) { // 执行 "/manage test <一张图片>" 时调用这个函数
* sendMessage("/manage image 被调用了, 图片是 ${image.imageId}")
* }
* }
* ```
*
* @see buildCommandArgumentContext
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public abstract class CompositeCommand @JvmOverloads constructor( public abstract class CompositeCommand @JvmOverloads constructor(
@ -80,3 +124,73 @@ public abstract class CompositeCommand @JvmOverloads constructor(
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
get() = CompositeCommandSubCommandAnnotationResolver get() = CompositeCommandSubCommandAnnotationResolver
} }
/**
* 复合指令. 指令注册时候会通过反射构造指令解析器.
*
* 示例:
* ```
* public final class MyCompositeCommand extends CompositeCommand {
* public static final MyCompositeCommand INSTANCE = new MyCompositeCommand();
*
* public MyCompositeCommand() {
* super(MyPluginMain.INSTANCE, "manage") // "manage" 是主指令名
* }
*
* // [参数智能解析]
* //
* //
* // 在控制台执行 "/manage <群号>.<群员> <持续时间>",
* // 或在聊天群内发送 "/manage <@一个群员> <持续时间>",
* // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>",
* // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>"
* // 时调用这个函数
* @SubCommand
* public void mute(CommandSender sender, Member target, int duration) { // 通过 /manage mute <target> <duration> 调用.
* sender.sendMessage("/manage mute 被调用了, 参数为: " + target + ", " + duration);
*
*
* String result;
* try {
* result = target.mute(duration).toString();
* } catch(Exception e) {
* result = ExceptionsKt.stackTraceToString(e);
* }
*
* sender.sendMessage("结果: " + result)
* }
*
* @SubCommand
* public void list(CommandSender sender) { // 执行 "/manage list" 时调用这个方法
* sender.sendMessage("/manage list 被调用了")
* }
*
* // 支持 Image 类型, 需在聊天中执行此指令.
* @SubCommand
* public void test(CommandSender sender, Image image) { // 执行 "/manage test <一张图片>" 时调用这个方法
* sender.sendMessage("/manage image 被调用了, 图片是 " + image.imageId)
* }
* }
* ```
*
* @see buildCommandArgumentContext
*/
@ConsoleExperimentalAPI
public abstract class JCompositeCommand(
owner: CommandOwner,
vararg names: String
) : CompositeCommand(owner, *names) {
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public final override var description: String = "<no descriptions given>"
protected set
/** 指令权限 */
public final override var permission: CommandPermission = CommandPermission.Default
protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public final override var prefixOptional: Boolean = false
protected set
}

View File

@ -7,18 +7,118 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("unused")
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.message.data.SingleMessage
/** /**
* 无参数解析, 接收原生参数的指令. * 无参数解析, 接收原生参数的指令.
*
* ### 指令执行流程
* [CommandManager.executeCommand] 所述第 3 , [RawCommand] 不会对参数做任何解析.
*
* @see JRawCommand Java 用户继承.
*
* @see SimpleCommand 简单指令
* @see CompositeCommand 复合指令
*/ */
public abstract class RawCommand @JvmOverloads constructor( public abstract class RawCommand @JvmOverloads constructor(
/**
* 指令拥有者.
* @see CommandOwner
*/
public override val owner: CommandOwner, public override val owner: CommandOwner,
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
public override vararg val names: String, public override vararg val names: String,
/** 用法说明, 用于发送给用户 */
public override val usage: String = "<no usages given>", public override val usage: String = "<no usages given>",
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public override val description: String = "<no descriptions given>", public override val description: String = "<no descriptions given>",
/** 指令权限 */
public override val permission: CommandPermission = CommandPermission.Default, public override val permission: CommandPermission = CommandPermission.Default,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
/**
* 在指令被执行时调用.
*
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] [String]. 且已经以 ' ' 分割.
*
* @see CommandManager.execute 查看更多信息
*/
public abstract override suspend fun CommandSender.onCommand(args: Array<out Any>) public abstract override suspend fun CommandSender.onCommand(args: Array<out Any>)
} }
/**
* Java 用户继承
*
* 请在构造时设置相关属性.
*
* ```java
* public final class MyCommand extends JRawCommand {
* public static final MyCommand INSTANCE = new MyCommand();
* private MyCommand () {
* super(MyPluginMain.INSTANCE, "test")
* // 可选设置如下属性
* setUsage("/test")
* setDescription("这是一个测试指令")
* setPermission(CommandPermission.Operator.INSTANCE)
* setPrefixOptional(true)
* }
*
* @Override
* public void onCommand(@NotNull CommandSender sender, @NotNull args: Object[]) {
* // 处理指令
* }
* }
* ```
*
* @see RawCommand
*/
public abstract class JRawCommand(
/**
* 指令拥有者.
* @see CommandOwner
*/
public override val owner: CommandOwner,
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
public override vararg val names: String
) : Command {
/** 用法说明, 用于发送给用户 */
public override var usage: String = "<no usages given>"
protected set
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public final override var description: String = "<no descriptions given>"
protected set
/** 指令权限 */
public final override var permission: CommandPermission = CommandPermission.Default
protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public final override var prefixOptional: Boolean = false
protected set
/**
* 在指令被执行时调用.
*
* @param args 指令参数. 数组元素类型可能是 [SingleMessage] [String]. 且已经以 ' ' 分割.
*
* @see CommandManager.execute 查看更多信息
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@JvmName("onCommand")
public abstract fun onCommand(sender: CommandSender, args: Array<out Any>)
public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
withContext(Dispatchers.IO) { onCommand(this@onCommand, args) }
}
}

View File

@ -17,19 +17,23 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.description.*
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
/** /**
* 简单指令. 参数支持自动解析. [CommandArgumentParser] * 简单的, 支持参数自动解析的指令.
*
* 要查看指令解析流程, 参考 [CommandManager.executeCommand]
* 要查看参数解析方式, 参考 [CommandArgumentParser]
* *
* Kotlin 实现: * Kotlin 实现:
* ``` * ```
* object MySimpleCommand : SimpleCommand( * object MySimpleCommand : SimpleCommand(
* MyPlugin, "tell", * MyPlugin, "tell",
* description = "Message somebody", * description = "Message somebody",
* usage = "/tell <target> <message>" * usage = "/tell <target> <message>" // usage 如不设置则自动根据带有 @Handler 的方法生成
* ) { * ) {
* @Handler * @Handler
* suspend fun CommandSender.onCommand(target: User, message: String) { * suspend fun CommandSender.onCommand(target: User, message: String) {
@ -38,18 +42,8 @@ import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotatio
* } * }
* ``` * ```
* *
* Java 实现: * @see JSimpleCommand Java 实现
* ```java * @see [CommandManager.executeCommand]
* public final class MySimpleCommand extends SimpleCommand {
* private MySimpleCommand() {
* super(MyPlugin.INSTANCE, new String[]{ "tell" }, "Message somebody", "/tell <target> <message>")
* }
* @Handler
* public void onCommand(CommandSender sender, User target, String message) {
* target.sendMessage(message)
* }
* }
* ```
*/ */
public abstract class SimpleCommand @JvmOverloads constructor( public abstract class SimpleCommand @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
@ -69,7 +63,7 @@ public abstract class SimpleCommand @JvmOverloads constructor(
*/ */
protected annotation class Handler protected annotation class Handler
public final override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext 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: Array<out Any>) {
subCommands.single().parseAndExecute(this, args, false) subCommands.single().parseAndExecute(this, args, false)
@ -86,3 +80,40 @@ public abstract class SimpleCommand @JvmOverloads constructor(
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
get() = SimpleCommandSubCommandAnnotationResolver get() = SimpleCommandSubCommandAnnotationResolver
} }
/**
* Java 实现:
* ```java
* public final class MySimpleCommand extends JSimpleCommand {
* private MySimpleCommand() {
* super(MyPlugin.INSTANCE, "tell")
* // 可选设置如下属性
* setDescription("这是一个测试指令")
* setUsage("/tell <target> <message>") // 如不设置则自动根据带有 @Handler 的方法生成
* setPermission(CommandPermission.Operator.INSTANCE)
* setPrefixOptional(true)
* }
*
* @Handler
* public void onCommand(CommandSender sender, User target, String message) {
* target.sendMessage(message)
* }
* }
* ```
*
* @see SimpleCommand
* @see [CommandManager.executeCommand]
*/
public abstract class JSimpleCommand(
owner: CommandOwner,
vararg names: String
) : SimpleCommand(owner, *names) {
public override var description: String = super.description
protected set
public override var permission: CommandPermission = super.permission
protected set
public override var prefixOptional: Boolean = super.prefixOptional
protected set
public override var context: CommandArgumentContext = super.context
protected set
}

View File

@ -92,7 +92,7 @@ public object BooleanArgumentParser : InternalCommandArgumentParserExtensions<Bo
/** /**
* 根据 [Bot.id] 解析一个登录后的 [Bot] * 根据 [Bot.id] 解析一个登录后的 [Bot]
*/ */
public object ExistBotArgumentParser : InternalCommandArgumentParserExtensions<Bot> { public object ExistingBotArgumentParser : InternalCommandArgumentParserExtensions<Bot> {
public override fun parse(raw: String, sender: CommandSender): Bot = public override fun parse(raw: String, sender: CommandSender): Bot =
if (raw == "~") sender.inferBotOrFail() if (raw == "~") sender.inferBotOrFail()
else raw.findBotOrFail() else raw.findBotOrFail()
@ -108,7 +108,7 @@ public object ExistBotArgumentParser : InternalCommandArgumentParserExtensions<B
* *
* 当只登录了一个 [Bot] , 无需上述 `botId` 参数即可 * 当只登录了一个 [Bot] , 无需上述 `botId` 参数即可
*/ */
public object ExistFriendArgumentParser : InternalCommandArgumentParserExtensions<Friend> { public object ExistingFriendArgumentParser : InternalCommandArgumentParserExtensions<Friend> {
private val syntax = """ private val syntax = """
- `botId.friendId` - `botId.friendId`
- `botId.friendNick` (模糊搜索, 寻找最优匹配) - `botId.friendNick` (模糊搜索, 寻找最优匹配)
@ -153,7 +153,7 @@ public object ExistFriendArgumentParser : InternalCommandArgumentParserExtension
* *
* 当只登录了一个 [Bot] , 无需上述 `botId` 参数即可 * 当只登录了一个 [Bot] , 无需上述 `botId` 参数即可
*/ */
public object ExistGroupArgumentParser : InternalCommandArgumentParserExtensions<Group> { public object ExistingGroupArgumentParser : InternalCommandArgumentParserExtensions<Group> {
private val syntax = """ private val syntax = """
- `botId.groupId` - `botId.groupId`
- `~` (指代指令调用人自己所在群. 仅群聊天环境下) - `~` (指代指令调用人自己所在群. 仅群聊天环境下)

View File

@ -26,15 +26,18 @@ import kotlin.reflect.full.isSubclassOf
/** /**
* [CommandArgumentParser] 的集合, 用于 [CompositeCommand] [SimpleCommand]. * 指令参数环境, [CommandArgumentParser] 的集合, 用于 [CompositeCommand] [SimpleCommand].
*
* 在指令解析时, 总是从 [CommandArgumentContextAware.context] 搜索相关解析器
*
* 要构造 [CommandArgumentContext], 参考 [buildCommandArgumentContext]
* *
* @see SimpleCommandArgumentContext 简单实现 * @see SimpleCommandArgumentContext 简单实现
* @see EmptyCommandArgumentContext 空实现, 类似 [emptyList] * @see EmptyCommandArgumentContext 空实现, 类似 [emptyList]
* @see CommandArgumentContext.EMPTY 空实现的另一种获取方式.
* *
* @see CommandArgumentContext.Builtins 内建 [CommandArgumentParser] * @see CommandArgumentContext.Builtins 内建 [CommandArgumentParser]
* *
* @see CommandArgumentContext DSL * @see buildCommandArgumentContext DSL 构造
*/ */
public interface CommandArgumentContext { public interface CommandArgumentContext {
/** /**
@ -62,7 +65,7 @@ public interface CommandArgumentContext {
/** /**
* 内建的默认 [CommandArgumentParser] * 内建的默认 [CommandArgumentParser]
*/ */
public object Builtins : CommandArgumentContext by (CommandArgumentContext { public object Builtins : CommandArgumentContext by (buildCommandArgumentContext {
Int::class with IntArgumentParser Int::class with IntArgumentParser
Byte::class with ByteArgumentParser Byte::class with ByteArgumentParser
Short::class with ShortArgumentParser Short::class with ShortArgumentParser
@ -73,14 +76,14 @@ public interface CommandArgumentContext {
Float::class with FloatArgumentParser Float::class with FloatArgumentParser
Member::class with ExistMemberArgumentParser Member::class with ExistMemberArgumentParser
Group::class with ExistGroupArgumentParser Group::class with ExistingGroupArgumentParser
Friend::class with ExistFriendArgumentParser Friend::class with ExistingFriendArgumentParser
Bot::class with ExistBotArgumentParser Bot::class with ExistingBotArgumentParser
}) })
} }
/** /**
* 拥有 [CommandArgumentContext] 的类 * 拥有 [buildCommandArgumentContext] 的类
* *
* @see SimpleCommand * @see SimpleCommand
* @see CompositeCommand * @see CompositeCommand
@ -95,7 +98,7 @@ public interface CommandArgumentContextAware {
public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf()) public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf())
/** /**
* 合并两个 [CommandArgumentContext], [replacer] 将会替换 [this] 中重复的 parser. * 合并两个 [buildCommandArgumentContext], [replacer] 将会替换 [this] 中重复的 parser.
*/ */
public operator fun CommandArgumentContext.plus(replacer: CommandArgumentContext): CommandArgumentContext { public operator fun CommandArgumentContext.plus(replacer: CommandArgumentContext): CommandArgumentContext {
if (replacer == EmptyCommandArgumentContext) return this if (replacer == EmptyCommandArgumentContext) return this
@ -125,9 +128,9 @@ public operator fun CommandArgumentContext.plus(replacer: List<ParserPair<*>>):
} }
/** /**
* 自定义 [CommandArgumentContext] * 自定义 [buildCommandArgumentContext]
* *
* @see CommandArgumentContext * @see buildCommandArgumentContext
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public class SimpleCommandArgumentContext( public class SimpleCommandArgumentContext(
@ -140,10 +143,11 @@ public class SimpleCommandArgumentContext(
} }
/** /**
* 构建一个 [CommandArgumentContext]. * 构建一个 [buildCommandArgumentContext].
* *
* Kotlin 实现:
* ``` * ```
* CommandArgumentContext { * val context = buildCommandArgumentContext {
* Int::class with IntArgParser * Int::class with IntArgParser
* Member::class with ExistMemberArgParser * Member::class with ExistMemberArgParser
* Group::class with { s: String, sender: CommandSender -> * Group::class with { s: String, sender: CommandSender ->
@ -155,22 +159,47 @@ public class SimpleCommandArgumentContext(
* } * }
* ``` * ```
* *
* Java 实现:
* ```java
* CommandArgumentContext context =
* new CommandArgumentContextBuilder()
* .add(clazz1, parser1)
* .add(String.class, new CommandArgumentParser<String>() {
* public String parse(String raw, CommandSender sender) {
* // ...
* }
* })
* // 更多 add
* .build()
* ```
*
* @see CommandArgumentContextBuilder * @see CommandArgumentContextBuilder
* @see CommandArgumentContext * @see buildCommandArgumentContext
*/ */
@Suppress("FunctionName")
@JvmSynthetic @JvmSynthetic
public fun CommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext { public fun buildCommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext {
return SimpleCommandArgumentContext(CommandArgumentContextBuilder().apply(block).distinctByReversed { it.klass }) return CommandArgumentContextBuilder().apply(block).build()
} }
/** /**
* @see CommandArgumentContext * 参考 [buildCommandArgumentContext]
*/ */
public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutableListOf() { public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
@JvmName("add") // TODO: 2020/8/19 java class support /**
public inline infix fun <T : Any> KClass<T>.with(parser: CommandArgumentParser<T>): ParserPair<*> = * 添加一个指令解析器.
ParserPair(this, parser).also { add(it) } */
@JvmName("add")
public infix fun <T : Any> Class<T>.with(parser: CommandArgumentParser<T>): CommandArgumentContextBuilder =
this.kotlin with parser
/**
* 添加一个指令解析器
*/
@JvmName("add")
public inline infix fun <T : Any> KClass<T>.with(parser: CommandArgumentParser<T>): CommandArgumentContextBuilder {
add(ParserPair(this, parser))
return this@CommandArgumentContextBuilder
}
/** /**
* 添加一个指令解析器 * 添加一个指令解析器
@ -179,7 +208,12 @@ public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutab
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public inline infix fun <T : Any> KClass<T>.with( public inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgumentParser<T>.(s: String, sender: CommandSender) -> T crossinline parser: CommandArgumentParser<T>.(s: String, sender: CommandSender) -> T
): ParserPair<*> = ParserPair(this, CommandArgumentParser(parser)).also { add(it) } ): CommandArgumentContextBuilder {
add(ParserPair(this, object : CommandArgumentParser<T> {
override fun parse(raw: String, sender: CommandSender): T = parser(raw, sender)
}))
return this@CommandArgumentContextBuilder
}
/** /**
* 添加一个指令解析器 * 添加一个指令解析器
@ -187,12 +221,18 @@ public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutab
@JvmSynthetic @JvmSynthetic
public inline infix fun <T : Any> KClass<T>.with( public inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgumentParser<T>.(s: String) -> T crossinline parser: CommandArgumentParser<T>.(s: String) -> T
): ParserPair<*> = ): CommandArgumentContextBuilder {
ParserPair(this, CommandArgumentParser { s: String, _: CommandSender -> parser(s) }).also { add(it) } add(ParserPair(this, object : CommandArgumentParser<T> {
override fun parse(raw: String, sender: CommandSender): T = parser(raw)
}))
return this@CommandArgumentContextBuilder
}
@JvmSynthetic @JvmSynthetic
public inline fun <reified T : Any> add(parser: CommandArgumentParser<T>): ParserPair<*> = public inline fun <reified T : Any> add(parser: CommandArgumentParser<T>): CommandArgumentContextBuilder {
ParserPair(T::class, parser).also { add(it) } add(ParserPair(T::class, parser))
return this@CommandArgumentContextBuilder
}
/** /**
* 添加一个指令解析器 * 添加一个指令解析器
@ -201,7 +241,9 @@ public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutab
@JvmSynthetic @JvmSynthetic
public inline infix fun <reified T : Any> add( public inline infix fun <reified T : Any> add(
crossinline parser: CommandArgumentParser<*>.(s: String) -> T crossinline parser: CommandArgumentParser<*>.(s: String) -> T
): ParserPair<*> = T::class with CommandArgumentParser { s: String, _: CommandSender -> parser(s) } ): CommandArgumentContextBuilder = T::class with object : CommandArgumentParser<T> {
override fun parse(raw: String, sender: CommandSender): T = parser(raw)
}
/** /**
* 添加一个指令解析器 * 添加一个指令解析器
@ -211,7 +253,14 @@ public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutab
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public inline infix fun <reified T : Any> add( public inline infix fun <reified T : Any> add(
crossinline parser: CommandArgumentParser<*>.(s: String, sender: CommandSender) -> T crossinline parser: CommandArgumentParser<*>.(s: String, sender: CommandSender) -> T
): ParserPair<*> = T::class with CommandArgumentParser(parser) ): CommandArgumentContextBuilder = T::class with object : CommandArgumentParser<T> {
override fun parse(raw: String, sender: CommandSender): T = parser(raw, sender)
}
/**
* 完成构建, 得到 [CommandArgumentContext]
*/
public fun build(): CommandArgumentContext = SimpleCommandArgumentContext(this.distinctByReversed { it.klass })
} }
internal inline fun <T, K> List<T>.distinctByReversed(selector: (T) -> K): List<T> { internal inline fun <T, K> List<T>.distinctByReversed(selector: (T) -> K): List<T> {

View File

@ -11,27 +11,82 @@
package net.mamoe.mirai.console.command.description package net.mamoe.mirai.console.command.description
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.SimpleCommand
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
import net.mamoe.mirai.message.data.content import net.mamoe.mirai.message.data.content
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
/** /**
* 指令参数解析器. * 指令参数解析器. 用于解析字符串或 [SingleMessage] 到特定参数类型.
* *
* @see CommandArgumentContext * ### 参数解析
* @see ExistFriendArgumentParser *
* [SimpleCommand] 中的示例:
* ```
* suspend fun CommandSender.mute(target: Member, duration: Int)
* ```
* [CommandManager] 总是从 [SimpleCommand.context] 搜索一个 [T] [Member] [CommandArgumentParser], 并调用其 [CommandArgumentParser.parse]
*
* ### 内建指令解析器
* - 基础类型: [ByteArgumentParser], [ShortArgumentParser], [IntArgumentParser], [LongArgumentParser]
* [FloatArgumentParser], [DoubleArgumentParser],
* [BooleanArgumentParser], [StringArgumentParser]
*
* - [Bot]: [ExistingBotArgumentParser]
* - [Friend]: [ExistingFriendArgumentParser]
* - [Group]: [ExistingGroupArgumentParser]
* - [Member]: [ExistMemberArgumentParser]
*
*
* @see SimpleCommand 简单指令
* @see CompositeCommand 复合指令
*
* @see buildCommandArgumentContext 指令参数环境, [CommandArgumentParser] 的集合
*/ */
public interface CommandArgumentParser<out T : Any> { public interface CommandArgumentParser<out T : Any> {
/**
* 解析一个字符串为 [T] 类型参数
*
* **实现提示**: 在解析时遇到意料之中的问题, 如无法找到目标群员, 可抛出 [CommandArgumentParserException].
* 此异常将会被特殊处理, 不会引发一个错误, 而是作为指令调用成功的情况, 将错误信息发送给用户.
*
* @throws CommandArgumentParserException 当解析时遇到*意料之中*的问题时抛出.
*
* @see CommandArgumentParserException
*/
@Throws(CommandArgumentParserException::class)
public fun parse(raw: String, sender: CommandSender): T public fun parse(raw: String, sender: CommandSender): T
/**
* 解析一个字符串为 [T] 类型参数
*
* **实现提示**: 在解析时遇到意料之中的问题, 如无法找到目标群员, 可抛出 [CommandArgumentParserException].
* 此异常将会被特殊处理, 不会引发一个错误, 而是作为指令调用成功的情况, 将错误信息发送给用户.
*
* @throws CommandArgumentParserException 当解析时遇到*意料之中*的问题时抛出.
*
* @see CommandArgumentParserException
*/
@Throws(CommandArgumentParserException::class)
@JvmDefault @JvmDefault
public fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender) public fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
} }
/** /**
* 解析 * 解析一个字符串或 [SingleMessage] [T] 类型参数
*
* @throws IllegalArgumentException [raw] 既不是 [SingleMessage], 也不是 [String] 时抛出.
*/ */
@JvmSynthetic
@Throws(IllegalArgumentException::class)
public fun <T : Any> CommandArgumentParser<T>.parse(raw: Any, sender: CommandSender): T { public fun <T : Any> CommandArgumentParser<T>.parse(raw: Any, sender: CommandSender): T {
contract { contract {
returns() implies (raw is String || raw is SingleMessage) returns() implies (raw is String || raw is SingleMessage)
@ -45,14 +100,23 @@ public fun <T : Any> CommandArgumentParser<T>.parse(raw: Any, sender: CommandSen
} }
/** /**
* 抛出一个 [CommandArgumentParserException] * 抛出一个 [CommandArgumentParserException] 的捷径
*
* @throws CommandArgumentParserException
*/ */
@Suppress("unused") @Suppress("unused")
@JvmSynthetic @JvmSynthetic
@Throws(CommandArgumentParserException::class)
public inline fun CommandArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing { public inline fun CommandArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing {
throw CommandArgumentParserException(message, cause) throw CommandArgumentParserException(message, cause)
} }
/**
* 检查参数 [condition]. 当它为 `false` 时调用 [message] 并以其返回值作为消息, 抛出异常 [CommandArgumentParserException]
*
* @throws CommandArgumentParserException
*/
@Throws(CommandArgumentParserException::class)
@JvmSynthetic @JvmSynthetic
public inline fun CommandArgumentParser<*>.checkArgument( public inline fun CommandArgumentParser<*>.checkArgument(
condition: Boolean, condition: Boolean,
@ -60,10 +124,13 @@ public inline fun CommandArgumentParser<*>.checkArgument(
) { ) {
contract { contract {
returns() implies condition returns() implies condition
callsInPlace(message, InvocationKind.AT_MOST_ONCE)
} }
if (!condition) illegalArgument(message()) if (!condition) illegalArgument(message())
} }
/*
/** /**
* 创建匿名 [CommandArgumentParser] * 创建匿名 [CommandArgumentParser]
*/ */
@ -88,3 +155,4 @@ public inline fun <T : Any> CommandArgumentParser(
override fun parse(raw: SingleMessage, sender: CommandSender): T = messageParser(raw, sender) override fun parse(raw: SingleMessage, sender: CommandSender): T = messageParser(raw, sender)
} }
*/

View File

@ -7,16 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.internal.command
import kotlinx.atomicfu.locks.withLock import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.Command.Companion.primaryName
import net.mamoe.mirai.console.internal.command.executeCommandInternal
import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.console.internal.command.intersectsIgnoringCase
import net.mamoe.mirai.event.Listener import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.subscribeAlways import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.message.MessageEvent import net.mamoe.mirai.message.MessageEvent
@ -88,7 +86,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by Coroutine
///// IMPL ///// IMPL
override val CommandOwner.registeredCommands: List<Command> get() = this@CommandManagerImpl.registeredCommands.filter { it.owner == this } override val CommandOwner.registeredCommands: List<Command> get() = CommandManagerImpl.registeredCommands.filter { it.owner == this }
override val allRegisteredCommands: List<Command> get() = registeredCommands.toList() // copy override val allRegisteredCommands: List<Command> get() = registeredCommands.toList() // copy
override val commandPrefix: String get() = "/" override val commandPrefix: String get() = "/"
override fun CommandOwner.unregisterAllCommands() { override fun CommandOwner.unregisterAllCommands() {

View File

@ -9,9 +9,10 @@
@file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.console.command.description package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.description.CommandArgumentParser
import java.lang.reflect.Parameter import java.lang.reflect.Parameter
import kotlin.reflect.KClass import kotlin.reflect.KClass

View File

@ -14,7 +14,6 @@ package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.command.description.CommandArgumentContextAware
import net.mamoe.mirai.console.command.description.CommandParameter
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KAnnotatedElement

View File

@ -109,18 +109,14 @@ internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Arr
@JvmSynthetic @JvmSynthetic
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
internal suspend inline fun CommandSender.executeCommandInternal( internal suspend fun CommandSender.executeCommandInternal(
command: Command, command: Command,
args: Array<out Any>, args: Array<out Any>,
commandName: String, commandName: String,
checkPermission: Boolean checkPermission: Boolean
) { ) {
if (checkPermission && !command.testPermission(this)) { if (checkPermission && !command.testPermission(this)) {
throw CommandExecutionException( throw CommandExecutionException(this, command, commandName, CommandPermissionDeniedException(this, command))
command,
commandName,
CommandPermissionDeniedException(command)
)
} }
kotlin.runCatching { kotlin.runCatching {
@ -128,7 +124,7 @@ internal suspend inline fun CommandSender.executeCommandInternal(
}.onFailure { }.onFailure {
catchExecutionException(it) catchExecutionException(it)
if (it !is CommandArgumentParserException) { if (it !is CommandArgumentParserException) {
throw CommandExecutionException(command, commandName, it) throw CommandExecutionException(this, command, commandName, it)
} }
} }
} }

View File

@ -22,8 +22,8 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.registeredCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.registeredCommands
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregister import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregister
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands
import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.command.description.CommandArgumentParser import net.mamoe.mirai.console.command.description.CommandArgumentParser
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
import net.mamoe.mirai.console.initTestEnvironment import net.mamoe.mirai.console.initTestEnvironment
import net.mamoe.mirai.console.internal.command.flattenCommandComponents import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
@ -187,7 +187,7 @@ internal class TestCommand {
val composite = object : CompositeCommand( val composite = object : CompositeCommand(
ConsoleCommandOwner, ConsoleCommandOwner,
"test", "test",
overrideContext = CommandArgumentContext { overrideContext = buildCommandArgumentContext {
add(object : CommandArgumentParser<MyClass> { add(object : CommandArgumentParser<MyClass> {
override fun parse(raw: String, sender: CommandSender): MyClass { override fun parse(raw: String, sender: CommandSender): MyClass {
return MyClass(raw.toInt()) return MyClass(raw.toInt())