Review CommandArgumentContext and command

This commit is contained in:
Him188 2020-10-25 14:27:27 +08:00
parent 5c16e685b3
commit d1ebe44f3e
12 changed files with 137 additions and 142 deletions

View File

@ -15,7 +15,11 @@ import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.descriptor.* import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.map
import net.mamoe.mirai.console.command.descriptor.PermissionIdValueArgumentParser
import net.mamoe.mirai.console.command.descriptor.PermitteeIdValueArgumentParser
import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext
import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.command.CommandManagerImpl
import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands
import net.mamoe.mirai.console.internal.util.runIgnoreException import net.mamoe.mirai.console.internal.util.runIgnoreException
@ -154,7 +158,7 @@ public object BuiltInCommands {
Permission::class with PermissionIdValueArgumentParser.map { id -> Permission::class with PermissionIdValueArgumentParser.map { id ->
kotlin.runCatching { kotlin.runCatching {
id.findCorrespondingPermissionOrFail() id.findCorrespondingPermissionOrFail()
}.getOrElse { illegalArgument("指令不存在: $id", it) } }.getOrElse { throw CommandArgumentParserException("指令不存在: $id", it) }
} }
}, },
), BuiltInCommandInternal { ), BuiltInCommandInternal {

View File

@ -14,7 +14,6 @@ package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.descriptor.CommandArgumentContextAware import net.mamoe.mirai.console.command.descriptor.CommandArgumentContextAware
import net.mamoe.mirai.console.command.descriptor.CommandSignatureVariant import net.mamoe.mirai.console.command.descriptor.CommandSignatureVariant
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.java.JCommand
import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
@ -31,8 +30,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* @see SimpleCommand 简单的, 支持参数自动解析的指令 * @see SimpleCommand 简单的, 支持参数自动解析的指令
* *
* @see CommandArgumentContextAware * @see CommandArgumentContextAware
*
* @see JCommand Java 用户添加协程帮助的 [Command]
*/ */
public interface Command { public interface Command {
/** /**

View File

@ -23,6 +23,8 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageContent import net.mamoe.mirai.message.data.MessageContent
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
import kotlin.contracts.InvocationKind.EXACTLY_ONCE
import kotlin.contracts.contract
import kotlin.internal.LowPriorityInOverloadResolution import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
@ -49,9 +51,17 @@ public interface CommandArgumentContext {
public data class ParserPair<T : Any>( public data class ParserPair<T : Any>(
val klass: KClass<T>, val klass: KClass<T>,
val parser: CommandValueArgumentParser<T>, val parser: CommandValueArgumentParser<T>,
) ) {
public companion object {
@JvmStatic
public fun <T : Any> ParserPair<T>.toPair(): Pair<KClass<T>, CommandValueArgumentParser<T>> = klass to parser
}
}
public operator fun <T : Any> get(klass: KClass<out T>): CommandValueArgumentParser<T>? /**
* 获取一个 [kClass] 类型的解析器.
*/
public operator fun <T : Any> get(kClass: KClass<T>): CommandValueArgumentParser<T>?
public fun toList(): List<ParserPair<*>> public fun toList(): List<ParserPair<*>>
@ -59,7 +69,7 @@ public interface CommandArgumentContext {
/** /**
* For Java callers. * For Java callers.
* *
* @see [EmptyCommandArgumentContext] * @see EmptyCommandArgumentContext
*/ */
@JvmStatic @JvmStatic
public val EMPTY: CommandArgumentContext = EmptyCommandArgumentContext public val EMPTY: CommandArgumentContext = EmptyCommandArgumentContext
@ -108,6 +118,9 @@ public interface CommandArgumentContextAware {
public val context: CommandArgumentContext public val context: CommandArgumentContext
} }
/**
* @see CommandArgumentContext.EMPTY
*/
public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf()) public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf())
/** /**
@ -117,8 +130,8 @@ public operator fun CommandArgumentContext.plus(replacer: CommandArgumentContext
if (replacer == EmptyCommandArgumentContext) return this if (replacer == EmptyCommandArgumentContext) return this
if (this == EmptyCommandArgumentContext) return replacer if (this == EmptyCommandArgumentContext) return replacer
return object : CommandArgumentContext { return object : CommandArgumentContext {
override fun <T : Any> get(klass: KClass<out T>): CommandValueArgumentParser<T>? = override fun <T : Any> get(kClass: KClass<T>): CommandValueArgumentParser<T>? =
replacer[klass] ?: this@plus[klass] replacer[kClass] ?: this@plus[kClass]
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList() override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
} }
@ -132,9 +145,9 @@ public operator fun CommandArgumentContext.plus(replacer: List<ParserPair<*>>):
if (this == EmptyCommandArgumentContext) return SimpleCommandArgumentContext(replacer) if (this == EmptyCommandArgumentContext) return SimpleCommandArgumentContext(replacer)
return object : CommandArgumentContext { return object : CommandArgumentContext {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : Any> get(klass: KClass<out T>): CommandValueArgumentParser<T>? = override fun <T : Any> get(kClass: KClass<T>): CommandValueArgumentParser<T>? =
replacer.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandValueArgumentParser<T>? replacer.firstOrNull { kClass.isSubclassOf(it.klass) }?.parser as CommandValueArgumentParser<T>?
?: this@plus[klass] ?: this@plus[kClass]
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList() override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
} }
@ -149,9 +162,9 @@ public operator fun CommandArgumentContext.plus(replacer: List<ParserPair<*>>):
public class SimpleCommandArgumentContext( public class SimpleCommandArgumentContext(
public val list: List<ParserPair<*>>, public val list: List<ParserPair<*>>,
) : CommandArgumentContext { ) : CommandArgumentContext {
override fun <T : Any> get(klass: KClass<out T>): CommandValueArgumentParser<T>? = override fun <T : Any> get(kClass: KClass<T>): CommandValueArgumentParser<T>? =
(this.list.firstOrNull { klass == it.klass }?.parser (this.list.firstOrNull { kClass == it.klass }?.parser
?: this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser) as CommandValueArgumentParser<T>? ?: this.list.firstOrNull { kClass.isSubclassOf(it.klass) }?.parser) as CommandValueArgumentParser<T>?
override fun toList(): List<ParserPair<*>> = list override fun toList(): List<ParserPair<*>> = list
} }
@ -192,6 +205,9 @@ public class SimpleCommandArgumentContext(
*/ */
@JvmSynthetic @JvmSynthetic
public fun buildCommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext { public fun buildCommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext {
contract {
callsInPlace(block, EXACTLY_ONCE)
}
return CommandArgumentContextBuilder().apply(block).build() return CommandArgumentContextBuilder().apply(block).build()
} }

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("EXPOSED_SUPER_CLASS")
package net.mamoe.mirai.console.command.descriptor package net.mamoe.mirai.console.command.descriptor
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
@ -25,7 +27,7 @@ import net.mamoe.mirai.message.data.*
/** /**
* 使用 [String.toInt] 解析 * 使用 [String.toInt] 解析
*/ */
public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensions<Int> { public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensions<Int>() {
public override fun parse(raw: String, sender: CommandSender): Int = public override fun parse(raw: String, sender: CommandSender): Int =
raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数") raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数")
} }
@ -33,7 +35,7 @@ public object IntValueArgumentParser : InternalCommandValueArgumentParserExtensi
/** /**
* 使用 [String.toLong] 解析 * 使用 [String.toLong] 解析
*/ */
public object LongValueArgumentParser : InternalCommandValueArgumentParserExtensions<Long> { public object LongValueArgumentParser : InternalCommandValueArgumentParserExtensions<Long>() {
public override fun parse(raw: String, sender: CommandSender): Long = public override fun parse(raw: String, sender: CommandSender): Long =
raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数") raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数")
} }
@ -41,7 +43,7 @@ public object LongValueArgumentParser : InternalCommandValueArgumentParserExtens
/** /**
* 使用 [String.toShort] 解析 * 使用 [String.toShort] 解析
*/ */
public object ShortValueArgumentParser : InternalCommandValueArgumentParserExtensions<Short> { public object ShortValueArgumentParser : InternalCommandValueArgumentParserExtensions<Short>() {
public override fun parse(raw: String, sender: CommandSender): Short = public override fun parse(raw: String, sender: CommandSender): Short =
raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数") raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数")
} }
@ -49,7 +51,7 @@ public object ShortValueArgumentParser : InternalCommandValueArgumentParserExten
/** /**
* 使用 [String.toByte] 解析 * 使用 [String.toByte] 解析
*/ */
public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtensions<Byte> { public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtensions<Byte>() {
public override fun parse(raw: String, sender: CommandSender): Byte = public override fun parse(raw: String, sender: CommandSender): Byte =
raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节") raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节")
} }
@ -57,7 +59,7 @@ public object ByteValueArgumentParser : InternalCommandValueArgumentParserExtens
/** /**
* 使用 [String.toDouble] 解析 * 使用 [String.toDouble] 解析
*/ */
public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExtensions<Double> { public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExtensions<Double>() {
public override fun parse(raw: String, sender: CommandSender): Double = public override fun parse(raw: String, sender: CommandSender): Double =
raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数") raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数")
} }
@ -65,7 +67,7 @@ public object DoubleValueArgumentParser : InternalCommandValueArgumentParserExte
/** /**
* 使用 [String.toFloat] 解析 * 使用 [String.toFloat] 解析
*/ */
public object FloatValueArgumentParser : InternalCommandValueArgumentParserExtensions<Float> { public object FloatValueArgumentParser : InternalCommandValueArgumentParserExtensions<Float>() {
public override fun parse(raw: String, sender: CommandSender): Float = public override fun parse(raw: String, sender: CommandSender): Float =
raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数") raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数")
} }
@ -73,14 +75,14 @@ public object FloatValueArgumentParser : InternalCommandValueArgumentParserExten
/** /**
* 直接返回 [String], 或取用 [SingleMessage.contentToString] * 直接返回 [String], 或取用 [SingleMessage.contentToString]
*/ */
public object StringValueArgumentParser : InternalCommandValueArgumentParserExtensions<String> { public object StringValueArgumentParser : InternalCommandValueArgumentParserExtensions<String>() {
public override fun parse(raw: String, sender: CommandSender): String = raw public override fun parse(raw: String, sender: CommandSender): String = raw
} }
/** /**
* 解析 [String] 通过 [Image]. * 解析 [String] 通过 [Image].
*/ */
public object ImageValueArgumentParser : InternalCommandValueArgumentParserExtensions<Image> { public object ImageValueArgumentParser : InternalCommandValueArgumentParserExtensions<Image>() {
public override fun parse(raw: String, sender: CommandSender): Image { public override fun parse(raw: String, sender: CommandSender): Image {
return kotlin.runCatching { return kotlin.runCatching {
Image(raw) Image(raw)
@ -95,7 +97,7 @@ public object ImageValueArgumentParser : InternalCommandValueArgumentParserExten
} }
} }
public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserExtensions<PlainText> { public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserExtensions<PlainText>() {
public override fun parse(raw: String, sender: CommandSender): PlainText { public override fun parse(raw: String, sender: CommandSender): PlainText {
return PlainText(raw) return PlainText(raw)
} }
@ -109,7 +111,7 @@ public object PlainTextValueArgumentParser : InternalCommandValueArgumentParserE
/** /**
* 当字符串内容为(不区分大小写) "true", "yes", "enabled" * 当字符串内容为(不区分大小写) "true", "yes", "enabled"
*/ */
public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExtensions<Boolean> { public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExtensions<Boolean>() {
public override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str -> public override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str ->
str.equals("true", ignoreCase = true) str.equals("true", ignoreCase = true)
|| str.equals("yes", ignoreCase = true) || str.equals("yes", ignoreCase = true)
@ -121,7 +123,7 @@ public object BooleanValueArgumentParser : InternalCommandValueArgumentParserExt
/** /**
* 根据 [Bot.id] 解析一个登录后的 [Bot] * 根据 [Bot.id] 解析一个登录后的 [Bot]
*/ */
public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParserExtensions<Bot> { public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParserExtensions<Bot>() {
public override fun parse(raw: String, sender: CommandSender): Bot = public override fun parse(raw: String, sender: CommandSender): Bot =
if (raw == "~") sender.inferBotOrFail() if (raw == "~") sender.inferBotOrFail()
else raw.findBotOrFail() else raw.findBotOrFail()
@ -136,7 +138,7 @@ public object ExistingBotValueArgumentParser : InternalCommandValueArgumentParse
/** /**
* 解析任意一个存在的好友. * 解析任意一个存在的好友.
*/ */
public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentParserExtensions<Friend> { public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentParserExtensions<Friend>() {
private val syntax = """ private val syntax = """
- `botId.friendId` - `botId.friendId`
- `botId.friendNick` (模糊搜索, 寻找最优匹配) - `botId.friendNick` (模糊搜索, 寻找最优匹配)
@ -175,7 +177,7 @@ public object ExistingFriendValueArgumentParser : InternalCommandValueArgumentPa
/** /**
* 解析任意一个存在的群. * 解析任意一个存在的群.
*/ */
public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentParserExtensions<Group> { public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentParserExtensions<Group>() {
private val syntax = """ private val syntax = """
- `botId.groupId` - `botId.groupId`
- `~` (指代指令调用人自己所在群. 仅群聊天环境下) - `~` (指代指令调用人自己所在群. 仅群聊天环境下)
@ -202,7 +204,7 @@ public object ExistingGroupValueArgumentParser : InternalCommandValueArgumentPar
} }
} }
public object ExistingUserValueArgumentParser : InternalCommandValueArgumentParserExtensions<User> { public object ExistingUserValueArgumentParser : InternalCommandValueArgumentParserExtensions<User>() {
private val syntax: String = """ private val syntax: String = """
- `botId.groupId.memberId` - `botId.groupId.memberId`
- `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配)
@ -246,7 +248,7 @@ public object ExistingUserValueArgumentParser : InternalCommandValueArgumentPars
} }
public object ExistingContactValueArgumentParser : InternalCommandValueArgumentParserExtensions<Contact> { public object ExistingContactValueArgumentParser : InternalCommandValueArgumentParserExtensions<Contact>() {
private val syntax: String = """ private val syntax: String = """
- `botId.groupId.memberId` - `botId.groupId.memberId`
- `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配)
@ -286,7 +288,7 @@ public object ExistingContactValueArgumentParser : InternalCommandValueArgumentP
/** /**
* 解析任意一个群成员. * 解析任意一个群成员.
*/ */
public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentParserExtensions<Member> { public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentParserExtensions<Member>() {
private val syntax: String = """ private val syntax: String = """
- `botId.groupId.memberId` - `botId.groupId.memberId`
- `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配) - `botId.groupId.memberCard` (模糊搜索, 寻找最优匹配)
@ -333,7 +335,7 @@ public object ExistingMemberValueArgumentParser : InternalCommandValueArgumentPa
} }
} }
public object PermissionIdValueArgumentParser : CommandValueArgumentParser<PermissionId> { public object PermissionIdValueArgumentParser : InternalCommandValueArgumentParserExtensions<PermissionId>() {
override fun parse(raw: String, sender: CommandSender): PermissionId { override fun parse(raw: String, sender: CommandSender): PermissionId {
return kotlin.runCatching { PermissionId.parseFromString(raw) }.getOrElse { return kotlin.runCatching { PermissionId.parseFromString(raw) }.getOrElse {
illegalArgument("无法解析 $raw 为 PermissionId") illegalArgument("无法解析 $raw 为 PermissionId")
@ -341,7 +343,7 @@ public object PermissionIdValueArgumentParser : CommandValueArgumentParser<Permi
} }
} }
public object PermitteeIdValueArgumentParser : CommandValueArgumentParser<PermitteeId> { public object PermitteeIdValueArgumentParser : InternalCommandValueArgumentParserExtensions<PermitteeId>() {
override fun parse(raw: String, sender: CommandSender): PermitteeId { override fun parse(raw: String, sender: CommandSender): PermitteeId {
return if (raw == "~") sender.permitteeId return if (raw == "~") sender.permitteeId
else kotlin.runCatching { AbstractPermitteeId.parseFromString(raw) }.getOrElse { else kotlin.runCatching { AbstractPermitteeId.parseFromString(raw) }.getOrElse {
@ -363,26 +365,26 @@ public object RawContentValueArgumentParser : CommandValueArgumentParser<Message
override fun parse(raw: MessageContent, sender: CommandSender): MessageContent = raw override fun parse(raw: MessageContent, sender: CommandSender): MessageContent = raw
} }
internal interface InternalCommandValueArgumentParserExtensions<T : Any> : CommandValueArgumentParser<T> { internal abstract class InternalCommandValueArgumentParserExtensions<T : Any> : AbstractCommandValueArgumentParser<T>() {
fun String.parseToLongOrFail(): Long = toLongOrNull() ?: illegalArgument("无法解析 $this 为整数") private fun String.parseToLongOrFail(): Long = toLongOrNull() ?: illegalArgument("无法解析 $this 为整数")
fun Long.findBotOrFail(): Bot = Bot.getInstanceOrNull(this) ?: illegalArgument("无法找到 Bot: $this") protected fun Long.findBotOrFail(): Bot = Bot.getInstanceOrNull(this) ?: illegalArgument("无法找到 Bot: $this")
fun String.findBotOrFail(): Bot = protected fun String.findBotOrFail(): Bot =
Bot.getInstanceOrNull(this.parseToLongOrFail()) ?: illegalArgument("无法找到 Bot: $this") Bot.getInstanceOrNull(this.parseToLongOrFail()) ?: illegalArgument("无法找到 Bot: $this")
fun Bot.findGroupOrFail(id: Long): Group = getGroupOrNull(id) ?: illegalArgument("无法找到群: $this") protected fun Bot.findGroupOrFail(id: Long): Group = getGroupOrNull(id) ?: illegalArgument("无法找到群: $this")
fun Bot.findGroupOrFail(id: String): Group = protected fun Bot.findGroupOrFail(id: String): Group =
getGroupOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群: $this") getGroupOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群: $this")
fun Bot.findFriendOrFail(id: String): Friend = protected fun Bot.findFriendOrFail(id: String): Friend =
getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到好友: $this") getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到好友: $this")
fun Bot.findMemberOrFail(id: String): Friend = protected fun Bot.findMemberOrFail(id: String): Friend =
getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群员: $this") getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群员: $this")
fun Group.findMemberOrFail(idOrCard: String): Member { protected fun Group.findMemberOrFail(idOrCard: String): Member {
if (idOrCard == "\$") return members.randomOrNull() ?: illegalArgument("当前语境下无法推断随机群员") if (idOrCard == "\$") return members.randomOrNull() ?: illegalArgument("当前语境下无法推断随机群员")
idOrCard.toLongOrNull()?.let { getOrNull(it) }?.let { return it } idOrCard.toLongOrNull()?.let { getOrNull(it) }?.let { return it }
this.members.singleOrNull { it.nameCardOrNick.contains(idOrCard) }?.let { return it } this.members.singleOrNull { it.nameCardOrNick.contains(idOrCard) }?.let { return it }
@ -405,23 +407,21 @@ internal interface InternalCommandValueArgumentParserExtensions<T : Any> : Comma
} }
} }
fun CommandSender.inferBotOrFail(): Bot = protected fun CommandSender.inferBotOrFail(): Bot =
(this as? UserCommandSender)?.bot (this as? UserCommandSender)?.bot
?: Bot.botInstancesSequence.singleOrNull() ?: Bot.botInstancesSequence.singleOrNull()
?: illegalArgument("当前语境下无法推断目标 Bot, 因为目前有多个 Bot 在线.") ?: illegalArgument("当前语境下无法推断目标 Bot, 因为目前有多个 Bot 在线.")
fun CommandSender.inferGroupOrFail(): Group = protected fun CommandSender.inferGroupOrFail(): Group =
inferGroup() ?: illegalArgument("当前语境下无法推断目标群") inferGroup() ?: illegalArgument("当前语境下无法推断目标群")
fun CommandSender.inferGroup(): Group? = (this as? GroupAwareCommandSender)?.group protected fun CommandSender.inferGroup(): Group? = (this as? GroupAwareCommandSender)?.group
fun CommandSender.inferFriendOrFail(): Friend = protected fun CommandSender.inferFriendOrFail(): Friend =
(this as? FriendCommandSender)?.user ?: illegalArgument("当前语境下无法推断目标好友") (this as? FriendCommandSender)?.user ?: illegalArgument("当前语境下无法推断目标好友")
} }
internal fun Double.toDecimalPlace(n: Int): String { internal fun Double.toDecimalPlace(n: Int): String = "%.${n}f".format(this)
return "%.${n}f".format(this)
}
internal fun String.truncate(lengthLimit: Int, replacement: String = "..."): String = buildString { internal fun String.truncate(lengthLimit: Int, replacement: String = "..."): String = buildString {
var lengthSum = 0 var lengthSum = 0

View File

@ -12,6 +12,7 @@
package net.mamoe.mirai.console.command.descriptor package net.mamoe.mirai.console.command.descriptor
import net.mamoe.mirai.console.command.IllegalCommandArgumentException import net.mamoe.mirai.console.command.IllegalCommandArgumentException
import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueArgumentParser.Companion.illegalArgument
/** /**
* 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范等. * 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范等.
@ -20,7 +21,7 @@ import net.mamoe.mirai.console.command.IllegalCommandArgumentException
* *
* @see IllegalCommandArgumentException * @see IllegalCommandArgumentException
* @see CommandValueArgumentParser * @see CommandValueArgumentParser
* @see CommandValueArgumentParser.illegalArgument * @see AbstractCommandValueArgumentParser.illegalArgument
*/ */
public class CommandArgumentParserException : IllegalCommandArgumentException { public class CommandArgumentParserException : IllegalCommandArgumentException {
public constructor() : super() public constructor() : super()

View File

@ -16,10 +16,9 @@ import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.command.SimpleCommand
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.parse
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.MessageContent import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.SingleMessage
import net.mamoe.mirai.message.data.content
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -78,62 +77,60 @@ public interface CommandValueArgumentParser<out T : Any> {
*/ */
@Throws(CommandArgumentParserException::class) @Throws(CommandArgumentParserException::class)
public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender) public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender)
}
/**
* 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型.
*/
public fun <T : Any, R : Any> CommandValueArgumentParser<T>.map(
mapper: CommandValueArgumentParser<R>.(T) -> R,
): CommandValueArgumentParser<R> = MappingCommandValueArgumentParser(this, mapper)
private class MappingCommandValueArgumentParser<T : Any, R : Any>(
private val original: CommandValueArgumentParser<T>,
private val mapper: CommandValueArgumentParser<R>.(T) -> R,
) : CommandValueArgumentParser<R> {
override fun parse(raw: String, sender: CommandSender): R = mapper(original.parse(raw, sender))
override fun parse(raw: MessageContent, sender: CommandSender): R = mapper(original.parse(raw, sender))
}
public companion object {
/** /**
* 解析一个字符串或 [SingleMessage] [T] 类型参数 * 解析一个字符串或 [SingleMessage] [T] 类型参数
* *
* @throws IllegalArgumentException [raw] 既不是 [SingleMessage], 也不是 [String] 时抛出. * @throws IllegalArgumentException [raw] 既不是 [SingleMessage], 也不是 [String] 时抛出.
*
* @see CommandValueArgumentParser.parse
*/ */
@JvmSynthetic @JvmStatic
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
public fun <T : Any> CommandValueArgumentParser<T>.parse(raw: Any, sender: CommandSender): T { public fun <T : Any> CommandValueArgumentParser<T>.parse(raw: Message, sender: CommandSender): T {
contract {
returns() implies (raw is String || raw is SingleMessage)
}
return when (raw) { return when (raw) {
is String -> parse(raw, sender) is PlainText -> parse(raw.content, sender)
is MessageContent -> parse(raw, sender) is MessageContent -> parse(raw, sender)
else -> throw IllegalArgumentException("Illegal raw argument type: ${raw::class.qualifiedName}") else -> throw IllegalArgumentException("Illegal raw argument type: ${raw::class.qualifiedName}")
} }
} }
/**
* 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型.
*/
@JvmStatic
public fun <Original : Any, Result : Any> CommandValueArgumentParser<Original>.map(
mapper: MappingCommandValueArgumentParser<Original, Result>.(Original) -> Result,
): CommandValueArgumentParser<Result> = MappingCommandValueArgumentParser(this, mapper)
}
}
/**
* @see CommandValueArgumentParser 的基础实现.
*/
public abstract class AbstractCommandValueArgumentParser<T : Any> : CommandValueArgumentParser<T> {
public companion object {
/** /**
* 抛出一个 [CommandArgumentParserException] 的捷径 * 抛出一个 [CommandArgumentParserException] 的捷径
* *
* @throws CommandArgumentParserException * @throws CommandArgumentParserException
*/ */
@Suppress("unused") @JvmStatic
@JvmSynthetic @JvmSynthetic
@Throws(CommandArgumentParserException::class) @Throws(CommandArgumentParserException::class)
public inline fun CommandValueArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing { protected inline fun CommandValueArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing =
throw CommandArgumentParserException(message, cause) throw CommandArgumentParserException(message, cause)
}
/** /**
* 检查参数 [condition]. 当它为 `false` 时调用 [message] 并以其返回值作为消息, 抛出异常 [CommandArgumentParserException] * 检查参数 [condition]. 当它为 `false` 时调用 [message] 并以其返回值作为消息, 抛出异常 [CommandArgumentParserException]
* *
* @throws CommandArgumentParserException * @throws CommandArgumentParserException
*/ */
@JvmStatic
@Throws(CommandArgumentParserException::class) @Throws(CommandArgumentParserException::class)
@JvmSynthetic @JvmSynthetic
public inline fun CommandValueArgumentParser<*>.checkArgument( protected inline fun CommandValueArgumentParser<*>.checkArgument(
condition: Boolean, condition: Boolean,
crossinline message: () -> String = { "Check failed." }, crossinline message: () -> String = { "Check failed." },
) { ) {
@ -143,3 +140,13 @@ public inline fun CommandValueArgumentParser<*>.checkArgument(
} }
if (!condition) illegalArgument(message()) if (!condition) illegalArgument(message())
} }
}
}
public class MappingCommandValueArgumentParser<T : Any, R : Any>(
private val original: CommandValueArgumentParser<T>,
private val mapper: MappingCommandValueArgumentParser<T, R>.(T) -> R,
) : AbstractCommandValueArgumentParser<R>() {
override fun parse(raw: String, sender: CommandSender): R = mapper(original.parse(raw, sender))
override fun parse(raw: MessageContent, sender: CommandSender): R = mapper(original.parse(raw, sender))
}

View File

@ -1,25 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.command.java
import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/**
* Java 用户添加协程帮助的 [Command].
*
* 注意, [JSimpleCommand], [JCompositeCommand], [JRawCommand] 都不实现这个接口. [JCommand] 只设计为 Java 使用者自己实现 [Command] 相关内容.
*
* @see Command
*/
@ConsoleExperimentalApi("Not yet supported")
public interface JCommand : Command {
// TODO: 2020/10/18 JCommand
}

View File

@ -18,7 +18,6 @@ import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext
import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/** /**
* 复合指令. 指令注册时候会通过反射构造指令解析器. * 复合指令. 指令注册时候会通过反射构造指令解析器.
@ -70,7 +69,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* *
* @see buildCommandArgumentContext * @see buildCommandArgumentContext
*/ */
@ConsoleExperimentalApi("Not yet supported")
public abstract class JCompositeCommand public abstract class JCompositeCommand
@JvmOverloads constructor( @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,

View File

@ -18,7 +18,6 @@ import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.internal.command.createOrFindCommandPermission import net.mamoe.mirai.console.internal.command.createOrFindCommandPermission
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/** /**
* Java 用户继承 * Java 用户继承
@ -46,7 +45,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* *
* @see JRawCommand * @see JRawCommand
*/ */
@ConsoleExperimentalApi("Not yet supported")
public abstract class JRawCommand public abstract class JRawCommand
@JvmOverloads constructor( @JvmOverloads constructor(
/** /**

View File

@ -17,7 +17,6 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/** /**
* Java 实现: * Java 实现:
@ -43,7 +42,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* @see SimpleCommand * @see SimpleCommand
* @see [CommandManager.executeCommand] * @see [CommandManager.executeCommand]
*/ */
@ConsoleExperimentalApi("Not yet supported")
public abstract class JSimpleCommand( public abstract class JSimpleCommand(
owner: CommandOwner, owner: CommandOwner,
@ResolveContext(COMMAND_NAME) primaryName: String, @ResolveContext(COMMAND_NAME) primaryName: String,

View File

@ -77,7 +77,7 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
} else { } else {
if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) { if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) {
// merge vararg arguments // merge vararg arguments
val (varargParameter, varargFirstArgument) val (varargParameter, _)
= zipped.removeLast() = zipped.removeLast()
zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain())) zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain()))

View File

@ -13,6 +13,7 @@ import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.descriptor.* import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.parse
import net.mamoe.mirai.console.command.parse.CommandCall import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.command.parse.CommandValueArgument import net.mamoe.mirai.console.command.parse.CommandValueArgument
import net.mamoe.mirai.console.command.parse.mapToTypeOrNull import net.mamoe.mirai.console.command.parse.mapToTypeOrNull