mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 00:30:17 +08:00
Implement command permission checking and relevant exceptions
This commit is contained in:
parent
a26b607f35
commit
4bc19a0a9d
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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("unused")
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
@ -78,6 +87,23 @@ sealed class CommandExecuteResult {
|
|||||||
override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND
|
override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 权限不足 */
|
||||||
|
class PermissionDenied(
|
||||||
|
/** 尝试执行的指令 */
|
||||||
|
override val command: Command,
|
||||||
|
/** 尝试执行的指令名 */
|
||||||
|
override val commandName: String
|
||||||
|
) : CommandExecuteResult() {
|
||||||
|
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
|
||||||
|
override val args: Nothing? get() = null
|
||||||
|
|
||||||
|
/** 指令执行时发生的错误, 总是 `null` */
|
||||||
|
override val exception: Nothing? get() = null
|
||||||
|
|
||||||
|
/** 指令最终执行状态, 总是 [CommandExecuteStatus.PERMISSION_DENIED] */
|
||||||
|
override val status: CommandExecuteStatus get() = CommandExecuteStatus.PERMISSION_DENIED
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指令的执行状态
|
* 指令的执行状态
|
||||||
*/
|
*/
|
||||||
@ -89,7 +115,10 @@ sealed class CommandExecuteResult {
|
|||||||
EXECUTION_EXCEPTION,
|
EXECUTION_EXCEPTION,
|
||||||
|
|
||||||
/** 没有匹配的指令 */
|
/** 没有匹配的指令 */
|
||||||
COMMAND_NOT_FOUND
|
COMMAND_NOT_FOUND,
|
||||||
|
|
||||||
|
/** 权限不足 */
|
||||||
|
PERMISSION_DENIED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +150,18 @@ fun CommandExecuteResult.isExecutionException(): Boolean {
|
|||||||
return this is CommandExecuteResult.ExecutionException
|
return this is CommandExecuteResult.ExecutionException
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当 [this] 为 [CommandExecuteResult.ExecutionException] 时返回 `true`
|
||||||
|
*/
|
||||||
|
@JvmSynthetic
|
||||||
|
fun CommandExecuteResult.isPermissionDenied(): Boolean {
|
||||||
|
contract {
|
||||||
|
returns(true) implies (this@isPermissionDenied is CommandExecuteResult.PermissionDenied)
|
||||||
|
returns(false) implies (this@isPermissionDenied !is CommandExecuteResult.PermissionDenied)
|
||||||
|
}
|
||||||
|
return this is CommandExecuteResult.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当 [this] 为 [CommandExecuteResult.ExecutionException] 时返回 `true`
|
* 当 [this] 为 [CommandExecuteResult.ExecutionException] 时返回 `true`
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 [executeCommand] 中, [Command.onCommand] 抛出异常时包装的异常.
|
||||||
|
*/
|
||||||
|
class CommandExecutionException(
|
||||||
|
/**
|
||||||
|
* 执行过程发生异常的指令
|
||||||
|
*/
|
||||||
|
val command: Command,
|
||||||
|
/**
|
||||||
|
* 匹配到的指令名
|
||||||
|
*/
|
||||||
|
val name: String,
|
||||||
|
cause: Throwable
|
||||||
|
) : RuntimeException(
|
||||||
|
"Exception while executing command '${command.primaryName}'",
|
||||||
|
cause
|
||||||
|
) {
|
||||||
|
override fun toString(): String =
|
||||||
|
"CommandExecutionException(command=$command, name='$name')"
|
||||||
|
}
|
||||||
|
|
@ -131,7 +131,7 @@ fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
|||||||
//// executing without detailed result (faster)
|
//// executing without detailed result (faster)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析并执行一个指令
|
* 解析并执行一个指令. 将会检查指令权限, 在无权限时抛出
|
||||||
*
|
*
|
||||||
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
||||||
*
|
*
|
||||||
@ -160,32 +160,6 @@ suspend fun CommandSender.executeCommand(message: MessageChain): Command? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [executeCommand] 中, [Command.onCommand] 抛出异常时包装的异常.
|
|
||||||
*/
|
|
||||||
class CommandExecutionException(
|
|
||||||
/**
|
|
||||||
* 执行过程发生异常的指令
|
|
||||||
*/
|
|
||||||
val command: Command,
|
|
||||||
/**
|
|
||||||
* 匹配到的指令名
|
|
||||||
*/
|
|
||||||
val name: String,
|
|
||||||
/**
|
|
||||||
* 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String]
|
|
||||||
*/
|
|
||||||
val args: Array<out Any>,
|
|
||||||
cause: Throwable
|
|
||||||
) : RuntimeException(
|
|
||||||
"Exception while executing command '${command.primaryName}' with args ${args.joinToString { "'$it'" }}",
|
|
||||||
cause
|
|
||||||
) {
|
|
||||||
override fun toString(): String =
|
|
||||||
"CommandExecutionException(command=$command, name='$name', args=${args.contentToString()})"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//// execution with detailed result
|
//// execution with detailed result
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,6 +198,10 @@ internal suspend inline fun CommandSender.executeCommandDetailedInternal(
|
|||||||
val command =
|
val command =
|
||||||
InternalCommandManager.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName)
|
InternalCommandManager.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName)
|
||||||
val args = messages.flattenCommandComponents().dropToTypedArray(1)
|
val args = messages.flattenCommandComponents().dropToTypedArray(1)
|
||||||
|
|
||||||
|
if (!command.testPermission(this)) {
|
||||||
|
return CommandExecuteResult.PermissionDenied(command, commandName)
|
||||||
|
}
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
command.onCommand(this, args)
|
command.onCommand(this, args)
|
||||||
}.fold(
|
}.fold(
|
||||||
|
@ -25,6 +25,9 @@ import net.mamoe.mirai.contact.isOwner
|
|||||||
interface CommandPermission {
|
interface CommandPermission {
|
||||||
/**
|
/**
|
||||||
* 判断 [this] 是否拥有这个指令的权限
|
* 判断 [this] 是否拥有这个指令的权限
|
||||||
|
*
|
||||||
|
* @see CommandSender.hasPermission
|
||||||
|
* @see CommandPermission.testPermission
|
||||||
*/
|
*/
|
||||||
fun CommandSender.hasPermission(): Boolean
|
fun CommandSender.hasPermission(): Boolean
|
||||||
|
|
||||||
@ -32,11 +35,13 @@ interface CommandPermission {
|
|||||||
/**
|
/**
|
||||||
* 满足两个权限其中一个即可使用指令
|
* 满足两个权限其中一个即可使用指令
|
||||||
*/ // no extension for Java
|
*/ // no extension for Java
|
||||||
|
@JvmDefault
|
||||||
infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
|
infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同时拥有两个权限才能使用指令
|
* 同时拥有两个权限才能使用指令
|
||||||
*/ // no extension for Java
|
*/ // no extension for Java
|
||||||
|
@JvmDefault
|
||||||
infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
|
infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +53,7 @@ interface CommandPermission {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任何人都不能使用这个指令. 指令只能通过代码在 [execute] 使用
|
* 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行.
|
||||||
*/
|
*/
|
||||||
object None : CommandPermission {
|
object None : CommandPermission {
|
||||||
override fun CommandSender.hasPermission(): Boolean = false
|
override fun CommandSender.hasPermission(): Boolean = false
|
||||||
@ -94,7 +99,7 @@ interface CommandPermission {
|
|||||||
* 仅控制台能使用和这个指令
|
* 仅控制台能使用和这个指令
|
||||||
*/
|
*/
|
||||||
object Console : CommandPermission {
|
object Console : CommandPermission {
|
||||||
override fun CommandSender.hasPermission(): Boolean = false
|
override fun CommandSender.hasPermission(): Boolean = this is ConsoleCommandSender
|
||||||
}
|
}
|
||||||
|
|
||||||
object Default : CommandPermission by (Manager or Console)
|
object Default : CommandPermission by (Manager or Console)
|
||||||
@ -117,6 +122,8 @@ inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
|
|||||||
|
|
||||||
inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
|
inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
|
||||||
|
|
||||||
|
inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission)
|
||||||
|
|
||||||
internal class OrCommandPermission(
|
internal class OrCommandPermission(
|
||||||
private val first: CommandPermission,
|
private val first: CommandPermission,
|
||||||
private val second: CommandPermission
|
private val second: CommandPermission
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 [executeCommand] 中, [CommandSender] 未拥有 [Command.permission] 所要求的权限时抛出的异常.
|
||||||
|
*
|
||||||
|
* 总是作为 [CommandExecutionException.cause].
|
||||||
|
*/
|
||||||
|
class CommandPermissionDeniedException(
|
||||||
|
/**
|
||||||
|
* 执行过程发生异常的指令
|
||||||
|
*/
|
||||||
|
val command: Command
|
||||||
|
) : RuntimeException("Permission denied while executing command '${command.primaryName}'") {
|
||||||
|
override fun toString(): String =
|
||||||
|
"CommandPermissionDeniedException(command=$command)"
|
||||||
|
}
|
@ -171,18 +171,19 @@ internal suspend inline fun CommandSender.executeCommandInternal(
|
|||||||
commandName: String
|
commandName: String
|
||||||
): Command? {
|
): Command? {
|
||||||
val command = InternalCommandManager.matchCommand(commandName) ?: return null
|
val command = InternalCommandManager.matchCommand(commandName) ?: return null
|
||||||
val rawInput = messages.flattenCommandComponents()
|
|
||||||
|
|
||||||
val loweredArgs = rawInput.dropToTypedArray(1)
|
if (!command.testPermission(this)) {
|
||||||
|
throw CommandExecutionException(command, commandName, CommandPermissionDeniedException(command))
|
||||||
|
}
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
command.onCommand(this, loweredArgs)
|
command.onCommand(this, messages.flattenCommandComponents().dropToTypedArray(1))
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
return command
|
return command
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
throw CommandExecutionException(command, commandName, loweredArgs, it)
|
throw CommandExecutionException(command, commandName, it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user