mamoe / mirai

高效率 QQ 机器人支持库
https://mirai.mamoe.net
GNU Affero General Public License v3.0
14.5k stars 2.55k forks source link

为子命令添加权限节点 #2091

Open kinhirozix opened 2 years ago

kinhirozix commented 2 years ago

在开发者编写 CompositeCommand 时可以定义多条子命令,而子命令的定义则是依靠 @SubCommand 注解添加的,默认会以方法名当做子命令名,也可以注解中带入一条或多条全英文的字符串作为子命令的自定义名。

但是子命令名并不能包含英文格式的 ?!# 等符号,虽然开发者可以使用 SimpleCommand 加自定义的 CommandArgumentContext 来充当子命令并且支持 ?!#,或者暴力手搓 命令

但是我想着如果 Mirai Console 可以提供官方性的支持将会更加美观一些。(这是一个意义不是很大的提议,嗯~)

命令系统中 CompositeCommandSubCommand 也统一归属于 parent permission,假设有如下代码:

object FooCommand : CompositeCommand(
    FooPlugin, "foo",
    description = "示例复合命令"
) {
    @SubCommand
    suspend fun CommandSenderOnMessage<*>.help() {
        sendMessage("测试")
    }
}

假设上面命令插件id为 dev.foo/foo 命令的权限id为 dev.foo:command.foo,此时 /foo help 的权限id也是这个,是否可以让子命令也有自己的权限id呢,以此做到更好的权限管理。

这里额外提一下,关于 #1744 的提议和在之后给出的 PR 中给出了一个在命令使用过程中使用 特定格式 的命令参数上下文解决方案对 用户群体 不是很友好,如今该提议已经被重新 Open,希望广大 PR 带佬可以在基于 Mirai Console 已有的命令系统的情况下得到最佳解决方案(咳咳)。

kinhirozix commented 2 years ago

咳咳,为了避免被骂,这里先给自己揪住 Command 功能不放给个求放过的解释:反正提供了 Command 功能,那支持更好一些嘛

在之前的 #1744 和这次的提议都已经用比较暴力的方式解决了,但是如果官方提供了更好的方式在代码上也确实会好看很多,少喷点,咳咳

Karlatemp commented 2 years ago

为子命令添加权限

console command 的命令设计是先根据第一个参数找到主命令再进入二级解析, 也就是如果要支持子命令的权限节点的话, 那么需要三个权限节点: <完整执行命令的权限节点>, <进入主命令的权限节点>, <子命令节点>

而为了最大兼容性 <完整执行命令的权限节点> 只能是 dev.foo:command.foo <进入主命令的权限节点> 那就需要是 dev.foo:command.foo.execute

如果需要直接拥有 dev.foo:command.foo.sub1 就能执行 /foo sub1, 我们可能需要重写命令系统, 这会是个较大的工作


子命令使用 ?!# 等符号

为什么要这样做呢, 好几年了无论是 mirai-console 还是各类 minecraft-server 等我都从没见过命令是使用这些特殊符号的. 如果说是要方便用户记住那么有意义的英文单词不是更好吗

kinhirozix commented 1 year ago

刚刚想到一个可用的解决方案,如下:

object FooPlugin : KotlinPlugin(
    JvmPluginDescription("foo", "0.0.1")
) {
    override fun onEnable() {
        FooCommand.register()
        PermissionService.INSTANCE.register(FooPlugin.permissionId("command.foo.sub"), "xx", FooCommand.parentPermission)
    }

    override fun onDisable() {}
}

object FooCommand : CompositeCommand(FooPlugin, "foo", parentPermission = parentPermission) {
    val parentPermission: Permission by lazy {
        PermissionService.INSTANCE.register(FooPlugin.permissionId("foo"), "xx")
    }

    @SubCommand
    suspend fun CommandSenderOnMessage<*>.sub() {
        val subPerm = PermissionService.INSTANCE[FooPlugin.permissionId("command.foo.sub")]!!
        if (fromEvent.sender.asCommandSender(false).hasPermission(subPerm)) sendMessage("子命令获得权限执行")
    }
}

这样的实现是否有更好的优化,因为这相当于提前注册了一个权限,如果每个子命令都给单独的注册权限再判断权限,这会是一个效率不好且代码不美丽的方案。

我知道可以让需要给子命令权限的提出去变成例如 /foo-sub 的单例命令,但是假设命令很长很多就不适合做太多缩写命令和这种拼接表示命令之间关系的单独的命令,所以我才需要给子命令权限。

我提出的这个实现或许不需要改变 mirai console 的命令系统的底层实现,只是觉得有些笨重,我的代码能力有限,希望 PR 们可以注意到这个方案,在 MC 命令领域子命令是有权限节点的,虽然是服务器插件上 :)