Rearrange implementations

This commit is contained in:
Him188 2020-06-26 20:07:29 +08:00
parent d2194c2c2b
commit 2ba18ab7e4
13 changed files with 363 additions and 308 deletions

View File

@ -64,7 +64,7 @@ interface MiraiConsole {
@MiraiExperimentalAPI
fun newLogger(identity: String?): MiraiLogger
companion object INSTANCE : MiraiConsole by MiraiConsoleImpl
companion object INSTANCE : MiraiConsole by MiraiConsoleInternal
}
@ -77,7 +77,7 @@ internal object MiraiConsoleInitializer {
/** 由前端调用 */
internal fun init(instance: IMiraiConsole) {
this.instance = instance
MiraiConsoleImpl.initialize()
MiraiConsoleInternal.initialize()
}
}
@ -90,7 +90,7 @@ internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mira
/**
* mirai 控制台实例.
*/
internal object MiraiConsoleImpl : CoroutineScope, IMiraiConsole, MiraiConsole {
internal object MiraiConsoleInternal : CoroutineScope, IMiraiConsole, MiraiConsole {
override val pluginCenter: PluginCenter get() = CuiPluginCenter
private val instance: IMiraiConsole

View File

@ -11,17 +11,15 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.description.*
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.SingleMessage
import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass
import kotlin.reflect.full.*
/**
* 指令
* 通常情况下, 你的指令应继承 @see CompositeCommand/SimpleCommand
* @see register 注册这个指令
*
* @see SimpleCommand
* @see CompositeCommand
*/
interface Command {
/**
@ -30,6 +28,7 @@ interface Command {
val names: Array<out String>
val usage: String
val description: String
/**
@ -47,289 +46,12 @@ interface Command {
/**
* @param args 指令参数. 可能是 [SingleMessage] [String]. 且已经以 ' ' 分割.
*/
suspend fun onCommand(sender: CommandSender, args: Array<out Any>)
suspend fun CommandSender.onCommand(args: Array<out Any>)
}
suspend inline fun Command.onCommand(sender: CommandSender, args: Array<out Any>) = sender.run { onCommand(args) }
/**
* 主要指令名. [Command.names] 的第一个元素.
*/
val Command.primaryName: String get() = names[0]
/**
* 功能最集中的Commend
* 支持且只支持有sub的指令
* :
* /mute add
* /mute remove
* /mute addandremove (sub is case insensitive, lowercase are recommend)
* /mute add and remove('add and remove' consider as a sub)
*/
abstract class CompositeCommand @JvmOverloads constructor(
override val owner: CommandOwner,
vararg names: String,
description: String = "no description available",
override val permission: CommandPermission = CommandPermission.Default,
override val prefixOptional: Boolean = false,
overrideContext: CommandParserContext = EmptyCommandParserContext
) : Command {
override val description = description.trimIndent()
override val names: Array<out String> =
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
override var usage: String = "<command build failed>" // initialized by subCommand reflection
internal set
/** 指定子指令要求的权限 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Permission(val permission: KClass<out CommandPermission>)
/** 标记一个函数为子指令 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class SubCommand(vararg val name: String)
/** 指令描述 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Description(val description: String)
/** 参数名, 将参与构成 [usage] */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Name(val name: String)
final override suspend fun onCommand(sender: CommandSender, args: Array<out Any>) {
matchSubCommand(args)?.parseAndExecute(sender, args) ?: kotlin.run {
defaultSubCommand.onCommand(sender, args)
}
subCommands
}
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
DefaultSubCommandDescriptor(
"",
CommandPermission.Default,
onCommand = block { sender: CommandSender, args: Array<out Any> ->
false//not supported yet
}
)
}
class IllegalParameterException internal constructor(message: String) : Exception(message)
internal val subCommands: Array<SubCommandDescriptor> by lazy {
val buildUsage = StringBuilder(this.description).append(": \n")
this@CompositeCommand::class.declaredFunctions.filter { it.hasAnnotation<SubCommand>() }.map { function ->
val notStatic = function.findAnnotation<JvmStatic>()==null
val overridePermission = function.findAnnotation<Permission>()//optional
val subDescription = function.findAnnotation<Description>()?.description?:"no description available"
if ((function.returnType.classifier as? KClass<*>)?.isSubclassOf(Boolean::class) != true) {
throw IllegalParameterException("Return Type of SubCommand must be Boolean")
}
val parameter = function.parameters.toMutableList()
if (parameter.isEmpty()){
throw IllegalParameterException("First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.primaryName + " should be <out CommandSender>")
}
if (notStatic) {
parameter.removeAt(0)
}
(parameter.removeAt(0)).let {receiver ->
if (
receiver.isVararg ||
((receiver.type.classifier as? KClass<*>).also { print(it) }
?.isSubclassOf(CommandSender::class) != true)
) {
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 {
if (!it.isValidSubName()) {
error("SubName $it is not valid")
}
it
}.toTypedArray()
//map parameter
val parms = parameter.map {
buildUsage.append("/$primaryName ")
if (it.isVararg) {
throw IllegalParameterException("parameter for sub commend " + function.name + " from " + this.primaryName + " should not be var arg")
}
if (it.isOptional) {
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"
buildUsage.append("<").append(argName).append("> ").append(" ")
CommandParam(
argName,
(it.type.classifier as? KClass<*>)
?: throw IllegalParameterException("unsolved type reference from param " + it.name + " in " + function.name + " from " + this.primaryName)
)
}.toTypedArray()
buildUsage.append(subDescription).append("\n")
SubCommandDescriptor(
commandName,
parms,
subDescription,
overridePermission?.permission?.getInstance() ?: permission,
onCommand = block { sender: CommandSender, args: Array<out Any> ->
if (notStatic) {
function.callSuspend(this, sender, *args) as Boolean
} else {
function.callSuspend(sender, *args) as Boolean
}
}
)
}.toTypedArray().also {
usage = buildUsage.toString()
}
}
private fun block(block: suspend (CommandSender, Array<out Any>) -> Boolean): suspend (CommandSender, Array<out Any>) -> Boolean {
return block
}
@JvmField
internal val bakedCommandNameToSubDescriptorArray: Map<Array<String>, SubCommandDescriptor> = kotlin.run {
val map = LinkedHashMap<Array<String>, SubCommandDescriptor>(subCommands.size * 2)
for (descriptor in subCommands) {
for (name in descriptor.bakedSubNames) {
map[name] = descriptor
}
}
map.toSortedMap(Comparator { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() })
}
internal inner class DefaultSubCommandDescriptor(
val description: String,
val permission: CommandPermission,
val onCommand: suspend (sender: CommandSender, rawArgs: Array<out Any>) -> Boolean
)
internal inner class SubCommandDescriptor(
val names: Array<String>,
val params: Array<CommandParam<*>>,
val description: String,
val permission: CommandPermission,
val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean
) {
internal suspend inline fun parseAndExecute(
sender: CommandSender,
argsWithSubCommandNameNotRemoved: Array<out Any>
) {
if (!onCommand(sender, parseArgs(sender, argsWithSubCommandNameNotRemoved, names.size))) {
sender.sendMessage(usage)
}
}
@JvmField
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): Array<out Any> {
require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size - offset}" }
return Array(this.params.size) { index ->
val param = params[index]
val rawArg = rawArgs[offset + index]
when (rawArg) {
is String -> context[param.type]?.parse(rawArg, sender)
is SingleMessage -> context[param.type]?.parse(rawArg, sender)
else -> throw IllegalArgumentException("Illegal argument type: ${rawArg::class.qualifiedName}")
} ?: error("Cannot find a parser for $rawArg")
}
}
}
/**
* @param rawArgs 元素类型必须为 [SingleMessage] [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException]
*/
internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor? {
val maxCount = rawArgs.size - 1
var cur = 0
bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) ->
if (name.size != cur) {
if (cur++ == maxCount) return null
}
if (name.contentEqualsOffset(rawArgs, length = cur)) {
return descriptor
}
}
return null
}
}
abstract class SimpleCommand(
override val owner: CommandOwner,
val name: String,
val alias: Array<String> = arrayOf()
) : Command {
abstract suspend fun CommandSender.onCommand(args: List<Any>)
}
abstract class RawCommand(
final override val owner: CommandOwner,
name: String,
alias: Array<String> = arrayOf()
) : Command {
final override val names: Array<String> = arrayOf(name, *alias)
}
private fun <T> Array<T>.contentEqualsOffset(other: Array<out Any>, length: Int): Boolean {
repeat(length) { index ->
if (other[index].toString() != this[index]) {
return false
}
}
return true
}
internal val ILLEGAL_SUB_NAME_CHARS = "\\/!@#$%^&*()_+-={}[];':\",.<>?`~".toCharArray()
internal fun String.isValidSubName(): Boolean = ILLEGAL_SUB_NAME_CHARS.none { it in this }
internal fun String.bakeSubName(): Array<String> = split(' ').filterNot { it.isBlank() }.toTypedArray()
internal fun Any.flattenCommandComponents(): ArrayList<Any> {
val list = ArrayList<Any>()
when (this::class.java) { // faster than is
String::class.java -> (this as String).splitToSequence(' ').filterNot { it.isBlank() }.forEach { list.add(it) }
PlainText::class.java -> (this as PlainText).content.splitToSequence(' ').filterNot { it.isBlank() }
.forEach { list.add(it) }
SingleMessage::class.java -> list.add(this as SingleMessage)
Array<Any>::class.java -> (this as Array<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
Iterable::class.java -> (this as Iterable<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
else -> list.add(this.toString())
}
return list
}
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
findAnnotation<T>() != null
internal inline fun <T : Any> KClass<out T>.getInstance(): T {
return this.objectInstance ?: this.createInstance()
}

View File

@ -18,6 +18,8 @@ package net.mamoe.mirai.console.command
import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.console.MiraiConsoleInternal
import net.mamoe.mirai.console.command.internal.*
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
@ -50,7 +52,11 @@ abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner() {
*
* 由前端实现
*/
internal abstract class ConsoleCommandOwner : CommandOwner()
internal abstract class ConsoleCommandOwner : CommandOwner() {
companion object {
internal val instance get() = MiraiConsoleInternal.consoleCommandOwner
}
}
/**
* 获取已经注册了的属于这个 [CommandOwner] 的指令列表.

View File

@ -13,18 +13,17 @@ package net.mamoe.mirai.console.command
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.utils.JavaFriendlyAPI
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText
import org.jetbrains.annotations.Contract
/**
* 指令发送者
*
* @see AbstractCommandSender 请继承于该抽象类
* @see ConsoleCommandSender
* @see UserCommandSender
*/
@Suppress("FunctionName")
interface CommandSender {

View File

@ -0,0 +1,80 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.description.CommandArgParser
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.console.command.internal.CompositeCommandImpl
import net.mamoe.mirai.console.command.internal.isValidSubName
import kotlin.reflect.KClass
/**
* 功能最集中的Commend
* 支持且只支持有sub的指令
* :
* /mute add
* /mute remove
* /mute addandremove (sub is case insensitive, lowercase are recommend)
* /mute add and remove('add and remove' consider as a sub)
*/
abstract class CompositeCommand @JvmOverloads constructor(
final override val owner: CommandOwner,
vararg names: String,
description: String = "no description available",
final override val permission: CommandPermission = CommandPermission.Default,
final override val prefixOptional: Boolean = false,
overrideContext: CommandParserContext = EmptyCommandParserContext
) : Command, CompositeCommandImpl() {
final override val description = description.trimIndent()
final override val names: Array<out String> =
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
final override val usage: String get() = super.usage
/** 指定子指令要求的权限 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Permission(val permission: KClass<out CommandPermission>)
/** 标记一个函数为子指令 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class SubCommand(vararg val name: String)
/** 指令描述 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Description(val description: String)
/** 参数名, 将参与构成 [usage] */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Name(val name: String)
final override suspend fun onCommand(sender: CommandSender, args: Array<out Any>) {
matchSubCommand(args)?.parseAndExecute(sender, args) ?: kotlin.run {
defaultSubCommand.onCommand(sender, args)
}
}
}

View File

@ -0,0 +1,12 @@
package net.mamoe.mirai.console.command
abstract class SimpleCommand(
override val owner: CommandOwner,
override vararg val names: String,
override val usage: String,
override val description: String,
override val permission: CommandPermission = CommandPermission.Default,
override val prefixOptional: Boolean = false
) : Command {
abstract override suspend fun CommandSender.onCommand(args: Array<out Any>)
}

View File

@ -20,9 +20,11 @@ import kotlin.contracts.contract
* this output type of that arg
* input is always String
*/
abstract class CommandArgParser<out T : Any> {
abstract fun parse(raw: String, sender: CommandSender): T
open fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
interface CommandArgParser<out T : Any> {
fun parse(raw: String, sender: CommandSender): T
@JvmDefault
fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
}
fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T {
@ -61,7 +63,7 @@ inline fun CommandArgParser<*>.checkArgument(
@JvmSynthetic
inline fun <T : Any> CommandArgParser(
crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
): CommandArgParser<T> = object : CommandArgParser<T>() {
): CommandArgParser<T> = object : CommandArgParser<T> {
override fun parse(raw: String, sender: CommandSender): T = parser(raw, sender)
}

View File

@ -10,7 +10,11 @@
package net.mamoe.mirai.console.command.description
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.BotAwareCommandSender
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.MemberCommandSender
import net.mamoe.mirai.console.command.UserCommandSender
import net.mamoe.mirai.console.command.internal.fuzzySearchMember
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member

View File

@ -0,0 +1,219 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.command.internal
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.description.CommandParam
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.SingleMessage
import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass
import kotlin.reflect.full.*
internal abstract class CompositeCommandImpl : Command {
@JvmField
@Suppress("PropertyName")
internal var _usage: String = "<command build failed>"
override val usage: String // initialized by subCommand reflection
get() = _usage
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
DefaultSubCommandDescriptor(
"",
CommandPermission.Default,
onCommand = block { sender: CommandSender, args: Array<out Any> ->
false//not supported yet
}
)
}
internal val subCommands: Array<SubCommandDescriptor> by lazy {
@Suppress("CAST_NEVER_SUCCEEDS")
this as CompositeCommand
val buildUsage = StringBuilder(this.description).append(": \n")
this::class.declaredFunctions.filter { it.hasAnnotation<CompositeCommand.SubCommand>() }.map { function ->
val notStatic = !function.hasAnnotation<JvmStatic>()
val overridePermission = function.findAnnotation<CompositeCommand.Permission>()//optional
val subDescription =
function.findAnnotation<CompositeCommand.Description>()?.description ?: "no description available"
if ((function.returnType.classifier as? KClass<*>)?.isSubclassOf(Boolean::class) != true) {
error("Return Type of SubCommand must be Boolean")
}
val parameters = function.parameters.toMutableList()
check(parameters.isNotEmpty()) {
"First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.primaryName + " should be <out CommandSender>"
}
if (notStatic) parameters.removeAt(0) // instance
(parameters.removeAt(0)).let { receiver ->
check(!receiver.isVararg && !((receiver.type.classifier as? KClass<*>).also { print(it) }
?.isSubclassOf(CommandSender::class) != true)) {
"First parameter (receiver for kotlin) for sub commend " + function.name + " from " + this.primaryName + " should be <out CommandSender>"
}
}
val commandName = function.findAnnotation<CompositeCommand.SubCommand>()!!.name.map {
if (!it.isValidSubName()) {
error("SubName $it is not valid")
}
it
}.toTypedArray()
//map parameter
val params = parameters.map { param ->
buildUsage.append("/$primaryName ")
if (param.isVararg) error("parameter for sub commend " + function.name + " from " + this.primaryName + " should not be var arg")
if (param.isOptional) error("parameter for sub commend " + function.name + " from " + this.primaryName + " should not be var optional")
val argName = param.findAnnotation<CompositeCommand.Name>()?.name ?: param.name ?: "unknown"
buildUsage.append("<").append(argName).append("> ").append(" ")
CommandParam(
argName,
(param.type.classifier as? KClass<*>)
?: throw IllegalArgumentException("unsolved type reference from param " + param.name + " in " + function.name + " from " + this.primaryName)
)
}.toTypedArray()
buildUsage.append(subDescription).append("\n")
SubCommandDescriptor(
commandName,
params,
subDescription,
overridePermission?.permission?.getInstance() ?: permission,
onCommand = block { sender: CommandSender, args: Array<out Any> ->
if (notStatic) {
function.callSuspend(this, sender, *args) as Boolean
} else {
function.callSuspend(sender, *args) as Boolean
}
}
)
}.toTypedArray().also {
_usage = buildUsage.toString()
}
}
private fun block(block: suspend (CommandSender, Array<out Any>) -> Boolean): suspend (CommandSender, Array<out Any>) -> Boolean {
return block
}
@JvmField
internal val bakedCommandNameToSubDescriptorArray: Map<Array<String>, SubCommandDescriptor> = kotlin.run {
val map = LinkedHashMap<Array<String>, SubCommandDescriptor>(subCommands.size * 2)
for (descriptor in subCommands) {
for (name in descriptor.bakedSubNames) {
map[name] = descriptor
}
}
map.toSortedMap(Comparator { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() })
}
internal class DefaultSubCommandDescriptor(
val description: String,
val permission: CommandPermission,
val onCommand: suspend (sender: CommandSender, rawArgs: Array<out Any>) -> Boolean
)
internal inner class SubCommandDescriptor(
val names: Array<String>,
val params: Array<CommandParam<*>>,
val description: String,
val permission: CommandPermission,
val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean
) {
internal suspend inline fun parseAndExecute(
sender: CommandSender,
argsWithSubCommandNameNotRemoved: Array<out Any>
) {
if (!onCommand(sender, parseArgs(sender, argsWithSubCommandNameNotRemoved, names.size))) {
sender.sendMessage(usage)
}
}
@JvmField
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): Array<out Any> {
@Suppress("CAST_NEVER_SUCCEEDS")
this as CompositeCommand
require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size - offset}" }
return Array(this.params.size) { index ->
val param = params[index]
val rawArg = rawArgs[offset + index]
when (rawArg) {
is String -> context[param.type]?.parse(rawArg, sender)
is SingleMessage -> context[param.type]?.parse(rawArg, sender)
else -> throw IllegalArgumentException("Illegal argument type: ${rawArg::class.qualifiedName}")
} ?: error("Cannot find a parser for $rawArg")
}
}
}
/**
* @param rawArgs 元素类型必须为 [SingleMessage] [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException]
*/
internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor? {
val maxCount = rawArgs.size - 1
var cur = 0
bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) ->
if (name.size != cur) {
if (cur++ == maxCount) return null
}
if (name.contentEqualsOffset(rawArgs, length = cur)) {
return descriptor
}
}
return null
}
}
internal fun <T> Array<T>.contentEqualsOffset(other: Array<out Any>, length: Int): Boolean {
repeat(length) { index ->
if (other[index].toString() != this[index]) {
return false
}
}
return true
}
internal val ILLEGAL_SUB_NAME_CHARS = "\\/!@#$%^&*()_+-={}[];':\",.<>?`~".toCharArray()
internal fun String.isValidSubName(): Boolean = ILLEGAL_SUB_NAME_CHARS.none { it in this }
internal fun String.bakeSubName(): Array<String> = split(' ').filterNot { it.isBlank() }.toTypedArray()
internal fun Any.flattenCommandComponents(): ArrayList<Any> {
val list = ArrayList<Any>()
when (this::class.java) { // faster than is
String::class.java -> (this as String).splitToSequence(' ').filterNot { it.isBlank() }.forEach { list.add(it) }
PlainText::class.java -> (this as PlainText).content.splitToSequence(' ').filterNot { it.isBlank() }
.forEach { list.add(it) }
SingleMessage::class.java -> list.add(this as SingleMessage)
Array<Any>::class.java -> (this as Array<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
Iterable::class.java -> (this as Iterable<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
else -> list.add(this.toString())
}
return list
}
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
findAnnotation<T>() != null
internal inline fun <T : Any> KClass<out T>.getInstance(): T {
return this.objectInstance ?: this.createInstance()
}

View File

@ -7,8 +7,9 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.command
package net.mamoe.mirai.console.command.internal
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import java.util.concurrent.locks.ReentrantLock
@ -51,7 +52,9 @@ internal object InternalCommandManager {
*/
internal fun matchCommand(rawCommand: String): Command? {
if (rawCommand.startsWith(COMMAND_PREFIX)) {
return requiredPrefixCommandMap[rawCommand.substringAfter(COMMAND_PREFIX)]
return requiredPrefixCommandMap[rawCommand.substringAfter(
COMMAND_PREFIX
)]
}
return optionalPrefixCommandMap[rawCommand]
}
@ -170,10 +173,16 @@ internal suspend inline fun CommandSender.executeCommandInternal(
messages: Any,
commandName: String
): Command? {
val command = InternalCommandManager.matchCommand(commandName) ?: return null
val command = InternalCommandManager.matchCommand(
commandName
) ?: return null
if (!command.testPermission(this)) {
throw CommandExecutionException(command, commandName, CommandPermissionDeniedException(command))
throw CommandExecutionException(
command,
commandName,
CommandPermissionDeniedException(command)
)
}
kotlin.runCatching {

View File

@ -9,16 +9,12 @@
package net.mamoe.mirai.console.event
import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.event.AbstractEvent
import net.mamoe.mirai.event.CancellableEvent
/*
data class CommandExecutionEvent( // TODO: 2020/6/26 impl CommandExecutionEvent
val sender: CommandSender,
val command: Command,
val rawArgs: Array<Any>
) : AbstractEvent(), CancellableEvent, ConsoleEvent {
) : CancellableEvent, ConsoleEvent, AbstractEvent() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@ -39,3 +35,4 @@ data class CommandExecutionEvent( // TODO: 2020/6/26 impl CommandExecutionEvent
return result
}
}
*/

View File

@ -27,6 +27,9 @@ import kotlin.coroutines.EmptyCoroutineContext
internal val <T> T.job: Job where T : CoroutineScope, T : Plugin get() = this.coroutineContext[Job]!!
/**
* Hides implementations from [JvmPlugin]
*/
@PublishedApi
internal abstract class JvmPluginImpl(
parentCoroutineContext: CoroutineContext

View File

@ -25,4 +25,6 @@ abstract class AbstractJvmPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
// TODO: 2020/6/24 添加 PluginSetting 继承 Setting, 实现 onValueChanged 并绑定自动保存.
abstract class PluginSetting
}