change command structure

This commit is contained in:
jiahua.liu 2020-05-16 18:15:37 +08:00
parent 868a840494
commit c9f3cd92dd
3 changed files with 92 additions and 205 deletions

View File

@ -20,33 +20,35 @@ internal const val FOR_BINARY_COMPATIBILITY = "for binary compatibility"
/** /**
* 指令 * 指令
* * 通常情况下, 你的指令应继承 @see CompositeCommand/SimpleCommand
* @see register 注册这个指令 * @see register 注册这个指令
*/ */
interface Command { interface Command {
val owner: CommandOwner
val descriptor: CommandDescriptor val descriptor: CommandDescriptor
/**
* 指令的默认执行方法
* 当所有的 sub 方法均不满足时, 原始参数将送到此方法调用
* 如果 arg String, 他会被包装为 PlainText(AKA PlainMessage)
*/
@Permission(CommandPermission.Console::class)
suspend fun CommandSender.onDefault(args: List<Message>): Boolean
/**
* 在更多的情况下, 你应当使用 @SubCommand 来注册一共 sub 指令
*/
@Target(AnnotationTarget.FUNCTION)
annotation class SubCommand(val name:String)
/** /**
* Permission of the command * Permission of the command
*/ */
@Target(AnnotationTarget.FUNCTION) @Target(AnnotationTarget.FUNCTION)
annotation class Permission(val permission:KClass<*>) annotation class Permission(val permission:KClass<*>)
/**
* If a command is prefix optional
* e.g
* mute work as (/mute) if prefix optional or vise versa
*/
@Target(AnnotationTarget.CLASS)
annotation class PrefixOptional()
}
abstract class CompositeCommand(val name:String, val alias:Array<String> = listOf()):Command{
/**
* 你应当使用 @SubCommand 来注册 sub 指令
*/
@Target(AnnotationTarget.FUNCTION)
annotation class SubCommand(val name:String)
/** /**
* Usage of the sub command * Usage of the sub command
* you should not include arg names, which will be insert automatically * you should not include arg names, which will be insert automatically
@ -64,16 +66,30 @@ interface Command {
annotation class Name(val name:String) annotation class Name(val name:String)
/** override val descriptor: CommandDescriptor by lazy {
* If a command is prefix optional CommandDescriptor()
* e.g }
* mute work as (/mute) if prefix optional or vise versa
*/
@Target(AnnotationTarget.CLASS)
annotation class PrefixOptional()
} }
abstract class SimpleCommand(val name: String, val alias: Array<String> = arrayOf()
):Command{
override val descriptor: CommandDescriptor by lazy {
CommandDescriptor()
}
/**
* 你必须实现onCommand方法
*/
}
abstract class RawCommand(name:String, alias: Array<String> = arrayOf()):Command{
override val descriptor: CommandDescriptor by lazy {
CommandDescriptor()
}
}
/** /**
* 解析完成的指令实际参数列表. 参数顺序与 [Command.descriptor] [CommandDescriptor.params] 相同. * 解析完成的指令实际参数列表. 参数顺序与 [Command.descriptor] [CommandDescriptor.params] 相同.
*/ */

View File

