From 4fd379cfece2fd82381833e590bd243dfd9edd15 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 4 Sep 2020 21:42:55 +0800 Subject: [PATCH 01/62] Add suffix ".yml" for data files --- .../console/internal/data/MultiFilePluginDataStorageImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt index aae4ef01b..4fa616485 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -54,7 +54,7 @@ internal open class MultiFilePluginDataStorageImpl( } dir.mkdir() - val file = dir.resolve(name) + val file = dir.resolve("$name.yml") if (file.isDirectory) { error("Target File $file is occupied by a directory therefore data ${instance::class.qualifiedNameOrTip} can't be saved.") } From be01b4a493326e4881a75fe0370e23b16513a21a Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 5 Sep 2020 12:00:48 +0800 Subject: [PATCH 02/62] Update README.md --- docs/README.md | 65 ++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/docs/README.md b/docs/README.md index ffb1db774..c7a5f6c66 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,6 +2,39 @@ 欢迎来到 mirai-console 开发文档! +## 目录 + +- **[准备工作](#准备工作)** +- **[启动 Console](#Run.md)** + +### 后端插件开发基础 + +- 插件 - [Plugin 模块](Plugins.md) +- 指令 - [Command 模块](Commands.md) +- 存储 - [PluginData 模块](PluginData.md) +- 权限 - [Permission 模块](Permissions.md) + +### 后端插件开发进阶 + +- 扩展 - [Extension 模块和扩展点](Extensions.md) +- 扩展 - [实现 PluginLoader](PluginLoader.md) +- 扩展 - [实现 PermissionService](PermissionService.md) + +### 实现前端 +- [FrontEnd](FrontEnd.md) + +[`Plugin`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt +[`Annotations`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt +[`PluginData`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginData.kt +[`JavaPluginScheduler`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt +[`JvmPlugin`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt +[`PluginConfig`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt +[`PluginLoader`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt +[`ConsoleInput`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt +[`PluginDataStorage`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt +[`BotManager`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt +[`Command`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt + ## 准备工作 ***如果跳过本节内容,你很可能遇到无法解决的问题。*** @@ -43,36 +76,6 @@ - 对于 Java 使用者,请阅读 [Java 用户的使用指南](#java-用户的使用指南),[在 Java 使用 Mirai Console 中的 Kotlin `suspend` 函数](#在-java-使用-mirai-console-中的-kotlin-suspend-函数) - 对于 Kotlin 使用者,请熟知 [Kotlin `1.4` 版本带来的新特性](#mirai-console-使用的-kotlin-14-版本的新特性) -## 目录 - -### 后端插件开发基础 - -- 插件 - [Plugin 模块](Plugins.md) -- 指令 - [Command 模块](Commands.md) -- 存储 - [PluginData 模块](PluginData.md) -- 权限 - [Permission 模块](Permissions.md) - -### 后端插件开发进阶 - -- 扩展 - [Extension 模块和扩展点](Extensions.md) -- 扩展 - [实现 PluginLoader](PluginLoader.md) -- 扩展 - [实现 PermissionService](PermissionService.md) - -### 实现前端 -- [FrontEnd](FrontEnd.md) - -[`Plugin`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt -[`Annotations`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt -[`PluginData`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginData.kt -[`JavaPluginScheduler`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt -[`JvmPlugin`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt -[`PluginConfig`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt -[`PluginLoader`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt -[`ConsoleInput`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt -[`PluginDataStorage`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt -[`BotManager`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt -[`Command`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt - ## 附录 @@ -164,4 +167,4 @@ Mirai Console 的版本号遵循 [语义化版本 2.0.0](https://semver.org/lang 在 `1.2.0` 上升为 `ERROR`(使用时会得到一个编译错误); 在 `1.3.0` 上升为 `HIDDEN`(使用者无法看到这些 API)。 -`HIDDEN` 的 API 仍然会保留在代码中并正常编译,以提供二进制兼容性,直到下一个主版本更新。 \ No newline at end of file +`HIDDEN` 的 API 仍然会保留在代码中并正常编译,以提供二进制兼容性,直到下一个主版本更新。 From c3a4e5ef6f39c2c7b62e654359538138a1fb1c7d Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 5 Sep 2020 14:30:22 +0800 Subject: [PATCH 03/62] Create Run.md --- docs/Run.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/Run.md diff --git a/docs/Run.md b/docs/Run.md new file mode 100644 index 000000000..435f45900 --- /dev/null +++ b/docs/Run.md @@ -0,0 +1,29 @@ +# Mirai Console - Run + +Mirai Console 可以独立启动,也可以被嵌入到某个应用中。 + +## 使用第三方工具自动启动 + +## 独立启动 + +### 环境 +- JDK 11 + +### 准备文件 + +要启动 Mirai Console,你需要: +- mirai-core-qqandroid +- mirai-console 后端 +- mirai-console 任一前端 +- 相关依赖 + +只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 pure 前端可用。 + +### 启动 mirai-console-pure 前端 + +mirai 在版本发布时会同时发布打包依赖的 Shadow JAR,存放在 [mirai-repo]。 + +1. 在 [mirai-repo] 下载如下三个模块的最新版本文件: + - [] + +[mirai-repo]: https://github.com/project-mirai/mirai-repo/ From 9add8f71efff4d4b0002cc39d3e45e8fb52e4c6c Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 6 Sep 2020 11:44:16 +0800 Subject: [PATCH 04/62] Add fake constructor for JvmPluginDescription --- .../plugin/jvm/JvmPluginDescription.kt | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt index 66e554c6a..6f9de4245 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt @@ -48,4 +48,41 @@ public data class SimpleJvmPluginDescription init { require(!name.contains(':')) { "':' is forbidden in plugin name" } } -} \ No newline at end of file +} + + +@Deprecated( + "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", + replaceWith = ReplaceWith( + "SimpleJvmPluginDescription(name, version, author, info, dependencies, kind)", + "net.mamoe.mirai.console.plugin.jvm.SimpleJvmPluginDescription" + ), + level = DeprecationLevel.WARNING +) +@Suppress("FunctionName") +public fun JvmPluginDescription( + name: String, + version: Semver, + author: String = "", + info: String = "", + dependencies: List = listOf(), + kind: PluginKind = PluginKind.NORMAL +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) + +@Deprecated( + "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", + replaceWith = ReplaceWith( + "SimpleJvmPluginDescription(name, version, author, info, dependencies, kind)", + "net.mamoe.mirai.console.plugin.jvm.SimpleJvmPluginDescription" + ), + level = DeprecationLevel.WARNING +) +@Suppress("FunctionName") +public fun JvmPluginDescription( + name: String, + version: String, + author: String = "", + info: String = "", + dependencies: List = listOf(), + kind: PluginKind = PluginKind.NORMAL +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) From 52d417ad0ed2968185de986a643b5c5b69de5645 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 6 Sep 2020 11:54:16 +0800 Subject: [PATCH 05/62] Check forbidden plugin names --- .../console/internal/plugin/JvmPluginInternal.kt | 14 +++++++++++--- .../console/internal/plugin/PluginManagerImpl.kt | 8 ++++++++ .../plugin/description/PluginDescription.kt | 9 ++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index 43fabd034..a9b5b039c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -15,6 +15,8 @@ import kotlinx.coroutines.* import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.data.runCatchingLog import net.mamoe.mirai.console.internal.data.mkdir +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Identifier import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader @@ -41,7 +43,13 @@ internal abstract class JvmPluginInternal( final override var isEnabled: Boolean = false private val resourceContainerDelegate by lazy { this::class.java.classLoader.asResourceContainer() } - override fun getResourceAsStream(path: String): InputStream? = resourceContainerDelegate.getResourceAsStream(path) + final override fun getResourceAsStream(path: String): InputStream? = + resourceContainerDelegate.getResourceAsStream(path) + + @OptIn(ExperimentalPermission::class) + override fun allocate(identifierString: String): Identifier { + return Identifier(description.name) + } // region JvmPlugin final override val logger: MiraiLogger by lazy { @@ -62,11 +70,11 @@ internal abstract class JvmPluginInternal( dataFolderPath.toFile() } - override val configFolderPath: Path by lazy { + final override val configFolderPath: Path by lazy { PluginManager.pluginsConfigPath.resolve(description.name).apply { mkdir() } } - override val configFolder: File by lazy { + final override val configFolder: File by lazy { configFolderPath.toFile() } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt index 4bec6f753..9f0f1c7b9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt @@ -159,6 +159,13 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol } } + @kotlin.jvm.Throws(PluginLoadException::class) + internal fun checkPluginDescription(description: PluginDescription) { + when (description.name.toLowerCase()) { + "main", "console", "plugin", "config", "data" -> throw PluginLoadException("Plugin name '${description.name}' is forbidden.") + } + } + /** * @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable] */ @@ -171,6 +178,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol .onEach { (loader, descriptions) -> loader as PluginLoader + descriptions.forEach(PluginManagerImpl::checkPluginDescription) descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies().loadAndEnableAllInOrder() } .flatMap { it.second.asSequence() } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt index a5f259502..64e9058f2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt @@ -27,7 +27,14 @@ public interface PluginDescription { public val kind: PluginKind /** - * 插件名称. 不允许存在 ":" + * 插件名称. 不允许存在 ":". + * + * 插件名称不能完全是以下其中一种. + * - console + * - main + * - plugin + * - config + * - data */ public val name: String From 7da6d338158da64cca06e8f141c1dfaa11a87f44 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 6 Sep 2020 14:21:41 +0800 Subject: [PATCH 06/62] Add PostStartupExtension --- .../extensions/PostStartupExtension.kt | 19 +++++++++++++++ .../MiraiConsoleImplementationBridge.kt | 24 ++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt new file mode 100644 index 000000000..1e8508f6d --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt @@ -0,0 +1,19 @@ +/* + * 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.extensions + +import net.mamoe.mirai.console.extension.AbstractExtensionPoint +import net.mamoe.mirai.console.extension.FunctionExtension + +public fun interface PostStartupExtension : FunctionExtension { + public operator fun invoke() + + public companion object ExtensionPoint : AbstractExtensionPoint(PostStartupExtension::class) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index b1a292caa..7f7176866 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -15,7 +15,9 @@ import com.vdurmont.semver4j.Semver import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.runBlocking import net.mamoe.mirai.Bot +import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.console.MalformedMiraiConsoleImplementationError import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription @@ -24,6 +26,8 @@ import net.mamoe.mirai.console.command.BuiltInCommands import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.data.PluginDataStorage +import net.mamoe.mirai.console.extension.useExtensions +import net.mamoe.mirai.console.extensions.PostStartupExtension import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope @@ -109,16 +113,18 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." } mainLogger.info { "mirai-console started successfully." } - for ((id, password) in AutoLoginConfig.plainPasswords) { - mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password) + runBlocking { + for ((id, password) in AutoLoginConfig.plainPasswords) { + mainLogger.info { "Auto-login $id" } + MiraiConsole.addBot(id, password).alsoLogin() + } + + for ((id, password) in AutoLoginConfig.md5Passwords) { + mainLogger.info { "Auto-login $id" } + MiraiConsole.addBot(id, password).alsoLogin() + } } - for ((id, password) in AutoLoginConfig.md5Passwords) { - mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password) - } - - // Only for initialize + PostStartupExtension.useExtensions { it() } } } \ No newline at end of file From 77176f5242936d57540c5fa5b00cc3129bcb1218 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 6 Sep 2020 14:22:01 +0800 Subject: [PATCH 07/62] Adjust exception message for ExtensionException --- .../net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt index 4a52ef01a..81c93c68e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt @@ -105,7 +105,7 @@ internal fun AbstractExtensionPoint.throwExtensionExcepti throwable: Throwable ) { throw ExtensionException( - "Exception while executing extension ${extension.kClassQualifiedNameOrTip} from ${plugin.name}, registered for ${this.type.qualifiedName}", + "Exception while executing extension ${extension.kClassQualifiedNameOrTip} provided by plugin '${plugin.name}', registered for ${this.type.qualifiedName}", throwable ) } From 78d5f56c493d48e4f572b8da566c52c29892de7c Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 6 Sep 2020 14:24:50 +0800 Subject: [PATCH 08/62] PermissionService fundamental --- .../mirai/console/command/CommandSender.kt | 9 +- .../extensions/PermissionServiceProvider.kt | 4 +- .../internal/plugin/JvmPluginInternal.kt | 6 +- .../permission/ExperimentalPermission.kt | 23 ++++ .../mirai/console/permission/Permissible.kt | 25 ++++ .../mirai/console/permission/Permission.kt | 21 ++++ .../console/permission/PermissionChecker.kt | 19 +++ .../console/permission/PermissionGroup.kt | 84 +++++++++++++ .../permission/PermissionIdentifier.kt | 26 ++++ .../console/permission/PermissionService.kt | 112 ++++++++++++++++-- .../mirai/console/plugin/jvm/JvmPlugin.kt | 5 +- 11 files changed, 319 insertions(+), 15 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt index 1dbe2a424..e8ea527ac 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt @@ -32,6 +32,8 @@ import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.data.castOrNull import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Permissible import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext @@ -132,7 +134,8 @@ import kotlin.internal.LowPriorityInOverloadResolution * @see toCommandSender * @see asCommandSender */ -public interface CommandSender : CoroutineScope { +@OptIn(ExperimentalPermission::class) +public interface CommandSender : CoroutineScope, Permissible { /** * 与这个 [CommandSender] 相关的 [Bot]. * 当通过控制台执行时为 `null`. @@ -158,6 +161,10 @@ public interface CommandSender : CoroutineScope { */ public val name: String + @ExperimentalPermission + override val identifier: String + get() = user?.id?.toString() ?: bot?.id?.toString() ?: error("Internal error: bot user and bot are null") + /** * 立刻发送一条消息. * diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt index 2d35375d8..b50eeefae 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt @@ -2,11 +2,13 @@ package net.mamoe.mirai.console.extensions import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.SingletonExtension +import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +@ExperimentalPermission @ConsoleExperimentalAPI -public interface PermissionServiceProvider : SingletonExtension { +public interface PermissionServiceProvider : SingletonExtension> { public companion object ExtensionPoint : AbstractExtensionPoint(PermissionServiceProvider::class) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index a9b5b039c..f4181039a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -16,7 +16,7 @@ import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.data.runCatchingLog import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.permission.ExperimentalPermission -import net.mamoe.mirai.console.permission.Identifier +import net.mamoe.mirai.console.permission.PermissionIdentifier import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader @@ -47,8 +47,8 @@ internal abstract class JvmPluginInternal( resourceContainerDelegate.getResourceAsStream(path) @OptIn(ExperimentalPermission::class) - override fun allocate(identifierString: String): Identifier { - return Identifier(description.name) + override fun permissionIdentifier(identifierString: String): PermissionIdentifier { + return PermissionIdentifier(description.name, identifierString) } // region JvmPlugin diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt new file mode 100644 index 000000000..479bf5d9d --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt @@ -0,0 +1,23 @@ +/* + * 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.permission + +import kotlin.annotation.AnnotationTarget.* + +/** + * 标记一个实验性的权限系统 API + */ +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) +@MustBeDocumented +public annotation class ExperimentalPermission( + val message: String = "" +) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt new file mode 100644 index 000000000..bc12c3706 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -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 + */ + +@file:Suppress("NOTHING_TO_INLINE", "unused") + +package net.mamoe.mirai.console.permission + +@ExperimentalPermission +public interface Permissible { + public val identifier: String +} + +@ExperimentalPermission +public inline fun Permissible.hasPermission(permission: Permission): Boolean = + PermissionService.run { permission.testPermission(this@hasPermission) } + +@ExperimentalPermission +public inline fun Permissible.hasPermission(permission: PermissionIdentifier): Boolean = + PermissionService.run { permission.testPermission(this@hasPermission) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt new file mode 100644 index 000000000..790a1f73f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -0,0 +1,21 @@ +/* + * 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.permission + + +/** + * 一个权限节点 + */ +@ExperimentalPermission +public interface Permission { + public val identifier: PermissionIdentifier + public val description: String + public val base: PermissionIdentifier? +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt new file mode 100644 index 000000000..21fbb4c42 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt @@ -0,0 +1,19 @@ +/* + * 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.permission + +import net.mamoe.mirai.console.command.CommandSender + +@ExperimentalPermission +public fun interface PermissionChecker { + public fun CommandSender.testPermission(): Boolean +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt new file mode 100644 index 000000000..4e5006494 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt @@ -0,0 +1,84 @@ +/* + * 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.permission + +import net.mamoe.mirai.console.command.CommandSender +import kotlin.reflect.KProperty + +@ExperimentalPermission +public abstract class PermissionGroup( + private val identifierNamespace: PermissionIdentifierNamespace, +) { + @ExperimentalPermission + public inner class PermissionBuilder { + @JvmField + internal var description: String = "" + + @JvmField + internal var basePermission: PermissionIdentifier? = null + + @JvmField + internal var permissionChecker: PermissionChecker? = null + + public fun description(description: String): PermissionBuilder = apply { this.description = description } + public fun dependsOn(basePermission: PermissionIdentifier?): PermissionBuilder = + apply { this.basePermission = basePermission } + + public fun dependsOn(basePermission: Permission?): PermissionBuilder = + apply { this.basePermission = basePermission?.identifier } + + public fun basePermission(basePermission: PermissionIdentifier?): PermissionBuilder = + apply { this.basePermission = basePermission } + + public fun basePermission(basePermission: Permission?): PermissionBuilder = + apply { this.basePermission = basePermission?.identifier } + + public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder = + apply { this.permissionChecker = permissionChecker } + + public fun build(property: KProperty<*>): Permission { + return PermissionService.register( + identifierNamespace.permissionIdentifier(property.name), + description, + basePermission + ) + } + } + + public infix fun String.dependsOn(permission: Permission): PermissionBuilder { + return PermissionBuilder().apply { description(this@dependsOn);dependsOn(permission) } + } + + + public infix fun PermissionBuilder.defaults(permission: PermissionChecker): PermissionBuilder { + return apply { defaults(permission) } + } + + public infix fun PermissionBuilder.defaults(permission: CommandSender.() -> Boolean): PermissionBuilder { + return apply { defaults(permission) } + } + + public infix fun String.defaults(permission: PermissionChecker): PermissionBuilder { + return PermissionBuilder().apply { defaults(permission) } + } + + + public operator fun String.invoke(block: PermissionBuilder.() -> Unit): PermissionBuilder { + return PermissionBuilder().apply(block) + } + + public operator fun String.provideDelegate(thisRef: PermissionGroup, property: KProperty<*>): Permission = + PermissionBuilder().apply { description(this@provideDelegate) }.build(property) + + public operator fun Permission.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission = this + public operator fun PermissionBuilder.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission = + this.build(property) +} + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt new file mode 100644 index 000000000..ea773f0ed --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt @@ -0,0 +1,26 @@ +/* + * 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.permission + +import kotlinx.serialization.Serializable + + +@Serializable +@ExperimentalPermission +public data class PermissionIdentifier( + public val namespace: String, + public val id: String +) + +@ExperimentalPermission +public interface PermissionIdentifierNamespace { + @ExperimentalPermission + public fun permissionIdentifier(identifierString: String): PermissionIdentifier +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 0d01df6b8..938c73b9e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -7,28 +7,122 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("NOTHING_TO_INLINE", "unused") + package net.mamoe.mirai.console.permission -import net.mamoe.mirai.console.extension.useExtensions +import net.mamoe.mirai.console.data.AutoSavePluginConfig +import net.mamoe.mirai.console.data.Value +import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.extensions.PermissionServiceProvider -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import java.util.concurrent.ConcurrentLinkedQueue /** * [PermissionServiceProvider] */ -@ConsoleExperimentalAPI -public interface PermissionService { +@ExperimentalPermission +public interface PermissionService

