diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt index 8f6e7b8bb..43a0b6d84 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt @@ -2,106 +2,51 @@ package net.mamoe.mirai.console.command -import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.SingleMessage -import kotlin.reflect.KClass /** * 指令描述. 包含名称, 权限要求, 参数解析器环境, 参数列表. + * + * 这是指令系统较低级的 API. 大部分情况下请使用 [Command] */ class CommandDescriptor( /** - * 包含子命令的全名. 如 "`group kick`", 其中 `kick` 为 `group` 的子命令 + * 子指令列表 */ - fullName: CommandFullName, + val subCommands: List, /** - * 用法说明 + * 是否建议 console 将这个指令强制注册为需要带 [前缀][CommandPrefix] 的指令. */ - usage: String, - /** - * 指令参数列表, 有顺序. - */ - val params: List>, - /** - * 指令说明 - */ - description: String = "", - /** - * 覆盖内建的指令参数解析器环境. - */ - overrideContext: CommandParserContext = CommandParserContext.Empty, - /** - * 指令别名 - */ - aliases: Array = arrayOf(), - /** - * 指令权限 - * - * @see CommandPermission.or 要求其中一个权限 - * @see CommandPermission.and 同时要求两个权限 - */ - val permission: CommandPermission = CommandPermission.Default + val suggestForcePrefix: Boolean = true, + /** 覆盖内建的指令参数解析器环境. */ + overrideContext: CommandParserContext = CommandParserContext.Empty ) { + /** 子指令描述 */ + inner class SubCommandDescriptor( + /** 为空字符串时代表默认 */ + val name: String, + /** 用法说明 */ + val usage: String, + /** 指令参数列表, 有顺序. */ + val params: List>, + /** 指令说明 */ + val description: String, + /** 指令别名 */ + val aliases: Array = arrayOf(), + /** + * 指令权限 + * @see CommandPermission.or 要求其中一个权限 + * @see CommandPermission.and 同时要求两个权限 + */ + val permission: CommandPermission = CommandPermission.Default + ) + /** * 指令参数解析器环境. */ val context: CommandParserContext = CommandParserContext.Builtins + overrideContext - - /** - * 指令别名 - */ - val aliases: Array = aliases.map { it.checkFullName("alias") }.toTypedArray() - - /** - * 指令说明 - */ - val description: String = description.trim() - - /** - * 用法说明 - */ - val usage: String = usage.trim() - - /** - * 包含子命令的全名. 如 "`group kick`", 其中 `kick` 为 `group` 的子命令 - * 元素类型可以为 [Message] 或 [String] - */ - val fullName: CommandFullName = fullName.checkFullName("fullName") - - /** - * `fullName + aliases` - */ - val allNames: Array = arrayOf(fullName, *aliases) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CommandDescriptor - - if (!fullName.contentEquals(other.fullName)) return false - if (usage != other.usage) return false - if (params != other.params) return false - if (description != other.description) return false - if (context != other.context) return false - if (!aliases.contentEquals(other.aliases)) return false - if (permission != other.permission) return false - - return true - } - - override fun hashCode(): Int { - var result = fullName.hashCode() - result = 31 * result + usage.hashCode() - result = 31 * result + params.hashCode() - result = 31 * result + description.hashCode() - result = 31 * result + context.hashCode() - result = 31 * result + aliases.contentHashCode() - result = 31 * result + permission.hashCode() - return result - } } @@ -109,13 +54,7 @@ class CommandDescriptor( * 检查指令参数数量是否足够, 类型是否匹配. * @throws IllegalArgumentException */ -fun Command.checkArgs(args: CommandArgs) = this.descriptor.checkArgs(args) - -/** - * 检查指令参数数量是否足够, 类型是否匹配. - * @throws IllegalArgumentException - */ -fun CommandDescriptor.checkArgs(args: CommandArgs) { +fun CommandDescriptor.SubCommandDescriptor.checkArgs(args: CommandArgs) { require(args.size >= this.params.size) { "No enough args. Required ${params.size}, but given ${args.size}" } params.forEachIndexed { index, commandParam -> require(commandParam.type.isInstance(args[index])) { @@ -125,6 +64,7 @@ fun CommandDescriptor.checkArgs(args: CommandArgs) { } +/* /** * 构建一个 [CommandDescriptor] */ @@ -133,15 +73,15 @@ inline fun CommandDescriptor( /** * 指令全名 */ - vararg fullNameComponents: Any, + vararg fullNameComponents: String, block: CommandDescriptorBuilder.() -> Unit = {} ): CommandDescriptor = CommandDescriptorBuilder(*fullNameComponents).apply(block).build() class CommandDescriptorBuilder( - vararg fullName: Any + vararg fullName: String ) { @PublishedApi - internal var fullName: CommandFullName = fullName.checkFullName("fullName") + internal var fullName: Array = fullName.checkFullName("fullName") @PublishedApi internal var context: CommandParserContext = CommandParserContext.Builtins @@ -156,7 +96,7 @@ class CommandDescriptorBuilder( internal var usage: String = "" @PublishedApi - internal var aliases: MutableList = mutableListOf() + internal var aliases: MutableList> = mutableListOf() @PublishedApi internal var description: String = "" @@ -194,9 +134,10 @@ class CommandDescriptorBuilder( /** * 添加一个别名 + * @param fullName 全名称. 见 [CommandDescriptor.fullName] */ - fun alias(vararg fullName: Any): CommandDescriptorBuilder = apply { - this.aliases.add(fullName) + fun alias(fullName: String): CommandDescriptorBuilder = apply { + this.aliases.add(fullName.checkFullName("fullName")) } fun param(vararg params: CommandParam<*>): CommandDescriptorBuilder = apply { @@ -250,6 +191,7 @@ class CommandDescriptorBuilder( 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>) { /** 添加一个名称为 [this], 类型为 [klass] 的参数. 返回添加成功的对象 */ @@ -265,23 +207,24 @@ inline class ParamBlock internal constructor(@PublishedApi internal val list: Mu this typed T::class using parser } + */ /////// /// internal -internal fun Any.flattenCommandComponents(): Sequence = when (this) { +internal fun Any.flattenCommandComponents(): Sequence = when (this) { is Array<*> -> this.asSequence().flatMap { it?.flattenCommandComponents() ?: throw java.lang.IllegalArgumentException("unexpected null value") } is String -> splitToSequence(' ').filterNot { it.isBlank() } is PlainText -> content.flattenCommandComponents() - is SingleMessage -> sequenceOf(this) + is SingleMessage -> sequenceOf(this.toString()) is MessageChain -> this.asSequence().flatMap { it.flattenCommandComponents() } else -> throw IllegalArgumentException("Illegal component: $this") } -internal fun CommandFullName.checkFullName(errorHint: String): CommandFullName { +internal fun Any.checkFullName(errorHint: String): Array { return flattenCommandComponents().toList().also { require(it.isNotEmpty()) { "$errorHint must not be empty" } }.toTypedArray() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt index 4d15ef759..c8b05be87 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt @@ -10,8 +10,6 @@ import net.mamoe.mirai.message.data.MessageChain import java.util.* import java.util.concurrent.locks.ReentrantLock -typealias CommandFullName = Array - sealed class CommandOwner abstract class PluginCommandOwner(plugin: PluginBase) : CommandOwner() @@ -21,6 +19,10 @@ internal abstract class ConsoleCommandOwner : CommandOwner() val CommandOwner.registeredCommands: List get() = InternalCommandManager.registeredCommands.filter { it.owner == this } +@get:JvmName("getCommandPrefix") +val CommandPrefix: String + get() = InternalCommandManager._commandPrefix + fun CommandOwner.unregisterAllCommands() { for (registeredCommand in registeredCommands) { registeredCommand.unregister() @@ -112,7 +114,7 @@ internal suspend fun List.executeCommand(sender: CommandSender): Boolean { } } -internal infix fun CommandFullName.matchesBeginning(list: List): Boolean { +internal infix fun Array.matchesBeginning(list: List): Boolean { this.forEachIndexed { index, any -> if (list[index] != any) return false } @@ -124,11 +126,12 @@ internal object InternalCommandManager { internal val registeredCommands: MutableList = mutableListOf() @JvmField - internal val nameToCommandMap: TreeMap = TreeMap(Comparator.comparingInt { it.size }) + internal val nameToCommandMap: TreeMap, Command> = TreeMap(Comparator.comparingInt { it.size }) @JvmField internal val modifyLock = ReentrantLock() + internal var _commandPrefix: String = "/" internal fun matchCommand(splitted: List): Command? { nameToCommandMap.entries.forEach { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandParam.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandParam.kt index 9d58feabb..b29876c87 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandParam.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandParam.kt @@ -9,17 +9,15 @@ import kotlin.reflect.KClass */ data class CommandParam( /** - * 参数名, 为 `null` 时即为匿名参数. - * 参数名允许重复 (尽管并不建议这样做). - * 参数名仅提供给 [CommandArgParser] 以发送更好的错误信息. + * 参数名. 不允许重复. */ - val name: String?, + val name: String, /** * 参数类型. 将从 [CommandDescriptor.context] 中寻找 [CommandArgParser] 解析. */ val type: KClass // exact type ) { - constructor(name: String?, type: KClass, parser: CommandArgParser) : this(name, type) { + constructor(name: String, type: KClass, parser: CommandArgParser) : this(name, type) { this._overrideParser = parser } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt index 22e71c2ce..43830534e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt @@ -22,11 +22,11 @@ import net.mamoe.mirai.contact.isOwner * * @see AnonymousCommandPermission */ -abstract class CommandPermission { +interface CommandPermission { /** * 判断 [this] 是否拥有这个指令的权限 */ - abstract fun CommandSender.hasPermission(): Boolean + fun CommandSender.hasPermission(): Boolean /** @@ -43,14 +43,14 @@ abstract class CommandPermission { /** * 任何人都可以使用这个指令 */ - object Any : CommandPermission() { + object Any : CommandPermission { override fun CommandSender.hasPermission(): Boolean = true } /** * 任何人都不能使用这个指令. 指令只能通过代码在 [execute] 使用 */ - object None : CommandPermission() { + object None : CommandPermission { override fun CommandSender.hasPermission(): Boolean = false } @@ -62,7 +62,7 @@ abstract class CommandPermission { * 指定只有来自某个 [Bot] 的管理员或群主才可以使用这个指令 */ vararg val fromBot: Long - ) : CommandPermission() { + ) : CommandPermission { constructor(vararg fromBot: Bot) : this(*fromBot.map { it.id }.toLongArray()) override fun CommandSender.hasPermission(): Boolean { @@ -72,7 +72,7 @@ abstract class CommandPermission { /** * 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令 */ - companion object Any : CommandPermission() { + companion object Any : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this is MemberCommandSender && this.user.isOperator() } @@ -87,7 +87,7 @@ abstract class CommandPermission { * 指定只有来自某个 [Bot] 的群主才可以使用这个指令 */ vararg val fromBot: Long - ) : CommandPermission() { + ) : CommandPermission { constructor(vararg fromBot: Bot) : this(*fromBot.map { it.id }.toLongArray()) override fun CommandSender.hasPermission(): Boolean { @@ -97,7 +97,7 @@ abstract class CommandPermission { /** * 来自任何 [Bot] 的任何一个群主都可以使用这个指令 */ - companion object Any : CommandPermission() { + companion object Any : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this is MemberCommandSender && this.user.isOwner() } @@ -112,7 +112,7 @@ abstract class CommandPermission { * 指定只有来自某个 [Bot] 的管理员 (不包含群主) 才可以使用这个指令 */ vararg val fromBot: Long - ) : CommandPermission() { + ) : CommandPermission { constructor(vararg fromBot: Bot) : this(*fromBot.map { it.id }.toLongArray()) override fun CommandSender.hasPermission(): Boolean { @@ -122,7 +122,7 @@ abstract class CommandPermission { /** * 来自任何 [Bot] 的任何一个管理员 (不包含群主) 都可以使用这个指令 */ - companion object Any : CommandPermission() { + companion object Any : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this is MemberCommandSender && this.user.isAdministrator() } @@ -137,7 +137,7 @@ abstract class CommandPermission { * 指定只有来自某个 [Bot] 的管理员或群主才可以使用这个指令 */ vararg val fromBot: Long - ) : CommandPermission() { + ) : CommandPermission { constructor(vararg fromBot: Bot) : this(*fromBot.map { it.id }.toLongArray()) override fun CommandSender.hasPermission(): Boolean { @@ -147,7 +147,7 @@ abstract class CommandPermission { /** * 任何 [Bot] 的 manager 都可以使用这个指令 */ - companion object Any : CommandPermission() { + companion object Any : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this is MemberCommandSender && this.user.isManager } @@ -157,7 +157,7 @@ abstract class CommandPermission { /** * 仅控制台能使用和这个指令 */ - object Console : CommandPermission() { + object Console : CommandPermission { override fun CommandSender.hasPermission(): Boolean = false } @@ -173,7 +173,7 @@ abstract class CommandPermission { @JvmSynthetic @Suppress("FunctionName") inline fun AnonymousCommandPermission(crossinline block: CommandSender.() -> Boolean): CommandPermission { - return object : CommandPermission() { + return object : CommandPermission { override fun CommandSender.hasPermission(): Boolean = block() } } @@ -188,7 +188,7 @@ inline fun CommandPermission.hasPermission(sender: CommandSender): Boolean = thi internal class OrCommandPermission( private val first: CommandPermission, private val second: CommandPermission -) : CommandPermission() { +) : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this.hasPermission(first) || this.hasPermission(second) } @@ -197,7 +197,7 @@ internal class OrCommandPermission( internal class AndCommandPermission( private val first: CommandPermission, private val second: CommandPermission -) : CommandPermission() { +) : CommandPermission { override fun CommandSender.hasPermission(): Boolean { return this.hasPermission(first) && this.hasPermission(second) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt index 263e294ce..83c2cd4e7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt @@ -6,23 +6,13 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + package net.mamoe.mirai.console.plugins -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.Serializable -import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.command.Command -import net.mamoe.mirai.console.command.CommandSender -import net.mamoe.mirai.console.command.description -import net.mamoe.mirai.console.encodeToString -import net.mamoe.mirai.utils.LockFreeLinkedList -import java.io.File -import java.io.InputStream -import java.net.JarURLConnection -import java.net.URL -import java.util.jar.JarFile sealed class JarPlugin : Plugin(), CoroutineScope { @@ -62,7 +52,7 @@ object JarPluginLoader : PluginLoader { } } - +/* object PluginManagerOld { /** * 通过插件获取介绍 @@ -433,4 +423,4 @@ object PluginManagerOld { jar.entries().asSequence().filter { it.name == toFind }.firstOrNull() ?: return null return URL("jar:file:" + jarFile.absoluteFile + "!/" + toFindFile.name).openConnection().inputStream } -} \ No newline at end of file +}*/ \ No newline at end of file