@ -12,29 +12,24 @@ import net.mamoe.mirai.message.data.SingleMessage
* 这是指令系统较低级的 API. 大部分情况下请使用 [Command] * 这是指令系统较低级的 API. 大部分情况下请使用 [Command]
*/ */
class CommandDescriptor( class CommandDescriptor(
/** val names: Array<String> = arrayOf(),
* 子指令列表. 第一个元素为默认值.
*/
val subCommands: List<SubCommandDescriptor>, val subCommands: List<SubCommandDescriptor>,
/**
* 是否建议 console 将这个指令强制注册为需要带 [前缀][CommandPrefix] 的指令. val prefixOptional: Boolean = false,
*/
val suggestForcePrefix: Boolean = true,
/** 覆盖内建的指令参数解析器环境. */ /** 覆盖内建的指令参数解析器环境. */
overrideContext: CommandParserContext = CommandParserContext.Empty overrideContext: CommandParserContext = CommandParserContext.Empty
) { ) {
/** 子指令描述 */ /** 子指令描述 */
inner class SubCommandDescriptor( inner class SubCommandDescriptor(
/** 子指令名, 如 "/mute group add" 中的 "group add". 当表示默认指令时为父指令名. */ /** 子指令名, 如 "/mute group add" 中的 "group add". 当表示默认指令时为父指令名. 包含别名*/
val subName: String, val names: Array<String> = arrayOf(),
/** 用法说明 */ /** 用法说明 */
val usage: String, val usage: String,
/** 指令参数列表, 有顺序. */ /** 指令参数列表, 有顺序. */
val params: List<CommandParam<*>>, val params: List<CommandParam<*>>,
/** 指令说明 */ /** 指令说明 */
val description: String, val description: String,
/** 指令别名 */
val aliases: Array<String> = arrayOf(),
/** /**
* 指令权限 * 指令权限
* @see CommandPermission.or 要求其中一个权限 * @see CommandPermission.or 要求其中一个权限
@ -45,13 +40,14 @@ class CommandDescriptor(
val onCommand: suspend (sender: CommandSender, args: CommandArgs) -> Boolean val onCommand: suspend (sender: CommandSender, args: CommandArgs) -> Boolean
) { ) {
init { init {
require(!(subName.startsWith(' ') || subName.endsWith(' '))) { "subName mustn't start or end with a caret" } names.forEach {subName ->
require(subName.isValidSubName()) { "subName mustn't contain any of $ILLEGAL_SUB_NAME_CHARS" } require(!(subName.startsWith(' ') || subName.endsWith(' '))) { "subName mustn't start or end with a caret" }
require(subName.isValidSubName()) { "subName mustn't contain any of $ILLEGAL_SUB_NAME_CHARS" }
}
} }
@JvmField @JvmField
internal val bakedSubNames: Array<Array<String>> = internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
listOf(subName, *aliases).map { it.bakeSubName() }.toTypedArray()
internal inline val parent: CommandDescriptor get() = this@CommandDescriptor internal inline val parent: CommandDescriptor get() = this@CommandDescriptor
} }
@ -82,156 +78,6 @@ fun CommandDescriptor.SubCommandDescriptor.checkArgs(args: CommandArgs) {
} }
} }
/*
/**
* 构建一个 [CommandDescriptor]
*/
@Suppress("FunctionName")
inline fun CommandDescriptor(
/**
* 指令全名
*/
vararg fullNameComponents: String,
block: CommandDescriptorBuilder.() -> Unit = {}
): CommandDescriptor = CommandDescriptorBuilder(*fullNameComponents).apply(block).build()
class CommandDescriptorBuilder(
vararg fullName: String
) {
@PublishedApi
internal var fullName: Array<String> = fullName.checkFullName("fullName")
@PublishedApi
internal var context: CommandParserContext = CommandParserContext.Builtins
@PublishedApi
internal var permission: CommandPermission = CommandPermission.Default
@PublishedApi
internal var params: MutableList<CommandParam<*>> = mutableListOf()
@PublishedApi
internal var usage: String = "<no usage>"
@PublishedApi
internal var aliases: MutableList<Array<String>> = mutableListOf()
@PublishedApi
internal var description: String = ""
/** 增加指令参数解析器列表 */
@JvmSynthetic
inline fun context(block: CommandParserContextBuilder.() -> Unit) {
this.context += CommandParserContext(block)
}
/** 增加指令参数解析器列表 */
@JvmSynthetic
inline fun context(context: CommandParserContext): CommandDescriptorBuilder = apply {
this.context += context
}
/** 设置权限要求 */
fun permission(permission: CommandPermission): CommandDescriptorBuilder = apply {
this.permission = permission
}
/** 设置权限要求 */
@JvmSynthetic
inline fun permission(crossinline block: CommandSender.() -> Boolean) {
this.permission = AnonymousCommandPermission(block)
}
fun usage(message: String): CommandDescriptorBuilder = apply {
usage = message
}
fun description(description: String): CommandDescriptorBuilder = apply {
this.description = description
}
/**
* 添加一个别名
* @param fullName 全名称. [CommandDescriptor.fullName]
*/
fun alias(fullName: String): CommandDescriptorBuilder = apply {
this.aliases.add(fullName.checkFullName("fullName"))
}
fun param(vararg params: CommandParam<*>): CommandDescriptorBuilder = apply {
this.params.addAll(params)
}
@JvmSynthetic
fun <T : Any> param(
name: String?,
type: KClass<T>,
overrideParser: CommandArgParser<T>? = null
): CommandDescriptorBuilder = apply {
this.params.add(CommandParam(name, type).apply { this._overrideParser = overrideParser })
}
fun <T : Any> param(
name: String?,
type: Class<T>,
overrideParser: CommandArgParser<T>? = null
): CommandDescriptorBuilder =
param(name, type, overrideParser)
inline fun <reified T : Any> param(
name: String? = null,
overrideParser: CommandArgParser<T>? = null
): CommandDescriptorBuilder =
param(name, T::class, overrideParser)
@JvmSynthetic
fun param(vararg pairs: Pair<String?, KClass<*>>): CommandDescriptorBuilder = apply {
for (pair in pairs) {
this.params.add(CommandParam(pair.first, pair.second))
}
}
@JvmSynthetic
fun params(block: ParamBlock.() -> Unit): CommandDescriptorBuilder = apply {
ParamBlock(params).apply(block)
}
@JvmSynthetic
fun param(vararg types: KClass<*>): CommandDescriptorBuilder = apply {
types.forEach { type -> params.add(CommandParam(null, type)) }
}
fun param(vararg types: Class<*>): CommandDescriptorBuilder = apply {
types.forEach { type -> params.add(CommandParam(null, type.kotlin)) }
}
fun build(): CommandDescriptor =
CommandDescriptor(fullName, usage, params, description, context, aliases.toTypedArray(), permission)
}
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
inline class ParamBlock internal constructor(@PublishedApi internal val list: MutableList<CommandParam<*>>) {
/** 添加一个名称为 [this], 类型为 [klass] 的参数. 返回添加成功的对象 */
infix fun <T : Any> String.typed(klass: KClass<T>): CommandParam<T> =
CommandParam(this, klass).also { list.add(it) }
/** 指定 [CommandParam.overrideParser] */
infix fun <T : Any> CommandParam<T>.using(parser: CommandArgParser<T>): CommandParam<T> =
this.apply { this._overrideParser = parser }
/** 覆盖 [CommandArgParser] */
inline infix fun <reified T : Any> String.using(parser: CommandArgParser<T>): CommandParam<T> =
this typed T::class using parser
}
*/
///////
/// internal
internal fun Any.flattenCommandComponents(): List<String> { internal fun Any.flattenCommandComponents(): List<String> {
val list = ArrayList<String>() val list = ArrayList<String>()
when (this) { when (this) {

View File

@ -9,6 +9,7 @@ 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.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import kotlin.reflect.KClass
sealed class CommandOwner sealed class CommandOwner
@ -20,8 +21,7 @@ internal abstract class ConsoleCommandOwner : CommandOwner()
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this } val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
@get:JvmName("getCommandPrefix") @get:JvmName("getCommandPrefix")
val CommandPrefix: String val CommandPrefix: String get() = InternalCommandManager.COMMAND_PREFIX
get() = InternalCommandManager._commandPrefix
fun CommandOwner.unregisterAllCommands() { fun CommandOwner.unregisterAllCommands() {
for (registeredCommand in registeredCommands) { for (registeredCommand in registeredCommands) {
@ -108,13 +108,6 @@ internal suspend fun List<Any>.executeCommand(origin: String, sender: CommandSen
if (this.isEmpty()) return false if (this.isEmpty()) return false
val command = InternalCommandManager.matchCommand(origin) ?: return false val command = InternalCommandManager.matchCommand(origin) ?: return false
TODO() TODO()
/*
command.descriptor.subCommands.forEach { sub ->
}.run {
sender.onDefault(
CommandArgs.parseFrom(command, sender, this@executeCommand.drop(command.fullName.size))
)
}*/
} }
internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean { internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
@ -125,19 +118,51 @@ internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
} }
internal object InternalCommandManager { internal object InternalCommandManager {
const val COMMAND_PREFIX = "/"
/**
* 全部注册的指令
* /mute -> MuteCommand
* /jinyan -> MuteCommand
*/
@JvmField @JvmField
internal val registeredCommands: MutableList<Command> = mutableListOf() internal val registeredCommands: MutableMap<String, Command> = mutableMapOf()
/**
* Command name of commands that are prefix optional
* mute -> MuteCommand
*/
private val quickMatchCommands: MutableMap<String, Command> = mutableMapOf()
@JvmField @JvmField
internal val modifyLock = ReentrantLock() internal val modifyLock = ReentrantLock()
internal var _commandPrefix: String = "/"
internal fun matchCommand(name: String): Command? { /**
return registeredCommands.firstOrNull { command -> * 从原始的command中解析出Command对象
command.descriptor.base.bakedSubNames.any { */
name.startsWith(it[0]) && (name.length <= it[0].length || name[it[0].length] == ' ') // 判断跟随空格 internal fun matchCommand(rawCommand: String): Command? {
} if(!rawCommand.startsWith('/')){
return quickMatchCommands[rawCommand
.substringBefore(' ')
.trim()
]
} }
return registeredCommands[rawCommand
.substringBefore(' ')
.trim()
]
} }
/**
* 从解析好的第一个字节来获取Command对象
*/
internal fun findCommand(name: String): Command? {
if(!name.startsWith('/')){
return quickMatchCommands[name]
}
return registeredCommands[name]
}
} }