{ + public fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? = null + ): P + + public operator fun get(identifier: PermissionIdentifier): P? - public companion object : PermissionService { + public fun getGrantedPermissions(permissible: Permissible): List + + + public fun testPermission(permissible: Permissible, permission: P): Boolean = + permissible.getGrantedPermissions().any { it == permission.identifier } + + + public companion object INSTANCE : PermissionService { + private val builtIn: PermissionService get() = TODO("PS IMPL") + + @Suppress("UNCHECKED_CAST") private val instance by lazy { - PermissionServiceProvider.useExtensions { } + PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance + ?: builtIn // TODO: 2020/9/4 ask for further choice + as PermissionService } + override fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): Permission = instance.register(identifier, description, base) + + override fun get(identifier: PermissionIdentifier): Permission? = instance[identifier] + override fun getGrantedPermissions(permissible: Permissible): List = + instance.getGrantedPermissions(permissible) } } -public interface proprietary +@ExperimentalPermission +public interface HotDeploymentSupportPermissionService

: PermissionService

{ + public fun grant(permissible: Permissible, permission: P) + public fun deny(permissible: Permissible, permission: P) +} -@ConsoleExperimentalAPI -public interface Permission +@ExperimentalPermission +public open class HotDeploymentNotSupportedException : Exception { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} + + +/** + * [PermissionServiceProvider] + */ +@ExperimentalPermission +public abstract class AbstractPermissionService

: AutoSavePluginConfig(), PermissionService

{ + @JvmField + protected val permissions: ConcurrentLinkedQueue

= ConcurrentLinkedQueue() + + @JvmField + protected val grantedPermissionMap: Value>> = value() + + public override fun getGrantedPermissions(permissible: Permissible): List = + grantedPermissionMap.value[permissible.identifier].orEmpty() + + public override operator fun get(identifier: PermissionIdentifier): P? = + permissions.find { it.identifier == identifier } + + public override fun testPermission(permissible: Permissible, permission: P): Boolean = + permissible.getGrantedPermissions().any { it == permission.identifier } +} + +@ExperimentalPermission +public inline fun Permissible.getGrantedPermissions(): List = + PermissionService.run { this.getGrantedPermissions(this@getGrantedPermissions) } + +@ExperimentalPermission +public inline fun Permission.testPermission(permissible: Permissible): Boolean = + PermissionService.run { testPermission(permissible, this@testPermission) } + +@ExperimentalPermission +public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean { + val p = PermissionService[this] ?: return false + return p.testPermission(permissible) +} + +@OptIn(ExperimentalPermission::class) +private class PermissionServiceImpl : AbstractPermissionService() { + private val instances: ConcurrentLinkedQueue = ConcurrentLinkedQueue() + + private class PermissionImpl( + override val identifier: PermissionIdentifier, + override val description: String, + override val base: PermissionIdentifier? + ) : Permission + + override fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): PermissionImpl = PermissionImpl(identifier, description, base) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt index 85df5ea00..82ba0b863 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt @@ -21,6 +21,8 @@ import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.console.data.AutoSavePluginDataHolder import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginData +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissionIdentifierNamespace import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginFileExtensions import net.mamoe.mirai.console.plugin.ResourceContainer @@ -41,8 +43,9 @@ import net.mamoe.mirai.utils.MiraiLogger * @see JvmPlugin 支持文件系统扩展 * @see ResourceContainer 支持资源获取 (如 Jar 中的资源文件) */ +@OptIn(ExperimentalPermission::class) public interface JvmPlugin : Plugin, CoroutineScope, - PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder { + PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdentifierNamespace { /** 日志 */ public val logger: MiraiLogger From 8e3e3286728a5ca4b87725e5ea9cc6d4bbec8e6d Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 7 Sep 2020 12:12:27 +0800 Subject: [PATCH 09/62] PermissionService fundamental --- .../mirai/console/command/CommandSender.kt | 4 - .../MiraiConsoleImplementationBridge.kt | 10 +- .../permission/PermissionServiceImpl.kt | 101 +++++++++++++ .../permission/BuiltInPermissionServices.kt | 58 +++++++ .../mirai/console/permission/Exceptions.kt | 27 ++++ .../HotDeploymentSupportPermissionService.kt | 17 +++ .../mirai/console/permission/Permissible.kt | 8 +- .../mirai/console/permission/Permission.kt | 10 ++ .../console/permission/PermissionGroup.kt | 2 +- .../permission/PermissionIdentifier.kt | 25 ++- .../console/permission/PermissionService.kt | 142 ++++++++---------- buildSrc/src/main/kotlin/Versions.kt | 2 +- 12 files changed, 310 insertions(+), 96 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt index e8ea527ac..c797a967f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt @@ -161,10 +161,6 @@ public interface CommandSender : CoroutineScope, Permissible { */ public val name: String - @ExperimentalPermission - override val identifier: String - get() = user?.id?.toString() ?: bot?.id?.toString() ?: error("Internal error: bot user and bot are null") - /** * 立刻发送一条消息. * diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index 7f7176866..c92678565 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -33,6 +33,9 @@ import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.HotDeploymentSupportPermissionService +import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter @@ -80,7 +83,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI override fun createLogger(identity: String?): MiraiLogger = instance.createLogger(identity) - @OptIn(ConsoleExperimentalAPI::class) + @OptIn(ConsoleExperimentalAPI::class, ExperimentalPermission::class) internal fun doStart() { val buildDateFormatted = buildDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) @@ -102,6 +105,11 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.info { "Reloading configurations..." } ConsoleDataScope.reloadAll() + PermissionService // init + if (PermissionService.INSTANCE is HotDeploymentSupportPermissionService<*>) { + + } + BuiltInCommands.registerAll() mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } CommandManager diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt new file mode 100644 index 000000000..1155d9fbe --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt @@ -0,0 +1,101 @@ +package net.mamoe.mirai.console.internal.permission + +import net.mamoe.mirai.console.command.ConsoleCommandSender +import net.mamoe.mirai.console.command.UserCommandSender +import net.mamoe.mirai.console.data.AutoSavePluginConfig +import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault +import net.mamoe.mirai.console.data.Value +import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.extensions.PermissionServiceProvider +import net.mamoe.mirai.console.permission.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass + +/** + * [PermissionServiceProvider] + */ +@Suppress("RedundantVisibilityModifier") +@ExperimentalPermission +internal abstract class AbstractPermissionService : + PermissionService { + protected abstract val Permissible.identifier: TPermissibleIdentifier + + @JvmField + protected val permissions: MutableMap = ConcurrentHashMap() + + @JvmField + protected val grantedPermissionMap: MutableMap> = + ConcurrentHashMap() + + public override fun getGrantedPermissions(permissible: Permissible): Sequence = + grantedPermissionMap[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty() + + public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier] + + public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean = + permissible.getGrantedPermissions().any { it == permission } +} + +/** + * [PermissionServiceProvider] + */ +@Suppress("RedundantVisibilityModifier") +@ExperimentalPermission +internal abstract class AbstractHotDeploymentSupportPermissionService : + PermissionService, + HotDeploymentSupportPermissionService, AutoSavePluginConfig() { + + protected abstract val Permissible.identifier: TPermissibleIdentifier + + @JvmField + protected val permissions: MutableMap = ConcurrentHashMap() + + @JvmField + protected val grantedPermissionMap: Value>> = + value>>().withEmptyDefault() + + public override fun getGrantedPermissions(permissible: Permissible): Sequence = + grantedPermissionMap.value[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty() + + public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier] + + public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean = + permissible.getGrantedPermissions().any { it == permission } +} + + +internal data class LiteralPermissibleIdentifier( + val context: String, + val value: String +) + +@OptIn(ExperimentalPermission::class) +private object PermissionServiceImpl : + AbstractHotDeploymentSupportPermissionService() { + + override fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): PermissionImpl = PermissionImpl(identifier, description, base) + + override fun grant(permissible: Permissible, permission: PermissionImpl) { + grantedPermissionMap.value[permissible.identifier]!!.add(permission.identifier) + } + + override fun deny(permissible: Permissible, permission: PermissionImpl) { + grantedPermissionMap.value[permissible.identifier]!!.remove(permission.identifier) + } + + override val permissionType: KClass + get() = PermissionImpl::class + override val Permissible.identifier: LiteralPermissibleIdentifier + get() = LiteralPermissibleIdentifier( + "", + when (this) { + is ConsoleCommandSender -> "CONSOLE" + is UserCommandSender -> this.user.id.toString() + else -> "" + } + ) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt new file mode 100644 index 000000000..464e7a4f6 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -0,0 +1,58 @@ +/* + * 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.permission + +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass + + +@ExperimentalPermission +public object AllGrantPermissionService : PermissionService { + private val all = ConcurrentHashMap() + override val permissionType: KClass + get() = PermissionImpl::class + + override fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): PermissionImpl { + val new = PermissionImpl(identifier, description, base) + if (all.putIfAbsent(identifier, new) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + } + return new + } + + override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] + override fun getGrantedPermissions(permissible: Permissible): Sequence = all.values.asSequence() +} + +@ExperimentalPermission +public object AllDenyPermissionService : PermissionService { + private val all = ConcurrentHashMap() + override val permissionType: KClass + get() = PermissionImpl::class + + override fun register( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): PermissionImpl { + val new = PermissionImpl(identifier, description, base) + if (all.putIfAbsent(identifier, new) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + } + return new + } + + override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] + override fun getGrantedPermissions(permissible: Permissible): Sequence = emptySequence() +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt new file mode 100644 index 000000000..c8b358936 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt @@ -0,0 +1,27 @@ +/* + * 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.permission + + +@ExperimentalPermission +public open class HotDeploymentNotSupportedException : Exception { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} + +@ExperimentalPermission +public open class DuplicatedRegistrationException : Exception { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt new file mode 100644 index 000000000..607989ece --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt @@ -0,0 +1,17 @@ +/* + * 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.permission + + +@ExperimentalPermission +public interface HotDeploymentSupportPermissionService

: PermissionService

{ + public fun grant(permissible: Permissible, permission: P) + public fun deny(permissible: Permissible, permission: P) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt index bc12c3706..f0c1f6383 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -11,10 +11,12 @@ package net.mamoe.mirai.console.permission +/** + * + * 注意: 请不要自主实现 [Permissible] + */ @ExperimentalPermission -public interface Permissible { - public val identifier: String -} +public interface Permissible @ExperimentalPermission public inline fun Permissible.hasPermission(permission: Permission): Boolean = diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 790a1f73f..4d1a3ff88 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -19,3 +19,13 @@ public interface Permission { public val description: String public val base: PermissionIdentifier? } + +/** + * [Permission] 的简单实现 + */ +@ExperimentalPermission +public open class PermissionImpl( + override val identifier: PermissionIdentifier, + override val description: String, + override val base: PermissionIdentifier? +) : Permission \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt index 4e5006494..e898d0c07 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt @@ -44,7 +44,7 @@ public abstract class PermissionGroup( apply { this.permissionChecker = permissionChecker } public fun build(property: KProperty<*>): Permission { - return PermissionService.register( + return PermissionService.INSTANCE.register( identifierNamespace.permissionIdentifier(property.name), description, basePermission diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt index ea773f0ed..b9d7b8d25 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt @@ -9,15 +9,34 @@ package net.mamoe.mirai.console.permission -import kotlinx.serialization.Serializable +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import net.mamoe.mirai.console.internal.data.map -@Serializable @ExperimentalPermission public data class PermissionIdentifier( public val namespace: String, public val id: String -) +) { + init { + require(!namespace.contains(':')) { + "':' is not allowed in namespace" + } + require(!id.contains(':')) { + "':' is not allowed in id" + } + } + + @Serializer(forClass = PermissionIdentifier::class) + public object AsClassSerializer + + public object AsStringSerializer : KSerializer by String.serializer().map( + serializer = { it.namespace + ":" + it.id }, + deserializer = { it.split(':').let { (namespace, id) -> PermissionIdentifier(namespace, id) } } + ) +} @ExperimentalPermission public interface PermissionIdentifierNamespace { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 938c73b9e..645a50797 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -11,17 +11,19 @@ package net.mamoe.mirai.console.permission -import net.mamoe.mirai.console.data.AutoSavePluginConfig -import net.mamoe.mirai.console.data.Value -import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.extensions.PermissionServiceProvider -import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.ConcurrentHashMap +import kotlin.reflect.KClass /** * [PermissionServiceProvider] */ @ExperimentalPermission public interface PermissionService

{ + @ExperimentalPermission + public val permissionType: KClass

+ + @Throws(DuplicatedRegistrationException::class) public fun register( identifier: PermissionIdentifier, description: String, @@ -30,99 +32,73 @@ public interface PermissionService

{ public operator fun get(identifier: PermissionIdentifier): P? - - public fun getGrantedPermissions(permissible: Permissible): List + public fun getGrantedPermissions(permissible: Permissible): Sequence

public fun testPermission(permissible: Permissible, permission: P): Boolean = - permissible.getGrantedPermissions().any { it == permission.identifier } + permissible.getGrantedPermissions().any { it == permission } + public companion object { + private val builtIn: PermissionService get() = AllGrantPermissionService - public companion object INSTANCE : PermissionService { - private val builtIn: PermissionService get() = TODO("PS IMPL") - - @Suppress("UNCHECKED_CAST") - private val instance by lazy { - PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance - ?: builtIn // TODO: 2020/9/4 ask for further choice - as PermissionService + @get:JvmName("getInstance") + @JvmStatic + public val INSTANCE: PermissionService by lazy { + PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance ?: builtIn + // TODO: 2020/9/4 ExtensionSelector } - - override fun register( - identifier: PermissionIdentifier, - description: String, - base: PermissionIdentifier? - ): Permission = instance.register(identifier, description, base) - - override fun get(identifier: PermissionIdentifier): Permission? = instance[identifier] - override fun getGrantedPermissions(permissible: Permissible): List = - instance.getGrantedPermissions(permissible) } } @ExperimentalPermission -public interface HotDeploymentSupportPermissionService

: PermissionService

{ - public fun grant(permissible: Permissible, permission: P) - public fun deny(permissible: Permissible, permission: P) -} +public abstract class AbstractPermissionService

: PermissionService

{ + protected val all: MutableMap = ConcurrentHashMap() -@ExperimentalPermission -public open class HotDeploymentNotSupportedException : Exception { - public constructor() : super() - public constructor(message: String?) : super(message) - public constructor(message: String?, cause: Throwable?) : super(message, cause) - public constructor(cause: Throwable?) : super(cause) -} - - -/** - * [PermissionServiceProvider] - */ -@ExperimentalPermission -public abstract class AbstractPermissionService

: AutoSavePluginConfig(), PermissionService

{ - @JvmField - protected val permissions: ConcurrentLinkedQueue

= ConcurrentLinkedQueue() - - @JvmField - protected val grantedPermissionMap: Value>> = value() - - public override fun getGrantedPermissions(permissible: Permissible): List = - grantedPermissionMap.value[permissible.identifier].orEmpty() - - public override operator fun get(identifier: PermissionIdentifier): P? = - permissions.find { it.identifier == identifier } - - public override fun testPermission(permissible: Permissible, permission: P): Boolean = - permissible.getGrantedPermissions().any { it == permission.identifier } -} - -@ExperimentalPermission -public inline fun Permissible.getGrantedPermissions(): List = - PermissionService.run { this.getGrantedPermissions(this@getGrantedPermissions) } - -@ExperimentalPermission -public inline fun Permission.testPermission(permissible: Permissible): Boolean = - PermissionService.run { testPermission(permissible, this@testPermission) } - -@ExperimentalPermission -public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean { - val p = PermissionService[this] ?: return false - return p.testPermission(permissible) -} - -@OptIn(ExperimentalPermission::class) -private class PermissionServiceImpl : AbstractPermissionService() { - private val instances: ConcurrentLinkedQueue = ConcurrentLinkedQueue() - - private class PermissionImpl( - override val identifier: PermissionIdentifier, - override val description: String, - override val base: PermissionIdentifier? - ) : Permission + protected abstract fun createPermission( + identifier: PermissionIdentifier, + description: String, + base: PermissionIdentifier? + ): P override fun register( identifier: PermissionIdentifier, description: String, base: PermissionIdentifier? - ): PermissionImpl = PermissionImpl(identifier, description, base) + ): P { + val new = createPermission(identifier, description, base) + if (all.putIfAbsent(identifier, new) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + } + return new + } + + override fun get(identifier: PermissionIdentifier): P? = all[identifier] + override fun getGrantedPermissions(permissible: Permissible): Sequence

= all.values.asSequence() +} + +@ExperimentalPermission +public inline fun Permissible.getGrantedPermissions(): Sequence = + PermissionService.INSTANCE.run { + getGrantedPermissions(this@getGrantedPermissions) + } + + +@ExperimentalPermission +public inline fun Permission.testPermission(permissible: Permissible): Boolean = + PermissionService.INSTANCE.run { + require(permissionType.isInstance(this@testPermission)) { + "Custom-constructed Permission instance is not allowed. " + + "Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get" + } + + @Suppress("UNCHECKED_CAST") + this as PermissionService + + testPermission(permissible, this@testPermission) + } + +@ExperimentalPermission +public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean { + val p = PermissionService.INSTANCE[this] ?: return false + return p.testPermission(permissible) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 3970cc2a4..a93801e38 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -18,7 +18,7 @@ object Versions { const val core = "1.2.2" - const val console = "1.0-M4-dev-4" + const val console = "1.0-M4-dev-5" const val consoleGraphical = "0.0.7" const val consoleTerminal = "0.1.0" const val consolePure = console From 1f9705deaf77981859d27372cc4729f2a893df8f Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 7 Sep 2020 21:59:15 +0800 Subject: [PATCH 10/62] Permissions --- .../mirai/console/command/CommandSender.kt | 16 ++- .../MiraiConsoleImplementationBridge.kt | 20 ++-- .../data/builtins/ConsoleDataScope.kt | 9 +- .../permission/PermissionServiceImpl.kt | 101 ------------------ .../internal/plugin/JvmPluginInternal.kt | 6 +- .../permission/BuiltInPermissionServices.kt | 58 +++++++--- .../HotDeploymentSupportPermissionService.kt | 17 --- .../mirai/console/permission/Permissible.kt | 72 ++++++++++++- .../mirai/console/permission/Permission.kt | 8 +- .../console/permission/PermissionGroup.kt | 10 +- ...ermissionIdentifier.kt => PermissionId.kt} | 10 +- .../console/permission/PermissionService.kt | 81 +++++++++----- .../permission/StorablePermissionService.kt | 26 +++++ 13 files changed, 245 insertions(+), 189 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/{PermissionIdentifier.kt => PermissionId.kt} (82%) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt index c797a967f..2820a90da 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt @@ -32,8 +32,10 @@ import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.data.castOrNull import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf +import net.mamoe.mirai.console.permission.AbstractPermissibleIdentifier import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.Permissible +import net.mamoe.mirai.console.permission.PermissibleIdentifier import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext @@ -512,6 +514,9 @@ public abstract class ConsoleCommandSender @ConsoleFrontEndImplementation constr public final override val name: String get() = NAME public final override fun toString(): String = NAME + @ExperimentalPermission + public final override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.Console + public companion object INSTANCE : ConsoleCommandSender(), CoroutineScope { public const val NAME: String = "ConsoleCommandSender" public override val coroutineContext: CoroutineContext by lazy { MiraiConsole.childScopeContext(NAME) } @@ -607,6 +612,9 @@ public open class FriendCommandSender internal constructor( public override val subject: Contact get() = user public override fun toString(): String = "FriendCommandSender($user)" + @ExperimentalPermission + public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactFriend(user.id) + @JvmBlockingBridge public override suspend fun sendMessage(message: String): MessageReceipt = sendMessage(PlainText(message)) @@ -623,10 +631,13 @@ public open class MemberCommandSender internal constructor( ) : AbstractUserCommandSender(), GroupAwareCommandSender, CoroutineScope by user.childScope("MemberCommandSender") { - public override val group: Group get() = user.group + public final override val group: Group get() = user.group public override val subject: Group get() = group public override fun toString(): String = "MemberCommandSender($user)" + @ExperimentalPermission + public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactMember(user.id, group.id) + @JvmBlockingBridge public override suspend fun sendMessage(message: String): MessageReceipt = sendMessage(PlainText(message)) @@ -647,6 +658,9 @@ public open class TempCommandSender internal constructor( public override val subject: Contact get() = group public override fun toString(): String = "TempCommandSender($user)" + @ExperimentalPermission + override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactTemp(user.id) + @JvmBlockingBridge public override suspend fun sendMessage(message: String): MessageReceipt = sendMessage(PlainText(message)) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index c92678565..e90654632 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -34,8 +34,8 @@ import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl import net.mamoe.mirai.console.permission.ExperimentalPermission -import net.mamoe.mirai.console.permission.HotDeploymentSupportPermissionService import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.StorablePermissionService import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter @@ -102,23 +102,27 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } } - mainLogger.info { "Reloading configurations..." } + mainLogger.verbose { "Loading configurations..." } ConsoleDataScope.reloadAll() - PermissionService // init - if (PermissionService.INSTANCE is HotDeploymentSupportPermissionService<*>) { - + mainLogger.verbose { "Loading PermissionService..." } + PermissionService.INSTANCE.let { ps -> + if (ps is StorablePermissionService<*>) { + ConsoleDataScope.addAndReloadConfig(ps.config) + mainLogger.verbose { "Reloaded PermissionService settings." } + } } + mainLogger.verbose { "Loading built-in commands..." } BuiltInCommands.registerAll() - mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } + mainLogger.verbose { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } CommandManager CommandManagerImpl.commandListener // start - mainLogger.info { "Loading plugins..." } + mainLogger.verbose { "Loading plugins..." } PluginManager PluginManagerImpl.loadEnablePlugins() - mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." } + mainLogger.verbose { "${PluginManager.plugins.size} plugin(s) loaded." } mainLogger.info { "mirai-console started successfully." } runBlocking { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt index 177dd533d..37899e3d5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt @@ -21,8 +21,13 @@ import net.mamoe.mirai.utils.minutesToMillis internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") { - private val data: Array = arrayOf() - private val configs: Array = arrayOf(ManagersConfig, AutoLoginConfig) + private val data: List = mutableListOf() + private val configs: MutableList = mutableListOf(ManagersConfig, AutoLoginConfig) + + fun addAndReloadConfig(config: PluginConfig) { + configs.add(config) + ConsoleBuiltInPluginConfigStorage.load(ConsoleBuiltInPluginConfigHolder, config) + } fun reloadAll() { data.forEach { dt -> diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt deleted file mode 100644 index 1155d9fbe..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/permission/PermissionServiceImpl.kt +++ /dev/null @@ -1,101 +0,0 @@ -package net.mamoe.mirai.console.internal.permission - -import net.mamoe.mirai.console.command.ConsoleCommandSender -import net.mamoe.mirai.console.command.UserCommandSender -import net.mamoe.mirai.console.data.AutoSavePluginConfig -import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault -import net.mamoe.mirai.console.data.Value -import net.mamoe.mirai.console.data.value -import net.mamoe.mirai.console.extensions.PermissionServiceProvider -import net.mamoe.mirai.console.permission.* -import java.util.concurrent.ConcurrentHashMap -import kotlin.reflect.KClass - -/** - * [PermissionServiceProvider] - */ -@Suppress("RedundantVisibilityModifier") -@ExperimentalPermission -internal abstract class AbstractPermissionService : - PermissionService { - protected abstract val Permissible.identifier: TPermissibleIdentifier - - @JvmField - protected val permissions: MutableMap = ConcurrentHashMap() - - @JvmField - protected val grantedPermissionMap: MutableMap> = - ConcurrentHashMap() - - public override fun getGrantedPermissions(permissible: Permissible): Sequence = - grantedPermissionMap[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty() - - public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier] - - public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean = - permissible.getGrantedPermissions().any { it == permission } -} - -/** - * [PermissionServiceProvider] - */ -@Suppress("RedundantVisibilityModifier") -@ExperimentalPermission -internal abstract class AbstractHotDeploymentSupportPermissionService : - PermissionService, - HotDeploymentSupportPermissionService, AutoSavePluginConfig() { - - protected abstract val Permissible.identifier: TPermissibleIdentifier - - @JvmField - protected val permissions: MutableMap = ConcurrentHashMap() - - @JvmField - protected val grantedPermissionMap: Value>> = - value>>().withEmptyDefault() - - public override fun getGrantedPermissions(permissible: Permissible): Sequence = - grantedPermissionMap.value[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty() - - public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier] - - public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean = - permissible.getGrantedPermissions().any { it == permission } -} - - -internal data class LiteralPermissibleIdentifier( - val context: String, - val value: String -) - -@OptIn(ExperimentalPermission::class) -private object PermissionServiceImpl : - AbstractHotDeploymentSupportPermissionService() { - - override fun register( - identifier: PermissionIdentifier, - description: String, - base: PermissionIdentifier? - ): PermissionImpl = PermissionImpl(identifier, description, base) - - override fun grant(permissible: Permissible, permission: PermissionImpl) { - grantedPermissionMap.value[permissible.identifier]!!.add(permission.identifier) - } - - override fun deny(permissible: Permissible, permission: PermissionImpl) { - grantedPermissionMap.value[permissible.identifier]!!.remove(permission.identifier) - } - - override val permissionType: KClass - get() = PermissionImpl::class - override val Permissible.identifier: LiteralPermissibleIdentifier - get() = LiteralPermissibleIdentifier( - "", - when (this) { - is ConsoleCommandSender -> "CONSOLE" - is UserCommandSender -> this.user.id.toString() - else -> "" - } - ) -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index f4181039a..0036fc1a8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -16,7 +16,7 @@ import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.data.runCatchingLog import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.permission.ExperimentalPermission -import net.mamoe.mirai.console.permission.PermissionIdentifier +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader @@ -47,8 +47,8 @@ internal abstract class JvmPluginInternal( resourceContainerDelegate.getResourceAsStream(path) @OptIn(ExperimentalPermission::class) - override fun permissionIdentifier(identifierString: String): PermissionIdentifier { - return PermissionIdentifier(description.name, identifierString) + override fun permissionIdentifier(identifierString: String): PermissionId { + return PermissionId(description.name, identifierString) } // region JvmPlugin diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 464e7a4f6..42989b3c5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -9,50 +9,80 @@ package net.mamoe.mirai.console.permission +import net.mamoe.mirai.console.data.AutoSavePluginConfig import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass @ExperimentalPermission public object AllGrantPermissionService : PermissionService { - private val all = ConcurrentHashMap() + private val all = ConcurrentHashMap() override val permissionType: KClass get() = PermissionImpl::class override fun register( - identifier: PermissionIdentifier, + id: PermissionId, description: String, - base: PermissionIdentifier? + base: PermissionId? ): PermissionImpl { - val new = PermissionImpl(identifier, description, base) - if (all.putIfAbsent(identifier, new) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + val new = PermissionImpl(id, description, base) + if (all.putIfAbsent(id, new) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}") } return new } - override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] + override fun get(id: PermissionId): PermissionImpl? = all[id] override fun getGrantedPermissions(permissible: Permissible): Sequence = all.values.asSequence() + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { + } + + override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = true + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { + } } @ExperimentalPermission public object AllDenyPermissionService : PermissionService { - private val all = ConcurrentHashMap() + private val all = ConcurrentHashMap() override val permissionType: KClass get() = PermissionImpl::class override fun register( - identifier: PermissionIdentifier, + id: PermissionId, description: String, - base: PermissionIdentifier? + base: PermissionId? ): PermissionImpl { - val new = PermissionImpl(identifier, description, base) - if (all.putIfAbsent(identifier, new) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + val new = PermissionImpl(id, description, base) + if (all.putIfAbsent(id, new) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}") } return new } - override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] + override fun get(id: PermissionId): PermissionImpl? = all[id] override fun getGrantedPermissions(permissible: Permissible): Sequence = emptySequence() + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { + } + + override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = false + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { + } } + +@ExperimentalPermission +internal object BuiltInPermissionService : AbstractConcurrentPermissionService(), + StorablePermissionService { + + @ExperimentalPermission + override val permissionType: KClass + get() = PermissionImpl::class + override val permissions: MutableMap get() = config.permissions + override val grantedPermissionsMap: MutableMap> get() = config.grantedPermissionMap + + override fun createPermission(id: PermissionId, description: String, base: PermissionId?): PermissionImpl = + PermissionImpl(id, description, base) + + override val config: StorablePermissionService.ConcurrentSaveData = + StorablePermissionService.ConcurrentSaveData("PermissionService", AutoSavePluginConfig()) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt deleted file mode 100644 index 607989ece..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/HotDeploymentSupportPermissionService.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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.permission - - -@ExperimentalPermission -public interface HotDeploymentSupportPermissionService

: PermissionService

{ - public fun grant(permissible: Permissible, permission: P) - public fun deny(permissible: Permissible, permission: P) -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt index f0c1f6383..ea36e635d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -7,21 +7,87 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("NOTHING_TO_INLINE", "unused") +@file:Suppress("NOTHING_TO_INLINE", "unused", "MemberVisibilityCanBePrivate") package net.mamoe.mirai.console.permission +import net.mamoe.mirai.console.command.CommandSender + /** + * 可拥有权限的对象. + * + * 典型的实例为 [CommandSender] * * 注意: 请不要自主实现 [Permissible] + * + * @see CommandSender */ @ExperimentalPermission -public interface Permissible +public interface Permissible { + public val identifier: PermissibleIdentifier +} + + +@ExperimentalPermission("Classname is subject to change") +public interface PermissibleIdentifier { + public val parents: Array + + public companion object { + @ExperimentalPermission + public infix fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean { + return allParentsWithSelf().any { it == with } + } + + internal fun PermissibleIdentifier.allParentsWithSelf(): Sequence { + return sequence { + yield(this@allParentsWithSelf) + yieldAll(parents.asSequence()) + } + } + } +} + +/** + * + */ +@ExperimentalPermission +public sealed class AbstractPermissibleIdentifier( + public final override vararg val parents: PermissibleIdentifier +) : PermissibleIdentifier { + public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) + public class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + + public class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) + public class ExactMember( + public val groupId: Long, + public val memberId: Long + ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) + + public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) + public class ExactFriend( + public val id: Long + ) : AbstractPermissibleIdentifier(ExactUser(id)) + + public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) + public class ExactTemp( + public val id: Long + ) : AbstractPermissibleIdentifier(ExactUser(id)) + + public object AnyUser : AbstractPermissibleIdentifier(AnyContact) + public class ExactUser( + public val id: Long + ) : AbstractPermissibleIdentifier(AnyUser) + + public object AnyContact : AbstractPermissibleIdentifier() + + public object Console : AbstractPermissibleIdentifier() +} @ExperimentalPermission public inline fun Permissible.hasPermission(permission: Permission): Boolean = PermissionService.run { permission.testPermission(this@hasPermission) } @ExperimentalPermission -public inline fun Permissible.hasPermission(permission: PermissionIdentifier): Boolean = +public inline fun Permissible.hasPermission(permission: PermissionId): Boolean = PermissionService.run { permission.testPermission(this@hasPermission) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 4d1a3ff88..0d33e8307 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -15,9 +15,9 @@ package net.mamoe.mirai.console.permission */ @ExperimentalPermission public interface Permission { - public val identifier: PermissionIdentifier + public val id: PermissionId public val description: String - public val base: PermissionIdentifier? + public val base: PermissionId? } /** @@ -25,7 +25,7 @@ public interface Permission { */ @ExperimentalPermission public open class PermissionImpl( - override val identifier: PermissionIdentifier, + override val id: PermissionId, override val description: String, - override val base: PermissionIdentifier? + override val base: PermissionId? ) : Permission \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt index e898d0c07..3366c56ce 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt @@ -22,23 +22,23 @@ public abstract class PermissionGroup( internal var description: String = "" @JvmField - internal var basePermission: PermissionIdentifier? = null + internal var basePermission: PermissionId? = null @JvmField internal var permissionChecker: PermissionChecker? = null public fun description(description: String): PermissionBuilder = apply { this.description = description } - public fun dependsOn(basePermission: PermissionIdentifier?): PermissionBuilder = + public fun dependsOn(basePermission: PermissionId?): PermissionBuilder = apply { this.basePermission = basePermission } public fun dependsOn(basePermission: Permission?): PermissionBuilder = - apply { this.basePermission = basePermission?.identifier } + apply { this.basePermission = basePermission?.id } - public fun basePermission(basePermission: PermissionIdentifier?): PermissionBuilder = + public fun basePermission(basePermission: PermissionId?): PermissionBuilder = apply { this.basePermission = basePermission } public fun basePermission(basePermission: Permission?): PermissionBuilder = - apply { this.basePermission = basePermission?.identifier } + apply { this.basePermission = basePermission?.id } public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder = apply { this.permissionChecker = permissionChecker } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt similarity index 82% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index b9d7b8d25..5e5eda248 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -16,7 +16,7 @@ import net.mamoe.mirai.console.internal.data.map @ExperimentalPermission -public data class PermissionIdentifier( +public data class PermissionId( public val namespace: String, public val id: String ) { @@ -29,17 +29,17 @@ public data class PermissionIdentifier( } } - @Serializer(forClass = PermissionIdentifier::class) + @Serializer(forClass = PermissionId::class) public object AsClassSerializer - public object AsStringSerializer : KSerializer by String.serializer().map( + public object AsStringSerializer : KSerializer by String.serializer().map( serializer = { it.namespace + ":" + it.id }, - deserializer = { it.split(':').let { (namespace, id) -> PermissionIdentifier(namespace, id) } } + deserializer = { it.split(':').let { (namespace, id) -> PermissionId(namespace, id) } } ) } @ExperimentalPermission public interface PermissionIdentifierNamespace { @ExperimentalPermission - public fun permissionIdentifier(identifierString: String): PermissionIdentifier + public fun permissionIdentifier(identifierString: String): PermissionId } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 645a50797..bce73ddfc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -12,7 +12,8 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.extensions.PermissionServiceProvider -import java.util.concurrent.ConcurrentHashMap +import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith +import java.util.concurrent.CopyOnWriteArrayList import kotlin.reflect.KClass /** @@ -23,23 +24,32 @@ public interface PermissionService

{ @ExperimentalPermission public val permissionType: KClass

- @Throws(DuplicatedRegistrationException::class) - public fun register( - identifier: PermissionIdentifier, - description: String, - base: PermissionIdentifier? = null - ): P + /////////////////////////////////////////////////////////////////////////// - public operator fun get(identifier: PermissionIdentifier): P? + public operator fun get(id: PermissionId): P? public fun getGrantedPermissions(permissible: Permissible): Sequence

- public fun testPermission(permissible: Permissible, permission: P): Boolean = permissible.getGrantedPermissions().any { it == permission } + + /////////////////////////////////////////////////////////////////////////// + + @Throws(DuplicatedRegistrationException::class) + public fun register( + id: PermissionId, + description: String, + base: PermissionId? = null + ): P + + /////////////////////////////////////////////////////////////////////////// + + public fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) + public fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) + public companion object { - private val builtIn: PermissionService get() = AllGrantPermissionService + private val builtIn: PermissionService get() = BuiltInPermissionService @get:JvmName("getInstance") @JvmStatic @@ -51,29 +61,48 @@ public interface PermissionService

{ } @ExperimentalPermission -public abstract class AbstractPermissionService

: PermissionService

{ - protected val all: MutableMap = ConcurrentHashMap() +public abstract class AbstractConcurrentPermissionService

: PermissionService

{ + protected abstract val permissions: MutableMap + protected abstract val grantedPermissionsMap: MutableMap> protected abstract fun createPermission( - identifier: PermissionIdentifier, + id: PermissionId, description: String, - base: PermissionIdentifier? + base: PermissionId? ): P - override fun register( - identifier: PermissionIdentifier, - description: String, - base: PermissionIdentifier? - ): P { - val new = createPermission(identifier, description, base) - if (all.putIfAbsent(identifier, new) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") + override fun get(id: PermissionId): P? = permissions[id] + + override fun register(id: PermissionId, description: String, base: PermissionId?): P { + grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed + val instance = createPermission(id, description, base) + if (permissions.putIfAbsent(id, instance) != null) { + throw DuplicatedRegistrationException("Duplicated Permission registry. new: $instance, old: ${permissions[id]}") } - return new + return instance } - override fun get(identifier: PermissionIdentifier): P? = all[identifier] - override fun getGrantedPermissions(permissible: Permissible): Sequence

= all.values.asSequence() + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) { + val id = permission.id + grantedPermissionsMap[id]?.add(permissibleIdentifier) + ?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.") + } + + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) { + grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) + } + + public override fun getGrantedPermissions(permissible: Permissible): Sequence

= sequence

{ + for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { + val myIdentifier = permissible.identifier + + val granted = + if (permissibleIdentifiers.isEmpty()) false + else permissibleIdentifiers.any { myIdentifier grantedWith it } + + if (granted) get(permissionIdentifier)?.let { yield(it) } + } + } } @ExperimentalPermission @@ -98,7 +127,7 @@ public inline fun Permission.testPermission(permissible: Permissible): Boolean = } @ExperimentalPermission -public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean { +public inline fun PermissionId.testPermission(permissible: Permissible): Boolean { val p = PermissionService.INSTANCE[this] ?: return false return p.testPermission(permissible) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt new file mode 100644 index 000000000..e04bf965a --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt @@ -0,0 +1,26 @@ +package net.mamoe.mirai.console.permission + +import net.mamoe.mirai.console.data.PluginConfig +import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault +import net.mamoe.mirai.console.data.value +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArrayList + +@ExperimentalPermission +public interface StorablePermissionService

: PermissionService

{ + /** + * The config to be stored + */ + public val config: PluginConfig + + @ExperimentalPermission + public class ConcurrentSaveData

( + public override val saveName: String, + delegate: PluginConfig + ) : PluginConfig by delegate { + public val permissions: MutableMap by value>(ConcurrentHashMap()) + + public val grantedPermissionMap: MutableMap> by + value>>(ConcurrentHashMap()).withDefault { CopyOnWriteArrayList() } + } +} From fac66583edd4310e35c7da727ea86caf0de4951c Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 09:54:39 +0800 Subject: [PATCH 11/62] Make inheritors of AbstractPermissibleIdentifier data class --- .../mamoe/mirai/console/permission/Permissible.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt index ea36e635d..30a5976a2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -55,27 +55,27 @@ public sealed class AbstractPermissibleIdentifier( public final override vararg val parents: PermissibleIdentifier ) : PermissibleIdentifier { public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) - public class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) - public class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) - public class ExactMember( + public data class ExactMember( public val groupId: Long, public val memberId: Long ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) - public class ExactFriend( + public data class ExactFriend( public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(id)) public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) - public class ExactTemp( + public data class ExactTemp( public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(id)) public object AnyUser : AbstractPermissibleIdentifier(AnyContact) - public class ExactUser( + public data class ExactUser( public val id: Long ) : AbstractPermissibleIdentifier(AnyUser) From 6af2094b8fe7125206ab0a9372d3958e099f8594 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 12:23:27 +0800 Subject: [PATCH 12/62] Don't require plugin to be enabled when registering extensions --- .../net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt index 81c93c68e..28560aaf9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt @@ -38,7 +38,7 @@ public open class AbstractExtensionPoint( @Synchronized @ConsoleExperimentalAPI public fun registerExtension(plugin: Plugin, extension: T) { - require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." } + // require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." } instances.add(ExtensionRegistry(plugin, extension)) } From 9b3c7c19028d89a4e6d33c5b7df11dd2f9631545 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 12:33:12 +0800 Subject: [PATCH 13/62] Install serializer for PermissionId --- .../kotlin/net/mamoe/mirai/console/permission/PermissionId.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index 5e5eda248..6488751e8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -10,11 +10,13 @@ package net.mamoe.mirai.console.permission import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable import kotlinx.serialization.Serializer import kotlinx.serialization.builtins.serializer import net.mamoe.mirai.console.internal.data.map +@Serializable @ExperimentalPermission public data class PermissionId( public val namespace: String, From 900eac58b448c24f7fe270eb27bae60749d316e0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 12:33:56 +0800 Subject: [PATCH 14/62] Install serializer for PermissionImpl, make PermissionImpl final --- .../kotlin/net/mamoe/mirai/console/permission/Permission.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 0d33e8307..f687376cb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -9,6 +9,8 @@ package net.mamoe.mirai.console.permission +import kotlinx.serialization.Serializable + /** * 一个权限节点 @@ -23,8 +25,9 @@ public interface Permission { /** * [Permission] 的简单实现 */ +@Serializable @ExperimentalPermission -public open class PermissionImpl( +public class PermissionImpl( override val id: PermissionId, override val description: String, override val base: PermissionId? From 048a10cc508c5f94330ce85912a6ed218fc5a357 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 12:34:20 +0800 Subject: [PATCH 15/62] Fix serializers for StorablePermissionService.SaveData --- .../permission/BuiltInPermissionServices.kt | 7 +++- .../permission/StorablePermissionService.kt | 36 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 42989b3c5..aa4a36d70 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.data.AutoSavePluginConfig import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass +import kotlin.reflect.full.createType @ExperimentalPermission @@ -84,5 +85,9 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

= - StorablePermissionService.ConcurrentSaveData("PermissionService", AutoSavePluginConfig()) + StorablePermissionService.ConcurrentSaveData( + PermissionImpl::class.createType(), + "PermissionService", + AutoSavePluginConfig() + ) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt index e04bf965a..f0b37efba 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt @@ -3,8 +3,13 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.data.valueFromKType import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList +import kotlin.reflect.KType +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KVariance +import kotlin.reflect.full.createType @ExperimentalPermission public interface StorablePermissionService

: PermissionService

{ @@ -14,13 +19,34 @@ public interface StorablePermissionService

: PermissionService

( + public class ConcurrentSaveData

private constructor( + permissionType: KType, public override val saveName: String, - delegate: PluginConfig + delegate: PluginConfig, + @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? ) : PluginConfig by delegate { - public val permissions: MutableMap by value>(ConcurrentHashMap()) + public val permissions: MutableMap + by valueFromKType>( + MutableMap::class.createType( + listOf( + KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()), + KTypeProjection(KVariance.INVARIANT, permissionType), + ) + ), + ConcurrentHashMap() + ) - public val grantedPermissionMap: MutableMap> by - value>>(ConcurrentHashMap()).withDefault { CopyOnWriteArrayList() } + public val grantedPermissionMap: MutableMap> + by value>>(ConcurrentHashMap()) + .withDefault { CopyOnWriteArrayList() } + + public companion object { + @JvmStatic + public operator fun

invoke( + permissionType: KType, + saveName: String, + delegate: PluginConfig, + ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, delegate, null) + } } } From 7638a7e7594f13c9f4b94f5ddffdae9d0a948065 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 15:51:45 +0800 Subject: [PATCH 16/62] No more kapt --- backend/mirai-console/build.gradle.kts | 7 +++---- frontend/mirai-console-pure/build.gradle.kts | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index f19e05e10..7de97c454 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -6,7 +6,6 @@ import java.time.Instant plugins { kotlin("jvm") kotlin("plugin.serialization") - kotlin("kapt") id("java") `maven-publish` id("com.jfrog.bintray") @@ -81,9 +80,9 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0") - val autoService = "1.0-rc7" - kapt("com.google.auto.service", "auto-service", autoService) - compileOnly("com.google.auto.service", "auto-service-annotations", autoService) +// val autoService = "1.0-rc7" +// kapt("com.google.auto.service", "auto-service", autoService) +// compileOnly("com.google.auto.service", "auto-service-annotations", autoService) } ext.apply { diff --git a/frontend/mirai-console-pure/build.gradle.kts b/frontend/mirai-console-pure/build.gradle.kts index 9f9428196..3639b0a7b 100644 --- a/frontend/mirai-console-pure/build.gradle.kts +++ b/frontend/mirai-console-pure/build.gradle.kts @@ -1,7 +1,6 @@ plugins { kotlin("jvm") kotlin("plugin.serialization") - kotlin("kapt") id("java") `maven-publish` id("com.jfrog.bintray") @@ -46,10 +45,10 @@ dependencies { testApi(project(":mirai-console")) - val autoService = "1.0-rc7" - kapt("com.google.auto.service", "auto-service", autoService) - compileOnly("com.google.auto.service", "auto-service-annotations", autoService) - testCompileOnly("com.google.auto.service", "auto-service-annotations", autoService) +// val autoService = "1.0-rc7" +// kapt("com.google.auto.service", "auto-service", autoService) +// compileOnly("com.google.auto.service", "auto-service-annotations", autoService) +// testCompileOnly("com.google.auto.service", "auto-service-annotations", autoService) } ext.apply { From 5c2a9a955337393e91fd3cf6eb7d1b65661ebf6c Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 17:21:53 +0800 Subject: [PATCH 17/62] Integrate Command with PermissionService --- .../mirai/console/command/BuiltInCommands.kt | 5 +- .../mamoe/mirai/console/command/Command.kt | 14 +- .../mirai/console/command/CommandOwner.kt | 10 +- .../console/command/CommandPermission.kt | 143 ------------------ .../mirai/console/command/CompositeCommand.kt | 10 +- .../mamoe/mirai/console/command/RawCommand.kt | 9 +- .../mirai/console/command/SimpleCommand.kt | 5 +- .../console/command/java/JCompositeCommand.kt | 18 ++- .../mirai/console/command/java/JRawCommand.kt | 10 +- .../console/command/java/JSimpleCommand.kt | 10 +- .../internal/command/CommandPermissionImpl.kt | 32 ---- .../command/CompositeCommandInternal.kt | 25 +-- .../console/internal/command/internal.kt | 9 +- .../internal/plugin/JvmPluginInternal.kt | 4 +- .../console/permission/PermissionGroup.kt | 4 +- .../mirai/console/permission/PermissionId.kt | 15 +- .../mirai/console/plugin/jvm/JvmPlugin.kt | 4 +- 17 files changed, 100 insertions(+), 227 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandPermissionImpl.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index b4549263e..78a17cd26 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -62,17 +62,14 @@ public object BuiltInCommands { public object Managers : CompositeCommand( ConsoleCommandOwner, "managers", - description = "Manage the managers for each bot", - permission = CommandPermission.Console or CommandPermission.Manager + description = "Manage the managers for each bot" ), BuiltInCommand { - @Permission(CommandPermission.Console::class) @SubCommand public suspend fun CommandSender.add(target: User) { target.bot.addManager(target.id) sendMessage("已成功添加 ${target.render()} 为 ${target.bot.render()} 的管理员") } - @Permission(CommandPermission.Console::class) @SubCommand public suspend fun CommandSender.remove(target: User) { target.bot.removeManager(target.id) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 2dcca4945..096be5791 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -15,7 +15,11 @@ import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.java.JCommand +import net.mamoe.mirai.console.internal.command.createCommandPermission import net.mamoe.mirai.console.internal.command.isValidSubName +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.SingleMessage @@ -51,7 +55,7 @@ public interface Command { /** * 指令权限 */ - public val permission: CommandPermission + public val permission: Permission /** * 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 @@ -95,13 +99,13 @@ public suspend inline fun Command.onCommand(sender: CommandSender, args: Message * @see CompositeCommand * @see RawCommand */ -public abstract class AbstractCommand @JvmOverloads constructor( +public abstract class AbstractCommand +@JvmOverloads constructor( /** 指令拥有者. */ public override val owner: CommandOwner, vararg names: String, description: String = "", - /** 指令权限 */ - public override val permission: CommandPermission = CommandPermission.Default, + basePermission: PermissionId? = null, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { @@ -111,4 +115,6 @@ public abstract class AbstractCommand @JvmOverloads constructor( list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") } }.toTypedArray() + @OptIn(ExperimentalPermission::class) + public override val permission: Permission by lazy { createCommandPermission(basePermission) } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt index 49d2715f3..49932f8df 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt @@ -10,6 +10,9 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissionId +import net.mamoe.mirai.console.permission.PermissionIdNamespace import net.mamoe.mirai.console.plugin.jvm.JvmPlugin /** @@ -20,9 +23,12 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin * * @see JvmPlugin 是一个 [CommandOwner] */ -public interface CommandOwner +public interface CommandOwner : PermissionIdNamespace /** * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. */ -internal object ConsoleCommandOwner : CommandOwner \ No newline at end of file +internal object ConsoleCommandOwner : CommandOwner { + @ExperimentalPermission + override fun permissionId(id: String): PermissionId = PermissionId("console", id) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt deleted file mode 100644 index 6ea195f7d..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt +++ /dev/null @@ -1,143 +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 - */ - -@file:Suppress("unused", "NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate") - -package net.mamoe.mirai.console.command - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand -import net.mamoe.mirai.console.internal.command.AndCommandPermissionImpl -import net.mamoe.mirai.console.internal.command.OrCommandPermissionImpl -import net.mamoe.mirai.console.util.BotManager.INSTANCE.isManager -import net.mamoe.mirai.contact.isAdministrator -import net.mamoe.mirai.contact.isOperator -import net.mamoe.mirai.contact.isOwner - -/** - * 指令权限. - * - * 在 [CommandManager.executeCommand] 时将会检查权限. - * - * @see Command.permission 从指令获取权限 - */ -public fun interface CommandPermission { - /** - * 判断 [this] 是否拥有这个指令的权限 - * - * @see CommandSender.hasPermission - * @see CommandPermission.testPermission - */ - public fun CommandSender.hasPermission(): Boolean - - - /** - * 满足两个权限其中一个即可使用指令 - */ // no extension for Java - public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermissionImpl(this, another) - - /** - * 同时拥有两个权限才能使用指令 - */ // no extension for Java - public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermissionImpl(this, another) - - - /** - * 任何人都可以使用这个指令 - */ - public object Any : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean = true - } - - /** - * 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行. - */ - public object None : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean = false - } - - /** - * 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令 - */ - public object Operator : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean { - return this is MemberCommandSender && this.user.isOperator() - } - } - - /** - * 来自任何 [Bot] 的任何一个群主都可以使用这个指令 - */ - public object GroupOwner : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean { - return this is MemberCommandSender && this.user.isOwner() - } - } - - /** - * 管理员 (不包含群主) 可以使用这个指令 - */ - public object GroupAdmin : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean { - return this is MemberCommandSender && this.user.isAdministrator() - } - } - - /** - * 任何 [Bot] 的 manager 都可以使用这个指令 - */ - public object Manager : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean { - return this is MemberCommandSender && this.user.isManager - } - } - - /** - * 仅控制台能使用和这个指令 - */ - public object Console : CommandPermission { - public override fun CommandSender.hasPermission(): Boolean = this is ConsoleCommandSender - } - - /** - * 默认权限. - * - * @return [Manager] or [Console] - */ - public object Default : CommandPermission by (Manager or Console) -} - -/** - * 判断 [this] 是否拥有权限 [permission] - * - * @see CommandSender.hasPermission - * @see CommandPermission.testPermission - * @see CommandPermission.hasPermission - */ -public inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean = - permission.run { this@hasPermission.hasPermission() } - - -/** - * 判断 [sender] 是否拥有权限 [this] - * - * @see CommandSender.hasPermission - * @see CommandPermission.testPermission - * @see CommandPermission.hasPermission - */ -public inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() } - -/** - * 判断 [sender] 是否拥有权限 [Command.permission] - * - * @see CommandSender.hasPermission - * @see CommandPermission.testPermission - * @see CommandPermission.hasPermission - */ -public inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt index c901ea4ca..26a71f9d8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt @@ -20,11 +20,12 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.message.data.MessageChain import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationTarget.FUNCTION -import kotlin.reflect.KClass /** * 复合指令. 指令注册时候会通过反射构造指令解析器. @@ -85,10 +86,10 @@ public abstract class CompositeCommand( owner: CommandOwner, vararg names: String, description: String = "no description available", - permission: CommandPermission = CommandPermission.Default, + basePermission: PermissionId? = null, prefixOptional: Boolean = false, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext -) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional), +) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional), CommandArgumentContextAware { /** @@ -112,7 +113,8 @@ public abstract class CompositeCommand( /** 指定子指令要求的权限 */ @Retention(RUNTIME) @Target(FUNCTION) - protected annotation class Permission(val value: KClass) + @ExperimentalPermission + protected annotation class Permission(val value: String) /** 指令描述 */ @Retention(RUNTIME) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt index 57a436a4f..fd1a36515 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt @@ -14,6 +14,10 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.java.JRawCommand +import net.mamoe.mirai.console.internal.command.createCommandPermission +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain /** @@ -40,10 +44,13 @@ public abstract class RawCommand( /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public override val description: String = "", /** 指令权限 */ - public override val permission: CommandPermission = CommandPermission.Default, + basePermission: PermissionId? = null, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { + @OptIn(ExperimentalPermission::class) + public override val permission: Permission by lazy { createCommandPermission(basePermission) } + /** * 在指令被执行时调用. * diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt index 12c4a16a2..9c83c9228 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt @@ -22,6 +22,7 @@ import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.java.JSimpleCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain /** @@ -51,10 +52,10 @@ public abstract class SimpleCommand( owner: CommandOwner, vararg names: String, description: String = "no description available", - permission: CommandPermission = CommandPermission.Default, + basePermission: PermissionId? = null, prefixOptional: Boolean = false, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext -) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional), +) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional), CommandArgumentContextAware { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt index ee585a8cc..ef400b3c0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt @@ -9,8 +9,13 @@ package net.mamoe.mirai.console.command.java -import net.mamoe.mirai.console.command.* +import net.mamoe.mirai.console.command.BuiltInCommands +import net.mamoe.mirai.console.command.CommandManager +import net.mamoe.mirai.console.command.CommandOwner +import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.description.buildCommandArgumentContext +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** @@ -64,16 +69,17 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI * @see buildCommandArgumentContext */ @ConsoleExperimentalAPI -public abstract class JCompositeCommand( +public abstract class JCompositeCommand @JvmOverloads constructor( owner: CommandOwner, - vararg names: String -) : CompositeCommand(owner, *names) { + vararg names: String, + basePermission: PermissionId? = null, +) : CompositeCommand(owner, *names, basePermission = basePermission) { /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public final override var description: String = "" protected set - /** 指令权限 */ - public final override var permission: CommandPermission = CommandPermission.Default + @OptIn(ExperimentalPermission::class) + public final override var permission: net.mamoe.mirai.console.permission.Permission = super.permission protected set /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt index d3b3fb87a..f53daf865 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt @@ -13,6 +13,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute +import net.mamoe.mirai.console.internal.command.createCommandPermission +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.SingleMessage @@ -42,14 +45,15 @@ import net.mamoe.mirai.message.data.SingleMessage * * @see JRawCommand */ -public abstract class JRawCommand( +public abstract class JRawCommand @JvmOverloads constructor( /** * 指令拥有者. * @see CommandOwner */ public override val owner: CommandOwner, /** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */ - public override vararg val names: String + public override vararg val names: String, + basePermission: PermissionId? = null, ) : Command { /** 用法说明, 用于发送给用户 */ public override var usage: String = "" @@ -60,7 +64,7 @@ public abstract class JRawCommand( protected set /** 指令权限 */ - public final override var permission: CommandPermission = CommandPermission.Default + public final override var permission: Permission = createCommandPermission(basePermission) protected set /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt index 25d886a55..2f6ce1e4d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt @@ -12,9 +12,10 @@ package net.mamoe.mirai.console.command.java import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandOwner -import net.mamoe.mirai.console.command.CommandPermission import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.command.description.CommandArgumentContext +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId /** * Java 实现: @@ -41,11 +42,12 @@ import net.mamoe.mirai.console.command.description.CommandArgumentContext */ public abstract class JSimpleCommand( owner: CommandOwner, - vararg names: String -) : SimpleCommand(owner, *names) { + vararg names: String, + basePermission: PermissionId, +) : SimpleCommand(owner, *names, basePermission = basePermission) { public override var description: String = super.description protected set - public override var permission: CommandPermission = super.permission + public override var permission: Permission = super.permission protected set public override var prefixOptional: Boolean = super.prefixOptional protected set diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandPermissionImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandPermissionImpl.kt deleted file mode 100644 index 8c98125fb..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandPermissionImpl.kt +++ /dev/null @@ -1,32 +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.internal.command - -import net.mamoe.mirai.console.command.CommandPermission -import net.mamoe.mirai.console.command.CommandSender -import net.mamoe.mirai.console.command.hasPermission - -internal class OrCommandPermissionImpl( - private val first: CommandPermission, - private val second: CommandPermission -) : CommandPermission { - override fun CommandSender.hasPermission(): Boolean { - return this.hasPermission(first) || this.hasPermission(second) - } -} - -internal class AndCommandPermissionImpl( - private val first: CommandPermission, - private val second: CommandPermission -) : CommandPermission { - override fun CommandSender.hasPermission(): Boolean { - return this.hasPermission(first) && this.hasPermission(second) - } -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index e0c5b78b5..cda7b6fd4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -16,11 +16,15 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip +import net.mamoe.mirai.console.permission.* import net.mamoe.mirai.message.data.* import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KClass import kotlin.reflect.KFunction -import kotlin.reflect.full.* +import kotlin.reflect.full.callSuspend +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.isSubclassOf internal object CompositeCommandSubCommandAnnotationResolver : AbstractReflectionCommand.SubCommandAnnotationResolver { @@ -44,13 +48,13 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( owner: CommandOwner, names: Array, description: String = "", - permission: CommandPermission = CommandPermission.Default, + basePermission: PermissionId? = null, prefixOptional: Boolean = false ) : Command, AbstractCommand( owner, names = names, description = description, - permission = permission, + basePermission = basePermission, prefixOptional = prefixOptional ), CommandArgumentContextAware { internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver @@ -70,7 +74,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { DefaultSubCommandDescriptor( "", - permission, + createCommandPermission(basePermission), onCommand = { sender: CommandSender, args: MessageChain -> sender.onDefault(args) } @@ -115,7 +119,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( internal class DefaultSubCommandDescriptor( val description: String, - val permission: CommandPermission, + val permission: Permission, val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit ) @@ -123,7 +127,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( val names: Array, val params: Array>, val description: String, - val permission: CommandPermission, + val permission: Permission, val onCommand: suspend (sender: CommandSender, parsedArgs: Array) -> Boolean, val context: CommandArgumentContext ) { @@ -209,10 +213,6 @@ internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain { internal inline fun KAnnotatedElement.hasAnnotation(): Boolean = findAnnotation() != null -internal inline fun KClass.getInstance(): T { - return this.objectInstance ?: this.createInstance() -} - internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "" internal fun Array.createUsage(baseCommand: AbstractReflectionCommand): String = @@ -246,6 +246,7 @@ internal fun AbstractReflectionCommand.SubCommandDescriptor.createUsage(baseComm appendLine() }.trimEnd() +@OptIn(ExperimentalPermission::class) internal fun AbstractReflectionCommand.createSubCommand( function: KFunction<*>, context: CommandArgumentContext @@ -322,8 +323,8 @@ internal fun AbstractReflectionCommand.createSubCommand( return SubCommandDescriptor( commandName, params, - subDescription, - overridePermission?.value?.getInstance() ?: permission, + subDescription, // overridePermission?.value + overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission, onCommand = { sender: CommandSender, args: Array -> val result = if (notStatic) { if (hasSenderParam) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt index cd8ef90a9..f89fc1731 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt @@ -10,6 +10,8 @@ package net.mamoe.mirai.console.internal.command import net.mamoe.mirai.console.command.* +import net.mamoe.mirai.console.command.Command.Companion.primaryName +import net.mamoe.mirai.console.permission.* import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member import net.mamoe.mirai.message.data.MessageChain @@ -136,12 +138,17 @@ internal fun Group.fuzzySearchMember( } } +@OptIn(ExperimentalPermission::class) +internal fun Command.createCommandPermission(basePermission: PermissionId?): Permission { + return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, basePermission) +} //// internal @JvmSynthetic internal inline fun List.dropToTypedArray(n: Int): Array = Array(size - n) { this[n + it] } +@OptIn(ExperimentalPermission::class) @JvmSynthetic @Throws(CommandExecutionException::class) internal suspend fun CommandSender.executeCommandInternal( @@ -150,7 +157,7 @@ internal suspend fun CommandSender.executeCommandInternal( commandName: String, checkPermission: Boolean ): CommandExecuteResult { - if (checkPermission && !command.testPermission(this)) { + if (checkPermission && !command.permission.testPermission(this)) { return CommandExecuteResult.PermissionDenied(command, commandName) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index 0036fc1a8..05c7f5127 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -47,8 +47,8 @@ internal abstract class JvmPluginInternal( resourceContainerDelegate.getResourceAsStream(path) @OptIn(ExperimentalPermission::class) - override fun permissionIdentifier(identifierString: String): PermissionId { - return PermissionId(description.name, identifierString) + override fun permissionId(id: String): PermissionId { + return PermissionId(description.name, id) } // region JvmPlugin diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt index 3366c56ce..115d76175 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt @@ -14,7 +14,7 @@ import kotlin.reflect.KProperty @ExperimentalPermission public abstract class PermissionGroup( - private val identifierNamespace: PermissionIdentifierNamespace, + private val idNamespace: PermissionIdNamespace, ) { @ExperimentalPermission public inner class PermissionBuilder { @@ -45,7 +45,7 @@ public abstract class PermissionGroup( public fun build(property: KProperty<*>): Permission { return PermissionService.INSTANCE.register( - identifierNamespace.permissionIdentifier(property.name), + idNamespace.permissionId(property.name), description, basePermission ) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index 6488751e8..a71b29924 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -36,12 +36,21 @@ public data class PermissionId( public object AsStringSerializer : KSerializer by String.serializer().map( serializer = { it.namespace + ":" + it.id }, - deserializer = { it.split(':').let { (namespace, id) -> PermissionId(namespace, id) } } + deserializer = ::parseFromString ) + + public override fun toString(): String { + return "$namespace:$id" + } + + public companion object { + public fun parseFromString(string: String): PermissionId = + string.split(':').let { (namespace, id) -> PermissionId(namespace, id) } + } } @ExperimentalPermission -public interface PermissionIdentifierNamespace { +public interface PermissionIdNamespace { @ExperimentalPermission - public fun permissionIdentifier(identifierString: String): PermissionId + public fun permissionId(id: String): PermissionId } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt index 82ba0b863..b6ce680a7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt @@ -22,7 +22,7 @@ import net.mamoe.mirai.console.data.AutoSavePluginDataHolder import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.permission.ExperimentalPermission -import net.mamoe.mirai.console.permission.PermissionIdentifierNamespace +import net.mamoe.mirai.console.permission.PermissionIdNamespace import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginFileExtensions import net.mamoe.mirai.console.plugin.ResourceContainer @@ -45,7 +45,7 @@ import net.mamoe.mirai.utils.MiraiLogger */ @OptIn(ExperimentalPermission::class) public interface JvmPlugin : Plugin, CoroutineScope, - PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdentifierNamespace { + PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdNamespace { /** 日志 */ public val logger: MiraiLogger From 60e13e73e904b60ec06a11918d3dca962a518cd5 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 17:24:02 +0800 Subject: [PATCH 18/62] Fix ExactMember, ExactTemp --- .../kotlin/net/mamoe/mirai/console/command/CommandSender.kt | 5 +++-- .../kotlin/net/mamoe/mirai/console/permission/Permissible.kt | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt index 2820a90da..af199bcb4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt @@ -636,7 +636,7 @@ public open class MemberCommandSender internal constructor( public override fun toString(): String = "MemberCommandSender($user)" @ExperimentalPermission - public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactMember(user.id, group.id) + public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactMember(group.id, user.id) @JvmBlockingBridge public override suspend fun sendMessage(message: String): MessageReceipt = sendMessage(PlainText(message)) @@ -659,7 +659,8 @@ public open class TempCommandSender internal constructor( public override fun toString(): String = "TempCommandSender($user)" @ExperimentalPermission - override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactTemp(user.id) + public override val identifier: PermissibleIdentifier = + AbstractPermissibleIdentifier.ExactTemp(user.group.id, user.id) @JvmBlockingBridge public override suspend fun sendMessage(message: String): MessageReceipt = sendMessage(PlainText(message)) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt index 30a5976a2..3b5c53b4e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -71,8 +71,9 @@ public sealed class AbstractPermissibleIdentifier( public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) public data class ExactTemp( + public val groupId: Long, public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(id)) + ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? public object AnyUser : AbstractPermissibleIdentifier(AnyContact) public data class ExactUser( From 5de88be1984bf3af9205f372535638955381118d Mon Sep 17 00:00:00 2001 From: AlightY <39412759+AlightY@users.noreply.github.com> Date: Tue, 8 Sep 2020 18:48:39 +0800 Subject: [PATCH 19/62] word revise --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c7a5f6c66..a46c1b636 100644 --- a/docs/README.md +++ b/docs/README.md @@ -81,7 +81,7 @@ ### Java 用户的使用指南 -- Java 中的「方法」在 Kotlin 中均被成为「函数」。 +- Java 中的「方法」在 Kotlin 中均被称为「函数」。 - Kotlin 默认的访问权限是 `public`。如 Kotlin `class Test` 相当于 Java 的 `public class Test {}` - Kotlin 的函数定义 `fun test(int: Int): String` 相当于 Java 的方法定义 `public String test(int int)` From 2f7d7fb0979f01a5252a83f7a51db7f3f044b27c Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 19:27:30 +0800 Subject: [PATCH 20/62] Add docs --- .../mirai/console/extension/AbstractExtensionPoint.kt | 3 +++ .../kotlin/net/mamoe/mirai/console/extension/Extension.kt | 8 ++++++++ .../mirai/console/extensions/PermissionServiceProvider.kt | 7 +++++-- .../mirai/console/extensions/PostStartupExtension.kt | 8 ++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt index 28560aaf9..c9b0ca7bd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt @@ -21,6 +21,9 @@ import kotlin.contracts.contract import kotlin.internal.LowPriorityInOverloadResolution import kotlin.reflect.KClass +/** + * 表示一个扩展点 + */ @ConsoleExperimentalAPI public open class AbstractExtensionPoint( @ConsoleExperimentalAPI diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt index 0c0250174..5eced7d95 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt @@ -13,9 +13,17 @@ import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.extensions.PluginLoaderProvider import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +/** + * 表示一个扩展. + * + * Console 许多不容易通过 + */ @ConsoleExperimentalAPI public interface Extension +/** + * 增加一些函数 (方法)的扩展 + */ @ConsoleExperimentalAPI public interface FunctionExtension : Extension diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt index b50eeefae..b0de62886 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt @@ -4,10 +4,13 @@ import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.SingletonExtension import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +/** + * [权限服务][PermissionService] 提供器. + * + * 此扩展 + */ @ExperimentalPermission -@ConsoleExperimentalAPI public interface PermissionServiceProvider : SingletonExtension> { public companion object ExtensionPoint : AbstractExtensionPoint(PermissionServiceProvider::class) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt index 1e8508f6d..ee8a97f45 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt @@ -12,7 +12,15 @@ package net.mamoe.mirai.console.extensions import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.FunctionExtension +/** + * 在 Console 启动完成后立即在主线程调用的扩展. 用于进行一些必要的延迟初始化. + * + * 这些扩展只会, 且一定会被调用正好一次. + */ public fun interface PostStartupExtension : FunctionExtension { + /** + * 将在 Console 主线程执行. + */ public operator fun invoke() public companion object ExtensionPoint : AbstractExtensionPoint(PostStartupExtension::class) From e1f81251634f774a5ee2c514da1568862c84ef12 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 20:20:53 +0800 Subject: [PATCH 21/62] Structured init phases; Load plugins and init services in order --- .../MiraiConsoleImplementationBridge.kt | 128 ++++++++++++------ .../internal/plugin/JarPluginLoaderImpl.kt | 15 +- .../internal/plugin/PluginManagerImpl.kt | 70 ++++++---- .../permission/BuiltInPermissionServices.kt | 5 +- .../permission/StorablePermissionService.kt | 4 +- .../console/plugin/description/PluginKind.kt | 21 ++- 6 files changed, 164 insertions(+), 79 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index e90654632..433733de8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -46,6 +46,8 @@ import java.nio.file.Path import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext /** @@ -84,59 +86,109 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI override fun createLogger(identity: String?): MiraiLogger = instance.createLogger(identity) @OptIn(ConsoleExperimentalAPI::class, ExperimentalPermission::class) + @Suppress("RemoveRedundantBackticks") internal fun doStart() { - val buildDateFormatted = - buildDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) - mainLogger.info { "Starting mirai-console..." } - mainLogger.info { "Backend: version $version, built on $buildDateFormatted." } - mainLogger.info { frontEndDescription.render() } + phase `greeting`@{ + val buildDateFormatted = + buildDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) - if (coroutineContext[Job] == null) { - throw MalformedMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.") - } - if (coroutineContext[CoroutineExceptionHandler] == null) { - throw MalformedMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a CoroutineExceptionHandler in it.") + mainLogger.info { "Starting mirai-console..." } + mainLogger.info { "Backend: version $version, built on $buildDateFormatted." } + mainLogger.info { frontEndDescription.render() } } - MiraiConsole.job.invokeOnCompletion { - Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } - } + phase `check coroutineContext`@{ + if (coroutineContext[Job] == null) { + throw MalformedMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.") + } + if (coroutineContext[CoroutineExceptionHandler] == null) { + throw MalformedMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a CoroutineExceptionHandler in it.") + } - mainLogger.verbose { "Loading configurations..." } - ConsoleDataScope.reloadAll() - - mainLogger.verbose { "Loading PermissionService..." } - PermissionService.INSTANCE.let { ps -> - if (ps is StorablePermissionService<*>) { - ConsoleDataScope.addAndReloadConfig(ps.config) - mainLogger.verbose { "Reloaded PermissionService settings." } + MiraiConsole.job.invokeOnCompletion { + Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } } } - mainLogger.verbose { "Loading built-in commands..." } - BuiltInCommands.registerAll() - mainLogger.verbose { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } - CommandManager - CommandManagerImpl.commandListener // start + // start - mainLogger.verbose { "Loading plugins..." } - PluginManager - PluginManagerImpl.loadEnablePlugins() - mainLogger.verbose { "${PluginManager.plugins.size} plugin(s) loaded." } - mainLogger.info { "mirai-console started successfully." } + phase `load configurations`@{ + mainLogger.verbose { "Loading configurations..." } + ConsoleDataScope.reloadAll() + } - runBlocking { - for ((id, password) in AutoLoginConfig.plainPasswords) { - mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password).alsoLogin() + val pluginLoadSession: PluginManagerImpl.PluginLoadSession + + phase `load plugins`@{ + PluginManager // init + + mainLogger.verbose { "Loading PluginLoader provider plugins..." } + PluginManagerImpl.loadEnablePluginProviderPlugins() + mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } + + mainLogger.verbose { "Scanning high-priority extension and normal plugins..." } + pluginLoadSession = PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider() + mainLogger.verbose { "${pluginLoadSession.allKindsOfPlugins.size} plugin(s) found." } + + mainLogger.verbose { "Loading Extension provider plugins..." } + PluginManagerImpl.loadEnableHighPriorityExtensionPlugins(pluginLoadSession) + mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } + } + + phase `load PermissionService`@{ + mainLogger.verbose { "Loading PermissionService..." } + PermissionService.INSTANCE.let { ps -> + if (ps is StorablePermissionService<*>) { + ConsoleDataScope.addAndReloadConfig(ps.config) + mainLogger.verbose { "Reloaded PermissionService settings." } + } } + } - for ((id, password) in AutoLoginConfig.md5Passwords) { - mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password).alsoLogin() + phase `prepare commands`@{ + mainLogger.verbose { "Loading built-in commands..." } + BuiltInCommands.registerAll() + mainLogger.verbose { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } + CommandManager + CommandManagerImpl.commandListener // start + } + + phase `load normal plugins`@{ + mainLogger.verbose { "Loading normal plugins..." } + val count = PluginManagerImpl.loadEnableNormalPlugins(pluginLoadSession) + mainLogger.verbose { "$count normal plugin(s) loaded." } + } + + mainLogger.info { "${PluginManagerImpl.plugins.size} plugin(s) loaded." } + + phase `auto-login bots`@{ + runBlocking { + for ((id, password) in AutoLoginConfig.plainPasswords) { + mainLogger.info { "Auto-login $id" } + MiraiConsole.addBot(id, password).alsoLogin() + } + + for ((id, password) in AutoLoginConfig.md5Passwords) { + mainLogger.info { "Auto-login $id" } + MiraiConsole.addBot(id, password).alsoLogin() + } } } PostStartupExtension.useExtensions { it() } + + mainLogger.info { "mirai-console started successfully." } + } + + @Retention(AnnotationRetention.SOURCE) + @DslMarker + private annotation class MiraiIsCool + + @MiraiIsCool + private inline fun phase(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt index 5683356c0..fbeb7d048 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt @@ -22,7 +22,6 @@ import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.jvm.* import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.info import java.io.File import java.net.URLClassLoader import java.util.concurrent.ConcurrentHashMap @@ -73,8 +72,8 @@ internal object JarPluginLoaderImpl : URLClassLoader(arrayOf(it.toURI().toURL()), MiraiConsole::class.java.classLoader) }.onEach { (_, classLoader) -> classLoaders.add(classLoader) - }.asSequence().findAllInstances().onEach { loaded -> - logger.info { "Successfully initialized JvmPlugin ${loaded}." } + }.asSequence().findAllInstances().onEach { + //logger.verbose { "Successfully initialized JvmPlugin ${loaded}." } }.onEach { (file, plugin) -> pluginFileToInstanceMap[file] = plugin } + pluginFileToInstanceMap.asSequence() @@ -97,9 +96,13 @@ internal object JarPluginLoaderImpl : override fun enable(plugin: JvmPlugin) { if (plugin.isEnabled) return ensureActive() - if (plugin is JvmPluginInternal) { - plugin.internalOnEnable() - } else plugin.onEnable() + runCatching { + if (plugin is JvmPluginInternal) { + plugin.internalOnEnable() + } else plugin.onEnable() + }.getOrElse { + throw PluginLoadException("Exception while loading ${plugin.description.name}", it) + } } override fun disable(plugin: JvmPlugin) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt index 9f0f1c7b9..5805b3d01 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt @@ -113,48 +113,58 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol ) } - /** - * STEPS: - * 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件 - * 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件 - * 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表. - * 4. 解决依赖并排序 - * 5. 依次 [PluginLoader.load] - * 但不 [PluginLoader.enable] - * - * @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable] - */ - @Suppress("UNCHECKED_CAST") - @Throws(PluginMissingDependencyException::class) - internal fun loadEnablePlugins() { - loadAndEnableLoaderProviders() - loadPluginLoaderProvidedByPlugins() + internal class PluginLoadSession( + val allKindsOfPlugins: List, List>> + ) + + // Phase #2 + internal fun scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider(): PluginLoadSession { + return PluginLoadSession(loadersLock.withLock { _pluginLoaders.listAllPlugins() }) + } + + // Phase #0 + internal fun loadEnablePluginProviderPlugins() { + loadAndEnableLoaderProvidersUsingBuiltInLoaders() + } + + // Phase #3 + internal fun loadEnableHighPriorityExtensionPlugins(session: PluginLoadSession): Int { loadersLock.withLock { - _pluginLoaders.listAllPlugins().flatMap { it.second } - .also { - logger.debug("All plugins: ${it.joinToString { (_, desc, _) -> desc.name }}") - } + session.allKindsOfPlugins.flatMap { it.second } + .filter { it.kind == PluginKind.HIGH_PRIORITY_EXTENSIONS } .sortByDependencies() - .also { - logger.debug("Sorted plugins: ${it.joinToString { (_, desc, _) -> desc.name }}") - } - .loadAndEnableAllInOrder() + .also { it.loadAndEnableAllInOrder() } + .let { return it.size } } } - private fun loadPluginLoaderProvidedByPlugins() { + // Phase #4 + internal fun loadEnableNormalPlugins(session: PluginLoadSession): Int { loadersLock.withLock { - PluginLoaderProvider.useExtensions { - logger.info { "Loaded PluginLoader ${it.instance} from $" } - _pluginLoaders.add(it.instance) + session.allKindsOfPlugins.flatMap { it.second } + .filter { it.kind == PluginKind.NORMAL } + .sortByDependencies() + .also { it.loadAndEnableAllInOrder() } + .let { return it.size } + } + } + + // Phase #1 + internal fun loadPluginLoaderProvidedByPlugins() { + loadersLock.withLock { + PluginLoaderProvider.useExtensions { ext, plugin -> + logger.info { "Loaded PluginLoader ${ext.instance} from ${plugin.name}" } + _pluginLoaders.add(ext.instance) } } } private fun List.loadAndEnableAllInOrder() { - return this.forEach { (loader, _, plugin) -> + this.forEach { (loader, _, plugin) -> loader.loadPluginNoEnable(plugin) + } + this.forEach { (loader, _, plugin) -> loader.enablePlugin(plugin) } } @@ -171,7 +181,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol */ @Suppress("UNCHECKED_CAST") @Throws(PluginMissingDependencyException::class) - private fun loadAndEnableLoaderProviders(): List { + private fun loadAndEnableLoaderProvidersUsingBuiltInLoaders(): List { val allDescriptions = builtInLoaders.listAllPlugins() .asSequence() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index aa4a36d70..2018036cc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -79,7 +79,10 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

get() = PermissionImpl::class override val permissions: MutableMap get() = config.permissions - override val grantedPermissionsMap: MutableMap> get() = config.grantedPermissionMap + + @Suppress("UNCHECKED_CAST") + override val grantedPermissionsMap: MutableMap> + get() = config.grantedPermissionMap as MutableMap> override fun createPermission(id: PermissionId, description: String, base: PermissionId?): PermissionImpl = PermissionImpl(id, description, base) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt index f0b37efba..ca41cebfa 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt @@ -36,8 +36,8 @@ public interface StorablePermissionService

: PermissionService

> - by value>>(ConcurrentHashMap()) + public val grantedPermissionMap: MutableMap> + by value>>(ConcurrentHashMap()) .withDefault { CopyOnWriteArrayList() } public companion object { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt index a2fc7e6b7..e7f6b0003 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt @@ -12,17 +12,34 @@ package net.mamoe.mirai.console.plugin.description import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer +import net.mamoe.mirai.console.extension.Extension +import net.mamoe.mirai.console.extensions.BotConfigurationAlterer +import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.internal.data.map import net.mamoe.mirai.console.plugin.PluginLoader +import net.mamoe.mirai.console.plugin.description.PluginKind.* /** - * 插件类型 + * 插件类型. + * + * 插件类型将影响加载顺序: [LOADER] -> [HIGH_PRIORITY_EXTENSIONS] -> [NORMAL]. + * + * 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [LOADER] 不允许依赖一个 [NORMAL] 类型的插件. */ @Serializable(with = PluginKind.AsStringSerializer::class) public enum class PluginKind { - /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ + /** 表示此插件提供一个 [PluginLoader], 也可以同时提供其他 [Extension] 应最早被加载 */ LOADER, + /** + * 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [NORMAL] 类型插件前加载 + * + * 高优先级的 [Extension] 通常是覆盖 Console 内置的部分服务的扩展. 如 [PermissionServiceProvider]. + * + * 一些普通的 [Extension], 如 [BotConfigurationAlterer], 也可以使用 [NORMAL] 类型插件注册. + */ + HIGH_PRIORITY_EXTENSIONS, + /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ NORMAL; From bf4c82f1a5ee051f574e8447c7e4aeda37d4e281 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:28:56 +0800 Subject: [PATCH 22/62] ExtensionSelectors and improved extensions API --- .../mirai/console/extension/Extension.kt | 2 + ...actExtensionPoint.kt => ExtensionPoint.kt} | 59 ++++++++++++---- .../console/extension/ExtensionRegistry.kt | 19 +++++ .../extensions/BotConfigurationAlterer.kt | 2 - .../extensions/PermissionServiceProvider.kt | 7 +- .../extensions/PluginLoaderProvider.kt | 3 + .../extensions/PostStartupExtension.kt | 2 + .../extensions/SingletonExtensionSelector.kt | 57 +++++++++++++++ .../MiraiConsoleImplementationBridge.kt | 7 +- .../BuiltInSingletonExtensionSelector.kt | 69 +++++++++++++++++++ .../permission/BuiltInPermissionServices.kt | 49 +++++++++++-- .../console/permission/PermissionService.kt | 6 +- .../permission/StorablePermissionService.kt | 52 -------------- 13 files changed, 256 insertions(+), 78 deletions(-) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/{AbstractExtensionPoint.kt => ExtensionPoint.kt} (68%) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionRegistry.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/SingletonExtensionSelector.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/extensions/BuiltInSingletonExtensionSelector.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt index 5eced7d95..6062095f5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt @@ -17,6 +17,8 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI * 表示一个扩展. * * Console 许多不容易通过 + * + * @see ExtensionWithConfig */ @ConsoleExperimentalAPI public interface Extension diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionPoint.kt similarity index 68% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionPoint.kt index c9b0ca7bd..ebb30b3ec 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/AbstractExtensionPoint.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionPoint.kt @@ -11,15 +11,48 @@ package net.mamoe.mirai.console.extension +import net.mamoe.mirai.console.extensions.SingletonExtensionSelector import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import java.util.* import java.util.concurrent.CopyOnWriteArraySet import kotlin.contracts.contract import kotlin.internal.LowPriorityInOverloadResolution import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf + +@ConsoleExperimentalAPI +public interface ExtensionPoint { + public val type: KClass + + public fun registerExtension(plugin: Plugin, extension: T) + public fun getExtensions(): Set> + + public companion object { + @JvmStatic + @JvmSynthetic + @ConsoleExperimentalAPI + public inline fun ExtensionPoint<*>.isFor(exactType: Boolean = false): Boolean { + return if (exactType) { + T::class == type + } else T::class.isSubclassOf(type) + } + } +} + +@ConsoleExperimentalAPI +public interface SingletonExtensionPoint> : ExtensionPoint { + public companion object { + @JvmStatic + @ConsoleExperimentalAPI + public fun > SingletonExtensionPoint.findSingleton(): T? { + return SingletonExtensionSelector.selectSingleton(type, this.getExtensions()) + } + } +} /** * 表示一个扩展点 @@ -27,26 +60,28 @@ import kotlin.reflect.KClass @ConsoleExperimentalAPI public open class AbstractExtensionPoint( @ConsoleExperimentalAPI - public val type: KClass -) { - - @ConsoleExperimentalAPI - public data class ExtensionRegistry( - public val plugin: Plugin, - public val extension: T - ) + public override val type: KClass +) : ExtensionPoint { + init { + @Suppress("LeakingThis") + allExtensionPoints.add(this) + } private val instances: MutableSet> = CopyOnWriteArraySet() - @Synchronized @ConsoleExperimentalAPI - public fun registerExtension(plugin: Plugin, extension: T) { + public override fun registerExtension(plugin: Plugin, extension: T) { // require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." } + requireNotNull(extension::class.qualifiedName) { "Extension must not be an anonymous object" } instances.add(ExtensionRegistry(plugin, extension)) } - @Synchronized - internal fun getExtensions(): Set> = instances + public override fun getExtensions(): Set> = Collections.unmodifiableSet(instances) + + internal companion object { + @ConsoleExperimentalAPI + internal val allExtensionPoints: MutableList> = mutableListOf() + } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionRegistry.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionRegistry.kt new file mode 100644 index 000000000..d79b1cdf3 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/ExtensionRegistry.kt @@ -0,0 +1,19 @@ +/* + * 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.extension + +import net.mamoe.mirai.console.plugin.Plugin +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI + +@ConsoleExperimentalAPI +public data class ExtensionRegistry( + public val plugin: Plugin, + public val extension: T +) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/BotConfigurationAlterer.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/BotConfigurationAlterer.kt index 69eda41c4..fd1c7c0dd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/BotConfigurationAlterer.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/BotConfigurationAlterer.kt @@ -5,7 +5,6 @@ package net.mamoe.mirai.console.extensions import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.FunctionExtension -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.utils.BotConfiguration /** @@ -13,7 +12,6 @@ import net.mamoe.mirai.utils.BotConfiguration * * @see MiraiConsole.addBot */ -@ConsoleExperimentalAPI public interface BotConfigurationAlterer : FunctionExtension { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt index b0de62886..7dfaa5e39 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PermissionServiceProvider.kt @@ -2,16 +2,19 @@ package net.mamoe.mirai.console.extensions import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.SingletonExtension +import net.mamoe.mirai.console.extension.SingletonExtensionPoint import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.plugin.description.PluginKind /** * [权限服务][PermissionService] 提供器. * - * 此扩展 + * 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 */ @ExperimentalPermission public interface PermissionServiceProvider : SingletonExtension> { public companion object ExtensionPoint : - AbstractExtensionPoint(PermissionServiceProvider::class) + AbstractExtensionPoint(PermissionServiceProvider::class), + SingletonExtensionPoint } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PluginLoaderProvider.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PluginLoaderProvider.kt index cf2271981..5b5016de9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PluginLoaderProvider.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PluginLoaderProvider.kt @@ -3,9 +3,12 @@ package net.mamoe.mirai.console.extensions import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.InstanceExtension import net.mamoe.mirai.console.plugin.PluginLoader +import net.mamoe.mirai.console.plugin.description.PluginKind /** * 提供扩展 [PluginLoader] + * + * 此扩展可由 [PluginKind.LOADER] 插件提供 */ public interface PluginLoaderProvider : InstanceExtension> { public companion object ExtensionPoint : AbstractExtensionPoint(PluginLoaderProvider::class) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt index ee8a97f45..aca64ca2f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/PostStartupExtension.kt @@ -16,6 +16,8 @@ import net.mamoe.mirai.console.extension.FunctionExtension * 在 Console 启动完成后立即在主线程调用的扩展. 用于进行一些必要的延迟初始化. * * 这些扩展只会, 且一定会被调用正好一次. + * + * 此扩展可由所有插件提供 */ public fun interface PostStartupExtension : FunctionExtension { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/SingletonExtensionSelector.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/SingletonExtensionSelector.kt new file mode 100644 index 000000000..fce60e204 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/SingletonExtensionSelector.kt @@ -0,0 +1,57 @@ +/* + * 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.extensions + +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.extension.* +import net.mamoe.mirai.console.internal.extensions.BuiltInSingletonExtensionSelector +import net.mamoe.mirai.console.plugin.description.PluginKind +import net.mamoe.mirai.console.plugin.name +import net.mamoe.mirai.utils.info +import kotlin.reflect.KClass + +/** + * 用于同时拥有多个 [SingletonExtension] 时选择一个实例. + * + * 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器. + * + * 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 + */ +public interface SingletonExtensionSelector : FunctionExtension { + + public fun selectSingleton( + extensionType: KClass, + candidates: Collection> + ): T? + + public companion object ExtensionPoint : + AbstractExtensionPoint(SingletonExtensionSelector::class) { + internal val instance: SingletonExtensionSelector by lazy { + val instances = SingletonExtensionSelector.getExtensions() + when { + instances.isEmpty() -> BuiltInSingletonExtensionSelector + instances.size == 1 -> { + instances.single().also { (plugin, ext) -> + MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: $ext from ${plugin.name}" } + }.extension + } + else -> { + error("Found too many SingletonExtensionSelectors: ${instances.joinToString { (p, i) -> "'$i' from '${p.name}'" }}. Check your plugins and ensure there is only one external SingletonExtensionSelectors") + } + } + } + + internal fun selectSingleton( + extensionType: KClass, + candidates: Collection> + ): T? = + instance.selectSingleton(extensionType, candidates) + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index 433733de8..e2225a537 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -28,14 +28,15 @@ import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.extension.useExtensions import net.mamoe.mirai.console.extensions.PostStartupExtension +import net.mamoe.mirai.console.extensions.SingletonExtensionSelector import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl +import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService -import net.mamoe.mirai.console.permission.StorablePermissionService import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter @@ -135,10 +136,12 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } } + SingletonExtensionSelector // init + phase `load PermissionService`@{ mainLogger.verbose { "Loading PermissionService..." } PermissionService.INSTANCE.let { ps -> - if (ps is StorablePermissionService<*>) { + if (ps is BuiltInPermissionService) { ConsoleDataScope.addAndReloadConfig(ps.config) mainLogger.verbose { "Reloaded PermissionService settings." } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/extensions/BuiltInSingletonExtensionSelector.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/extensions/BuiltInSingletonExtensionSelector.kt new file mode 100644 index 000000000..5d84aabdf --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/extensions/BuiltInSingletonExtensionSelector.kt @@ -0,0 +1,69 @@ +package net.mamoe.mirai.console.internal.extensions + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.data.AutoSavePluginData +import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.extension.Extension +import net.mamoe.mirai.console.extension.ExtensionRegistry +import net.mamoe.mirai.console.extensions.SingletonExtensionSelector +import net.mamoe.mirai.console.internal.data.kClassQualifiedName +import net.mamoe.mirai.console.plugin.name +import net.mamoe.mirai.console.util.ConsoleInput +import net.mamoe.mirai.utils.info +import kotlin.reflect.KClass + +internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector { + + private val config: SaveData = SaveData() + + private class SaveData : AutoSavePluginData() { + override val saveName: String get() = "ExtensionSelector" + + val value: MutableMap by value() + } + + override fun selectSingleton( + extensionType: KClass, + candidates: Collection> + ): T? = when { + candidates.isEmpty() -> null + candidates.size == 1 -> candidates.single().extension + else -> kotlin.run { + val target = config.value[extensionType.qualifiedName!!] + ?: return promptForSelectionAndSave(extensionType, candidates) + + val found = candidates.firstOrNull { it.extension::class.qualifiedName == target }?.extension + ?: return promptForSelectionAndSave(extensionType, candidates) + + found + } + } + + private fun promptForSelectionAndSave( + extensionType: KClass, + candidates: Collection> + ) = promptForManualSelection(extensionType, candidates) + .also { config.value[extensionType.qualifiedName!!] = it.extension.kClassQualifiedName!! }.extension + + private fun promptForManualSelection( + extensionType: KClass, + candidates: Collection> + ): ExtensionRegistry { + MiraiConsole.mainLogger.info { "There are multiple ${extensionType.simpleName}s, please select one:" } + + val candidatesList = candidates.toList() + + for ((index, candidate) in candidatesList.withIndex()) { + MiraiConsole.mainLogger.info { "${index + 1}. '${candidate.extension}' from '${candidate.plugin.name}'" } + } + + MiraiConsole.mainLogger.info { "Please choose a number from 1 to ${candidatesList.count()}" } + + val choiceStr = runBlocking { ConsoleInput.requestInput("Your choice: ") } + + val choice = choiceStr.toIntOrNull() ?: error("Bad choice") + + return candidatesList[choice - 1] + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 2018036cc..368b08cb8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -10,8 +10,16 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.data.AutoSavePluginConfig +import net.mamoe.mirai.console.data.PluginConfig +import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault +import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.data.valueFromKType import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArrayList import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KVariance import kotlin.reflect.full.createType @@ -72,8 +80,8 @@ public object AllDenyPermissionService : PermissionService { } @ExperimentalPermission -internal object BuiltInPermissionService : AbstractConcurrentPermissionService(), - StorablePermissionService { +public object BuiltInPermissionService : AbstractConcurrentPermissionService(), + PermissionService { @ExperimentalPermission override val permissionType: KClass @@ -87,10 +95,43 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

= - StorablePermissionService.ConcurrentSaveData( + internal val config: ConcurrentSaveData = + ConcurrentSaveData( PermissionImpl::class.createType(), "PermissionService", AutoSavePluginConfig() ) + + @Suppress("RedundantVisibilityModifier") + @ExperimentalPermission + internal class ConcurrentSaveData

private constructor( + permissionType: KType, + public override val saveName: String, + delegate: PluginConfig, + @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? + ) : PluginConfig by delegate { + public val permissions: MutableMap + by valueFromKType>( + MutableMap::class.createType( + listOf( + KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()), + KTypeProjection(KVariance.INVARIANT, permissionType), + ) + ), + ConcurrentHashMap() + ) + + public val grantedPermissionMap: MutableMap> + by value>>(ConcurrentHashMap()) + .withDefault { CopyOnWriteArrayList() } + + public companion object { + @JvmStatic + public operator fun

invoke( + permissionType: KType, + saveName: String, + delegate: PluginConfig, + ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, delegate, null) + } + } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index bce73ddfc..7b704ae2f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.console.permission +import net.mamoe.mirai.console.extension.SingletonExtensionPoint.Companion.findSingleton import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith import java.util.concurrent.CopyOnWriteArrayList @@ -49,13 +50,10 @@ public interface PermissionService

{ public fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) public companion object { - private val builtIn: PermissionService get() = BuiltInPermissionService - @get:JvmName("getInstance") @JvmStatic public val INSTANCE: PermissionService by lazy { - PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance ?: builtIn - // TODO: 2020/9/4 ExtensionSelector + PermissionServiceProvider.findSingleton()?.instance ?: BuiltInPermissionService } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt deleted file mode 100644 index ca41cebfa..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/StorablePermissionService.kt +++ /dev/null @@ -1,52 +0,0 @@ -package net.mamoe.mirai.console.permission - -import net.mamoe.mirai.console.data.PluginConfig -import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault -import net.mamoe.mirai.console.data.value -import net.mamoe.mirai.console.data.valueFromKType -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CopyOnWriteArrayList -import kotlin.reflect.KType -import kotlin.reflect.KTypeProjection -import kotlin.reflect.KVariance -import kotlin.reflect.full.createType - -@ExperimentalPermission -public interface StorablePermissionService

: PermissionService

{ - /** - * The config to be stored - */ - public val config: PluginConfig - - @ExperimentalPermission - public class ConcurrentSaveData

private constructor( - permissionType: KType, - public override val saveName: String, - delegate: PluginConfig, - @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? - ) : PluginConfig by delegate { - public val permissions: MutableMap - by valueFromKType>( - MutableMap::class.createType( - listOf( - KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()), - KTypeProjection(KVariance.INVARIANT, permissionType), - ) - ), - ConcurrentHashMap() - ) - - public val grantedPermissionMap: MutableMap> - by value>>(ConcurrentHashMap()) - .withDefault { CopyOnWriteArrayList() } - - public companion object { - @JvmStatic - public operator fun

invoke( - permissionType: KType, - saveName: String, - delegate: PluginConfig, - ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, delegate, null) - } - } -} From 5cfaef0fcde820089228fc2b8ae3e7b9f1d5d88b Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:29:30 +0800 Subject: [PATCH 23/62] Use 'Console' for dataHolderName for builtins --- .../mirai/console/internal/data/builtins/ConsoleDataScope.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt index 37899e3d5..61463c6c1 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt @@ -42,13 +42,13 @@ internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("Co internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder, CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginDataHolder") { override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis - override val dataHolderName: String get() = "ConsoleBuiltIns" + override val dataHolderName: String get() = "Console" } internal object ConsoleBuiltInPluginConfigHolder : AutoSavePluginDataHolder, CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginConfigHolder") { override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis - override val dataHolderName: String get() = "ConsoleBuiltIns" + override val dataHolderName: String get() = "Console" } internal object ConsoleBuiltInPluginDataStorage : From 485e7ec9f780b005d7e12541efd8c3c1e74cb9d0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:35:30 +0800 Subject: [PATCH 24/62] Rearrange implementations --- .../AbstractConcurrentPermissionService.kt | 58 +++++++++++++ .../permission/BuiltInPermissionServices.kt | 6 +- ...licatedPermissionRegistrationException.kt} | 13 +-- .../mirai/console/permission/Permissible.kt | 68 +-------------- .../permission/PermissibleIdentifier.kt | 57 +++++++++++++ .../console/permission/PermissionGroup.kt | 84 ------------------- .../mirai/console/permission/PermissionId.kt | 5 -- ...ionChecker.kt => PermissionIdNamespace.kt} | 9 +- .../console/permission/PermissionService.kt | 59 +++---------- 9 files changed, 136 insertions(+), 223 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/{Exceptions.kt => DuplicatedPermissionRegistrationException.kt} (63%) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/{PermissionChecker.kt => PermissionIdNamespace.kt} (71%) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt new file mode 100644 index 000000000..2abe901de --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -0,0 +1,58 @@ +/* + * 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.permission + +import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith +import java.util.concurrent.CopyOnWriteArrayList + +@ExperimentalPermission +public abstract class AbstractConcurrentPermissionService

: PermissionService

{ + protected abstract val permissions: MutableMap + protected abstract val grantedPermissionsMap: MutableMap> + + protected abstract fun createPermission( + id: PermissionId, + description: String, + base: PermissionId? + ): P + + override fun get(id: PermissionId): P? = permissions[id] + + override fun register(id: PermissionId, description: String, base: PermissionId?): P { + grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed + val instance = createPermission(id, description, base) + if (permissions.putIfAbsent(id, instance) != null) { + throw DuplicatedPermissionRegistrationException("Duplicated Permission registry. new: $instance, old: ${permissions[id]}") + } + return instance + } + + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) { + val id = permission.id + grantedPermissionsMap[id]?.add(permissibleIdentifier) + ?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.") + } + + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) { + grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) + } + + public override fun getGrantedPermissions(permissible: Permissible): Sequence

= sequence

{ + for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { + val myIdentifier = permissible.identifier + + val granted = + if (permissibleIdentifiers.isEmpty()) false + else permissibleIdentifiers.any { myIdentifier grantedWith it } + + if (granted) get(permissionIdentifier)?.let { yield(it) } + } + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 368b08cb8..bb84dc27a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -36,7 +36,7 @@ public object AllGrantPermissionService : PermissionService { ): PermissionImpl { val new = PermissionImpl(id, description, base) if (all.putIfAbsent(id, new) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}") + throw DuplicatedPermissionRegistrationException("Duplicated Permission registry: ${all[id]}") } return new } @@ -64,7 +64,7 @@ public object AllDenyPermissionService : PermissionService { ): PermissionImpl { val new = PermissionImpl(id, description, base) if (all.putIfAbsent(id, new) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}") + throw DuplicatedPermissionRegistrationException("Duplicated Permission registry: ${all[id]}") } return new } @@ -80,7 +80,7 @@ public object AllDenyPermissionService : PermissionService { } @ExperimentalPermission -public object BuiltInPermissionService : AbstractConcurrentPermissionService(), +internal object BuiltInPermissionService : AbstractConcurrentPermissionService(), PermissionService { @ExperimentalPermission diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt similarity index 63% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt index c8b358936..b521e1703 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Exceptions.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt @@ -7,19 +7,12 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("unused") + package net.mamoe.mirai.console.permission - @ExperimentalPermission -public open class HotDeploymentNotSupportedException : Exception { - public constructor() : super() - public constructor(message: String?) : super(message) - public constructor(message: String?, cause: Throwable?) : super(message, cause) - public constructor(cause: Throwable?) : super(cause) -} - -@ExperimentalPermission -public open class DuplicatedRegistrationException : Exception { +public open class DuplicatedPermissionRegistrationException : Exception { public constructor() : super() public constructor(message: String?) : super(message) public constructor(message: String?, cause: Throwable?) : super(message, cause) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt index 3b5c53b4e..b970e50e5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permissible.kt @@ -25,70 +25,4 @@ import net.mamoe.mirai.console.command.CommandSender @ExperimentalPermission public interface Permissible { public val identifier: PermissibleIdentifier -} - - -@ExperimentalPermission("Classname is subject to change") -public interface PermissibleIdentifier { - public val parents: Array - - public companion object { - @ExperimentalPermission - public infix fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean { - return allParentsWithSelf().any { it == with } - } - - internal fun PermissibleIdentifier.allParentsWithSelf(): Sequence { - return sequence { - yield(this@allParentsWithSelf) - yieldAll(parents.asSequence()) - } - } - } -} - -/** - * - */ -@ExperimentalPermission -public sealed class AbstractPermissibleIdentifier( - public final override vararg val parents: PermissibleIdentifier -) : PermissibleIdentifier { - public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) - public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) - - public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) - public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) - public data class ExactMember( - public val groupId: Long, - public val memberId: Long - ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) - - public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) - public data class ExactFriend( - public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(id)) - - public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) - public data class ExactTemp( - public val groupId: Long, - public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? - - public object AnyUser : AbstractPermissibleIdentifier(AnyContact) - public data class ExactUser( - public val id: Long - ) : AbstractPermissibleIdentifier(AnyUser) - - public object AnyContact : AbstractPermissibleIdentifier() - - public object Console : AbstractPermissibleIdentifier() -} - -@ExperimentalPermission -public inline fun Permissible.hasPermission(permission: Permission): Boolean = - PermissionService.run { permission.testPermission(this@hasPermission) } - -@ExperimentalPermission -public inline fun Permissible.hasPermission(permission: PermissionId): Boolean = - PermissionService.run { permission.testPermission(this@hasPermission) } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt new file mode 100644 index 000000000..4b0ad769e --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -0,0 +1,57 @@ +@file:Suppress("unused") + +package net.mamoe.mirai.console.permission + +@ExperimentalPermission("Classname is subject to change") +public interface PermissibleIdentifier { + public val parents: Array + + public companion object { + @ExperimentalPermission + public infix fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean { + return allParentsWithSelf().any { it == with } + } + + internal fun PermissibleIdentifier.allParentsWithSelf(): Sequence { + return sequence { + yield(this@allParentsWithSelf) + yieldAll(parents.asSequence()) + } + } + } +} + +@ExperimentalPermission +public sealed class AbstractPermissibleIdentifier( + public final override vararg val parents: PermissibleIdentifier +) : PermissibleIdentifier { + public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) + public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + + public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) + public data class ExactMember( + public val groupId: Long, + public val memberId: Long + ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) + + public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) + public data class ExactFriend( + public val id: Long + ) : AbstractPermissibleIdentifier(ExactUser(id)) + + public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) + public data class ExactTemp( + public val groupId: Long, + public val id: Long + ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? + + public object AnyUser : AbstractPermissibleIdentifier(AnyContact) + public data class ExactUser( + public val id: Long + ) : AbstractPermissibleIdentifier(AnyUser) + + public object AnyContact : AbstractPermissibleIdentifier() + + public object Console : AbstractPermissibleIdentifier() +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt deleted file mode 100644 index 115d76175..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionGroup.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.permission - -import net.mamoe.mirai.console.command.CommandSender -import kotlin.reflect.KProperty - -@ExperimentalPermission -public abstract class PermissionGroup( - private val idNamespace: PermissionIdNamespace, -) { - @ExperimentalPermission - public inner class PermissionBuilder { - @JvmField - internal var description: String = "" - - @JvmField - internal var basePermission: PermissionId? = null - - @JvmField - internal var permissionChecker: PermissionChecker? = null - - public fun description(description: String): PermissionBuilder = apply { this.description = description } - public fun dependsOn(basePermission: PermissionId?): PermissionBuilder = - apply { this.basePermission = basePermission } - - public fun dependsOn(basePermission: Permission?): PermissionBuilder = - apply { this.basePermission = basePermission?.id } - - public fun basePermission(basePermission: PermissionId?): PermissionBuilder = - apply { this.basePermission = basePermission } - - public fun basePermission(basePermission: Permission?): PermissionBuilder = - apply { this.basePermission = basePermission?.id } - - public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder = - apply { this.permissionChecker = permissionChecker } - - public fun build(property: KProperty<*>): Permission { - return PermissionService.INSTANCE.register( - idNamespace.permissionId(property.name), - description, - basePermission - ) - } - } - - public infix fun String.dependsOn(permission: Permission): PermissionBuilder { - return PermissionBuilder().apply { description(this@dependsOn);dependsOn(permission) } - } - - - public infix fun PermissionBuilder.defaults(permission: PermissionChecker): PermissionBuilder { - return apply { defaults(permission) } - } - - public infix fun PermissionBuilder.defaults(permission: CommandSender.() -> Boolean): PermissionBuilder { - return apply { defaults(permission) } - } - - public infix fun String.defaults(permission: PermissionChecker): PermissionBuilder { - return PermissionBuilder().apply { defaults(permission) } - } - - - public operator fun String.invoke(block: PermissionBuilder.() -> Unit): PermissionBuilder { - return PermissionBuilder().apply(block) - } - - public operator fun String.provideDelegate(thisRef: PermissionGroup, property: KProperty<*>): Permission = - PermissionBuilder().apply { description(this@provideDelegate) }.build(property) - - public operator fun Permission.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission = this - public operator fun PermissionBuilder.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission = - this.build(property) -} - diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index a71b29924..e3f01f193 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -49,8 +49,3 @@ public data class PermissionId( } } -@ExperimentalPermission -public interface PermissionIdNamespace { - @ExperimentalPermission - public fun permissionId(id: String): PermissionId -} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdNamespace.kt similarity index 71% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdNamespace.kt index 21fbb4c42..6ce0a93a5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionChecker.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionIdNamespace.kt @@ -7,13 +7,10 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("unused") - package net.mamoe.mirai.console.permission -import net.mamoe.mirai.console.command.CommandSender - @ExperimentalPermission -public fun interface PermissionChecker { - public fun CommandSender.testPermission(): Boolean +public interface PermissionIdNamespace { + @ExperimentalPermission + public fun permissionId(id: String): PermissionId } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 7b704ae2f..77bb77cb0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -13,8 +13,6 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.extension.SingletonExtensionPoint.Companion.findSingleton import net.mamoe.mirai.console.extensions.PermissionServiceProvider -import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith -import java.util.concurrent.CopyOnWriteArrayList import kotlin.reflect.KClass /** @@ -37,7 +35,7 @@ public interface PermissionService

{ /////////////////////////////////////////////////////////////////////////// - @Throws(DuplicatedRegistrationException::class) + @Throws(DuplicatedPermissionRegistrationException::class) public fun register( id: PermissionId, description: String, @@ -59,59 +57,23 @@ public interface PermissionService

{ } @ExperimentalPermission -public abstract class AbstractConcurrentPermissionService

: PermissionService

{ - protected abstract val permissions: MutableMap - protected abstract val grantedPermissionsMap: MutableMap> +public inline fun Permissible.hasPermission(permission: Permission): Boolean = + PermissionService.run { permission.testPermission(this@hasPermission) } - protected abstract fun createPermission( - id: PermissionId, - description: String, - base: PermissionId? - ): P - - override fun get(id: PermissionId): P? = permissions[id] - - override fun register(id: PermissionId, description: String, base: PermissionId?): P { - grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed - val instance = createPermission(id, description, base) - if (permissions.putIfAbsent(id, instance) != null) { - throw DuplicatedRegistrationException("Duplicated Permission registry. new: $instance, old: ${permissions[id]}") - } - return instance - } - - override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) { - val id = permission.id - grantedPermissionsMap[id]?.add(permissibleIdentifier) - ?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.") - } - - override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) { - grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) - } - - public override fun getGrantedPermissions(permissible: Permissible): Sequence

= sequence

{ - for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { - val myIdentifier = permissible.identifier - - val granted = - if (permissibleIdentifiers.isEmpty()) false - else permissibleIdentifiers.any { myIdentifier grantedWith it } - - if (granted) get(permissionIdentifier)?.let { yield(it) } - } - } -} +@ExperimentalPermission +public inline fun Permissible.hasPermission(permission: PermissionId): Boolean = + PermissionService.run { permission.testPermission(this@hasPermission) } +@JvmSynthetic @ExperimentalPermission public inline fun Permissible.getGrantedPermissions(): Sequence = PermissionService.INSTANCE.run { getGrantedPermissions(this@getGrantedPermissions) } - +@JvmSynthetic @ExperimentalPermission -public inline fun Permission.testPermission(permissible: Permissible): Boolean = +public fun Permission.testPermission(permissible: Permissible): Boolean = PermissionService.INSTANCE.run { require(permissionType.isInstance(this@testPermission)) { "Custom-constructed Permission instance is not allowed. " + @@ -124,8 +86,9 @@ public inline fun Permission.testPermission(permissible: Permissible): Boolean = testPermission(permissible, this@testPermission) } +@JvmSynthetic @ExperimentalPermission -public inline fun PermissionId.testPermission(permissible: Permissible): Boolean { +public fun PermissionId.testPermission(permissible: Permissible): Boolean { val p = PermissionService.INSTANCE[this] ?: return false return p.testPermission(permissible) } \ No newline at end of file From 48f6d685810d6fd49c212825b6963031f5f18add Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:35:52 +0800 Subject: [PATCH 25/62] Add missing license --- .../mirai/console/permission/PermissibleIdentifier.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index 4b0ad769e..ce9360f1d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -1,3 +1,12 @@ +/* + * 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 + */ + @file:Suppress("unused") package net.mamoe.mirai.console.permission From 6010d8c6182cdc3e703bfb63746d4e9b102dc714 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:37:20 +0800 Subject: [PATCH 26/62] Remove CuiPluginCenter --- .../MiraiConsoleImplementationBridge.kt | 3 +- .../internal/plugin/CuiPluginCenter.kt | 111 ------------------ 2 files changed, 1 insertion(+), 113 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index e2225a537..39eda192f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -32,7 +32,6 @@ import net.mamoe.mirai.console.extensions.SingletonExtensionSelector import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope -import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.ExperimentalPermission @@ -56,7 +55,7 @@ import kotlin.coroutines.CoroutineContext */ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleImplementation, MiraiConsole { - override val pluginCenter: PluginCenter get() = CuiPluginCenter + override val pluginCenter: PluginCenter get() = throw UnsupportedOperationException("PluginCenter is not supported yet") private val instance: MiraiConsoleImplementation by MiraiConsoleImplementation.Companion::instance override val buildDate: Instant by MiraiConsoleBuildConstants::buildDate diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt deleted file mode 100644 index 42ac52d11..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt +++ /dev/null @@ -1,111 +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 - */ - -@file:OptIn(ConsoleExperimentalAPI::class) - -package net.mamoe.mirai.console.internal.plugin - -import io.ktor.client.* -import io.ktor.client.engine.cio.* -import io.ktor.client.request.* -import io.ktor.util.* -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import net.mamoe.mirai.console.plugin.center.PluginCenter -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI -import net.mamoe.mirai.console.util.retryCatching -import java.io.File - -internal val json = Json { - isLenient = true - ignoreUnknownKeys = true -} - -@OptIn(KtorExperimentalAPI::class) -internal val Http = HttpClient(CIO) - -internal object CuiPluginCenter : PluginCenter { - - var plugins: List? = null - - /** - * 一页 10 个 pageMinNum=1 - */ - override suspend fun fetchPlugin(page: Int): Map { - check(page > 0) - val startIndex = (page - 1) * 10 - val endIndex = startIndex + 9 - val map = mutableMapOf() - (startIndex until endIndex).forEach { index -> - val plugins = plugins ?: kotlin.run { - refresh() - plugins - } ?: return mapOf() - - if (index >= plugins.size) { - return@forEach - } - - map[name] = plugins[index] - } - return map - } - - override suspend fun findPlugin(name: String): PluginCenter.PluginInfo? { - val result = retryCatching(3) { - Http.get("https://miraiapi.jasonczc.cn/getPluginDetailedInfo?name=$name") - }.getOrElse { return null } - if (result == "err:not found") return null - - return json.decodeFromString(PluginCenter.PluginInfo.serializer(), result) - } - - override suspend fun refresh() { - - @Serializable - data class Result( - val success: Boolean, - val result: List - ) - - val result = json.decodeFromString(Result.serializer(), Http.get("https://miraiapi.jasonczc.cn/getPluginList")) - - check(result.success) { "Failed to fetch plugin list from Cui Cloud" } - plugins = result.result - } - - override suspend fun T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File { - TODO() - /* - val info = findPlugin(name) ?: error("Plugin Not Found") - val targetFile = File(PluginManager.pluginsPath, "$name-" + info.version + ".jar") - withContext(Dispatchers.IO) { - tryNTimes { - val con = - URL("https://pan.jasonczc.cn/?/mirai/plugins/$name/$name-" + info.version + ".mp4").openConnection() as HttpURLConnection - val input = con.inputStream - val size = con.contentLength - var totalDownload = 0F - val outputStream = FileOutputStream(targetFile) - var len: Int - val buff = ByteArray(1024) - while (input.read(buff).also { len = it } != -1) { - totalDownload += len - outputStream.write(buff, 0, len) - progressListener.invoke(this@downloadPlugin, totalDownload / size) - } - } - } - return targetFile - */ - } - - override val name: String get() = "崔云" -} - From 05cde028bfbb2dc79800752a80aba6b51ec447af Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:37:26 +0800 Subject: [PATCH 27/62] Fix docs --- .../kotlin/net/mamoe/mirai/console/extension/Extension.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt index 6062095f5..50930174b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extension/Extension.kt @@ -15,10 +15,6 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** * 表示一个扩展. - * - * Console 许多不容易通过 - * - * @see ExtensionWithConfig */ @ConsoleExperimentalAPI public interface Extension From 9fec51977e0805bd649804427ebcea915b9a66ee Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:41:32 +0800 Subject: [PATCH 28/62] Remove the serializer of PluginKind --- .../net/mamoe/mirai/console/plugin/description/PluginKind.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt index e7f6b0003..fce47b97b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt @@ -10,7 +10,6 @@ package net.mamoe.mirai.console.plugin.description import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer import net.mamoe.mirai.console.extension.Extension import net.mamoe.mirai.console.extensions.BotConfigurationAlterer @@ -26,7 +25,6 @@ import net.mamoe.mirai.console.plugin.description.PluginKind.* * * 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [LOADER] 不允许依赖一个 [NORMAL] 类型的插件. */ -@Serializable(with = PluginKind.AsStringSerializer::class) public enum class PluginKind { /** 表示此插件提供一个 [PluginLoader], 也可以同时提供其他 [Extension] 应最早被加载 */ LOADER, From 3183bc401898dd27c4dea3c25a8356acf32299f5 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 8 Sep 2020 21:43:00 +0800 Subject: [PATCH 29/62] Init ConsoleInput and BotManager on start --- .../console/internal/MiraiConsoleImplementationBridge.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index 39eda192f..59108b740 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -39,6 +39,7 @@ import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter +import net.mamoe.mirai.console.util.BotManager import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.utils.* @@ -110,11 +111,15 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI } } + ConsoleInput + // start phase `load configurations`@{ mainLogger.verbose { "Loading configurations..." } ConsoleDataScope.reloadAll() + + BotManager } val pluginLoadSession: PluginManagerImpl.PluginLoadSession @@ -135,7 +140,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } } - SingletonExtensionSelector // init + SingletonExtensionSelector.instance // init phase `load PermissionService`@{ mainLogger.verbose { "Loading PermissionService..." } From a830a095dea6b569fe3fc914e95d5939387a83af Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 08:41:28 +0800 Subject: [PATCH 30/62] Add extensions for permission --- .../AbstractConcurrentPermissionService.kt | 5 +- .../permission/BuiltInPermissionServices.kt | 16 +++- .../console/permission/PermissionService.kt | 79 +++++++++++++------ 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index 2abe901de..fe5022af0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -44,13 +44,12 @@ public abstract class AbstractConcurrentPermissionService

: Perm grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) } - public override fun getGrantedPermissions(permissible: Permissible): Sequence

= sequence

{ + public override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

= sequence

{ for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { - val myIdentifier = permissible.identifier val granted = if (permissibleIdentifiers.isEmpty()) false - else permissibleIdentifiers.any { myIdentifier grantedWith it } + else permissibleIdentifiers.any { permissibleIdentifier grantedWith it } if (granted) get(permissionIdentifier)?.let { yield(it) } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index bb84dc27a..1a69b2e71 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -42,11 +42,15 @@ public object AllGrantPermissionService : PermissionService { } override fun get(id: PermissionId): PermissionImpl? = all[id] - override fun getGrantedPermissions(permissible: Permissible): Sequence = all.values.asSequence() + override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence = + all.values.asSequence() + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = true + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = + true + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } } @@ -70,11 +74,15 @@ public object AllDenyPermissionService : PermissionService { } override fun get(id: PermissionId): PermissionImpl? = all[id] - override fun getGrantedPermissions(permissible: Permissible): Sequence = emptySequence() + override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence = + emptySequence() + override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = false + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = + false + override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 77bb77cb0..2dace27ea 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -27,10 +27,10 @@ public interface PermissionService

{ public operator fun get(id: PermissionId): P? - public fun getGrantedPermissions(permissible: Permissible): Sequence

+ public fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

- public fun testPermission(permissible: Permissible, permission: P): Boolean = - permissible.getGrantedPermissions().any { it == permission } + public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: P): Boolean = + getGrantedPermissions(permissibleIdentifier).any { it == permission } /////////////////////////////////////////////////////////////////////////// @@ -57,38 +57,73 @@ public interface PermissionService

{ } @ExperimentalPermission -public inline fun Permissible.hasPermission(permission: Permission): Boolean = - PermissionService.run { permission.testPermission(this@hasPermission) } +public fun PermissibleIdentifier.grant(permission: Permission) { + PermissionService.INSTANCE.checkType(permission::class).grant(this, permission) +} @ExperimentalPermission -public inline fun Permissible.hasPermission(permission: PermissionId): Boolean = - PermissionService.run { permission.testPermission(this@hasPermission) } +public fun Permissible.hasPermission(permission: Permission): Boolean = + permission.testPermission(this@hasPermission) + +@ExperimentalPermission +public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean = + permission.testPermission(this@hasPermission) + +@Suppress("UNCHECKED_CAST") +@ExperimentalPermission +public fun PermissibleIdentifier.hasPermission(permission: PermissionId): Boolean = + (PermissionService.INSTANCE as PermissionService).run { + val p = this[permission] ?: return false + testPermission(this@hasPermission, p) + } + +@ExperimentalPermission +public fun Permissible.hasPermission(permissionId: PermissionId): Boolean = + PermissionService.run { permissionId.testPermission(this@hasPermission) } @JvmSynthetic @ExperimentalPermission -public inline fun Permissible.getGrantedPermissions(): Sequence = - PermissionService.INSTANCE.run { - getGrantedPermissions(this@getGrantedPermissions) - } +public fun Permissible.getGrantedPermissions(): Sequence = + PermissionService.INSTANCE.getGrantedPermissions(this@getGrantedPermissions.identifier) + +@JvmSynthetic +@ExperimentalPermission +public fun PermissibleIdentifier.getGrantedPermissions(): Sequence = + PermissionService.INSTANCE.getGrantedPermissions(this@getGrantedPermissions) @JvmSynthetic @ExperimentalPermission public fun Permission.testPermission(permissible: Permissible): Boolean = - PermissionService.INSTANCE.run { - require(permissionType.isInstance(this@testPermission)) { - "Custom-constructed Permission instance is not allowed. " + - "Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get" - } + PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission) - @Suppress("UNCHECKED_CAST") - this as PermissionService - - testPermission(permissible, this@testPermission) - } +@JvmSynthetic +@ExperimentalPermission +public fun Permission.testPermission(permissibleIdentifier: PermissibleIdentifier): Boolean = + PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission) @JvmSynthetic @ExperimentalPermission public fun PermissionId.testPermission(permissible: Permissible): Boolean { val p = PermissionService.INSTANCE[this] ?: return false return p.testPermission(permissible) -} \ No newline at end of file +} + +@JvmSynthetic +@ExperimentalPermission +public fun PermissionId.testPermission(permissible: PermissibleIdentifier): Boolean { + val p = PermissionService.INSTANCE[this] ?: return false + return p.testPermission(permissible) +} + +@OptIn(ExperimentalPermission::class) +internal fun PermissionService<*>.checkType(permissionType: KClass): PermissionService { + return PermissionService.INSTANCE.run { + require(permissionType.isInstance(this@checkType)) { + "Custom-constructed Permission instance is not allowed. " + + "Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get" + } + + @Suppress("UNCHECKED_CAST") + this as PermissionService + } +} From 44ef4e7c565a9ebaefb4a65042a7e23a74765b8c Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 08:59:39 +0800 Subject: [PATCH 31/62] Introduce BasePermission --- .../mirai/console/permission/Permission.kt | 25 +++++++++++++++++-- .../console/permission/PermissionService.kt | 22 ++++++++++------ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index f687376cb..c51d96dfc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.console.permission import kotlinx.serialization.Serializable +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** @@ -19,9 +20,29 @@ import kotlinx.serialization.Serializable public interface Permission { public val id: PermissionId public val description: String - public val base: PermissionId? + public val parent: PermissionId } +/** + * 所有权限的父权限. + */ +@ExperimentalPermission +public object BasePermission : + Permission { + override val id: PermissionId = PermissionId("console", "base") + override val description: String get() = "The parent of any permission" + override val parent: PermissionId get() = id + +} + +@ConsoleExperimentalAPI +@ExperimentalPermission +public fun Permission.parentsWithSelfSequence(): Sequence = + generateSequence(this) { p -> + p.parent.let { PermissionService.INSTANCE[it] } + ?.takeIf { parent -> parent != p } + } + /** * [Permission] 的简单实现 */ @@ -30,5 +51,5 @@ public interface Permission { public class PermissionImpl( override val id: PermissionId, override val description: String, - override val base: PermissionId? + override val parent: PermissionId = BasePermission.id ) : Permission \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 2dace27ea..7d770db76 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -29,8 +29,12 @@ public interface PermissionService

{ public fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

- public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: P): Boolean = - getGrantedPermissions(permissibleIdentifier).any { it == permission } + public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean { + val all = this[permissionId]?.parentsWithSelfSequence() ?: return false + return getGrantedPermissions(permissibleIdentifier).any { p -> + all.any { p.id == it.id } + } + } /////////////////////////////////////////////////////////////////////////// @@ -56,6 +60,9 @@ public interface PermissionService

{ } } +@ExperimentalPermission +public fun PermissionId.findCorrespondingPermission(): Permission? = PermissionService.INSTANCE[this] + @ExperimentalPermission public fun PermissibleIdentifier.grant(permission: Permission) { PermissionService.INSTANCE.checkType(permission::class).grant(this, permission) @@ -71,15 +78,14 @@ public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean @Suppress("UNCHECKED_CAST") @ExperimentalPermission -public fun PermissibleIdentifier.hasPermission(permission: PermissionId): Boolean = +public fun PermissibleIdentifier.hasPermission(permissionId: PermissionId): Boolean = (PermissionService.INSTANCE as PermissionService).run { - val p = this[permission] ?: return false - testPermission(this@hasPermission, p) + testPermission(this@hasPermission, permissionId) } @ExperimentalPermission public fun Permissible.hasPermission(permissionId: PermissionId): Boolean = - PermissionService.run { permissionId.testPermission(this@hasPermission) } + permissionId.testPermission(this@hasPermission) @JvmSynthetic @ExperimentalPermission @@ -94,12 +100,12 @@ public fun PermissibleIdentifier.getGrantedPermissions(): Sequence = @JvmSynthetic @ExperimentalPermission public fun Permission.testPermission(permissible: Permissible): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission) + PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission.id) @JvmSynthetic @ExperimentalPermission public fun Permission.testPermission(permissibleIdentifier: PermissibleIdentifier): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission) + PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission.id) @JvmSynthetic @ExperimentalPermission From 2926e55590cb6f6664573c933a89d04ab261cee9 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 10:02:56 +0800 Subject: [PATCH 32/62] Introduce parentPermission for commands --- .../net/mamoe/mirai/console/command/Command.kt | 4 ++-- .../net/mamoe/mirai/console/command/CommandOwner.kt | 12 +++++++++++- .../mamoe/mirai/console/command/CompositeCommand.kt | 4 ++-- .../net/mamoe/mirai/console/command/RawCommand.kt | 6 +++--- .../mamoe/mirai/console/command/SimpleCommand.kt | 2 +- .../mirai/console/command/java/JCompositeCommand.kt | 4 ++-- .../mamoe/mirai/console/command/java/JRawCommand.kt | 4 ++-- .../internal/command/CompositeCommandInternal.kt | 6 +++--- .../mirai/console/internal/command/internal.kt | 7 ++----- .../console/internal/plugin/JvmPluginInternal.kt | 13 +++++++++++++ .../AbstractConcurrentPermissionService.kt | 4 ++-- .../console/permission/BuiltInPermissionServices.kt | 10 +++++----- .../mamoe/mirai/console/permission/Permission.kt | 7 ++++--- .../mamoe/mirai/console/permission/PermissionId.kt | 3 +++ .../mirai/console/permission/PermissionService.kt | 5 ++++- .../console/plugin/description/PluginDescription.kt | 2 +- 16 files changed, 60 insertions(+), 33 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 096be5791..237376a1b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -105,7 +105,7 @@ public abstract class AbstractCommand public override val owner: CommandOwner, vararg names: String, description: String = "", - basePermission: PermissionId? = null, + parentPermission: PermissionId = owner.basePermission, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { @@ -116,5 +116,5 @@ public abstract class AbstractCommand }.toTypedArray() @OptIn(ExperimentalPermission::class) - public override val permission: Permission by lazy { createCommandPermission(basePermission) } + public override val permission: Permission by lazy { createCommandPermission(parentPermission) } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt index 49932f8df..ba6e0d6cf 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands +import net.mamoe.mirai.console.permission.BasePermission import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionIdNamespace @@ -23,12 +24,21 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin * * @see JvmPlugin 是一个 [CommandOwner] */ -public interface CommandOwner : PermissionIdNamespace +public interface CommandOwner : PermissionIdNamespace { + /** + * 此 [PermissionIdNamespace] 拥有的指令都默认将 [basePermission] 作为父权限. + * + * TODO document + */ + public val basePermission: PermissionId +} /** * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. */ internal object ConsoleCommandOwner : CommandOwner { + override val basePermission: PermissionId get() = BasePermission.id + @ExperimentalPermission override fun permissionId(id: String): PermissionId = PermissionId("console", id) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt index 26a71f9d8..6689d0712 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt @@ -86,10 +86,10 @@ public abstract class CompositeCommand( owner: CommandOwner, vararg names: String, description: String = "no description available", - basePermission: PermissionId? = null, + parentPermission: PermissionId = owner.basePermission, prefixOptional: Boolean = false, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext -) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional), +) : Command, AbstractReflectionCommand(owner, names, description, parentPermission, prefixOptional), CommandArgumentContextAware { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt index fd1a36515..a1ad43cc0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt @@ -43,13 +43,13 @@ public abstract class RawCommand( public override val usage: String = "", /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public override val description: String = "", - /** 指令权限 */ - basePermission: PermissionId? = null, + /** 指令父权限 */ + parentPermission: PermissionId = owner.basePermission, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { @OptIn(ExperimentalPermission::class) - public override val permission: Permission by lazy { createCommandPermission(basePermission) } + public override val permission: Permission by lazy { createCommandPermission(parentPermission) } /** * 在指令被执行时调用. diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt index 9c83c9228..cafad6148 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt @@ -52,7 +52,7 @@ public abstract class SimpleCommand( owner: CommandOwner, vararg names: String, description: String = "no description available", - basePermission: PermissionId? = null, + basePermission: PermissionId = owner.basePermission, prefixOptional: Boolean = false, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext ) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional), diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt index ef400b3c0..2b1dbb076 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt @@ -72,8 +72,8 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI public abstract class JCompositeCommand @JvmOverloads constructor( owner: CommandOwner, vararg names: String, - basePermission: PermissionId? = null, -) : CompositeCommand(owner, *names, basePermission = basePermission) { + parentPermission: PermissionId = owner.basePermission, +) : CompositeCommand(owner, *names, parentPermission = parentPermission) { /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public final override var description: String = "" protected set diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt index f53daf865..ec9f894ca 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt @@ -53,7 +53,7 @@ public abstract class JRawCommand @JvmOverloads constructor( public override val owner: CommandOwner, /** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */ public override vararg val names: String, - basePermission: PermissionId? = null, + parentPermission: PermissionId = owner.basePermission, ) : Command { /** 用法说明, 用于发送给用户 */ public override var usage: String = "" @@ -64,7 +64,7 @@ public abstract class JRawCommand @JvmOverloads constructor( protected set /** 指令权限 */ - public final override var permission: Permission = createCommandPermission(basePermission) + public final override var permission: Permission = createCommandPermission(parentPermission) protected set /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index cda7b6fd4..6e5564b7a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -48,13 +48,13 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( owner: CommandOwner, names: Array, description: String = "", - basePermission: PermissionId? = null, + parentPermission: PermissionId = owner.basePermission, prefixOptional: Boolean = false ) : Command, AbstractCommand( owner, names = names, description = description, - basePermission = basePermission, + parentPermission = parentPermission, prefixOptional = prefixOptional ), CommandArgumentContextAware { internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver @@ -74,7 +74,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { DefaultSubCommandDescriptor( "", - createCommandPermission(basePermission), + createCommandPermission(parentPermission), onCommand = { sender: CommandSender, args: MessageChain -> sender.onDefault(args) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt index f89fc1731..c6a2bc374 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt @@ -139,15 +139,12 @@ internal fun Group.fuzzySearchMember( } @OptIn(ExperimentalPermission::class) -internal fun Command.createCommandPermission(basePermission: PermissionId?): Permission { - return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, basePermission) +internal fun Command.createCommandPermission(parent: PermissionId): Permission { + return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, parent) } //// internal -@JvmSynthetic -internal inline fun List.dropToTypedArray(n: Int): Array = Array(size - n) { this[n + it] } - @OptIn(ExperimentalPermission::class) @JvmSynthetic @Throws(CommandExecutionException::class) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index 05c7f5127..e200e05b8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -17,11 +17,14 @@ import net.mamoe.mirai.console.data.runCatchingLog import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId +import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.allocatePermissionIdForPlugin import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader import net.mamoe.mirai.console.plugin.ResourceContainer.Companion.asResourceContainer import net.mamoe.mirai.console.plugin.jvm.JvmPlugin +import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.util.NamedSupervisorJob import net.mamoe.mirai.utils.MiraiLogger import java.io.File @@ -40,6 +43,14 @@ internal abstract class JvmPluginInternal( parentCoroutineContext: CoroutineContext ) : JvmPlugin, CoroutineScope { + @OptIn(ExperimentalPermission::class) + final override val basePermission: PermissionId by lazy { + PermissionService.INSTANCE.register( + PermissionService.INSTANCE.allocatePermissionIdForPlugin(name, "*"), + "The base permission" + ).id + } + final override var isEnabled: Boolean = false private val resourceContainerDelegate by lazy { this::class.java.classLoader.asResourceContainer() } @@ -99,6 +110,7 @@ internal abstract class JvmPluginInternal( } internal fun internalOnEnable(): Boolean { + basePermission if (!firstRun) refreshCoroutineContext() kotlin.runCatching { onEnable() @@ -124,6 +136,7 @@ internal abstract class JvmPluginInternal( internal val _intrinsicCoroutineContext: CoroutineContext by lazy { CoroutineName("Plugin $dataHolderName") } + @JvmField internal val coroutineContextInitializer = { CoroutineExceptionHandler { context, throwable -> diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index fe5022af0..1e0085e29 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -20,12 +20,12 @@ public abstract class AbstractConcurrentPermissionService

: Perm protected abstract fun createPermission( id: PermissionId, description: String, - base: PermissionId? + base: PermissionId = BasePermission.id ): P override fun get(id: PermissionId): P? = permissions[id] - override fun register(id: PermissionId, description: String, base: PermissionId?): P { + override fun register(id: PermissionId, description: String, base: PermissionId): P { grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed val instance = createPermission(id, description, base) if (permissions.putIfAbsent(id, instance) != null) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 1a69b2e71..e286fcc1f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -32,7 +32,7 @@ public object AllGrantPermissionService : PermissionService { override fun register( id: PermissionId, description: String, - base: PermissionId? + base: PermissionId ): PermissionImpl { val new = PermissionImpl(id, description, base) if (all.putIfAbsent(id, new) != null) { @@ -48,7 +48,7 @@ public object AllGrantPermissionService : PermissionService { override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean = true override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { @@ -64,7 +64,7 @@ public object AllDenyPermissionService : PermissionService { override fun register( id: PermissionId, description: String, - base: PermissionId? + base: PermissionId ): PermissionImpl { val new = PermissionImpl(id, description, base) if (all.putIfAbsent(id, new) != null) { @@ -80,7 +80,7 @@ public object AllDenyPermissionService : PermissionService { override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean = false override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { @@ -100,7 +100,7 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

> get() = config.grantedPermissionMap as MutableMap> - override fun createPermission(id: PermissionId, description: String, base: PermissionId?): PermissionImpl = + override fun createPermission(id: PermissionId, description: String, base: PermissionId): PermissionImpl = PermissionImpl(id, description, base) internal val config: ConcurrentSaveData = diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index c51d96dfc..2abbeae7c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -14,7 +14,9 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** - * 一个权限节点 + * 一个权限节点. + * + * 由 [PermissionService] 实现不同, [Permission] 可能会有多种实例. 但一个权限总是拥有确定的 [id] */ @ExperimentalPermission public interface Permission { @@ -29,10 +31,9 @@ public interface Permission { @ExperimentalPermission public object BasePermission : Permission { - override val id: PermissionId = PermissionId("console", "base") + override val id: PermissionId = PermissionId("*", "*") override val description: String get() = "The parent of any permission" override val parent: PermissionId get() = id - } @ConsoleExperimentalAPI diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index e3f01f193..0b88192cb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -16,6 +16,9 @@ import kotlinx.serialization.builtins.serializer import net.mamoe.mirai.console.internal.data.map +/** + * [PermissionId] 与 [Permission] 唯一对应. + */ @Serializable @ExperimentalPermission public data class PermissionId( diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 7d770db76..3aa630c3d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -43,7 +43,7 @@ public interface PermissionService

{ public fun register( id: PermissionId, description: String, - base: PermissionId? = null + base: PermissionId = BasePermission.id ): P /////////////////////////////////////////////////////////////////////////// @@ -60,6 +60,9 @@ public interface PermissionService

{ } } +internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = + PermissionId("plugin.${name.toLowerCase()}", id.toLowerCase()) + @ExperimentalPermission public fun PermissionId.findCorrespondingPermission(): Permission? = PermissionService.INSTANCE[this] diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt index 64e9058f2..c9e40ad0d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt @@ -27,7 +27,7 @@ public interface PluginDescription { public val kind: PluginKind /** - * 插件名称. 不允许存在 ":". + * 插件名称. 不允许存在 ":", 推荐全英文. * * 插件名称不能完全是以下其中一种. * - console From 72df43db27d394c33ec9ba98204a3c388481bc87 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 20:07:32 +0800 Subject: [PATCH 33/62] Cleanup --- .../mamoe/mirai/console/permission/PermissionId.kt | 12 ------------ .../mirai/console/permission/PermissionService.kt | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index 0b88192cb..f323c6591 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -9,11 +9,7 @@ package net.mamoe.mirai.console.permission -import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import net.mamoe.mirai.console.internal.data.map /** @@ -34,14 +30,6 @@ public data class PermissionId( } } - @Serializer(forClass = PermissionId::class) - public object AsClassSerializer - - public object AsStringSerializer : KSerializer by String.serializer().map( - serializer = { it.namespace + ":" + it.id }, - deserializer = ::parseFromString - ) - public override fun toString(): String { return "$namespace:$id" } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 3aa630c3d..06367334e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -60,6 +60,7 @@ public interface PermissionService

{ } } +@ExperimentalPermission internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = PermissionId("plugin.${name.toLowerCase()}", id.toLowerCase()) From 8661627607301115fa64131bdc5cf283e9e2e1fc Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:34:08 +0800 Subject: [PATCH 34/62] Introduce NotNullMap and NotNullMutableMap --- .../console/data/PluginDataExtensions.kt | 234 ++++++++++++++++-- .../internal/data/builtins/BotManagerImpl.kt | 8 +- 2 files changed, 213 insertions(+), 29 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt index 8523353bd..4681f1394 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt @@ -1,15 +1,63 @@ -@file:Suppress("unused", "INAPPLICABLE_JVM_NAME") +@file:Suppress("unused", "INAPPLICABLE_JVM_NAME", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package net.mamoe.mirai.console.data import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault import net.mamoe.mirai.console.internal.data.ShadowMap +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import kotlin.internal.LowPriorityInOverloadResolution /** * [PluginData] 相关一些扩展 */ public object PluginDataExtensions { + @ConsoleExperimentalAPI + public open class NotNullMap internal constructor( + private val delegate: Map + ) : Map by delegate { + override fun get(key: K): V = + delegate[key] ?: error("Internal error: delegate[key] returned null for NotNullMap.get") + + @Deprecated( + "getOrDefault on NotNullMap always returns the value in the map, and defaultValue will never be returned.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.get(key)") + ) + override fun getOrDefault(key: K, defaultValue: V): V { + return super.getOrDefault(key, defaultValue) + } + } + + @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") // as designed + public class NotNullMutableMap internal constructor( + private val delegate: MutableMap + ) : MutableMap by delegate, NotNullMap(delegate) { + override fun get(key: K): V = + delegate[key] ?: error("Internal error: delegate[key] returned null for NotNullMutableMap.get") + + @Deprecated( + "getOrDefault on NotNullMutableMap always returns the value in the map, and defaultValue will never be returned.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.get(key)") + ) + override fun getOrDefault(key: K, defaultValue: V): V { + return super.getOrDefault(key, defaultValue) + } + + override fun put(key: K, value: V): V { + return delegate.put(key, value) + ?: error("Internal error: delegate.put(key, value) returned null for NotNullMutableMap.put") + } + + @Deprecated( + "putIfAbsent on NotNullMutableMap always does nothing.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("") + ) + override fun putIfAbsent(key: K, value: V): Nothing? = null + } + /** * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先放入一个 [LinkedHashMap], 再从 [this] 中取出链接自动保存的 [LinkedHashMap]. ([MutableMap.getOrPut] 的替代) * @@ -17,7 +65,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultMapImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashMap() } } @@ -27,7 +75,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultMap") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashMap() } } @@ -38,7 +86,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultListImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { ArrayList() } } @@ -48,7 +96,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultList") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { ArrayList() } } @@ -59,7 +107,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultSetImmutable") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashSet() } } @@ -69,7 +117,7 @@ public object PluginDataExtensions { */ @JvmName("withEmptyDefaultSet") @JvmStatic - public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { + public fun SerializerAwareValue>>.withEmptyDefault(): SerializerAwareValue>> { return this.withDefault { LinkedHashSet() } } @@ -78,15 +126,47 @@ public object PluginDataExtensions { * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 */ @JvmStatic + @JvmName("withDefaultMapImmutableNotNull") + public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { + @Suppress("UNCHECKED_CAST") // magic + return (this as SerializerAwareValue>).withDefault(defaultValueComputer) as SerializerAwareValue> + } + + /** + * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 + */ + @JvmStatic + @LowPriorityInOverloadResolution @JvmName("withDefaultMapImmutable") public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { @Suppress("UNCHECKED_CAST") // magic return (this as SerializerAwareValue>).withDefault(defaultValueComputer) as SerializerAwareValue> } + @JvmStatic + @JvmName("withDefaultMapNotNull") + public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + private val instance = NotNullMutableMap(createDelegateInstance(origin, defaultValueComputer)) + + override var value: Map + get() = instance + set(value) { + origin.value = value as MutableMap // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } + /** * 创建一个代理对象, 当 [Map.get] 返回 `null` 时先调用 [defaultValueComputer] 并放入 [Map], 再返回调用的返回值 */ + @LowPriorityInOverloadResolution @JvmStatic @JvmName("withDefaultMap") public fun SerializerAwareValue>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue> { @@ -95,24 +175,7 @@ public object PluginDataExtensions { @Suppress("UNCHECKED_CAST") return SerializableValue( object : CompositeMapValue { - private val instance = object : MutableMap, AbstractMap() { - override val entries: MutableSet> get() = origin.value.entries - override val keys: MutableSet get() = origin.value.keys - override val values: MutableCollection get() = origin.value.values - override fun clear() = origin.value.clear() - override fun putAll(from: Map) = origin.value.putAll(from) - override fun remove(key: K): V? = origin.value.remove(key) - override fun put(key: K, value: V): V? = origin.value.put(key, value) - - override fun get(key: K): V? { - // the only difference - val result = origin.value[key] - if (result != null) return result - put(key, defaultValueComputer(key)) - return origin.value[key] - } - } - + private val instance = createDelegateInstance(origin, defaultValueComputer) override var value: Map get() = instance set(value) { @@ -123,6 +186,57 @@ public object PluginDataExtensions { ) } + private fun createDelegateInstance( + origin: SerializerAwareValue>, + defaultValueComputer: (K) -> V + ): MutableMap { + return object : MutableMap, AbstractMap() { + override val entries: MutableSet> get() = origin.value.entries + override val keys: MutableSet get() = origin.value.keys + override val values: MutableCollection get() = origin.value.values + override fun clear() = origin.value.clear() + override fun putAll(from: Map) = origin.value.putAll(from) + override fun remove(key: K): V? = origin.value.remove(key) + override fun put(key: K, value: V): V? = origin.value.put(key, value) + + override fun get(key: K): V? { + // the only difference + val result = origin.value[key] + if (result != null) return result + put(key, defaultValueComputer(key)) + return origin.value[key] + } + } + } + + + /** + * 替换 [MutableMap] 的 key + */ + @JvmName("mapKeysNotNull") + @JvmStatic + public fun SerializerAwareValue>.mapKeys( + oldToNew: (OldK) -> NewK, + newToOld: (NewK) -> OldK, + ): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + private val instance = + NotNullMutableMap(ShadowMap({ origin.value }, oldToNew, newToOld, { it }, { it })) + + override var value: Map + get() = instance + set(value) { + origin.value = + value.mapKeysTo(NotNullMutableMap(LinkedHashMap())) { it.key.let(newToOld) } // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } /** * 替换 [MutableMap] 的 key @@ -177,4 +291,72 @@ public object PluginDataExtensions { this.serializer ) } -} \ No newline at end of file + + /** + * 替换 [Map] 的 key + */ + @JvmName("mapKeysImmutableNotNull") + @JvmStatic + public fun SerializerAwareValue>.mapKeys( + oldToNew: (OldK) -> NewK, + newToOld: (NewK) -> OldK, + ): SerializerAwareValue> { + val origin = this + + @Suppress("UNCHECKED_CAST") + return SerializableValue( + object : CompositeMapValue { + // casting Map to MutableMap is OK here, as we don't call mutable functions + private val instance = + NotNullMap(ShadowMap({ origin.value as MutableMap }, oldToNew, newToOld, { it }, { it })) + + override var value: Map + get() = instance + set(value) { + origin.value = + value.mapKeysTo(NotNullMutableMap(LinkedHashMap())) { it.key.let(newToOld) } // erased cast + } + } as Value>, // erased cast + this.serializer + ) + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt index 0cdf45731..3abac0c9a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt @@ -44,8 +44,10 @@ internal object ManagersConfig : AutoSavePluginConfig() { 管理员列表 """ ) - private val managers by value>>().withEmptyDefault() - .mapKeys(Bot::getInstance, Bot::id) + private val managers + by value>>() + .withEmptyDefault() + .mapKeys(Bot::getInstance, Bot::id) - internal operator fun get(bot: Bot): MutableSet = managers[bot]!! + internal operator fun get(bot: Bot): MutableSet = managers[bot] } \ No newline at end of file From 8d115cd9541bb8630c6ca3a774ee9701dcc8218a Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:41:20 +0800 Subject: [PATCH 35/62] Don't infix for grantedWith --- .../console/permission/AbstractConcurrentPermissionService.kt | 2 +- .../net/mamoe/mirai/console/permission/PermissibleIdentifier.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index 1e0085e29..933b24a9d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -49,7 +49,7 @@ public abstract class AbstractConcurrentPermissionService

: Perm val granted = if (permissibleIdentifiers.isEmpty()) false - else permissibleIdentifiers.any { permissibleIdentifier grantedWith it } + else permissibleIdentifiers.any { permissibleIdentifier.grantedWith(it) } if (granted) get(permissionIdentifier)?.let { yield(it) } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index ce9360f1d..a39cfb3ea 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -17,7 +17,7 @@ public interface PermissibleIdentifier { public companion object { @ExperimentalPermission - public infix fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean { + public fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean { return allParentsWithSelf().any { it == with } } From c622c1b4fe27ab3d24945b6f795c8ca249853b6f Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:42:38 +0800 Subject: [PATCH 36/62] Fix NotNullMutableMap --- .../net/mamoe/mirai/console/data/PluginDataExtensions.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt index 4681f1394..57437271c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt @@ -45,11 +45,6 @@ public object PluginDataExtensions { return super.getOrDefault(key, defaultValue) } - override fun put(key: K, value: V): V { - return delegate.put(key, value) - ?: error("Internal error: delegate.put(key, value) returned null for NotNullMutableMap.put") - } - @Deprecated( "putIfAbsent on NotNullMutableMap always does nothing.", level = DeprecationLevel.WARNING, From 6cdc85facd12f4c818303f5cf4b4df542f265292 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:45:12 +0800 Subject: [PATCH 37/62] Fix permissionType checks --- .../net/mamoe/mirai/console/permission/PermissionService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 06367334e..3b5bef9f9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -14,6 +14,7 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.extension.SingletonExtensionPoint.Companion.findSingleton import net.mamoe.mirai.console.extensions.PermissionServiceProvider import kotlin.reflect.KClass +import kotlin.reflect.full.isSuperclassOf /** * [PermissionServiceProvider] @@ -128,7 +129,7 @@ public fun PermissionId.testPermission(permissible: PermissibleIdentifier): Bool @OptIn(ExperimentalPermission::class) internal fun PermissionService<*>.checkType(permissionType: KClass): PermissionService { return PermissionService.INSTANCE.run { - require(permissionType.isInstance(this@checkType)) { + require(this.permissionType.isSuperclassOf(permissionType)) { "Custom-constructed Permission instance is not allowed. " + "Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get" } From 1abb0204a2daa3962315a4b817aad98b6bffd56c Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:53:10 +0800 Subject: [PATCH 38/62] Update docs --- .../mirai/console/permission/ExperimentalPermission.kt | 4 +++- .../mirai/console/permission/PermissionService.kt | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt index 479bf5d9d..4e0252a7e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/ExperimentalPermission.kt @@ -12,7 +12,9 @@ package net.mamoe.mirai.console.permission import kotlin.annotation.AnnotationTarget.* /** - * 标记一个实验性的权限系统 API + * 标记一个实验性的权限系统 API. + * + * 权限系统是在 1.0-M4 引入的一个实验性系统, 目前不具有 API 稳定性. */ @Retention(AnnotationRetention.BINARY) @RequiresOptIn(level = RequiresOptIn.Level.WARNING) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 3b5bef9f9..f7b4e478b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -30,7 +30,8 @@ public interface PermissionService

{ public fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

- public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean { + public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: P): Boolean { + val permissionId = permission.id val all = this[permissionId]?.parentsWithSelfSequence() ?: return false return getGrantedPermissions(permissibleIdentifier).any { p -> all.any { p.id == it.id } @@ -61,6 +62,11 @@ public interface PermissionService

{ } } +@ExperimentalPermission +public fun

PermissionService

.getOrFail(id: PermissionId): P { + +} + @ExperimentalPermission internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = PermissionId("plugin.${name.toLowerCase()}", id.toLowerCase()) @@ -85,7 +91,7 @@ public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean @ExperimentalPermission public fun PermissibleIdentifier.hasPermission(permissionId: PermissionId): Boolean = (PermissionService.INSTANCE as PermissionService).run { - testPermission(this@hasPermission, permissionId) + testPermission(this@hasPermission, get(permissionId) ?: PermissionNotFoundException("")) } @ExperimentalPermission From a7777d008a78aa30dcf9f9db5197060bf4d6b723 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 21:55:48 +0800 Subject: [PATCH 39/62] Change parameter `permissionId: PermissionId` to `permission: P` for PermissionService.testPermission; Add PermissionService

.getOrFail --- .../permission/BuiltInPermissionServices.kt | 4 ++-- .../permission/PermissionNotFoundException.kt | 15 +++++++++++++++ .../mirai/console/permission/PermissionService.kt | 14 ++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionNotFoundException.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index e286fcc1f..28a7d421e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -48,7 +48,7 @@ public object AllGrantPermissionService : PermissionService { override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean = + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = true override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { @@ -80,7 +80,7 @@ public object AllDenyPermissionService : PermissionService { override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { } - override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permissionId: PermissionId): Boolean = + override fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl): Boolean = false override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionNotFoundException.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionNotFoundException.kt new file mode 100644 index 000000000..e005fd0aa --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionNotFoundException.kt @@ -0,0 +1,15 @@ +/* + * 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.permission + +@ExperimentalPermission +public open class PermissionNotFoundException public constructor( + public val id: PermissionId +) : Exception(id.toString()) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index f7b4e478b..394138bad 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -63,9 +63,8 @@ public interface PermissionService

{ } @ExperimentalPermission -public fun

PermissionService

.getOrFail(id: PermissionId): P { - -} +public fun

PermissionService

.getOrFail(id: PermissionId): P = + get(id) ?: throw PermissionNotFoundException(id) @ExperimentalPermission internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = @@ -74,6 +73,9 @@ internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id @ExperimentalPermission public fun PermissionId.findCorrespondingPermission(): Permission? = PermissionService.INSTANCE[this] +@ExperimentalPermission +public fun PermissionId.findCorrespondingPermissionOrFail(): Permission = PermissionService.INSTANCE.getOrFail(this) + @ExperimentalPermission public fun PermissibleIdentifier.grant(permission: Permission) { PermissionService.INSTANCE.checkType(permission::class).grant(this, permission) @@ -91,7 +93,7 @@ public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean @ExperimentalPermission public fun PermissibleIdentifier.hasPermission(permissionId: PermissionId): Boolean = (PermissionService.INSTANCE as PermissionService).run { - testPermission(this@hasPermission, get(permissionId) ?: PermissionNotFoundException("")) + testPermission(this@hasPermission, getOrFail(permissionId)) } @ExperimentalPermission @@ -111,12 +113,12 @@ public fun PermissibleIdentifier.getGrantedPermissions(): Sequence = @JvmSynthetic @ExperimentalPermission public fun Permission.testPermission(permissible: Permissible): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission.id) + PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission) @JvmSynthetic @ExperimentalPermission public fun Permission.testPermission(permissibleIdentifier: PermissibleIdentifier): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission.id) + PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission) @JvmSynthetic @ExperimentalPermission From d3e3db96626229e26abad2d6d0f78d9bb4dae9a1 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:00:06 +0800 Subject: [PATCH 40/62] (internal) Use MutableList for grantedPermissionMap --- .../mirai/console/permission/BuiltInPermissionServices.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 28a7d421e..7cd656046 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -129,8 +129,8 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

> - by value>>(ConcurrentHashMap()) + public val grantedPermissionMap: MutableMap> + by value>>(ConcurrentHashMap()) .withDefault { CopyOnWriteArrayList() } public companion object { From 703c4feae183067b033b694fec46423482569163 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:00:25 +0800 Subject: [PATCH 41/62] Add missing opt-in annotations --- .../kotlin/net/mamoe/mirai/console/command/Command.kt | 2 ++ .../net/mamoe/mirai/console/command/CommandOwner.kt | 3 +++ .../net/mamoe/mirai/console/command/CompositeCommand.kt | 2 +- .../kotlin/net/mamoe/mirai/console/command/RawCommand.kt | 2 +- .../net/mamoe/mirai/console/command/SimpleCommand.kt | 3 ++- .../mirai/console/command/java/JCompositeCommand.kt | 3 ++- .../net/mamoe/mirai/console/command/java/JRawCommand.kt | 5 ++++- .../mamoe/mirai/console/command/java/JSimpleCommand.kt | 5 ++++- .../console/internal/command/CompositeCommandInternal.kt | 9 ++++++--- 9 files changed, 25 insertions(+), 9 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 237376a1b..cf757ee70 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -55,6 +55,7 @@ public interface Command { /** * 指令权限 */ + @ExperimentalPermission public val permission: Permission /** @@ -100,6 +101,7 @@ public suspend inline fun Command.onCommand(sender: CommandSender, args: Message * @see RawCommand */ public abstract class AbstractCommand +@OptIn(ExperimentalPermission::class) @JvmOverloads constructor( /** 指令拥有者. */ public override val owner: CommandOwner, diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt index ba6e0d6cf..1482c2d3f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt @@ -24,12 +24,14 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin * * @see JvmPlugin 是一个 [CommandOwner] */ +@OptIn(ExperimentalPermission::class) public interface CommandOwner : PermissionIdNamespace { /** * 此 [PermissionIdNamespace] 拥有的指令都默认将 [basePermission] 作为父权限. * * TODO document */ + @ExperimentalPermission public val basePermission: PermissionId } @@ -37,6 +39,7 @@ public interface CommandOwner : PermissionIdNamespace { * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. */ internal object ConsoleCommandOwner : CommandOwner { + @ExperimentalPermission override val basePermission: PermissionId get() = BasePermission.id @ExperimentalPermission diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt index 6689d0712..02c8fc910 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt @@ -82,7 +82,7 @@ import kotlin.annotation.AnnotationTarget.FUNCTION * @see buildCommandArgumentContext */ @ConsoleExperimentalAPI -public abstract class CompositeCommand( +public abstract class CompositeCommand @OptIn(ExperimentalPermission::class) constructor( owner: CommandOwner, vararg names: String, description: String = "no description available", diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt index a1ad43cc0..47ac57558 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt @@ -31,7 +31,7 @@ import net.mamoe.mirai.message.data.MessageChain * @see SimpleCommand 简单指令 * @see CompositeCommand 复合指令 */ -public abstract class RawCommand( +public abstract class RawCommand @OptIn(ExperimentalPermission::class) constructor( /** * 指令拥有者. * @see CommandOwner diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt index cafad6148..29cd1e791 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt @@ -22,6 +22,7 @@ import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.java.JSimpleCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver +import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain @@ -48,7 +49,7 @@ import net.mamoe.mirai.message.data.MessageChain * @see JSimpleCommand Java 实现 * @see [CommandManager.executeCommand] */ -public abstract class SimpleCommand( +public abstract class SimpleCommand @OptIn(ExperimentalPermission::class) constructor( owner: CommandOwner, vararg names: String, description: String = "no description available", diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt index 2b1dbb076..2e190d600 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JCompositeCommand.kt @@ -69,7 +69,8 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI * @see buildCommandArgumentContext */ @ConsoleExperimentalAPI -public abstract class JCompositeCommand @JvmOverloads constructor( +public abstract class JCompositeCommand @OptIn(ExperimentalPermission::class) +@JvmOverloads constructor( owner: CommandOwner, vararg names: String, parentPermission: PermissionId = owner.basePermission, diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt index ec9f894ca..9074cc170 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JRawCommand.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.withContext import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.internal.command.createCommandPermission +import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.message.data.MessageChain @@ -45,7 +46,8 @@ import net.mamoe.mirai.message.data.SingleMessage * * @see JRawCommand */ -public abstract class JRawCommand @JvmOverloads constructor( +public abstract class JRawCommand @OptIn(ExperimentalPermission::class) +@JvmOverloads constructor( /** * 指令拥有者. * @see CommandOwner @@ -64,6 +66,7 @@ public abstract class JRawCommand @JvmOverloads constructor( protected set /** 指令权限 */ + @ExperimentalPermission public final override var permission: Permission = createCommandPermission(parentPermission) protected set diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt index 2f6ce1e4d..8a830713b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/java/JSimpleCommand.kt @@ -14,6 +14,7 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandOwner import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.command.description.CommandArgumentContext +import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.PermissionId @@ -40,13 +41,15 @@ import net.mamoe.mirai.console.permission.PermissionId * @see SimpleCommand * @see [CommandManager.executeCommand] */ -public abstract class JSimpleCommand( +public abstract class JSimpleCommand @OptIn(ExperimentalPermission::class) constructor( owner: CommandOwner, vararg names: String, basePermission: PermissionId, ) : SimpleCommand(owner, *names, basePermission = basePermission) { public override var description: String = super.description protected set + + @ExperimentalPermission public override var permission: Permission = super.permission protected set public override var prefixOptional: Boolean = super.prefixOptional diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index 6e5564b7a..c0d289edc 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -44,7 +44,8 @@ internal object SimpleCommandSubCommandAnnotationResolver : baseCommand.names } -internal abstract class AbstractReflectionCommand @JvmOverloads constructor( +internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission::class) +@JvmOverloads constructor( owner: CommandOwner, names: Array, description: String = "", @@ -117,13 +118,13 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( } } - internal class DefaultSubCommandDescriptor( + internal class DefaultSubCommandDescriptor @OptIn(ExperimentalPermission::class) constructor( val description: String, val permission: Permission, val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit ) - internal inner class SubCommandDescriptor( + internal inner class SubCommandDescriptor @OptIn(ExperimentalPermission::class) constructor( val names: Array, val params: Array>, val description: String, @@ -132,6 +133,8 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor( val context: CommandArgumentContext ) { val usage: String = createUsage(this@AbstractReflectionCommand) + + @OptIn(ExperimentalPermission::class) internal suspend fun parseAndExecute( sender: CommandSender, argsWithSubCommandNameNotRemoved: MessageChain, From 39ef0073700fb6b968411fe68a45c8a77c8bdaee Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:06:09 +0800 Subject: [PATCH 42/62] Review: Add duplication details for DuplicatedPermissionRegistrationException --- .../AbstractConcurrentPermissionService.kt | 8 +++++--- .../console/permission/BuiltInPermissionServices.kt | 10 ++++------ .../DuplicatedPermissionRegistrationException.kt | 12 +++++------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index 933b24a9d..5bfd5f14d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -12,6 +12,9 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith import java.util.concurrent.CopyOnWriteArrayList +/** + * + */ @ExperimentalPermission public abstract class AbstractConcurrentPermissionService

: PermissionService

{ protected abstract val permissions: MutableMap @@ -28,9 +31,8 @@ public abstract class AbstractConcurrentPermissionService

: Perm override fun register(id: PermissionId, description: String, base: PermissionId): P { grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed val instance = createPermission(id, description, base) - if (permissions.putIfAbsent(id, instance) != null) { - throw DuplicatedPermissionRegistrationException("Duplicated Permission registry. new: $instance, old: ${permissions[id]}") - } + val old = permissions.putIfAbsent(id, instance) + if (old != null) throw DuplicatedPermissionRegistrationException(instance, old) return instance } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 7cd656046..d873ffcc9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -35,9 +35,8 @@ public object AllGrantPermissionService : PermissionService { base: PermissionId ): PermissionImpl { val new = PermissionImpl(id, description, base) - if (all.putIfAbsent(id, new) != null) { - throw DuplicatedPermissionRegistrationException("Duplicated Permission registry: ${all[id]}") - } + val old = all.putIfAbsent(id, new) + if (old != null) throw DuplicatedPermissionRegistrationException(new, old) return new } @@ -67,9 +66,8 @@ public object AllDenyPermissionService : PermissionService { base: PermissionId ): PermissionImpl { val new = PermissionImpl(id, description, base) - if (all.putIfAbsent(id, new) != null) { - throw DuplicatedPermissionRegistrationException("Duplicated Permission registry: ${all[id]}") - } + val old = all.putIfAbsent(id, new) + if (old != null) throw DuplicatedPermissionRegistrationException(new, old) return new } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt index b521e1703..39cbb2807 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/DuplicatedPermissionRegistrationException.kt @@ -7,14 +7,12 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("unused") +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "CanBeParameter") package net.mamoe.mirai.console.permission @ExperimentalPermission -public open class DuplicatedPermissionRegistrationException : Exception { - public constructor() : super() - public constructor(message: String?) : super(message) - public constructor(message: String?, cause: Throwable?) : super(message, cause) - public constructor(cause: Throwable?) : super(cause) -} \ No newline at end of file +public class DuplicatedPermissionRegistrationException( + newInstance: Permission, + public val existingInstance: Permission +) : Exception("Duplicated Permission registry. new: $newInstance, existing: $existingInstance") \ No newline at end of file From 6429e400f1c80d1b4fcde44cf235f5a1217f2a20 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:08:15 +0800 Subject: [PATCH 43/62] Rename BasePermission to AncestorPermission --- .../kotlin/net/mamoe/mirai/console/command/CommandOwner.kt | 5 +++-- .../permission/AbstractConcurrentPermissionService.kt | 2 +- .../kotlin/net/mamoe/mirai/console/permission/Permission.kt | 4 ++-- .../net/mamoe/mirai/console/permission/PermissionService.kt | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt index 1482c2d3f..c75d6de2e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt @@ -10,7 +10,7 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands -import net.mamoe.mirai.console.permission.BasePermission +import net.mamoe.mirai.console.permission.AncestorPermission import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionIdNamespace @@ -40,7 +40,8 @@ public interface CommandOwner : PermissionIdNamespace { */ internal object ConsoleCommandOwner : CommandOwner { @ExperimentalPermission - override val basePermission: PermissionId get() = BasePermission.id + override val basePermission: PermissionId + get() = AncestorPermission.id @ExperimentalPermission override fun permissionId(id: String): PermissionId = PermissionId("console", id) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index 5bfd5f14d..8b3ccd7c6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -23,7 +23,7 @@ public abstract class AbstractConcurrentPermissionService

: Perm protected abstract fun createPermission( id: PermissionId, description: String, - base: PermissionId = BasePermission.id + base: PermissionId = AncestorPermission.id ): P override fun get(id: PermissionId): P? = permissions[id] diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 2abbeae7c..3385f4cc9 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -29,7 +29,7 @@ public interface Permission { * 所有权限的父权限. */ @ExperimentalPermission -public object BasePermission : +public object AncestorPermission : Permission { override val id: PermissionId = PermissionId("*", "*") override val description: String get() = "The parent of any permission" @@ -52,5 +52,5 @@ public fun Permission.parentsWithSelfSequence(): Sequence = public class PermissionImpl( override val id: PermissionId, override val description: String, - override val parent: PermissionId = BasePermission.id + override val parent: PermissionId = AncestorPermission.id ) : Permission \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 394138bad..7b16a1c2e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -45,7 +45,7 @@ public interface PermissionService

{ public fun register( id: PermissionId, description: String, - base: PermissionId = BasePermission.id + base: PermissionId = AncestorPermission.id ): P /////////////////////////////////////////////////////////////////////////// From a6109bb3010baeb7e9917c6a392932f41b582036 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:08:50 +0800 Subject: [PATCH 44/62] Rename Permission.parent to Permission.parentId --- .../net/mamoe/mirai/console/permission/Permission.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 3385f4cc9..afdd1bc81 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -22,7 +22,7 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI public interface Permission { public val id: PermissionId public val description: String - public val parent: PermissionId + public val parentId: PermissionId } /** @@ -33,15 +33,14 @@ public object AncestorPermission : Permission { override val id: PermissionId = PermissionId("*", "*") override val description: String get() = "The parent of any permission" - override val parent: PermissionId get() = id + override val parentId: PermissionId get() = id } @ConsoleExperimentalAPI @ExperimentalPermission public fun Permission.parentsWithSelfSequence(): Sequence = generateSequence(this) { p -> - p.parent.let { PermissionService.INSTANCE[it] } - ?.takeIf { parent -> parent != p } + p.parentId.findCorrespondingPermission()?.takeIf { parent -> parent != p } } /** @@ -52,5 +51,5 @@ public fun Permission.parentsWithSelfSequence(): Sequence = public class PermissionImpl( override val id: PermissionId, override val description: String, - override val parent: PermissionId = AncestorPermission.id + override val parentId: PermissionId = AncestorPermission.id ) : Permission \ No newline at end of file From 4822a8c5fefb3aa69d2c9ff5676e0237a8158721 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:09:37 +0800 Subject: [PATCH 45/62] Rename AncestorPermission to RootPermission --- .../kotlin/net/mamoe/mirai/console/command/CommandOwner.kt | 4 ++-- .../console/permission/AbstractConcurrentPermissionService.kt | 2 +- .../kotlin/net/mamoe/mirai/console/permission/Permission.kt | 4 ++-- .../net/mamoe/mirai/console/permission/PermissionService.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt index c75d6de2e..a8682841d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandOwner.kt @@ -10,10 +10,10 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands -import net.mamoe.mirai.console.permission.AncestorPermission import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionIdNamespace +import net.mamoe.mirai.console.permission.RootPermission import net.mamoe.mirai.console.plugin.jvm.JvmPlugin /** @@ -41,7 +41,7 @@ public interface CommandOwner : PermissionIdNamespace { internal object ConsoleCommandOwner : CommandOwner { @ExperimentalPermission override val basePermission: PermissionId - get() = AncestorPermission.id + get() = RootPermission.id @ExperimentalPermission override fun permissionId(id: String): PermissionId = PermissionId("console", id) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index 8b3ccd7c6..f76a769ef 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -23,7 +23,7 @@ public abstract class AbstractConcurrentPermissionService

: Perm protected abstract fun createPermission( id: PermissionId, description: String, - base: PermissionId = AncestorPermission.id + base: PermissionId = RootPermission.id ): P override fun get(id: PermissionId): P? = permissions[id] diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index afdd1bc81..81c34e43e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -29,7 +29,7 @@ public interface Permission { * 所有权限的父权限. */ @ExperimentalPermission -public object AncestorPermission : +public object RootPermission : Permission { override val id: PermissionId = PermissionId("*", "*") override val description: String get() = "The parent of any permission" @@ -51,5 +51,5 @@ public fun Permission.parentsWithSelfSequence(): Sequence = public class PermissionImpl( override val id: PermissionId, override val description: String, - override val parentId: PermissionId = AncestorPermission.id + override val parentId: PermissionId = RootPermission.id ) : Permission \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index 7b16a1c2e..e4c87d146 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -45,7 +45,7 @@ public interface PermissionService

{ public fun register( id: PermissionId, description: String, - base: PermissionId = AncestorPermission.id + base: PermissionId = RootPermission.id ): P /////////////////////////////////////////////////////////////////////////// From 6ec3390b8dbf615adc404c39251bc18580570da7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:20:18 +0800 Subject: [PATCH 46/62] Move extensions into PermissionService.Companion --- .../MiraiConsoleImplementationBridge.kt | 5 + .../command/CompositeCommandInternal.kt | 6 +- .../console/internal/command/internal.kt | 6 +- .../internal/plugin/JvmPluginInternal.kt | 2 +- .../mirai/console/permission/Permission.kt | 1 + .../console/permission/PermissionService.kt | 138 ++++++++---------- 6 files changed, 81 insertions(+), 77 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index 59108b740..a604cc13d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -25,6 +25,7 @@ import net.mamoe.mirai.console.MiraiConsoleImplementation import net.mamoe.mirai.console.command.BuiltInCommands import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.CommandManager +import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.extension.useExtensions import net.mamoe.mirai.console.extensions.PostStartupExtension @@ -36,6 +37,8 @@ import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.PermissionService.Companion.grantPermission +import net.mamoe.mirai.console.permission.RootPermission import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter @@ -150,6 +153,8 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.verbose { "Reloaded PermissionService settings." } } } + + ConsoleCommandSender.grantPermission(RootPermission) } phase `prepare commands`@{ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index c0d289edc..4251131ed 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -16,7 +16,11 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip -import net.mamoe.mirai.console.permission.* +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId +import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.PermissionService.Companion.testPermission import net.mamoe.mirai.message.data.* import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KClass diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt index c6a2bc374..1958f7c61 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt @@ -11,7 +11,11 @@ package net.mamoe.mirai.console.internal.command import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.Command.Companion.primaryName -import net.mamoe.mirai.console.permission.* +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.Permission +import net.mamoe.mirai.console.permission.PermissionId +import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.PermissionService.Companion.testPermission import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member import net.mamoe.mirai.message.data.MessageChain diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt index e200e05b8..061bf6a15 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt @@ -18,7 +18,7 @@ import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionService -import net.mamoe.mirai.console.permission.allocatePermissionIdForPlugin +import net.mamoe.mirai.console.permission.PermissionService.Companion.allocatePermissionIdForPlugin import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index 81c34e43e..ef1e8a212 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.console.permission import kotlinx.serialization.Serializable +import net.mamoe.mirai.console.permission.PermissionService.Companion.findCorrespondingPermission import net.mamoe.mirai.console.util.ConsoleExperimentalAPI diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index e4c87d146..d517392bf 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -7,7 +7,7 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("NOTHING_TO_INLINE", "unused") +@file:Suppress("NOTHING_TO_INLINE", "unused", "MemberVisibilityCanBePrivate") package net.mamoe.mirai.console.permission @@ -59,86 +59,76 @@ public interface PermissionService

{ public val INSTANCE: PermissionService by lazy { PermissionServiceProvider.findSingleton()?.instance ?: BuiltInPermissionService } + + public fun

PermissionService

.getOrFail(id: PermissionId): P = + get(id) ?: throw PermissionNotFoundException(id) + + internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = + PermissionId("plugin.${name.toLowerCase()}", id.toLowerCase()) + + public fun PermissionId.findCorrespondingPermission(): Permission? = INSTANCE[this] + + public fun PermissionId.findCorrespondingPermissionOrFail(): Permission = INSTANCE.getOrFail(this) + + public fun PermissibleIdentifier.grantPermission(permission: Permission) { + INSTANCE.checkType(permission::class).grant(this, permission) + } + + public fun Permissible.hasPermission(permission: Permission): Boolean = + permission.testPermission(this@hasPermission) + + public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean = + permission.testPermission(this@hasPermission) + + public fun PermissibleIdentifier.hasPermission(permissionId: PermissionId): Boolean { + val instance = permissionId.findCorrespondingPermissionOrFail() + return INSTANCE.checkType(instance::class).testPermission(this@hasPermission, instance) + } + + public fun Permissible.hasPermission(permissionId: PermissionId): Boolean = + permissionId.testPermission(this@hasPermission) + + public fun Permissible.getGrantedPermissions(): Sequence = + INSTANCE.getGrantedPermissions(this@getGrantedPermissions.identifier) + + public fun Permissible.grantPermission(vararg permissions: Permission) { + for (permission in permissions) { + INSTANCE.checkType(permission::class).grant(this.identifier, permission) + } + } + + public fun Permissible.denyPermission(vararg permissions: Permission) { + for (permission in permissions) { + INSTANCE.checkType(permission::class).deny(this.identifier, permission) + } + } + + public fun PermissibleIdentifier.getGrantedPermissions(): Sequence = + INSTANCE.getGrantedPermissions(this@getGrantedPermissions) + + public fun Permission.testPermission(permissible: Permissible): Boolean = + INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission) + + public fun Permission.testPermission(permissibleIdentifier: PermissibleIdentifier): Boolean = + INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission) + + public fun PermissionId.testPermission(permissible: Permissible): Boolean { + val p = INSTANCE[this] ?: return false + return p.testPermission(permissible) + } + + public fun PermissionId.testPermission(permissible: PermissibleIdentifier): Boolean { + val p = INSTANCE[this] ?: return false + return p.testPermission(permissible) + } } } -@ExperimentalPermission -public fun

PermissionService

.getOrFail(id: PermissionId): P = - get(id) ?: throw PermissionNotFoundException(id) - -@ExperimentalPermission -internal fun PermissionService<*>.allocatePermissionIdForPlugin(name: String, id: String) = - PermissionId("plugin.${name.toLowerCase()}", id.toLowerCase()) - -@ExperimentalPermission -public fun PermissionId.findCorrespondingPermission(): Permission? = PermissionService.INSTANCE[this] - -@ExperimentalPermission -public fun PermissionId.findCorrespondingPermissionOrFail(): Permission = PermissionService.INSTANCE.getOrFail(this) - -@ExperimentalPermission -public fun PermissibleIdentifier.grant(permission: Permission) { - PermissionService.INSTANCE.checkType(permission::class).grant(this, permission) -} - -@ExperimentalPermission -public fun Permissible.hasPermission(permission: Permission): Boolean = - permission.testPermission(this@hasPermission) - -@ExperimentalPermission -public fun PermissibleIdentifier.hasPermission(permission: Permission): Boolean = - permission.testPermission(this@hasPermission) - -@Suppress("UNCHECKED_CAST") -@ExperimentalPermission -public fun PermissibleIdentifier.hasPermission(permissionId: PermissionId): Boolean = - (PermissionService.INSTANCE as PermissionService).run { - testPermission(this@hasPermission, getOrFail(permissionId)) - } - -@ExperimentalPermission -public fun Permissible.hasPermission(permissionId: PermissionId): Boolean = - permissionId.testPermission(this@hasPermission) - -@JvmSynthetic -@ExperimentalPermission -public fun Permissible.getGrantedPermissions(): Sequence = - PermissionService.INSTANCE.getGrantedPermissions(this@getGrantedPermissions.identifier) - -@JvmSynthetic -@ExperimentalPermission -public fun PermissibleIdentifier.getGrantedPermissions(): Sequence = - PermissionService.INSTANCE.getGrantedPermissions(this@getGrantedPermissions) - -@JvmSynthetic -@ExperimentalPermission -public fun Permission.testPermission(permissible: Permissible): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissible.identifier, this@testPermission) - -@JvmSynthetic -@ExperimentalPermission -public fun Permission.testPermission(permissibleIdentifier: PermissibleIdentifier): Boolean = - PermissionService.INSTANCE.checkType(this::class).testPermission(permissibleIdentifier, this@testPermission) - -@JvmSynthetic -@ExperimentalPermission -public fun PermissionId.testPermission(permissible: Permissible): Boolean { - val p = PermissionService.INSTANCE[this] ?: return false - return p.testPermission(permissible) -} - -@JvmSynthetic -@ExperimentalPermission -public fun PermissionId.testPermission(permissible: PermissibleIdentifier): Boolean { - val p = PermissionService.INSTANCE[this] ?: return false - return p.testPermission(permissible) -} - @OptIn(ExperimentalPermission::class) internal fun PermissionService<*>.checkType(permissionType: KClass): PermissionService { return PermissionService.INSTANCE.run { require(this.permissionType.isSuperclassOf(permissionType)) { - "Custom-constructed Permission instance is not allowed. " + + "Custom-constructed Permission instance is not allowed (Required ${this.permissionType}, found ${permissionType}. " + "Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get" } From a6bd7b7d637a10dfa456669cb7d8be8070238554 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:49:48 +0800 Subject: [PATCH 47/62] Fix data comparison, fix commands --- .../console/data/PluginDataExtensions.kt | 2 +- .../net/mamoe/mirai/console/data/Value.kt | 20 +++++++++++++++++++ .../internal/data/_PluginData.value.kt | 17 ++++++++++++++++ .../mirai/console/permission/Permission.kt | 19 ++++++++++++------ .../mirai/console/command/TestCommand.kt | 4 ++-- .../permission/PermissionsBasicsTest.kt | 11 ++++++++++ 6 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/permission/PermissionsBasicsTest.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt index 57437271c..3a610c6f5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginDataExtensions.kt @@ -183,7 +183,7 @@ public object PluginDataExtensions { private fun createDelegateInstance( origin: SerializerAwareValue>, - defaultValueComputer: (K) -> V + defaultValueComputer: (K) -> V, ): MutableMap { return object : MutableMap, AbstractMap() { override val entries: MutableSet> get() = origin.value.entries diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/Value.kt index c5ccbcd3b..95ff1850a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/Value.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/Value.kt @@ -58,6 +58,26 @@ public class SerializableValue( ) : Value by delegate, SerializerAwareValue { public override fun toString(): String = delegate.toString() + public override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other?.javaClass != this.javaClass) return false + + @Suppress("UNCHECKED_CAST") + other as SerializableValue + if (other.delegate != this.delegate) return false + // if (other.serializer != this.serializer) return false + // TODO: 2020/9/9 serializers should be checked here, but it will cause incomparable issue when putting a SerializableValue as a Key + return true + } + + override fun hashCode(): Int { + @Suppress("UnnecessaryVariable", "CanBeVal") + var result = delegate.hashCode() + // result = 31 * result + serializer.hashCode() + // TODO: 2020/9/9 serializers should be checked here, but it will cause incomparable issue when putting a SerializableValue as a Key + return result + } + public companion object { @JvmStatic @JvmName("create") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PluginData.value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PluginData.value.kt index 8dad18e32..7e045e30e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PluginData.value.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PluginData.value.kt @@ -196,4 +196,21 @@ internal class LazyReferenceValueImpl : ReferenceValue, AbstractValueImpl< initialied = true valueField = value } + + override fun toString(): String { + return valueField.toString() + } + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other?.javaClass != this.javaClass) return false + + other as LazyReferenceValueImpl<*> + if (other.valueField != valueField) return false + return true + } + + override fun hashCode(): Int { + return valueField?.hashCode() ?: 0 + } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt index ef1e8a212..79ca00b77 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/Permission.kt @@ -17,7 +17,9 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** * 一个权限节点. * - * 由 [PermissionService] 实现不同, [Permission] 可能会有多种实例. 但一个权限总是拥有确定的 [id] + * 由 [PermissionService] 实现不同, [Permission] 可能会有多种实例. 但一个权限总是拥有确定的 [id]. + * + * 请不要手动实现这个接口. 总是从 [PermissionService.register] 获得实例. */ @ExperimentalPermission public interface Permission { @@ -26,15 +28,20 @@ public interface Permission { public val parentId: PermissionId } +@OptIn(ExperimentalPermission::class) +private val ROOT_PERMISSION_ID = PermissionId("*", "*") + /** * 所有权限的父权限. */ +@get:JvmName("getRootPermission") @ExperimentalPermission -public object RootPermission : - Permission { - override val id: PermissionId = PermissionId("*", "*") - override val description: String get() = "The parent of any permission" - override val parentId: PermissionId get() = id +public val RootPermission: Permission by lazy { + PermissionService.INSTANCE.register( + ROOT_PERMISSION_ID, + "The parent of any permission", + ROOT_PERMISSION_ID + ) } @ConsoleExperimentalAPI diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt index 3a44fb66a..3db1b0a52 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt @@ -189,7 +189,7 @@ internal class TestCommand { val composite = object : CompositeCommand( ConsoleCommandOwner, - "test", + "test22", overrideContext = buildCommandArgumentContext { add(object : CommandArgumentParser { override fun parse(raw: String, sender: CommandSender): MyClass { @@ -234,7 +234,7 @@ internal class TestCommand { simple.withRegistration { // assertEquals("xxx", withTesting { simple.execute(sender, "xxx") }) - assertEquals("xxx", withTesting { println(sender.executeCommand("/test xxx")) }) + assertEquals("xxx", withTesting { assertSuccess(sender.executeCommand("/test xxx")) }) } } } diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/permission/PermissionsBasicsTest.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/permission/PermissionsBasicsTest.kt new file mode 100644 index 000000000..f60845990 --- /dev/null +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/permission/PermissionsBasicsTest.kt @@ -0,0 +1,11 @@ +package net.mamoe.mirai.console.permission + +import org.junit.jupiter.api.Test + +internal class PermissionsBasicsTest { + + @Test + fun parentsWithSelfSequence() { + + } +} \ No newline at end of file From 0c94ea96a8c02158d735f262db169da53fc3a6c4 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 22:54:47 +0800 Subject: [PATCH 48/62] Fix generated usage --- .../mirai/console/internal/command/CompositeCommandInternal.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index 4251131ed..b427d982e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -234,7 +234,7 @@ internal fun Array.createUsage(b internal fun AbstractReflectionCommand.SubCommandDescriptor.createUsage(baseCommand: AbstractReflectionCommand): String = buildString { - if (!baseCommand.prefixOptional) { + if (baseCommand.prefixOptional) { append("(") append(CommandManager.commandPrefix) append(")") From 9ce468db9531c9e31cdbd845024aaa544c7d6c6e Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 23:01:01 +0800 Subject: [PATCH 49/62] Install serializers for Permission objects --- .../data/MultiFilePluginDataStorageImpl.kt | 18 ++++++++++---- .../permission/BuiltInPermissionServices.kt | 4 ++-- .../permission/PermissibleIdentifier.kt | 24 +++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt index 4fa616485..c0c917b56 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -9,10 +9,7 @@ package net.mamoe.mirai.console.internal.data -import net.mamoe.mirai.console.data.MultiFilePluginDataStorage -import net.mamoe.mirai.console.data.PluginData -import net.mamoe.mirai.console.data.PluginDataHolder -import net.mamoe.mirai.console.data.PluginDataStorage +import net.mamoe.mirai.console.data.* import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.utils.MiraiLogger @@ -20,6 +17,7 @@ import net.mamoe.mirai.utils.SilentLogger import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.warning import net.mamoe.yamlkt.Yaml +import net.mamoe.yamlkt.YamlConfiguration import java.io.File import java.nio.file.Path @@ -64,7 +62,17 @@ internal open class MultiFilePluginDataStorageImpl( @ConsoleExperimentalAPI public override fun store(holder: PluginDataHolder, instance: PluginData) { - getPluginDataFile(holder, instance).writeText(Yaml.default.encodeToString(instance.updaterSerializer, Unit)) + val yaml = if (instance is PluginConfig) Yaml( + configuration = YamlConfiguration( + mapSerialization = YamlConfiguration.MapSerialization.FLOW_MAP, + listSerialization = YamlConfiguration.ListSerialization.FLOW_SEQUENCE, + classSerialization = YamlConfiguration.MapSerialization.FLOW_MAP + ) + ) else Yaml.default + + getPluginDataFile(holder, instance).writeText( + yaml.encodeToString(instance.updaterSerializer, Unit) + ) logger.debug { "Successfully saved PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index d873ffcc9..61b7c9bd3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -127,8 +127,8 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

> - by value>>(ConcurrentHashMap()) + public val grantedPermissionMap: MutableMap> + by value>>(ConcurrentHashMap()) .withDefault { CopyOnWriteArrayList() } public companion object { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index a39cfb3ea..7d4d49204 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -11,6 +11,10 @@ package net.mamoe.mirai.console.permission +import kotlinx.serialization.Serializable + +/** + */ @ExperimentalPermission("Classname is subject to change") public interface PermissibleIdentifier { public val parents: Array @@ -30,37 +34,57 @@ public interface PermissibleIdentifier { } } +@Serializable @ExperimentalPermission public sealed class AbstractPermissibleIdentifier( public final override vararg val parents: PermissibleIdentifier ) : PermissibleIdentifier { + @Serializable public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) + + @Serializable public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + @Serializable public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + + @Serializable public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) + + @Serializable public data class ExactMember( public val groupId: Long, public val memberId: Long ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) + @Serializable public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) + + @Serializable public data class ExactFriend( public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(id)) + @Serializable public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) + + @Serializable public data class ExactTemp( public val groupId: Long, public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? + @Serializable public object AnyUser : AbstractPermissibleIdentifier(AnyContact) + + @Serializable public data class ExactUser( public val id: Long ) : AbstractPermissibleIdentifier(AnyUser) + @Serializable public object AnyContact : AbstractPermissibleIdentifier() + @Serializable public object Console : AbstractPermissibleIdentifier() } \ No newline at end of file From cce8c2db1659fbbf0221c7dd820d294323fc1b5c Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 23:06:56 +0800 Subject: [PATCH 50/62] Fix permission tips --- .../mamoe/mirai/console/internal/command/CommandManagerImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt index 7fde28a03..02e6c3312 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt @@ -66,7 +66,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by Coroutine when (val result = sender.executeCommand(message)) { is CommandExecuteResult.PermissionDenied -> { - if (!result.command.prefixOptional) { + if (!result.command.prefixOptional || message.content.startsWith(CommandManager.commandPrefix)) { sender.sendMessage("权限不足") intercept() } From a881235caa01ff9c06728426d372085ffe12527f Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 23:17:29 +0800 Subject: [PATCH 51/62] Add serializers for PermissionId --- .../mamoe/mirai/console/permission/PermissionId.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt index f323c6591..d4362a5ae 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt @@ -9,13 +9,17 @@ package net.mamoe.mirai.console.permission +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import net.mamoe.mirai.console.internal.data.map /** * [PermissionId] 与 [Permission] 唯一对应. */ -@Serializable +@Serializable(with = PermissionId.AsStringSerializer::class) @ExperimentalPermission public data class PermissionId( public val namespace: String, @@ -30,6 +34,14 @@ public data class PermissionId( } } + @Serializer(forClass = PermissionId::class) + public object AsClassSerializer + + public object AsStringSerializer : KSerializer by String.serializer().map( + serializer = { it.namespace + ":" + it.id }, + deserializer = { it.split(':').let { (namespace, id) -> PermissionId(namespace, id) } } + ) + public override fun toString(): String { return "$namespace:$id" } From ba3fe945f737488a573590dd79a5600113240658 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 9 Sep 2020 23:22:34 +0800 Subject: [PATCH 52/62] Fix PermissionService config saving --- .../data/MultiFilePluginDataStorageImpl.kt | 25 ++++++++++++++----- .../permission/BuiltInPermissionServices.kt | 13 +++++----- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt index c0c917b56..28bd99f1a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -9,15 +9,19 @@ package net.mamoe.mirai.console.internal.data -import net.mamoe.mirai.console.data.* +import kotlinx.serialization.json.Json +import net.mamoe.mirai.console.data.MultiFilePluginDataStorage +import net.mamoe.mirai.console.data.PluginData +import net.mamoe.mirai.console.data.PluginDataHolder +import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip +import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.SilentLogger import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.warning import net.mamoe.yamlkt.Yaml -import net.mamoe.yamlkt.YamlConfiguration import java.io.File import java.nio.file.Path @@ -62,16 +66,25 @@ internal open class MultiFilePluginDataStorageImpl( @ConsoleExperimentalAPI public override fun store(holder: PluginDataHolder, instance: PluginData) { - val yaml = if (instance is PluginConfig) Yaml( + @OptIn(ExperimentalPermission::class) + val yaml = if (instance.saveName == "PermissionService") Json { + prettyPrint = true + ignoreUnknownKeys = true + isLenient = true + allowStructuredMapKeys = true + } /*Yaml( configuration = YamlConfiguration( mapSerialization = YamlConfiguration.MapSerialization.FLOW_MAP, listSerialization = YamlConfiguration.ListSerialization.FLOW_SEQUENCE, classSerialization = YamlConfiguration.MapSerialization.FLOW_MAP ) - ) else Yaml.default - + )*/ else Yaml.default getPluginDataFile(holder, instance).writeText( - yaml.encodeToString(instance.updaterSerializer, Unit) + kotlin.runCatching { + yaml.encodeToString(instance.updaterSerializer, Unit) + }.getOrElse { + throw IllegalStateException("Exception while saving $instance, saveName=${instance.saveName}", it) + } ) logger.debug { "Successfully saved PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 61b7c9bd3..e0c6f26ac 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -10,7 +10,6 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.data.AutoSavePluginConfig -import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.data.valueFromKType @@ -105,17 +104,17 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

private constructor( permissionType: KType, public override val saveName: String, - delegate: PluginConfig, + // delegate: PluginConfig, @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? - ) : PluginConfig by delegate { + ) : AutoSavePluginConfig() { public val permissions: MutableMap by valueFromKType>( MutableMap::class.createType( @@ -136,8 +135,8 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

invoke( permissionType: KType, saveName: String, - delegate: PluginConfig, - ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, delegate, null) + // delegate: PluginConfig, + ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, null) } } } \ No newline at end of file From 6d4bc28ee50469376dda856f2b0b5982e13e030f Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 08:48:01 +0800 Subject: [PATCH 53/62] Add serializers for PermissibleIdentifier --- .../data/MultiFilePluginDataStorageImpl.kt | 5 +- .../console/internal/data/serializerHelper.kt | 2 +- .../permission/PermissibleIdentifier.kt | 92 ++++++++++++++----- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt index 28bd99f1a..bfb93c4c5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -9,7 +9,6 @@ package net.mamoe.mirai.console.internal.data -import kotlinx.serialization.json.Json import net.mamoe.mirai.console.data.MultiFilePluginDataStorage import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.data.PluginDataHolder @@ -67,7 +66,7 @@ internal open class MultiFilePluginDataStorageImpl( @ConsoleExperimentalAPI public override fun store(holder: PluginDataHolder, instance: PluginData) { @OptIn(ExperimentalPermission::class) - val yaml = if (instance.saveName == "PermissionService") Json { + val yaml =/* if (instance.saveName == "PermissionService") Json { prettyPrint = true ignoreUnknownKeys = true isLenient = true @@ -78,7 +77,7 @@ internal open class MultiFilePluginDataStorageImpl( listSerialization = YamlConfiguration.ListSerialization.FLOW_SEQUENCE, classSerialization = YamlConfiguration.MapSerialization.FLOW_MAP ) - )*/ else Yaml.default + )*/ else */Yaml.default getPluginDataFile(holder, instance).writeText( kotlin.runCatching { yaml.encodeToString(instance.updaterSerializer, Unit) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt index 728b8ac00..eced14e96 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt @@ -47,7 +47,7 @@ internal fun serializerMirai(type: KType): KSerializer { typeArguments.isEmpty() -> rootClass.serializer() else -> { val serializers = typeArguments - .map(::serializer) + .map(::serializerMirai) // Array is not supported, see KT-32839 when (rootClass) { List::class, MutableList::class, ArrayList::class -> ListSerializer(serializers[0]) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index 7d4d49204..544175a37 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -11,7 +11,11 @@ package net.mamoe.mirai.console.permission +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer +import net.mamoe.mirai.console.internal.data.map +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** */ @@ -25,7 +29,7 @@ public interface PermissibleIdentifier { return allParentsWithSelf().any { it == with } } - internal fun PermissibleIdentifier.allParentsWithSelf(): Sequence { + private fun PermissibleIdentifier.allParentsWithSelf(): Sequence { return sequence { yield(this@allParentsWithSelf) yieldAll(parents.asSequence()) @@ -34,57 +38,99 @@ public interface PermissibleIdentifier { } } -@Serializable +@Serializable(with = AbstractPermissibleIdentifier.AsStringSerializer::class) @ExperimentalPermission public sealed class AbstractPermissibleIdentifier( public final override vararg val parents: PermissibleIdentifier ) : PermissibleIdentifier { - @Serializable - public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) + internal companion object { + val objects by lazy { + // https://youtrack.jetbrains.com/issue/KT-41782 + AbstractPermissibleIdentifier::class.nestedClasses.mapNotNull { it.objectInstance } + } + + val regexes: List AbstractPermissibleIdentifier>> = + listOf( + Regex("""ExactGroup\(\s*([0-9]+)\s*\)""") to { (id) -> ExactGroup(id.toLong()) }, + Regex("""ExactFriend\(\s*([0-9]+)\s*\)""") to { (id) -> ExactFriend(id.toLong()) }, + Regex("""ExactUser\(\s*([0-9]+)\s*\)""") to { (id) -> ExactUser(id.toLong()) }, + Regex("""AnyMember\(\s*([0-9]+)\s*\)""") to { (id) -> AnyMember(id.toLong()) }, + Regex("""ExactMember\(\s*([0-9]+)\s*([0-9]+)\s*\)""") to { (a, b) -> + ExactMember( + a.toLong(), + b.toLong() + ) + }, + Regex("""ExactTemp\(\s*([0-9]+)\s*([0-9]+)\s*\)""") to { (a, b) -> ExactTemp(a.toLong(), b.toLong()) }, + ) + } + + @ConsoleExperimentalAPI + public object AsStringSerializer : KSerializer by String.serializer().map( + serializer = { it.toString() }, + + deserializer = d@{ str -> + @Suppress("NAME_SHADOWING") val str = str.trim() + objects.find { it.toString() == str }?.let { return@d it as AbstractPermissibleIdentifier } + for ((regex, block) in regexes) { + val result = regex.find(str) ?: continue + if (result.range.last != str.lastIndex) continue + if (result.range.first != 0) continue + return@d result.destructured.run(block) + } + error("Cannot deserialize '$str' as AbstractPermissibleIdentifier") + } + ) + + public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) { + override fun toString(): String = "AnyGroup" + } - @Serializable public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) - @Serializable public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) - @Serializable - public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) + public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "AnyMemberFromAnyGroup" + } - @Serializable public data class ExactMember( public val groupId: Long, public val memberId: Long ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) - @Serializable - public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) + public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "AnyFriend" + } - @Serializable public data class ExactFriend( public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(id)) + ) : AbstractPermissibleIdentifier(ExactUser(id)) { + override fun toString(): String = "ExactFriend" + } - @Serializable - public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) + public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "AnyTemp" + } - @Serializable public data class ExactTemp( public val groupId: Long, public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? - @Serializable - public object AnyUser : AbstractPermissibleIdentifier(AnyContact) + public object AnyUser : AbstractPermissibleIdentifier(AnyContact) { + override fun toString(): String = "AnyUser" + } - @Serializable public data class ExactUser( public val id: Long ) : AbstractPermissibleIdentifier(AnyUser) - @Serializable - public object AnyContact : AbstractPermissibleIdentifier() + public object AnyContact : AbstractPermissibleIdentifier() { + override fun toString(): String = "AnyContact" + } - @Serializable - public object Console : AbstractPermissibleIdentifier() + public object Console : AbstractPermissibleIdentifier() { + override fun toString(): String = "Console" + } } \ No newline at end of file From d403d5d79c735483e3867047b8ac84736b6bdf04 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 08:49:03 +0800 Subject: [PATCH 54/62] Remove mirai-console-terminal --- frontend/mirai-console-terminal/README.md | 6 - .../mirai-console-terminal/build.gradle.kts | 49 -- .../console/MiraiConsoleTerminalFrontEnd.kt | 727 ------------------ .../console/MiraiConsoleTerminalLoader.kt | 40 - 4 files changed, 822 deletions(-) delete mode 100644 frontend/mirai-console-terminal/README.md delete mode 100644 frontend/mirai-console-terminal/build.gradle.kts delete mode 100644 frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalFrontEnd.kt delete mode 100644 frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt diff --git a/frontend/mirai-console-terminal/README.md b/frontend/mirai-console-terminal/README.md deleted file mode 100644 index bd7980bfe..000000000 --- a/frontend/mirai-console-terminal/README.md +++ /dev/null @@ -1,6 +0,0 @@ -### Mirai Console Terminal -支持windows/mac/linux -在terminal环境下的Console, 由控制台富文本实现简易UI -优点: 可以在linux环境下运行/简洁使用效率高 -缺点: 需要有略微的terminal知识 -所使用插件系统与graphical版本一致 可以来回切换 \ No newline at end of file diff --git a/frontend/mirai-console-terminal/build.gradle.kts b/frontend/mirai-console-terminal/build.gradle.kts deleted file mode 100644 index cea7f593a..000000000 --- a/frontend/mirai-console-terminal/build.gradle.kts +++ /dev/null @@ -1,49 +0,0 @@ -plugins { - id("kotlinx-serialization") - id("kotlin") - id("java") -} - - -apply(plugin = "com.github.johnrengelman.shadow") - -version = Versions.Mirai.console - -tasks.withType { - manifest { - attributes["Main-Class"] = "net.mamoe.mirai.console.MiraiConsoleTerminalLoader" - } -} - -kotlin { - sourceSets { - all { - - languageSettings.useExperimentalAnnotation("kotlin.Experimental") - languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn") - languageSettings.progressiveMode = true - languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI") - } - } -} - -dependencies { - compileOnly("net.mamoe:mirai-core-qqandroid:${Versions.core}") - api(project(":mirai-console")) - api(group = "com.googlecode.lanterna", name = "lanterna", version = "3.0.2") -} -val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks -compileKotlin.kotlinOptions { - jvmTarget = "1.8" -} -val compileTestKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks -compileTestKotlin.kotlinOptions { - jvmTarget = "1.8" -} -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} -tasks.withType(JavaCompile::class.java) { - options.encoding = "UTF8" -} diff --git a/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalFrontEnd.kt b/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalFrontEnd.kt deleted file mode 100644 index 215b452d4..000000000 --- a/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalFrontEnd.kt +++ /dev/null @@ -1,727 +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 - */ - -@file:Suppress("MemberVisibilityCanBePrivate") - -package net.mamoe.mirai.console - -import com.googlecode.lanterna.SGR -import com.googlecode.lanterna.TerminalSize -import com.googlecode.lanterna.TextColor -import com.googlecode.lanterna.graphics.TextGraphics -import com.googlecode.lanterna.input.KeyStroke -import com.googlecode.lanterna.input.KeyType -import com.googlecode.lanterna.terminal.DefaultTerminalFactory -import com.googlecode.lanterna.terminal.Terminal -import com.googlecode.lanterna.terminal.swing.SwingTerminal -import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame -import kotlinx.coroutines.* -import kotlinx.coroutines.io.ByteWriteChannel -import kotlinx.coroutines.io.close -import kotlinx.coroutines.io.jvm.nio.copyTo -import kotlinx.coroutines.io.reader -import kotlinx.io.core.use -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.MiraiConsoleTerminalFrontEnd.LoggerDrawer.cleanPage -import net.mamoe.mirai.console.MiraiConsoleTerminalFrontEnd.LoggerDrawer.drawLog -import net.mamoe.mirai.console.MiraiConsoleTerminalFrontEnd.LoggerDrawer.redrawLogs -import net.mamoe.mirai.console.command.CommandManager -import net.mamoe.mirai.console.command.ConsoleCommandSender -import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd -import net.mamoe.mirai.utils.LoginSolver -import net.mamoe.mirai.utils.SimpleLogger.LogPriority -import java.awt.Image -import java.awt.image.BufferedImage -import java.io.File -import java.io.OutputStream -import java.io.PrintStream -import java.io.RandomAccessFile -import java.nio.ByteBuffer -import java.nio.charset.Charset -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedDeque -import javax.imageio.ImageIO -import kotlin.concurrent.thread -import kotlin.coroutines.CoroutineContext -import kotlin.system.exitProcess - -/** - * 此文件不推荐任何人看 - * 可能导致 - * 1:心肌梗死 - * 2:呼吸困难 - * 3:想要重写但是发现改任何一个看似不合理的地方都会崩 - * - * @author NaturalHG - * - */ - -val String.actualLength: Int get() = this.sumBy { if (it.isChineseChar) 2 else 1 } - - -fun String.getSubStringIndexByActualLength(widthMax: Int): Int { - return this.sumBy { if (it.isChineseChar) 2 else 1 }.coerceAtMost(widthMax).coerceAtLeast(2) -} - -val Char.isChineseChar: Boolean - get() { - return this.toString().isChineseChar - } - -val String.isChineseChar: Boolean - get() { - return this.matches(Regex("[\u4e00-\u9fa5]")) - } - - -object MiraiConsoleTerminalFrontEnd : MiraiConsoleFrontEnd { - const val cacheLogSize = 50 - var mainTitle = "Mirai Console v0.01 Core v0.15" - - override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) { - mainTitle = "Mirai Console(Terminal) $consoleVersion $consoleBuild Core $coreVersion" - } - - override fun pushLog(identity: Long, message: String) { - log[identity]!!.push(message) - if (identity == screens[currentScreenId]) { - drawLog(message) - } - } - - // 修改interface之后用来暂时占位 - override fun pushLog(priority: LogPriority, identityStr: String, identity: Long, message: String) { - this.pushLog(identity, message) - } - - override fun prePushBot(identity: Long) { - log[identity] = LimitLinkedQueue(cacheLogSize) - } - - override fun pushBot(bot: Bot) { - botAdminCount[bot.id] = 0 - screens.add(bot.id) - drawFrame(this.getScreenName(currentScreenId)) - if (terminal is SwingTerminalFrame) { - terminal.flush() - } - } - - @Volatile - var requesting = false - private var requestResult: String? = null - override suspend fun requestInput(hint:String): String { - if(hint.isNotEmpty()){ - this.pushLog(0, hint) - } - requesting = true - while (requesting) { - delay(100) - } - return requestResult!! - } - - - private fun provideInput(input: String) { - if (requesting) { - requestResult = input - requesting = false - } else { - CommandManager.runCommand(ConsoleCommandSender, commandBuilder.toString()) - } - } - - - override fun pushBotAdminStatus(identity: Long, admins: List) { - botAdminCount[identity] = admins.size - } - - override fun createLoginSolver(): LoginSolver { - return object : LoginSolver() { - override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? { - val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } - withContext(Dispatchers.IO) { - tempFile.createNewFile() - pushLog(0, "[Login Solver]需要图片验证码登录, 验证码为 4 字母") - try { - tempFile.writeChannel().apply { - writeFully(ByteBuffer.wrap(data)) - close() - } - pushLog(0, "请查看文件 ${tempFile.absolutePath}") - } catch (e: Exception) { - error("[Login Solver]验证码无法保存[Error0001]") - } - } - - lateinit var toLog: String - tempFile.inputStream().use { - val img = ImageIO.read(it) - toLog += img?.createCharImg((terminal.terminalSize.columns / 1.5).toInt()) ?: "无法创建字符图片. 请查看文件\n" - } - return requestInput("$toLog[Login Solver]请输验证码. ${tempFile.absolutePath}") - .takeUnless { it.isEmpty() || it.length != 4 } - .also { - pushLog(0, "[Login Solver]正在提交[$it]中...") - } - } - - override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? { - pushLog(0, "[Login Solver]需要滑动验证码") - pushLog(0, "[Login Solver]请在任意浏览器中打开以下链接并完成验证码. ") - pushLog(0, url) - return requestInput("[Login Solver]完成后请输入任意字符 ").also { - pushLog(0, "[Login Solver]正在提交中") - } - } - - override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? { - pushLog(0, "[Login Solver]需要进行账户安全认证") - pushLog(0, "[Login Solver]该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题") - pushLog(0, "[Login Solver]完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次") - pushLog(0, "[Login Solver]请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符") - pushLog(0, "[Login Solver]这步操作将在后续的版本中优化") - pushLog(0, url) - return requestInput("").also { - pushLog(0, "[Login Solver]正在提交中...") - } - } - - } - } - - private val log = ConcurrentHashMap>().also { - it[0L] = LimitLinkedQueue(cacheLogSize) - } - - private val botAdminCount = ConcurrentHashMap() - - private val screens = mutableListOf(0L) - private var currentScreenId = 0 - - - lateinit var terminal: Terminal - lateinit var textGraphics: TextGraphics - - private var hasStart = false - private lateinit var internalPrinter: PrintStream - fun start() { - if (hasStart) { - return - } - - internalPrinter = System.out - - - hasStart = true - val defaultTerminalFactory = DefaultTerminalFactory(internalPrinter, System.`in`, Charset.defaultCharset()) - try { - terminal = defaultTerminalFactory.createTerminal() - terminal.enterPrivateMode() - terminal.clearScreen() - terminal.setCursorVisible(false) - } catch (e: Exception) { - try { - terminal = SwingTerminalFrame("Mirai Console") - terminal.enterPrivateMode() - terminal.clearScreen() - terminal.setCursorVisible(false) - } catch (e: Exception) { - error("can not create terminal") - } - } - textGraphics = terminal.newTextGraphics() - - /* - var lastRedrawTime = 0L - var lastNewWidth = 0 - var lastNewHeight = 0 - - terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize -> - try { - if (lastNewHeight == newSize.rows - && - lastNewWidth == newSize.columns - ) { - return@TerminalResizeListener - } - lastNewHeight = newSize.rows - lastNewWidth = newSize.columns - terminal.clearScreen() - if(terminal !is SwingTerminalFrame) { - Thread.sleep(300) - } - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - }catch (ignored:Exception){ - - } - }) - - */ - var lastJob: Job? = null - terminal.addResizeListener { _: Terminal, _: TerminalSize -> - lastJob = GlobalScope.launch { - try { - delay(300) - if (lastJob == coroutineContext[Job]) { - @Suppress("BlockingMethodInNonBlockingContext") - terminal.clearScreen() - //inited = false - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - } - - if (terminal !is SwingTerminalFrame) { - System.setOut(PrintStream(object : OutputStream() { - var builder = java.lang.StringBuilder() - override fun write(b: Int) { - with(b.toChar()) { - if (this == '\n') { - pushLog(0, builder.toString()) - builder = java.lang.StringBuilder() - } else { - builder.append(this) - } - } - } - })) - } - - System.setErr(System.out) - - try { - update() - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - - val charList = listOf(',', '.', '/', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '=', '+', '!', ' ') - thread { - while (true) { - try { - val keyStroke: KeyStroke = terminal.readInput() - - when (keyStroke.keyType) { - KeyType.ArrowLeft -> { - currentScreenId = - getLeftScreenId() - clearRows(2) - cleanPage() - update() - } - KeyType.ArrowRight -> { - currentScreenId = - getRightScreenId() - clearRows(2) - cleanPage() - update() - } - KeyType.Enter -> { - provideInput(commandBuilder.toString()) - emptyCommand() - } - KeyType.Escape -> { - exit() - } - else -> { - if (keyStroke.character != null) { - if (keyStroke.character.toInt() == 8) { - deleteCommandChar() - } - if (keyStroke.character.isLetterOrDigit() || charList.contains(keyStroke.character)) { - addCommandChar(keyStroke.character) - } - } - } - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - } - } - - private fun getLeftScreenId(): Int { - var newId = currentScreenId - 1 - if (newId < 0) { - newId = screens.size - 1 - } - return newId - } - - private fun getRightScreenId(): Int { - var newId = 1 + currentScreenId - if (newId >= screens.size) { - newId = 0 - } - return newId - } - - private fun getScreenName(id: Int): String { - return when (screens[id]) { - 0L -> { - "Console Screen" - } - else -> { - "Bot: ${screens[id]}" - } - } - } - - - fun clearRows(row: Int) { - textGraphics.putString( - 0, row, " ".repeat( - terminal.terminalSize.columns - ) - ) - } - - fun drawFrame( - title: String - ) { - val width = terminal.terminalSize.columns - val height = terminal.terminalSize.rows - terminal.setBackgroundColor(TextColor.ANSI.DEFAULT) - - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.GREEN - textGraphics.putString((width - mainTitle.actualLength) / 2, 1, mainTitle, SGR.BOLD) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString(2, 3, "-".repeat(width - 4)) - textGraphics.putString(2, 5, "-".repeat(width - 4)) - textGraphics.putString(2, height - 4, "-".repeat(width - 4)) - textGraphics.putString(2, height - 3, "|>>>") - textGraphics.putString(width - 3, height - 3, "|") - textGraphics.putString(2, height - 2, "-".repeat(width - 4)) - - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - val leftName = - getScreenName(getLeftScreenId()) - // clearRows(2) - textGraphics.putString((width - title.actualLength) / 2 - "$leftName << ".length, 2, "$leftName << ") - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.YELLOW - textGraphics.putString((width - title.actualLength) / 2, 2, title, SGR.BOLD) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - val rightName = - getScreenName(getRightScreenId()) - textGraphics.putString((width + title.actualLength) / 2 + 1, 2, ">> $rightName") - } - - fun drawMainFrame( - onlineBotCount: Number - ) { - drawFrame("Console Screen") - val width = terminal.terminalSize.columns - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - clearRows(4) - textGraphics.putString(2, 4, "|Online Bots: $onlineBotCount") - textGraphics.putString( - width - 2 - "Powered By Mamoe Technologies|".actualLength, - 4, - "Powered By Mamoe Technologies|" - ) - } - - fun drawBotFrame( - qq: Long, - adminCount: Number - ) { - drawFrame("Bot: $qq") - val width = terminal.terminalSize.columns - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - clearRows(4) - textGraphics.putString(2, 4, "|Admins: $adminCount") - textGraphics.putString(width - 2 - "Add admins via commands|".actualLength, 4, "Add admins via commands|") - } - - - object LoggerDrawer { - var currentHeight = 6 - - fun drawLog(string: String, flush: Boolean = true) { - val maxHeight = terminal.terminalSize.rows - 4 - val heightNeed = (string.actualLength / (terminal.terminalSize.columns - 6)) + 1 - if (heightNeed - 1 > maxHeight) { - pushLog(0, "[UI ERROR]: 您的屏幕太小, 有一条超长LOG无法显示") - return//拒绝打印 - } - if (currentHeight + heightNeed > maxHeight) { - cleanPage()//翻页 - } - if (string.contains("\n")) { - string.split("\n").forEach { _ -> - drawLog(string, false) - } - } else { - val width = terminal.terminalSize.columns - 6 - var x = string - while (true) { - if (x == "") { - break - } - val toWrite = if (x.actualLength > width) { - val index = x.getSubStringIndexByActualLength(width) - x.substring(0, index).also { - x = if (index < x.length) { - x.substring(index) - } else { - "" - } - } - } else { - x.also { - x = "" - } - } - try { - textGraphics.foregroundColor = TextColor.ANSI.GREEN - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString( - 3, - currentHeight, toWrite, SGR.ITALIC - ) - } catch (ignored: Exception) { - // - } - ++currentHeight - } - } - if (flush && terminal is SwingTerminalFrame) { - terminal.flush() - } - } - - - fun cleanPage() { - for (index in 6 until terminal.terminalSize.rows - 4) { - clearRows(index) - } - currentHeight = 6 - } - - - fun redrawLogs(toDraw: Queue) { - //this.cleanPage() - currentHeight = 6 - var logsToDraw = 0 - var vara = 0 - val toPrint = mutableListOf() - toDraw.forEach { - val heightNeed = (it.actualLength / (terminal.terminalSize.columns - 6)) + 1 - vara += heightNeed - if (currentHeight + vara < terminal.terminalSize.rows - 4) { - logsToDraw++ - toPrint.add(it) - } else { - return@forEach - } - } - toPrint.reversed().forEach { - drawLog(it, false) - } - if (terminal is SwingTerminalFrame) { - terminal.flush() - } - } - } - - - private var commandBuilder = StringBuilder() - private fun redrawCommand() { - val height = terminal.terminalSize.rows - val width = terminal.terminalSize.columns - clearRows(height - 3) - textGraphics.foregroundColor = TextColor.ANSI.DEFAULT - textGraphics.putString(2, height - 3, "|>>>") - textGraphics.putString(width - 3, height - 3, "|") - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.BLACK - textGraphics.putString(7, height - 3, commandBuilder.toString()) - if (terminal is SwingTerminalFrame) { - terminal.flush() - } - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - } - - private fun addCommandChar( - c: Char - ) { - if (!requesting && commandBuilder.isEmpty() && c != '/') { - addCommandChar('/') - } - textGraphics.foregroundColor = TextColor.ANSI.WHITE - textGraphics.backgroundColor = TextColor.ANSI.BLACK - val height = terminal.terminalSize.rows - commandBuilder.append(c) - if (terminal is SwingTerminalFrame) { - redrawCommand() - } else { - textGraphics.putString(6 + commandBuilder.length, height - 3, c.toString()) - } - textGraphics.backgroundColor = TextColor.ANSI.DEFAULT - } - - private fun deleteCommandChar() { - if (commandBuilder.isNotEmpty()) { - commandBuilder = StringBuilder(commandBuilder.toString().substring(0, commandBuilder.length - 1)) - } - val height = terminal.terminalSize.rows - if (terminal is SwingTerminalFrame) { - redrawCommand() - } else { - textGraphics.putString(7 + commandBuilder.length, height - 3, " ") - } - } - - - private var lastEmpty: Job? = null - private fun emptyCommand() { - commandBuilder = StringBuilder() - if (terminal is SwingTerminal) { - redrawCommand() - terminal.flush() - } else { - lastEmpty = GlobalScope.launch { - try { - delay(100) - if (lastEmpty == coroutineContext[Job]) { - withContext(Dispatchers.IO) { - terminal.clearScreen() - } - //inited = false - update() - redrawCommand() - redrawLogs(log[screens[currentScreenId]]!!) - } - } catch (e: Exception) { - pushLog(0, "[UI ERROR] ${e.message}") - } - } - } - } - - private fun update() { - when (screens[currentScreenId]) { - 0L -> { - drawMainFrame(screens.size - 1) - } - else -> { - drawBotFrame( - screens[currentScreenId], - 0 - ) - } - } - redrawLogs(log[screens[currentScreenId]]!!) - } - - fun exit() { - try { - terminal.exitPrivateMode() - terminal.close() - exitProcess(0) - } catch (ignored: Exception) { - - } - } -} - - -class LimitLinkedQueue( - private val limit: Int = 50 -) : ConcurrentLinkedDeque() { - override fun push(e: T) { - if (size >= limit) { - this.pollLast() - } - return super.push(e) - } -} - -/** - * @author NaturalHG - */ -private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String { - val newHeight = (this.height * (outputWidth.toDouble() / this.width)).toInt() - val tmp = this.getScaledInstance(outputWidth, newHeight, Image.SCALE_SMOOTH) - val image = BufferedImage(outputWidth, newHeight, BufferedImage.TYPE_INT_ARGB) - val g2d = image.createGraphics() - g2d.drawImage(tmp, 0, 0, null) - fun gray(rgb: Int): Int { - val r = rgb and 0xff0000 shr 16 - val g = rgb and 0x00ff00 shr 8 - val b = rgb and 0x0000ff - return (r * 30 + g * 59 + b * 11 + 50) / 100 - } - - fun grayCompare(g1: Int, g2: Int): Boolean = - kotlin.math.min(g1, g2).toDouble() / kotlin.math.max(g1, g2) >= ignoreRate - - val background = gray(image.getRGB(0, 0)) - - return buildString(capacity = height) { - - val lines = mutableListOf() - - var minXPos = outputWidth - var maxXPos = 0 - - for (y in 0 until image.height) { - val builderLine = StringBuilder() - for (x in 0 until image.width) { - val gray = gray(image.getRGB(x, y)) - if (grayCompare(gray, background)) { - builderLine.append(" ") - } else { - builderLine.append("#") - if (x < minXPos) { - minXPos = x - } - if (x > maxXPos) { - maxXPos = x - } - } - } - if (builderLine.toString().isBlank()) { - continue - } - lines.add(builderLine) - } - for (line in lines) { - append(line.substring(minXPos, maxXPos)).append("\n") - } - } -} - -// Copied from Ktor CIO -private fun File.writeChannel( - coroutineContext: CoroutineContext = Dispatchers.IO -): ByteWriteChannel = GlobalScope.reader(CoroutineName("file-writer") + coroutineContext, autoFlush = true) { - @Suppress("BlockingMethodInNonBlockingContext") - RandomAccessFile(this@writeChannel, "rw").use { file -> - val copied = channel.copyTo(file.channel) - file.setLength(copied) // truncate tail that could remain from the previously written data - } -}.channel diff --git a/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt b/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt deleted file mode 100644 index 7cf2e3603..000000000 --- a/frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt +++ /dev/null @@ -1,40 +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 - -import net.mamoe.mirai.console.pure.MiraiConsoleUIPure -import kotlin.concurrent.thread - -class MiraiConsoleTerminalLoader { - companion object { - @JvmStatic - fun main(args: Array) { - if (args.contains("pure") || args.contains("-pure") || System.getProperty( - "os.name", - "" - ).toLowerCase().contains("windows") - ) { - println("[MiraiConsoleTerminalLoader]: 将以Pure[兼容模式]启动Console") - MiraiConsole.start(MiraiConsoleUIPure()) - } else { - MiraiConsoleTerminalFrontEnd.start() - thread { - MiraiConsole.start( - MiraiConsoleTerminalFrontEnd - ) - } - } - Runtime.getRuntime().addShutdownHook(thread(start = false) { - MiraiConsole.stop() - MiraiConsoleTerminalFrontEnd.exit() - }) - } - } -} \ No newline at end of file From 3c64b9955357b18e635819454871b6f0bfef6cb9 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 08:56:13 +0800 Subject: [PATCH 55/62] Improve messages --- .../net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt index 7f9686b6c..e52a0ef7e 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt @@ -92,11 +92,11 @@ internal object ConsoleCommandSenderImplPure : MiraiConsoleImplementation.Consol kotlin.runCatching { lineReader.printAbove(message) }.onFailure { - consoleLogger.error(it) + consoleLogger.error("Exception while ConsoleCommandSenderImplPure.sendMessage", it) } } override suspend fun sendMessage(message: Message) { - return sendMessage(message.contentToString()) + return sendMessage(message.toString()) } } \ No newline at end of file From 42a4c844e11ba1150a2b0779fc4336b83754e213 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 09:04:06 +0800 Subject: [PATCH 56/62] Ignore CancellationException on stop --- .../net/mamoe/mirai/console/command/BuiltInCommands.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index 78a17cd26..d653c9263 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -9,10 +9,7 @@ package net.mamoe.mirai.console.command -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.cancel -import kotlinx.coroutines.cancelAndJoin -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import net.mamoe.mirai.Bot @@ -122,8 +119,9 @@ public object BuiltInCommands { ignoreException { sendMessage("mirai-console stopped successfully.") } }, onFailure = { + if (it is CancellationException) return@fold @OptIn(ConsoleInternalAPI::class) - MiraiConsole.mainLogger.error(it) + MiraiConsole.mainLogger.error("Exception in stop", it) ignoreException { sendMessage( it.localizedMessage ?: it.message ?: it.toString() From 335299275b80993a3376aaf6688c7c939855dafb Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 09:04:31 +0800 Subject: [PATCH 57/62] Add CoroutineName for input thread --- .../main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt index c2f285cde..66408bb71 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.console.pure import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import net.mamoe.mirai.console.MiraiConsole @@ -28,7 +29,7 @@ val consoleLogger by lazy { DefaultLogger("console") } @OptIn(ConsoleInternalAPI::class) internal fun startupConsoleThread() { - MiraiConsole.launch { + MiraiConsole.launch(CoroutineName("Input")) { while (true) { try { val next = MiraiConsole.requestInput("").let { From 7123f1e227feaa4c65f40acaa159ae0979432058 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 09:05:08 +0800 Subject: [PATCH 58/62] Ignore CancellationException in MiraiConsole.coroutineContext[CoroutineExceptionHandler] --- .../mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt index 0d6abc78f..9bdf2d2ce 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt @@ -74,6 +74,9 @@ class MiraiConsoleImplementationPure ) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope( NamedSupervisorJob("MiraiConsoleImplementationPure") + CoroutineExceptionHandler { coroutineContext, throwable -> + if (throwable is CancellationException) { + return@CoroutineExceptionHandler + } val coroutineName = coroutineContext[CoroutineName]?.name ?: "" MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable) }) { From 405d8cd88f35d72d8dfe06b5842e077bc682f065 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 09:05:34 +0800 Subject: [PATCH 59/62] Don't save registered permissions map --- .../permission/BuiltInPermissionServices.kt | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index e0c6f26ac..9a7279545 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -12,13 +12,10 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.data.AutoSavePluginConfig import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault import net.mamoe.mirai.console.data.value -import net.mamoe.mirai.console.data.valueFromKType import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import kotlin.reflect.KClass import kotlin.reflect.KType -import kotlin.reflect.KTypeProjection -import kotlin.reflect.KVariance import kotlin.reflect.full.createType @@ -91,7 +88,7 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

get() = PermissionImpl::class - override val permissions: MutableMap get() = config.permissions + override val permissions: MutableMap = ConcurrentHashMap() @Suppress("UNCHECKED_CAST") override val grantedPermissionsMap: MutableMap> @@ -115,17 +112,6 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

- by valueFromKType>( - MutableMap::class.createType( - listOf( - KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()), - KTypeProjection(KVariance.INVARIANT, permissionType), - ) - ), - ConcurrentHashMap() - ) - public val grantedPermissionMap: MutableMap> by value>>(ConcurrentHashMap()) .withDefault { CopyOnWriteArrayList() } From a70e00464fa11162cab37493152e99df5675d6aa Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 09:08:19 +0800 Subject: [PATCH 60/62] Ignore CancellationException on stop --- .../kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index d653c9263..2693c59ef 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -113,7 +113,7 @@ public object BuiltInCommands { closingLock.withLock { sendMessage("Stopping mirai-console") kotlin.runCatching { - MiraiConsole.job.cancelAndJoin() + ignoreException { MiraiConsole.job.cancelAndJoin() } }.fold( onSuccess = { ignoreException { sendMessage("mirai-console stopped successfully.") } From c76a6dacc9e43d31c4ad1f229b00ed625be2f417 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 10:30:00 +0800 Subject: [PATCH 61/62] Introduce JvmPluginDescriptionBuilder, add checks --- .../MiraiConsoleImplementationBridge.kt | 3 - .../internal/data/builtins/BotManagerImpl.kt | 53 ----- .../data/builtins/ConsoleDataScope.kt | 2 +- .../internal/plugin/PluginManagerImpl.kt | 10 +- .../net/mamoe/mirai/console/plugin/Plugin.kt | 2 +- .../IllegalPluginDescriptionException.kt | 8 + .../plugin/description/PluginDependency.kt | 18 +- .../plugin/description/PluginDescription.kt | 124 ++++++++++- .../plugin/jvm/JvmPluginDescription.kt | 198 +++++++++++++++++- .../mamoe/mirai/console/util/BotManager.kt | 36 ---- .../mamoe/mirai/console/data/SettingTest.kt | 4 +- 11 files changed, 340 insertions(+), 118 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/IllegalPluginDescriptionException.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt index a604cc13d..c40a49b7e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt @@ -42,7 +42,6 @@ import net.mamoe.mirai.console.permission.RootPermission import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.center.PluginCenter -import net.mamoe.mirai.console.util.BotManager import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.utils.* @@ -121,8 +120,6 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI phase `load configurations`@{ mainLogger.verbose { "Loading configurations..." } ConsoleDataScope.reloadAll() - - BotManager } val pluginLoadSession: PluginManagerImpl.PluginLoadSession diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt deleted file mode 100644 index 3abac0c9a..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/BotManagerImpl.kt +++ /dev/null @@ -1,53 +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 - */ - -@file:Suppress("MemberVisibilityCanBePrivate") - -package net.mamoe.mirai.console.internal.data.builtins - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.data.AutoSavePluginConfig -import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys -import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault -import net.mamoe.mirai.console.data.ValueDescription -import net.mamoe.mirai.console.data.value -import net.mamoe.mirai.console.util.BotManager -import net.mamoe.mirai.contact.User - -internal object BotManagerImpl : BotManager { - override val User.isManager: Boolean get() = this.id in ManagersConfig[this.bot] - - override fun Bot.removeManager(id: Long): Boolean { - return ManagersConfig[this].remove(id) - } - - override val Bot.managers: List - get() = ManagersConfig[this].toList() - - override fun Bot.addManager(id: Long): Boolean { - return ManagersConfig[this].add(id) - } -} - -internal object ManagersConfig : AutoSavePluginConfig() { - override val saveName: String - get() = "Managers" - - @ValueDescription( - """ - 管理员列表 - """ - ) - private val managers - by value>>() - .withEmptyDefault() - .mapKeys(Bot::getInstance, Bot::id) - - internal operator fun get(bot: Bot): MutableSet = managers[bot] -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt index 61463c6c1..98b8077bd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/ConsoleDataScope.kt @@ -22,7 +22,7 @@ import net.mamoe.mirai.utils.minutesToMillis internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") { private val data: List = mutableListOf() - private val configs: MutableList = mutableListOf(ManagersConfig, AutoLoginConfig) + private val configs: MutableList = mutableListOf(AutoLoginConfig) fun addAndReloadConfig(config: PluginConfig) { configs.add(config) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt index 5805b3d01..bdef81153 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt @@ -171,8 +171,10 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol @kotlin.jvm.Throws(PluginLoadException::class) internal fun checkPluginDescription(description: PluginDescription) { - when (description.name.toLowerCase()) { - "main", "console", "plugin", "config", "data" -> throw PluginLoadException("Plugin name '${description.name}' is forbidden.") + kotlin.runCatching { + PluginDescription.checkPluginDescription(description) + }.getOrElse { + throw PluginLoadException("PluginDescription check failed.", it) } } @@ -214,7 +216,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol return cannotBeLoad } - fun List.filterIsMissing(): List = + fun Collection.filterIsMissing(): List = this.filterNot { it.isOptional || it in resolved } tailrec fun List.doSort() { @@ -257,4 +259,4 @@ internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>, plugin: Plug ) internal operator fun List.contains(dependency: PluginDependency): Boolean = - any { it.name == dependency.name } + any { it.id == dependency.id } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt index ec09365fa..a9248bb7d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt @@ -82,4 +82,4 @@ public inline val Plugin.author: String get() = this.description.author /** * 获取 [PluginDescription.dependencies] */ -public inline val Plugin.dependencies: List get() = this.description.dependencies +public inline val Plugin.dependencies: Set get() = this.description.dependencies diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/IllegalPluginDescriptionException.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/IllegalPluginDescriptionException.kt new file mode 100644 index 000000000..1083259ce --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/IllegalPluginDescriptionException.kt @@ -0,0 +1,8 @@ +package net.mamoe.mirai.console.plugin.description + +public class IllegalPluginDescriptionException : RuntimeException { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDependency.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDependency.kt index 0e4dc7802..957a14bf7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDependency.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDependency.kt @@ -18,9 +18,11 @@ import com.vdurmont.semver4j.Semver * * @see PluginDescription.dependencies */ -public data class PluginDependency( - /** 依赖插件名 */ - public val name: String, +public data class PluginDependency @JvmOverloads constructor( + /** + * 依赖插件 ID, [PluginDescription.id] + */ + public val id: String, /** * 依赖版本号. 为 null 时则为不限制版本. * @@ -34,6 +36,16 @@ public data class PluginDependency( */ public val isOptional: Boolean = false ) { + /** + * @see PluginDependency + */ + public constructor(name: String, isOptional: Boolean = false) : this( + name, null, isOptional + ) + + /** + * @see PluginDependency + */ public constructor(name: String, version: String, isOptional: Boolean) : this( name, Semver(version, Semver.SemverType.IVY), diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt index c9e40ad0d..6ef60f81a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.console.plugin.description import com.vdurmont.semver4j.Semver import net.mamoe.mirai.console.plugin.Plugin +import net.mamoe.mirai.console.plugin.PluginLoadException /** @@ -27,14 +28,44 @@ public interface PluginDescription { public val kind: PluginKind /** - * 插件名称. 不允许存在 ":", 推荐全英文. + * 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'. * - * 插件名称不能完全是以下其中一种. + * - 类似于 Java 包名, 插件 ID 需要 '域名.名称' 格式, 如 `net.mamoe.mirai.example-plugin` + * - 域名和名称都是必须的 + * - '.' 不允许位于首位或末尾 + * - '-' 和 '_' 仅允许存在于两个英文字母之间 + * + * ID 在插件发布后就应该保持不变, 以便其他插件添加依赖. + * + * 插件 ID 的域名和名称都不能完全是以下其中一个 ([FORBIDDEN_ID_WORDS]). + * - "console" + * - "main" + * - "plugin" + * - "config" + * - "data" + * + * + * ID 用于指令权限等一些内部处理 + * + * @see FORBIDDEN_ID_LETTERS + * @see FORBIDDEN_ID_WORDS + */ + public val id: String + + /** + * 插件名称. 允许中文, 允许各类符号. + * + * 插件名称不能完全是以下其中一种 ([FORBIDDEN_ID_WORDS]). * - console * - main * - plugin * - config * - data + * + * 插件名称用于显示给用户. + * + * @see FORBIDDEN_ID_LETTERS + * @see FORBIDDEN_ID_WORDS */ public val name: String @@ -46,7 +77,21 @@ public interface PluginDescription { /** * 插件版本. * - * 语法参考: ([语义化版本 2.0.0](https://semver.org/lang/zh-CN/)) + * 语法参考: ([语义化版本 2.0.0](https://semver.org/lang/zh-CN/)). + * + * 合法的版本号示例: + * - `1.0.0` + * - `1.0` + * - `1.0-M1` + * - `1.0.0-M1` + * - `1.0.0-M2-1` + * - `1` (尽管非常不建议这么做) + * + * 非法版本号实例: + * - `DEBUG-1` + * - `-1.0` + * - `v1.0` (不允许 "v") + * - `V1.0` (不允许 "V") * * @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本. */ @@ -62,6 +107,77 @@ public interface PluginDescription { * * @see PluginDependency */ - public val dependencies: List + public val dependencies: Set + + public companion object { + public val FORBIDDEN_ID_LETTERS: Array = "~!@#$%^&*()+/*<>{}|[]\\?".map(Char::toString).toTypedArray() + public val FORBIDDEN_ID_WORDS: Array = arrayOf("main", "console", "plugin", "config", "data") + + /** + * 依次检查 [PluginDescription] 的 [PluginDescription.id], [PluginDescription.name], [PluginDescription.dependencies] 的合法性 + * + * @throws IllegalPluginDescriptionException 当不合法时抛出. + */ + @Throws(IllegalPluginDescriptionException::class) + public fun checkPluginDescription(instance: PluginDescription) { + kotlin.runCatching { + checkPluginId(instance.id) + checkPluginName(instance.name) + checkDependencies(instance.id, instance.dependencies) + }.getOrElse { + throw IllegalPluginDescriptionException( + "Illegal description. Plugin ${instance.name} (${instance.id})", + it + ) + } + } + + /** + * 检查 [PluginDescription.id] 的合法性. + * + * @throws IllegalPluginDescriptionException 当不合法时抛出. + */ + @Throws(IllegalPluginDescriptionException::class) + public fun checkPluginId(id: String) { + if (id.isBlank()) throw IllegalPluginDescriptionException("Plugin id cannot be blank") + if (id.count { it == '.' } < 2) throw IllegalPluginDescriptionException("'$id' is illegal. Plugin id must consist of both domain and name. ") + + FORBIDDEN_ID_LETTERS.firstOrNull { it in id }?.let { illegal -> + throw IllegalPluginDescriptionException("Plugin id contains illegal char: $illegal.") + } + + val idSections = id.split('.') + FORBIDDEN_ID_WORDS.firstOrNull { it in idSections }?.let { illegal -> + throw IllegalPluginDescriptionException("Plugin id contains illegal word: '$illegal'.") + } + } + + /** + * 检查 [PluginDescription.name] 的合法性. + * + * @throws IllegalPluginDescriptionException 当不合法时抛出. + */ + @Throws(IllegalPluginDescriptionException::class) + public fun checkPluginName(name: String) { + if (name.isBlank()) throw IllegalPluginDescriptionException("Plugin name cannot be blank") + FORBIDDEN_ID_WORDS.firstOrNull { it in name }?.let { illegal -> + throw IllegalPluginDescriptionException("Plugin name is illegal: '$illegal'.") + } + } + + /** + * 检查 [PluginDescription.dependencies] 的合法性. + * + * @throws IllegalPluginDescriptionException 当不合法时抛出. + */ + @Throws(IllegalPluginDescriptionException::class) + public fun checkDependencies(pluginId: String, dependencies: Set) { + if (dependencies.distinctBy { it.id }.size != dependencies.size) + throw PluginLoadException("Duplicated dependency detected: A plugin cannot depend on different versions of dependencies of the same id") + + if (dependencies.any { it.id == pluginId }) + throw PluginLoadException("Recursive dependency detected: A plugin cannot depend on itself") + } + } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt index 6f9de4245..363f356df 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt @@ -7,7 +7,7 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("unused") +@file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_member") package net.mamoe.mirai.console.plugin.jvm @@ -15,35 +15,207 @@ import com.vdurmont.semver4j.Semver import net.mamoe.mirai.console.plugin.description.PluginDependency import net.mamoe.mirai.console.plugin.description.PluginDescription import net.mamoe.mirai.console.plugin.description.PluginKind +import kotlin.internal.LowPriorityInOverloadResolution /** * JVM 插件的描述. 通常作为 `plugin.yml` + * + * 请不要自行实现 [JvmPluginDescription] 接口. 它不具有继承稳定性. + * * @see SimpleJvmPluginDescription + * @see JvmPluginDescriptionBuilder */ -public interface JvmPluginDescription : PluginDescription +public interface JvmPluginDescription : PluginDescription { + public companion object { + /** + * 构建 [JvmPluginDescription] + * @see JvmPluginDescriptionBuilder + */ + @JvmSynthetic + public operator fun invoke( + name: String, + version: String, + block: JvmPluginDescriptionBuilder.() -> Unit = {} + ): JvmPluginDescription = JvmPluginDescriptionBuilder(name, version).apply(block).build() + + /** + * 构建 [JvmPluginDescription] + * @see JvmPluginDescriptionBuilder + */ + @JvmSynthetic + public operator fun invoke( + name: String, + version: Semver, + block: JvmPluginDescriptionBuilder.() -> Unit = {} + ): JvmPluginDescription = JvmPluginDescriptionBuilder(name, version).apply(block).build() + } +} /** + * [JvmPluginDescription] 构建器. + * + * #### Kotlin Example + * ``` + * val desc = JvmPluginDescription("org.example.example-plugin", "1.0.0") { + * info("This is an example plugin") + * dependsOn("org.example.another-plugin") + * } + * ``` + * + * #### Java Example + * ``` + * JvmPluginDescription desc = new JvmPluginDescriptionBuilder("org.example.example-plugin", "1.0.0") + * .info("This is an example plugin") + * .dependsOn("org.example.another-plugin") + * .build() + * ``` + * + * @see [JvmPluginDescription.invoke] + */ +public class JvmPluginDescriptionBuilder( + private var id: String, + private var version: Semver, +) { + public constructor(name: String, version: String) : this(name, Semver(version, Semver.SemverType.LOOSE)) + + private var name: String = id + private var author: String = "" + private var info: String = "" + private var dependencies: MutableSet = mutableSetOf() + private var kind: PluginKind = PluginKind.NORMAL + + @ILoveKuriyamaMiraiForever + public fun name(value: String): JvmPluginDescriptionBuilder = apply { this.name = value.trim() } + + @ILoveKuriyamaMiraiForever + public fun version(value: String): JvmPluginDescriptionBuilder = + apply { this.version = Semver(value, Semver.SemverType.LOOSE) } + + @ILoveKuriyamaMiraiForever + public fun version(value: Semver): JvmPluginDescriptionBuilder = apply { this.version = value } + + @ILoveKuriyamaMiraiForever + public fun id(value: String): JvmPluginDescriptionBuilder = apply { this.id = value.trim() } + + @ILoveKuriyamaMiraiForever + public fun author(value: String): JvmPluginDescriptionBuilder = apply { this.author = value.trim() } + + @ILoveKuriyamaMiraiForever + public fun info(value: String): JvmPluginDescriptionBuilder = apply { this.info = value.trimIndent() } + + @ILoveKuriyamaMiraiForever + public fun kind(value: PluginKind): JvmPluginDescriptionBuilder = apply { this.kind = value } + + @ILoveKuriyamaMiraiForever + public fun normalPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.NORMAL } + + @ILoveKuriyamaMiraiForever + public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.LOADER } + + @ILoveKuriyamaMiraiForever + public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder = + apply { this.kind = PluginKind.HIGH_PRIORITY_EXTENSIONS } + + @ILoveKuriyamaMiraiForever + public fun dependsOn( + pluginId: String, + version: String? = null, + isOptional: Boolean = false + ): JvmPluginDescriptionBuilder = apply { + if (version == null) this.dependencies.add(PluginDependency(pluginId, version, isOptional)) + else this.dependencies.add(PluginDependency(pluginId, version, isOptional)) + } + + @ILoveKuriyamaMiraiForever + public fun setDependencies( + value: Set + ): JvmPluginDescriptionBuilder = apply { + this.dependencies = value.toMutableSet() + } + + @ILoveKuriyamaMiraiForever + public fun dependsOn( + vararg dependencies: PluginDependency + ): JvmPluginDescriptionBuilder = apply { + for (dependency in dependencies) { + this.dependencies.add(dependency) + } + } + + @ILoveKuriyamaMiraiForever + public fun dependsOn( + pluginId: String, + version: Semver? = null, + isOptional: Boolean = false + ): JvmPluginDescriptionBuilder = apply { this.dependencies.add(PluginDependency(pluginId, version, isOptional)) } + + + @Suppress("DEPRECATION_ERROR") + public fun build(): JvmPluginDescription = + SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) + + @Retention(AnnotationRetention.SOURCE) + @DslMarker + private annotation class ILoveKuriyamaMiraiForever // https://zh.moegirl.org.cn/zh-cn/%E6%A0%97%E5%B1%B1%E6%9C%AA%E6%9D%A5 +} + +/** + * @constructor 推荐使用带名称的参数, 而不要按位置摆放. + * * @see JvmPluginDescription */ +@Deprecated( + """ + 将在 1.0-RC 删除. 请使用 JvmPluginDescription. +""", + replaceWith = ReplaceWith( + "JvmPluginDescription", + "net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription" + ), + level = DeprecationLevel.ERROR +) public data class SimpleJvmPluginDescription +@Deprecated( + """ + 构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder. +""", + replaceWith = ReplaceWith( + "JvmPluginDescription(name, version) {}", + "net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke" + ), + level = DeprecationLevel.ERROR +) @JvmOverloads public constructor( public override val name: String, public override val version: Semver, + public override val id: String = name, public override val author: String = "", public override val info: String = "", - public override val dependencies: List = listOf(), + public override val dependencies: Set = setOf(), public override val kind: PluginKind = PluginKind.NORMAL, ) : JvmPluginDescription { + @Deprecated( + """ + 构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder. +""", + replaceWith = ReplaceWith( + "JvmPluginDescription.invoke(name, version) {}", + "net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke" + ), + level = DeprecationLevel.ERROR + ) + @Suppress("DEPRECATION_ERROR") @JvmOverloads public constructor( name: String, version: String, + id: String = name, author: String = "", info: String = "", - dependencies: List = listOf(), + dependencies: Set = setOf(), kind: PluginKind = PluginKind.NORMAL, - ) : this(name, Semver(version, Semver.SemverType.LOOSE), author, info, dependencies, kind) + ) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, kind) init { require(!name.contains(':')) { "':' is forbidden in plugin name" } @@ -59,15 +231,17 @@ public data class SimpleJvmPluginDescription ), level = DeprecationLevel.WARNING ) -@Suppress("FunctionName") +@LowPriorityInOverloadResolution +@Suppress("DEPRECATION_ERROR", "FunctionName") public fun JvmPluginDescription( name: String, version: Semver, + id: String = name, author: String = "", info: String = "", - dependencies: List = listOf(), + dependencies: Set = setOf(), kind: PluginKind = PluginKind.NORMAL -): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) @Deprecated( "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", @@ -77,12 +251,14 @@ public fun JvmPluginDescription( ), level = DeprecationLevel.WARNING ) -@Suppress("FunctionName") +@LowPriorityInOverloadResolution +@Suppress("DEPRECATION_ERROR", "FunctionName") public fun JvmPluginDescription( name: String, version: String, + id: String = name, author: String = "", info: String = "", - dependencies: List = listOf(), + dependencies: Set = setOf(), kind: PluginKind = PluginKind.NORMAL -): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt deleted file mode 100644 index 6007e47df..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt +++ /dev/null @@ -1,36 +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 - */ - -@file:Suppress("NOTHING_TO_INLINE") -@file:JvmMultifileClass -@file:JvmName("ConsoleUtils") - -package net.mamoe.mirai.console.util - -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.internal.data.builtins.BotManagerImpl -import net.mamoe.mirai.contact.User - -public interface BotManager { - /** - * 判断此用户是否为 console 管理员 - */ - public val User.isManager: Boolean - public val Bot.managers: List - - public fun Bot.removeManager(id: Long): Boolean - public fun Bot.addManager(id: Long): Boolean - - public companion object INSTANCE : BotManager { // kotlin import handler doesn't recognize delegation. - override fun Bot.addManager(id: Long): Boolean = BotManagerImpl.run { addManager(id) } - override fun Bot.removeManager(id: Long): Boolean = BotManagerImpl.run { removeManager(id) } - override val User.isManager: Boolean get() = BotManagerImpl.run { isManager } - override val Bot.managers: List get() = BotManagerImpl.run { managers } - } -} \ No newline at end of file diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/data/SettingTest.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/data/SettingTest.kt index d5c01c6f9..861101fd1 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/data/SettingTest.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/data/SettingTest.kt @@ -10,8 +10,8 @@ package net.mamoe.mirai.console.data import kotlinx.serialization.json.Json +import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin -import net.mamoe.mirai.console.plugin.jvm.SimpleJvmPluginDescription import net.mamoe.mirai.console.util.ConsoleInternalAPI import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -21,7 +21,7 @@ import kotlin.test.assertSame internal class PluginDataTest { object MyPlugin : KotlinPlugin( - SimpleJvmPluginDescription( + JvmPluginDescription( "1", "2" ) ) From 8dd302073bbc511e58453640e4e21748666cd36b Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 10 Sep 2020 10:44:49 +0800 Subject: [PATCH 62/62] Add builtin Permission command --- .../mirai/console/command/BuiltInCommands.kt | 74 +++++++++++-------- .../CommandArgumentParserBuiltins.kt | 30 ++++++++ .../AbstractConcurrentPermissionService.kt | 1 + .../permission/BuiltInPermissionServices.kt | 2 + .../permission/PermissibleIdentifier.kt | 31 ++++---- .../console/permission/PermissionService.kt | 13 ++++ 6 files changed, 108 insertions(+), 43 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt index 2693c59ef..8f1a28139 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt @@ -16,17 +16,23 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register +import net.mamoe.mirai.console.command.description.PermissibleIdentifierArgumentParser +import net.mamoe.mirai.console.command.description.PermissionIdArgumentParser +import net.mamoe.mirai.console.command.description.buildCommandArgumentContext import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip -import net.mamoe.mirai.console.util.BotManager.INSTANCE.addManager -import net.mamoe.mirai.console.util.BotManager.INSTANCE.managers -import net.mamoe.mirai.console.util.BotManager.INSTANCE.removeManager +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissibleIdentifier +import net.mamoe.mirai.console.permission.PermissionId +import net.mamoe.mirai.console.permission.PermissionService +import net.mamoe.mirai.console.permission.PermissionService.Companion.denyPermission +import net.mamoe.mirai.console.permission.PermissionService.Companion.getGrantedPermissions +import net.mamoe.mirai.console.permission.PermissionService.Companion.grantPermission import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.events.EventCancelledException -import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.message.nextMessageOrNull import net.mamoe.mirai.utils.secondsToMillis import kotlin.concurrent.thread @@ -57,30 +63,6 @@ public object BuiltInCommands { } } - public object Managers : CompositeCommand( - ConsoleCommandOwner, "managers", - description = "Manage the managers for each bot" - ), BuiltInCommand { - @SubCommand - public suspend fun CommandSender.add(target: User) { - target.bot.addManager(target.id) - sendMessage("已成功添加 ${target.render()} 为 ${target.bot.render()} 的管理员") - } - - @SubCommand - public suspend fun CommandSender.remove(target: User) { - target.bot.removeManager(target.id) - sendMessage("已成功取消 ${target.render()} 对 ${target.bot.render()} 的管理员权限") - } - - @SubCommand - public suspend fun CommandSender.list(bot: Bot) { - sendMessage("$bot 的管理员列表:\n" + bot.managers.joinToString("\n") { - bot.getFriendOrNull(it)?.render() ?: it.toString() - }) - } - } - public object Help : SimpleCommand( ConsoleCommandOwner, "help", description = "Command list" @@ -136,7 +118,7 @@ public object BuiltInCommands { } public object Login : SimpleCommand( - ConsoleCommandOwner, "login", + ConsoleCommandOwner, "login", "登录", description = "Log in a bot account." ), BuiltInCommand { @Handler @@ -161,6 +143,40 @@ public object BuiltInCommands { ) } } + + @OptIn(ExperimentalPermission::class) + public object Permission : CompositeCommand( + ConsoleCommandOwner, "permission", "权限", + description = "Manage permissions", + overrideContext = buildCommandArgumentContext { + PermissibleIdentifier::class with PermissibleIdentifierArgumentParser + PermissionId::class with PermissionIdArgumentParser + } + ), BuiltInCommand { + // TODO: 2020/9/10 improve Permission command + @SubCommand + public suspend fun CommandSender.grant(target: PermissibleIdentifier, permission: PermissionId) { + target.grantPermission(permission) + sendMessage("OK") + } + + @SubCommand + public suspend fun CommandSender.deny(target: PermissibleIdentifier, permission: PermissionId) { + target.denyPermission(permission) + sendMessage("OK") + } + + @SubCommand("grantedPermissions", "gp") + public suspend fun CommandSender.grantedPermissions(target: PermissibleIdentifier) { + val grantedPermissions = target.getGrantedPermissions() + sendMessage(grantedPermissions.joinToString("\n") { it.id.toString() }) + } + + @SubCommand("listPermissions", "lp") + public suspend fun CommandSender.listPermissions() { + sendMessage(PermissionService.INSTANCE.getRegisteredPermissions().joinToString("\n") { it.id.toString() }) + } + } } internal inline fun ignoreException(block: () -> R): R? { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParserBuiltins.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParserBuiltins.kt index a72d00f9d..e43b9a190 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParserBuiltins.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParserBuiltins.kt @@ -11,7 +11,12 @@ package net.mamoe.mirai.console.command.description import net.mamoe.mirai.Bot import net.mamoe.mirai.console.command.* +import net.mamoe.mirai.console.command.CommandSender.Companion.asCommandSender import net.mamoe.mirai.console.internal.command.fuzzySearchMember +import net.mamoe.mirai.console.permission.AbstractPermissibleIdentifier +import net.mamoe.mirai.console.permission.ExperimentalPermission +import net.mamoe.mirai.console.permission.PermissibleIdentifier +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.contact.* import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.getGroupOrNull @@ -303,6 +308,31 @@ public object ExistingMemberArgumentParser : InternalCommandArgumentParserExtens } } +@ExperimentalPermission +public object PermissionIdArgumentParser : CommandArgumentParser { + override fun parse(raw: String, sender: CommandSender): PermissionId { + return kotlin.runCatching { PermissionId.parseFromString(raw) }.getOrElse { + illegalArgument("无法解析 $raw 为 PermissionId") + } + } +} + +@ExperimentalPermission +public object PermissibleIdentifierArgumentParser : CommandArgumentParser { + override fun parse(raw: String, sender: CommandSender): PermissibleIdentifier { + return kotlin.runCatching { AbstractPermissibleIdentifier.parseFromString(raw) }.getOrElse { + illegalArgument("无法解析 $raw 为 PermissionId") + } + } + + override fun parse(raw: MessageContent, sender: CommandSender): PermissibleIdentifier { + if (raw is At) { + return ExistingUserArgumentParser.parse(raw, sender).asCommandSender(false).identifier + } + return super.parse(raw, sender) + } +} + internal interface InternalCommandArgumentParserExtensions : CommandArgumentParser { fun String.parseToLongOrFail(): Long = toLongOrNull() ?: illegalArgument("无法解析 $this 为整数") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt index f76a769ef..12baee7b6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/AbstractConcurrentPermissionService.kt @@ -46,6 +46,7 @@ public abstract class AbstractConcurrentPermissionService

: Perm grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) } + override fun getRegisteredPermissions(): Sequence

= permissions.values.asSequence() public override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

= sequence

{ for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt index 9a7279545..de50373cb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/BuiltInPermissionServices.kt @@ -37,6 +37,7 @@ public object AllGrantPermissionService : PermissionService { } override fun get(id: PermissionId): PermissionImpl? = all[id] + override fun getRegisteredPermissions(): Sequence = all.values.asSequence() override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence = all.values.asSequence() @@ -68,6 +69,7 @@ public object AllDenyPermissionService : PermissionService { } override fun get(id: PermissionId): PermissionImpl? = all[id] + override fun getRegisteredPermissions(): Sequence = all.values.asSequence() override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence = emptySequence() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt index 544175a37..e65719740 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissibleIdentifier.kt @@ -43,13 +43,26 @@ public interface PermissibleIdentifier { public sealed class AbstractPermissibleIdentifier( public final override vararg val parents: PermissibleIdentifier ) : PermissibleIdentifier { - internal companion object { - val objects by lazy { + public companion object { + @JvmStatic + public fun parseFromString(string: String): AbstractPermissibleIdentifier { + val str = string.trim() + objects.find { it.toString() == str }?.let { return it as AbstractPermissibleIdentifier } + for ((regex, block) in regexes) { + val result = regex.find(str) ?: continue + if (result.range.last != str.lastIndex) continue + if (result.range.first != 0) continue + return result.destructured.run(block) + } + error("Cannot deserialize '$str' as AbstractPermissibleIdentifier") + } + + internal val objects by lazy { // https://youtrack.jetbrains.com/issue/KT-41782 AbstractPermissibleIdentifier::class.nestedClasses.mapNotNull { it.objectInstance } } - val regexes: List AbstractPermissibleIdentifier>> = + internal val regexes: List AbstractPermissibleIdentifier>> = listOf( Regex("""ExactGroup\(\s*([0-9]+)\s*\)""") to { (id) -> ExactGroup(id.toLong()) }, Regex("""ExactFriend\(\s*([0-9]+)\s*\)""") to { (id) -> ExactFriend(id.toLong()) }, @@ -69,17 +82,7 @@ public sealed class AbstractPermissibleIdentifier( public object AsStringSerializer : KSerializer by String.serializer().map( serializer = { it.toString() }, - deserializer = d@{ str -> - @Suppress("NAME_SHADOWING") val str = str.trim() - objects.find { it.toString() == str }?.let { return@d it as AbstractPermissibleIdentifier } - for ((regex, block) in regexes) { - val result = regex.find(str) ?: continue - if (result.range.last != str.lastIndex) continue - if (result.range.first != 0) continue - return@d result.destructured.run(block) - } - error("Cannot deserialize '$str' as AbstractPermissibleIdentifier") - } + deserializer = d@{ str -> parseFromString(str) } ) public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt index d517392bf..42e30cb20 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionService.kt @@ -28,6 +28,7 @@ public interface PermissionService

{ public operator fun get(id: PermissionId): P? + public fun getRegisteredPermissions(): Sequence

public fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence

public fun testPermission(permissibleIdentifier: PermissibleIdentifier, permission: P): Boolean { @@ -74,6 +75,18 @@ public interface PermissionService

{ INSTANCE.checkType(permission::class).grant(this, permission) } + public fun PermissibleIdentifier.grantPermission(permissionId: PermissionId) { + grantPermission(permissionId.findCorrespondingPermissionOrFail()) + } + + public fun PermissibleIdentifier.denyPermission(permission: Permission) { + INSTANCE.checkType(permission::class).deny(this, permission) + } + + public fun PermissibleIdentifier.denyPermission(permissionId: PermissionId) { + denyPermission(permissionId.findCorrespondingPermissionOrFail()) + } + public fun Permissible.hasPermission(permission: Permission): Boolean = permission.testPermission(this@hasPermission)