mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Add docs
This commit is contained in:
parent
c3f7101830
commit
f73f064d1a
@ -12,13 +12,8 @@
|
|||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
import net.mamoe.mirai.console.command.description.*
|
import net.mamoe.mirai.console.command.description.*
|
||||||
import net.mamoe.mirai.console.command.description.CommandParam
|
|
||||||
import net.mamoe.mirai.console.command.description.CommandParserContext
|
|
||||||
import net.mamoe.mirai.console.command.description.EmptyCommandParserContext
|
|
||||||
import net.mamoe.mirai.console.command.description.plus
|
|
||||||
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 java.lang.Exception
|
|
||||||
import kotlin.reflect.KAnnotatedElement
|
import kotlin.reflect.KAnnotatedElement
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.*
|
import kotlin.reflect.full.*
|
||||||
@ -29,20 +24,37 @@ import kotlin.reflect.full.*
|
|||||||
* @see register 注册这个指令
|
* @see register 注册这个指令
|
||||||
*/
|
*/
|
||||||
interface Command {
|
interface Command {
|
||||||
|
/**
|
||||||
|
* 指令名. 需要至少有一个元素. 所有元素都不能带有空格
|
||||||
|
*/
|
||||||
val names: Array<out String>
|
val names: Array<out String>
|
||||||
|
|
||||||
fun getPrimaryName():String = names[0]
|
|
||||||
|
|
||||||
val usage: String
|
val usage: String
|
||||||
val description: String
|
val description: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令权限
|
||||||
|
*/
|
||||||
val permission: CommandPermission
|
val permission: CommandPermission
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为 `true` 时表示 [指令前缀][CommandPrefix] 可选
|
||||||
|
*/
|
||||||
val prefixOptional: Boolean
|
val prefixOptional: Boolean
|
||||||
|
|
||||||
val owner: CommandOwner
|
val owner: CommandOwner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args 指令参数. 可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
|
||||||
|
*/
|
||||||
suspend fun onCommand(sender: CommandSender, args: Array<out Any>)
|
suspend fun onCommand(sender: CommandSender, args: Array<out Any>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主要指令名. 为 [Command.names] 的第一个元素.
|
||||||
|
*/
|
||||||
|
val Command.primaryName: String get() = names[0]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 功能最集中的Commend
|
* 功能最集中的Commend
|
||||||
* 支持且只支持有sub的指令
|
* 支持且只支持有sub的指令
|
||||||
@ -55,20 +67,26 @@ interface Command {
|
|||||||
abstract class CompositeCommand @JvmOverloads constructor(
|
abstract class CompositeCommand @JvmOverloads constructor(
|
||||||
override val owner: CommandOwner,
|
override val owner: CommandOwner,
|
||||||
vararg names: String,
|
vararg names: String,
|
||||||
override val description: String = "no description available",
|
description: String = "no description available",
|
||||||
override val permission: CommandPermission = CommandPermission.Default,
|
override val permission: CommandPermission = CommandPermission.Default,
|
||||||
override val prefixOptional: Boolean = false,
|
override val prefixOptional: Boolean = false,
|
||||||
overrideContext: CommandParserContext = EmptyCommandParserContext
|
overrideContext: CommandParserContext = EmptyCommandParserContext
|
||||||
) : Command {
|
) : Command {
|
||||||
|
override val description = description.trimIndent()
|
||||||
class IllegalParameterException(message:String): Exception(message)
|
|
||||||
|
|
||||||
|
|
||||||
override val names: Array<out String> =
|
override val names: Array<out String> =
|
||||||
names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).toTypedArray()
|
names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).also { list ->
|
||||||
|
list.firstOrNull { !it.isValidSubName() }?.let {
|
||||||
|
error("Name is not valid: $it")
|
||||||
|
}
|
||||||
|
}.toTypedArray()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [CommandArgParser] 的环境
|
||||||
|
*/
|
||||||
val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
|
val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
|
||||||
|
|
||||||
override lateinit var usage: String
|
override var usage: String = "<command build failed>" // initialized by subCommand reflection
|
||||||
|
internal set
|
||||||
|
|
||||||
/** 指定子指令要求的权限 */
|
/** 指定子指令要求的权限 */
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
@ -108,6 +126,9 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class IllegalParameterException internal constructor(message: String) : Exception(message)
|
||||||
|
|
||||||
internal val subCommands: Array<SubCommandDescriptor> by lazy {
|
internal val subCommands: Array<SubCommandDescriptor> by lazy {
|
||||||
|
|
||||||
val buildUsage = StringBuilder(this.description).append(": \n")
|
val buildUsage = StringBuilder(this.description).append(": \n")
|
||||||
@ -121,38 +142,38 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
val notStatic = function.findAnnotation<JvmStatic>()==null
|
val notStatic = function.findAnnotation<JvmStatic>() == null
|
||||||
|
|
||||||
val overridePermission = function.findAnnotation<Permission>()//optional
|
val overridePermission = function.findAnnotation<Permission>()//optional
|
||||||
|
|
||||||
val subDescription = function.findAnnotation<Description>()?.description?:"no description available"
|
val subDescription = function.findAnnotation<Description>()?.description ?: "no description available"
|
||||||
|
|
||||||
if((function.returnType.classifier as? KClass<*>)?.isSubclassOf(Boolean::class) != true){
|
if ((function.returnType.classifier as? KClass<*>)?.isSubclassOf(Boolean::class) != true) {
|
||||||
throw IllegalParameterException("Return Type of SubCommand must be Boolean")
|
throw IllegalParameterException("Return Type of SubCommand must be Boolean")
|
||||||
}
|
}
|
||||||
|
|
||||||
val parameter = function.parameters.toMutableList()
|
val parameter = function.parameters.toMutableList()
|
||||||
|
|
||||||
if (parameter.isEmpty()){
|
if (parameter.isEmpty()) {
|
||||||
throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.getPrimaryName() + " should be <out CommandSender>")
|
throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.primaryName + " should be <out CommandSender>")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(notStatic){
|
if (notStatic) {
|
||||||
parameter.removeAt(0)
|
parameter.removeAt(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
(parameter.removeAt(0)).let {receiver ->
|
(parameter.removeAt(0)).let { receiver ->
|
||||||
if (
|
if (
|
||||||
receiver.isVararg ||
|
receiver.isVararg ||
|
||||||
((receiver.type.classifier as? KClass<*>).also { print(it) }
|
((receiver.type.classifier as? KClass<*>).also { print(it) }
|
||||||
?.isSubclassOf(CommandSender::class) != true)
|
?.isSubclassOf(CommandSender::class) != true)
|
||||||
) {
|
) {
|
||||||
throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.getPrimaryName() + " should be <out CommandSender>")
|
throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.primaryName + " should be <out CommandSender>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val commandName = function.findAnnotation<SubCommand>()!!.name.map {
|
val commandName = function.findAnnotation<SubCommand>()!!.name.map {
|
||||||
if(!it.isValidSubName()){
|
if (!it.isValidSubName()) {
|
||||||
error("SubName $it is not valid")
|
error("SubName $it is not valid")
|
||||||
}
|
}
|
||||||
it
|
it
|
||||||
@ -160,20 +181,22 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
|
|
||||||
//map parameter
|
//map parameter
|
||||||
val parms = parameter.map {
|
val parms = parameter.map {
|
||||||
buildUsage.append("/" + getPrimaryName() + " ")
|
buildUsage.append("/$primaryName ")
|
||||||
|
|
||||||
if(it.isVararg){
|
if (it.isVararg) {
|
||||||
throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.getPrimaryName() + " should not be var arg")
|
throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.primaryName + " should not be var arg")
|
||||||
}
|
}
|
||||||
if(it.isOptional){
|
if (it.isOptional) {
|
||||||
throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.getPrimaryName() + " should not be var optional")
|
throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.primaryName + " should not be var optional")
|
||||||
}
|
}
|
||||||
|
|
||||||
val argName = it.findAnnotation<Name>()?.name?:it.name?:"unknown"
|
val argName = it.findAnnotation<Name>()?.name ?: it.name ?: "unknown"
|
||||||
buildUsage.append("<").append(argName).append("> ").append(" ")
|
buildUsage.append("<").append(argName).append("> ").append(" ")
|
||||||
CommandParam(
|
CommandParam(
|
||||||
argName,
|
argName,
|
||||||
(it.type.classifier as? KClass<*>)?: throw IllegalParameterException("unsolved type reference from param " + it.name + " in " + function.name + " from " + this.getPrimaryName()))
|
(it.type.classifier as? KClass<*>)
|
||||||
|
?: throw IllegalParameterException("unsolved type reference from param " + it.name + " in " + function.name + " from " + this.primaryName)
|
||||||
|
)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
buildUsage.append(subDescription).append("\n")
|
buildUsage.append(subDescription).append("\n")
|
||||||
@ -184,9 +207,9 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
subDescription,
|
subDescription,
|
||||||
overridePermission?.permission?.getInstance() ?: permission,
|
overridePermission?.permission?.getInstance() ?: permission,
|
||||||
onCommand = block { sender: CommandSender, args: Array<out Any> ->
|
onCommand = block { sender: CommandSender, args: Array<out Any> ->
|
||||||
if(notStatic) {
|
if (notStatic) {
|
||||||
function.callSuspend(this,sender, *args) as Boolean
|
function.callSuspend(this, sender, *args) as Boolean
|
||||||
}else{
|
} else {
|
||||||
function.callSuspend(sender, *args) as Boolean
|
function.callSuspend(sender, *args) as Boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +340,7 @@ internal fun Any.flattenCommandComponents(): ArrayList<Any> {
|
|||||||
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
|
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
|
||||||
findAnnotation<T>() != null
|
findAnnotation<T>() != null
|
||||||
|
|
||||||
internal inline fun <T:Any> KClass<out T>.getInstance():T {
|
internal inline fun <T : Any> KClass<out T>.getInstance(): T {
|
||||||
return this.objectInstance ?: this.createInstance()
|
return this.objectInstance ?: this.createInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,34 +8,67 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
@file:JvmName("CommandManager")
|
@file:JvmName("CommandManagerKt")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
import kotlinx.atomicfu.locks.withLock
|
import kotlinx.atomicfu.locks.withLock
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.mamoe.mirai.console.plugin.Plugin
|
import net.mamoe.mirai.console.plugin.Plugin
|
||||||
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
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令的所有者.
|
||||||
|
* @see PluginCommandOwner
|
||||||
|
*/
|
||||||
sealed class CommandOwner
|
sealed class CommandOwner
|
||||||
|
|
||||||
|
@MiraiInternalAPI
|
||||||
object TestCommandOwner : CommandOwner()
|
object TestCommandOwner : CommandOwner()
|
||||||
|
|
||||||
abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner()
|
/**
|
||||||
|
* 插件指令所有者. 插件只能通过 [PluginCommandOwner] 管理指令.
|
||||||
|
*/
|
||||||
|
abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner() {
|
||||||
|
init {
|
||||||
|
if (plugin is CoroutineScope) { // JVM Plugin
|
||||||
|
plugin.coroutineContext[Job]?.invokeOnCompletion {
|
||||||
|
this.unregisterAllCommands()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 由前端实现
|
/**
|
||||||
|
* 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner].
|
||||||
|
*
|
||||||
|
* 由前端实现
|
||||||
|
*/
|
||||||
internal abstract class ConsoleCommandOwner : CommandOwner()
|
internal abstract class ConsoleCommandOwner : CommandOwner()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已经注册了的指令列表
|
* 获取已经注册了的属于这个 [CommandOwner] 的指令列表.
|
||||||
|
* @see JCommandManager.getRegisteredCommands Java 方法
|
||||||
*/
|
*/
|
||||||
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
|
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令前缀, 如 '/'
|
||||||
|
* @see JCommandManager.getCommandPrefix Java 方法
|
||||||
|
*/
|
||||||
@get:JvmName("getCommandPrefix")
|
@get:JvmName("getCommandPrefix")
|
||||||
val CommandPrefix: String
|
val CommandPrefix: String
|
||||||
get() = InternalCommandManager.COMMAND_PREFIX
|
get() = InternalCommandManager.COMMAND_PREFIX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消注册所有属于 [this] 的指令
|
||||||
|
* @see JCommandManager.unregisterAllCommands Java 方法
|
||||||
|
*/
|
||||||
fun CommandOwner.unregisterAllCommands() {
|
fun CommandOwner.unregisterAllCommands() {
|
||||||
for (registeredCommand in registeredCommands) {
|
for (registeredCommand in registeredCommands) {
|
||||||
registeredCommand.unregister()
|
registeredCommand.unregister()
|
||||||
@ -43,10 +76,25 @@ fun CommandOwner.unregisterAllCommands() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册一个指令. 若此指令已经注册或有已经注册的指令与 [SubCommandDescriptor] 重名, 返回 `false`
|
* 注册一个指令.
|
||||||
|
*
|
||||||
|
* @param override 是否覆盖重名指令.
|
||||||
|
*
|
||||||
|
* 若原有指令 P, 其 [Command.names] 为 'a', 'b', 'c'.
|
||||||
|
* 新指令 Q, 其 [Command.names] 为 'b', 将会覆盖原指令 A 注册的 'b'.
|
||||||
|
*
|
||||||
|
* 即注册完成后, 'a' 和 'c' 将会解析到指令 P, 而 'b' 会解析到指令 Q.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* 若已有重名指令, 且 [override] 为 `false`, 返回 `false`;
|
||||||
|
* 若已有重名指令, 但 [override] 为 `true`, 覆盖原有指令并返回 `true`.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
@JvmOverloads
|
||||||
if (findDuplicate() != null) return false
|
fun Command.register(override: Boolean = false): Boolean = InternalCommandManager.modifyLock.withLock {
|
||||||
|
if (!override) {
|
||||||
|
if (findDuplicate() != null) return false
|
||||||
|
}
|
||||||
InternalCommandManager.registeredCommands.add(this@register)
|
InternalCommandManager.registeredCommands.add(this@register)
|
||||||
if (this.prefixOptional) {
|
if (this.prefixOptional) {
|
||||||
for (name in this.names) {
|
for (name in this.names) {
|
||||||
@ -54,6 +102,7 @@ fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (name in this.names) {
|
for (name in this.names) {
|
||||||
|
InternalCommandManager.optionalPrefixCommandMap.remove(name) // ensure resolution consistency
|
||||||
InternalCommandManager.requiredPrefixCommandMap[name] = this
|
InternalCommandManager.requiredPrefixCommandMap[name] = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,13 +110,13 @@ fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找是否有重名的指令. 返回重名的指令.
|
* 查找并返回重名的指令. 返回重名指令.
|
||||||
*/
|
*/
|
||||||
fun Command.findDuplicate(): Command? =
|
fun Command.findDuplicate(): Command? =
|
||||||
InternalCommandManager.registeredCommands.firstOrNull { it.names intersects this.names }
|
InternalCommandManager.registeredCommands.firstOrNull { it.names intersects this.names }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消注册这个指令. 若指令未注册, 返回 `false`
|
* 取消注册这个指令. 若指令未注册, 返回 `false`.
|
||||||
*/
|
*/
|
||||||
fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
||||||
InternalCommandManager.registeredCommands.remove(this)
|
InternalCommandManager.registeredCommands.remove(this)
|
||||||
@ -78,6 +127,8 @@ fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
|||||||
/**
|
/**
|
||||||
* 解析并执行一个指令
|
* 解析并执行一个指令
|
||||||
*
|
*
|
||||||
|
* Java 调用方式: `<static> CommandManager.executeCommand(Command)`
|
||||||
|
*
|
||||||
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
||||||
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
||||||
*/
|
*/
|
||||||
@ -88,6 +139,7 @@ suspend fun CommandSender.executeCommand(vararg messages: Any): Boolean {
|
|||||||
messages[0].let { if (it is SingleMessage) it.toString() else it.toString().substringBefore(' ') })
|
messages[0].let { if (it is SingleMessage) it.toString() else it.toString().substringBefore(' ') })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] }
|
internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,6 +151,7 @@ suspend fun CommandSender.executeCommand(message: MessageChain): Boolean {
|
|||||||
return executeCommandInternal(message, message[0].toString())
|
return executeCommandInternal(message, message[0].toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
internal suspend inline fun CommandSender.executeCommandInternal(
|
internal suspend inline fun CommandSender.executeCommandInternal(
|
||||||
messages: Any,
|
messages: Any,
|
||||||
commandName: String
|
commandName: String
|
||||||
|
Loading…
Reference in New Issue
Block a user