mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Documentations and several improvements
This commit is contained in:
parent
0a7097b354
commit
775b888273
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
/**
|
/**
|
||||||
* 执行过程发生异常的指令
|
* 执行过程发生异常的指令
|
||||||
*/
|
*/
|
||||||
|
@ -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 执行结果
|
||||||
|
@ -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,
|
||||||
/**
|
/**
|
||||||
* 执行过程发生异常的指令
|
* 执行过程发生异常的指令
|
||||||
*/
|
*/
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
}
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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`
|
||||||
- `~` (指代指令调用人自己所在群. 仅群聊天环境下)
|
- `~` (指代指令调用人自己所在群. 仅群聊天环境下)
|
||||||
|
@ -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> {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
@ -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() {
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user