mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +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")
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
@ -78,6 +87,23 @@ sealed class CommandExecuteResult {
|
||||
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,
|
||||
|
||||
/** 没有匹配的指令 */
|
||||
COMMAND_NOT_FOUND
|
||||
COMMAND_NOT_FOUND,
|
||||
|
||||
/** 权限不足 */
|
||||
PERMISSION_DENIED
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +150,18 @@ fun CommandExecuteResult.isExecutionException(): Boolean {
|
||||
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`
|
||||
*/
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
* 解析并执行一个指令
|
||||
* 解析并执行一个指令. 将会检查指令权限, 在无权限时抛出
|
||||
*
|
||||
* @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
|
||||
|
||||
/**
|
||||
@ -224,6 +198,10 @@ internal suspend inline fun CommandSender.executeCommandDetailedInternal(
|
||||
val command =
|
||||
InternalCommandManager.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName)
|
||||
val args = messages.flattenCommandComponents().dropToTypedArray(1)
|
||||
|
||||
if (!command.testPermission(this)) {
|
||||
return CommandExecuteResult.PermissionDenied(command, commandName)
|
||||
}
|
||||
kotlin.runCatching {
|
||||
command.onCommand(this, args)
|
||||
}.fold(
|
||||
|
@ -25,6 +25,9 @@ import net.mamoe.mirai.contact.isOwner
|
||||
interface CommandPermission {
|
||||
/**
|
||||
* 判断 [this] 是否拥有这个指令的权限
|
||||
*
|
||||
* @see CommandSender.hasPermission
|
||||
* @see CommandPermission.testPermission
|
||||
*/
|
||||
fun CommandSender.hasPermission(): Boolean
|
||||
|
||||
@ -32,11 +35,13 @@ interface CommandPermission {
|
||||
/**
|
||||
* 满足两个权限其中一个即可使用指令
|
||||
*/ // no extension for Java
|
||||
@JvmDefault
|
||||
infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
|
||||
|
||||
/**
|
||||
* 同时拥有两个权限才能使用指令
|
||||
*/ // no extension for Java
|
||||
@JvmDefault
|
||||
infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
|
||||
|
||||
|
||||
@ -48,7 +53,7 @@ interface CommandPermission {
|
||||
}
|
||||
|
||||
/**
|
||||
* 任何人都不能使用这个指令. 指令只能通过代码在 [execute] 使用
|
||||
* 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行.
|
||||
*/
|
||||
object None : CommandPermission {
|
||||
override fun CommandSender.hasPermission(): Boolean = false
|
||||
@ -94,7 +99,7 @@ interface 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)
|
||||
@ -117,6 +122,8 @@ inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
|
||||
|
||||
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(
|
||||
private val first: 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
|
||||
): Command? {
|
||||
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 {
|
||||
command.onCommand(this, loweredArgs)
|
||||
command.onCommand(this, messages.flattenCommandComponents().dropToTypedArray(1))
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
return command
|
||||
},
|
||||
onFailure = {
|
||||
throw CommandExecutionException(command, commandName, loweredArgs, it)
|
||||
throw CommandExecutionException(command, commandName, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user