mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
change command structure
This commit is contained in:
parent
868a840494
commit
c9f3cd92dd
@ -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] 相同.
|
||||||
*/
|
*/
|
||||||
|
@ -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) {
|
||||||
|
@ -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]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user