diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/MessageScopeCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/MessageScopeCodegen.kt index 0f4cf60b2..69b39e2e3 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/MessageScopeCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/MessageScopeCodegen.kt @@ -84,46 +84,46 @@ internal object MessageScopeCodegen { ) appendLine() } +// +// for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { +// appendKCode( +// """ +// @LowPriorityInOverloadResolution +// public fun ${a}.scopeWith(vararg others: ${b}): MessageScope { +// return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +// } +// """ +// ) +// appendLine() +// } for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { appendKCode( """ @LowPriorityInOverloadResolution - public fun ${a}.scopeWith(vararg others: ${b}): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } + public fun ${a}?.scopeWith(vararg others: ${b}?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } """ ) appendLine() } +// +// for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { +// appendKCode( +// """ +// public fun ${a}.scopeWith(other: ${b}): MessageScope { +// return CombinedScope(asMessageScope(), other.asMessageScope()) +// } +// """ +// ) +// appendLine() +// } for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { appendKCode( """ - @LowPriorityInOverloadResolution - public fun ${a}?.scopeWithNotNull(vararg others: ${b}?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } - } - """ - ) - appendLine() - } - - for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { - appendKCode( - """ - public fun ${a}.scopeWith(other: ${b}): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) - } - """ - ) - appendLine() - } - - for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { - appendKCode( - """ - public fun ${a}?.scopeWithNotNull(other: ${b}?): MessageScope { + public fun ${a}?.scopeWith(other: ${b}?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -137,11 +137,22 @@ internal object MessageScopeCodegen { ) appendLine() } +// +// for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { +// appendKCode( +// """ +// public inline fun ${a}.scopeWith(vararg others: ${b}, action: MessageScope.() -> R): R { +// return scopeWith(*others).invoke(action) +// } +// """ +// ) +// appendLine() +// } for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { appendKCode( """ - public inline fun ${a}.scopeWith(vararg others: ${b}, action: MessageScope.() -> R): R { + public inline fun ${a}?.scopeWith(vararg others: ${b}?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } """ @@ -149,17 +160,6 @@ internal object MessageScopeCodegen { appendLine() } - for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { - appendKCode( - """ - public inline fun ${a}?.scopeWithNotNull(vararg others: ${b}?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) - } - """ - ) - appendLine() - } - for (a in (TypeCandidatesForMessageScope + KtMessageScope)) { appendKCode( """ diff --git a/backend/mirai-console/README.md b/backend/mirai-console/README.md deleted file mode 100644 index 901c73a94..000000000 --- a/backend/mirai-console/README.md +++ /dev/null @@ -1,193 +0,0 @@ -# mirai-console backend - -欢迎来到 mirai-console 后端开发文档。 - -[`Plugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt -[`PluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt -[`PluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt -[`PluginManager`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt -[`JarPluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt -[`JvmPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt -[`JvmPluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt -[`AbstractJvmPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin.kt -[`KotlinPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/KotlinPlugin.kt -[`JavaPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPlugin.kt - - -[`PluginData`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginData.kt -[`PluginConfig`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt -[`PluginDataStorage`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt - -[`MiraiConsole`]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt -[`MiraiConsoleImplementation`]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt - - -[`Command`]: src/main/kotlin/net/mamoe/mirai/console/command/Command.kt -[`CompositeCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt -[`SimpleCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt -[`RawCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt -[`CommandManager`]: src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt - -[`BotManager`]: src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt -[`Annotations`]: src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt -[`ConsoleInput`]: src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt -[`JavaPluginScheduler`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt -[`ResourceContainer`]: src/main/kotlin/net/mamoe/mirai/console/plugin/ResourceContainer.kt - - -## 基础 - -### `Plugin` 模块 - -Console 支持拥有强扩展性的插件加载器。内建 JVM 插件支持 ([`JarPluginLoader`])。 - -#### [插件加载器 `PluginLoader`][`PluginLoader`] 和 [插件管理器][`PluginManager`] -Console 本身是一套高扩展性的「框架」,拥有通用的 [插件加载器][`PluginLoader`]。 - -Console 内置 [`JarPluginLoader`],支持加载使用 Kotlin、 Java,或其他 JVM 平台编程语言并打包为 ‘jar’ 的插件 (详见下文 `JvmPlugin`)。 - -扩展的 [插件加载器][`PluginLoader`] 可以由一个特别的 [JVM 插件][`JvmPlugin`] 提供。 - - -##### 服务器启动过程中的插件加载流程 - -在服务器启动过程中, Console 首先加载那些提供扩展 [插件加载器][`PluginLoader`] 的插件。 -随后对插件按依赖顺序调用 `onLoad()`, 告知插件主类加载完毕, 相关依赖解决完毕. -当所有插件的 `onLoad()` 都被调用后, [`PluginManager`] 按依赖顺序依次调用 `onEnable()` - -如果 A 依赖 B, B 依赖 C. 那么启动时的调用顺序为: -`C.onLoad()` -> `B.onLoad()` -> `A.onLoad()` -> `C.onEnable` -> `B.onEnable()` -> `A.onEnable()` - -#### [`Plugin`] -所有 Console 插件都必须实现 [`Plugin`] 接口。 - -`Plugin` 很通用,它只拥有很少的成员: -```kotlin -interface Plugin : CommandOwner { - val isEnabled: Boolean - val loader: PluginLoader<*, *> // 能处理这个 Plugin 的 PluginLoader -} -``` - -[`Plugin`] 可在相应 [插件加载器 `PluginLoader`][`PluginLoader`] 的帮助下,成为任何语言实现的插件与 Console 建立联系的桥梁。 - - -#### [JVM 插件][`JvmPlugin`] - -##### [`JvmPlugin`] - -```kotlin -interface JvmPlugin : Plugin, CoroutineScope, PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder { - val logger: MiraiLogger - val description: JvmPluginDescription - val loader: JarPluginLoader - fun loadPluginData(clazz: Class): T - fun loadPluginConfig(clazz: Class): T - fun onLoad() {} - fun onEnable() {} - fun onDisable() {} -} -``` - -##### 提供插件信息 - -JVM 插件, 通常需要打包为 `jar` 后才能被加载. Console 使用类似 Java ServiceLoader 的方式加载插件. - -- 方法 A. (推荐) 自动创建 service 文件 (使用 Google auto-service) - 在 `build.gradle.kts` 添加: - ```kotlin - plugins { - kotlin("kapt") - } - dependencies { - val autoService = "1.0-rc7" - kapt("com.google.auto.service", "auto-service", autoService) - compileOnly("com.google.auto.service", "auto-service-annotations", autoService) - } - ``` - *对于 `build.gradle` 用户, 请自行按照 Groovy DSL 语法翻译* - -- 方法 B. 手动创建 service 文件 - 在 `jar` 内 `META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin` 文件内存放插件主类全名. - - -**注意**: -- 插件自身的版本要求遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) 规范, 合格的版本例如: `1.0.0`, `1.0`, `1.0-M1`, `1.0-pre-1` -- 插件依赖的版本遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) 规范, 同时支持 [Apache Ivy 风格表示方法](http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html). - - -#### 实现 Kotlin 插件 - -一个 Kotlin 插件的主类通常需: -- 继承 [`KotlinPlugin`] -- 访问权限为 `public` 或默认 (不指定) - -```kotlin -@AutoService(JvmPlugin::class) // 让 Console 知道这个 object 是一个插件主类. -object SchedulePlugin : KotlinPlugin( - SimpleJvmPluginDescription( // 插件的描述, name 和 version 是必须的 - name = "Schedule", - version = "1.0.0", - // author, description, ... - ) -) { - // ... -} -``` - -#### 实现 Java 插件 - -一个 Java 插件的主类通常需: -- 继承 [`KotlinPlugin`] -- 访问权限为 `public` 或默认 (不指定) - -(推荐) 静态初始化: -```java -@AutoService(JvmPlugin.class) -public final class JExample extends JavaPlugin { - public static final JExample INSTANCE = new JExample(); // 可以像 Kotlin 一样静态初始化单例 - private JExample() { - super(new SimpleJvmPluginDescription( - "JExample", // name - "1.0.0" // version - )); - } -} -``` - -由 Console 初始化: -```java -@AutoService(JvmPlugin.class) -public final class JExample extends JavaPlugin { - private static final JExample instance; - public static JExample getInstance() { - return instance; - } - public JExample() { // 此时必须 public - super(new SimpleJvmPluginDescription( - "JExample", // name - "1.0.0" // version - )); - instance = this; - } -} -``` - -#### 获取资源文件 [`ResourceContainer`] - -[`JvmPlugin`] 实现接口 [`ResourceContainer`], 可在 `jar` 包内搜索资源文件. - -提供三个获取方法: -```kotlin -interface ResourceContainer { - fun getResourceAsStream(path: String): InputStream? - fun getResource(path: String): String? - fun getResource(path: String, charset: Charset): String? -} -``` - -### [`PluginData`] 模块 - -[`PluginData`] - -... 待续 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 8f1a28139..770b9dbb3 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,13 @@ 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.command.description.* 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.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.* import net.mamoe.mirai.console.permission.PermissionService.Companion.denyPermission +import net.mamoe.mirai.console.permission.PermissionService.Companion.findCorrespondingPermissionOrFail 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 @@ -51,6 +47,7 @@ internal interface BuiltInCommandInternal : Command */ @ConsoleExperimentalAPI @Suppress("unused") +@OptIn(ExperimentalPermission::class) public object BuiltInCommands { public val all: Array by lazy { @@ -63,9 +60,10 @@ public object BuiltInCommands { } } - public object Help : SimpleCommand( + public object HelpCommand : SimpleCommand( ConsoleCommandOwner, "help", - description = "Command list" + description = "Command list", + parentPermission = RootConsoleBuiltInPermission, ), BuiltInCommand { @Handler public suspend fun CommandSender.handle() { @@ -82,9 +80,10 @@ public object BuiltInCommands { }) } - public object Stop : SimpleCommand( + public object StopCommand : SimpleCommand( ConsoleCommandOwner, "stop", "shutdown", "exit", - description = "Stop the whole world." + description = "Stop the whole world.", + parentPermission = RootConsoleBuiltInPermission, ), BuiltInCommand { private val closingLock = Mutex() @@ -117,9 +116,10 @@ public object BuiltInCommands { } } - public object Login : SimpleCommand( + public object LoginCommand : SimpleCommand( ConsoleCommandOwner, "login", "登录", - description = "Log in a bot account." + description = "Log in a bot account.", + parentPermission = RootConsoleBuiltInPermission, ), BuiltInCommand { @Handler public suspend fun CommandSender.handle(id: Long, password: String) { @@ -145,23 +145,28 @@ public object BuiltInCommands { } @OptIn(ExperimentalPermission::class) - public object Permission : CompositeCommand( - ConsoleCommandOwner, "permission", "权限", + public object PermissionCommand : CompositeCommand( + ConsoleCommandOwner, "permission", "权限", "perm", description = "Manage permissions", overrideContext = buildCommandArgumentContext { PermissibleIdentifier::class with PermissibleIdentifierArgumentParser - PermissionId::class with PermissionIdArgumentParser - } + Permission::class with PermissionIdArgumentParser.map { id -> + kotlin.runCatching { + id.findCorrespondingPermissionOrFail() + }.getOrElse { illegalArgument("指令不存在: $id", it) } + } + }, + parentPermission = RootConsoleBuiltInPermission, ), BuiltInCommand { // TODO: 2020/9/10 improve Permission command @SubCommand - public suspend fun CommandSender.grant(target: PermissibleIdentifier, permission: PermissionId) { + public suspend fun CommandSender.grant(target: PermissibleIdentifier, permission: Permission) { target.grantPermission(permission) sendMessage("OK") } @SubCommand - public suspend fun CommandSender.deny(target: PermissibleIdentifier, permission: PermissionId) { + public suspend fun CommandSender.deny(target: PermissibleIdentifier, permission: Permission) { target.denyPermission(permission) sendMessage("OK") } 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 cf757ee70..2bbdc769a 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,11 +15,10 @@ 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.createOrFindCommandPermission 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 @@ -107,7 +106,7 @@ public abstract class AbstractCommand public override val owner: CommandOwner, vararg names: String, description: String = "", - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { @@ -118,5 +117,5 @@ public abstract class AbstractCommand }.toTypedArray() @OptIn(ExperimentalPermission::class) - public override val permission: Permission by lazy { createCommandPermission(parentPermission) } + public override val permission: Permission by lazy { createOrFindCommandPermission(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 a8682841d..bbe1bd642 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,7 @@ 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.permission.RootPermission +import net.mamoe.mirai.console.permission.* import net.mamoe.mirai.console.plugin.jvm.JvmPlugin /** @@ -27,12 +24,12 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin @OptIn(ExperimentalPermission::class) public interface CommandOwner : PermissionIdNamespace { /** - * 此 [PermissionIdNamespace] 拥有的指令都默认将 [basePermission] 作为父权限. + * 此 [PermissionIdNamespace] 拥有的指令都默认将 [parentPermission] 作为父权限. * * TODO document */ @ExperimentalPermission - public val basePermission: PermissionId + public val parentPermission: Permission } /** @@ -40,8 +37,8 @@ public interface CommandOwner : PermissionIdNamespace { */ internal object ConsoleCommandOwner : CommandOwner { @ExperimentalPermission - override val basePermission: PermissionId - get() = RootPermission.id + override val parentPermission: Permission + get() = RootPermission @ExperimentalPermission override fun permissionId(id: String): PermissionId = PermissionId("console", id) 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 02c8fc910..7cea5b132 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 @@ -21,7 +21,7 @@ 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.permission.Permission import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.message.data.MessageChain import kotlin.annotation.AnnotationRetention.RUNTIME @@ -86,7 +86,7 @@ public abstract class CompositeCommand @OptIn(ExperimentalPermission::class) con owner: CommandOwner, vararg names: String, description: String = "no description available", - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, prefixOptional: Boolean = false, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext ) : Command, AbstractReflectionCommand(owner, names, description, parentPermission, prefixOptional), @@ -110,12 +110,6 @@ public abstract class CompositeCommand @OptIn(ExperimentalPermission::class) con @Target(FUNCTION) protected annotation class SubCommand(vararg val value: String) - /** 指定子指令要求的权限 */ - @Retention(RUNTIME) - @Target(FUNCTION) - @ExperimentalPermission - protected annotation class Permission(val value: String) - /** 指令描述 */ @Retention(RUNTIME) @Target(FUNCTION) 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 47ac57558..c52a32052 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,10 +14,9 @@ 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.internal.command.createOrFindCommandPermission 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 /** @@ -44,12 +43,12 @@ public abstract class RawCommand @OptIn(ExperimentalPermission::class) construct /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public override val description: String = "", /** 指令父权限 */ - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ public override val prefixOptional: Boolean = false ) : Command { @OptIn(ExperimentalPermission::class) - public override val permission: Permission by lazy { createCommandPermission(parentPermission) } + public override val permission: Permission by lazy { createOrFindCommandPermission(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 29cd1e791..abc94c388 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 @@ -23,7 +23,7 @@ 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.console.permission.Permission import net.mamoe.mirai.message.data.MessageChain /** @@ -53,10 +53,10 @@ public abstract class SimpleCommand @OptIn(ExperimentalPermission::class) constr owner: CommandOwner, vararg names: String, description: String = "no description available", - basePermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, 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/description/CommandArgumentParser.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParser.kt index 0296822d3..3d717cbb6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParser.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParser.kt @@ -81,6 +81,21 @@ public interface CommandArgumentParser { public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender) } +/** + * 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型. + */ +public fun CommandArgumentParser.map( + mapper: CommandArgumentParser.(T) -> R +): CommandArgumentParser = MappingCommandArgumentParser(this, mapper) + +private class MappingCommandArgumentParser( + private val original: CommandArgumentParser, + private val mapper: CommandArgumentParser.(T) -> R +) : CommandArgumentParser { + override fun parse(raw: String, sender: CommandSender): R = mapper(original.parse(raw, sender)) + override fun parse(raw: MessageContent, sender: CommandSender): R = mapper(original.parse(raw, sender)) +} + /** * 解析一个字符串或 [SingleMessage] 为 [T] 类型参数 * 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 e43b9a190..b78fc129c 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 @@ -321,7 +321,7 @@ public object PermissionIdArgumentParser : CommandArgumentParser { public object PermissibleIdentifierArgumentParser : CommandArgumentParser { override fun parse(raw: String, sender: CommandSender): PermissibleIdentifier { return kotlin.runCatching { AbstractPermissibleIdentifier.parseFromString(raw) }.getOrElse { - illegalArgument("无法解析 $raw 为 PermissionId") + illegalArgument("无法解析 $raw 为 PermissibleIdentifier") } } 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 2e190d600..6ce98456a 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 @@ -15,7 +15,7 @@ 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.permission.Permission import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** @@ -73,14 +73,14 @@ public abstract class JCompositeCommand @OptIn(ExperimentalPermission::class) @JvmOverloads constructor( owner: CommandOwner, vararg names: String, - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, ) : CompositeCommand(owner, *names, parentPermission = parentPermission) { /** 指令描述, 用于显示在 [BuiltInCommands.Help] */ public final override var description: String = "" protected set @OptIn(ExperimentalPermission::class) - public final override var permission: net.mamoe.mirai.console.permission.Permission = super.permission + public final override var 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 9074cc170..7767f204c 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,10 +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.internal.command.createOrFindCommandPermission 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 @@ -55,7 +54,7 @@ public abstract class JRawCommand @OptIn(ExperimentalPermission::class) public override val owner: CommandOwner, /** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */ public override vararg val names: String, - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, ) : Command { /** 用法说明, 用于发送给用户 */ public override var usage: String = "" @@ -67,7 +66,7 @@ public abstract class JRawCommand @OptIn(ExperimentalPermission::class) /** 指令权限 */ @ExperimentalPermission - public final override var permission: Permission = createCommandPermission(parentPermission) + public final override var permission: Permission = createOrFindCommandPermission(parentPermission) 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 8a830713b..212c3850a 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 @@ -16,7 +16,6 @@ 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 /** * Java 实现: @@ -44,8 +43,8 @@ import net.mamoe.mirai.console.permission.PermissionId public abstract class JSimpleCommand @OptIn(ExperimentalPermission::class) constructor( owner: CommandOwner, vararg names: String, - basePermission: PermissionId, -) : SimpleCommand(owner, *names, basePermission = basePermission) { + basePermission: Permission, +) : SimpleCommand(owner, *names, parentPermission = basePermission) { public override var description: String = super.description protected set 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 50930174b..0f1963954 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 @@ -11,6 +11,7 @@ package net.mamoe.mirai.console.extension import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.extensions.PluginLoaderProvider +import net.mamoe.mirai.console.extensions.SingletonExtensionSelector import net.mamoe.mirai.console.util.ConsoleExperimentalAPI /** @@ -28,6 +29,8 @@ public interface FunctionExtension : Extension /** * 为某单例服务注册的 [Extension]. * + * 若同时有多个实例可用, 将会使用 [SingletonExtensionSelector.selectSingleton] 选择 + * * @see PermissionServiceProvider */ @ConsoleExperimentalAPI 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 7dfaa5e39..8a7a415e3 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 @@ -5,12 +5,14 @@ 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 +import net.mamoe.mirai.console.plugin.description.PluginLoadPriority /** * [权限服务][PermissionService] 提供器. * - * 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 + * 当插件注册 [PermissionService] 后, 默认会使用插件的 [PermissionService]. + * + * 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 和 [PluginLoadPriority.ON_EXTENSIONS] 插件提供 */ @ExperimentalPermission public interface PermissionServiceProvider : SingletonExtension> { 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 5b5016de9..0905229a5 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,12 +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 +import net.mamoe.mirai.console.plugin.description.PluginLoadPriority /** * 提供扩展 [PluginLoader] * - * 此扩展可由 [PluginKind.LOADER] 插件提供 + * 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供 */ 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/SingletonExtensionSelector.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/extensions/SingletonExtensionSelector.kt index fce60e204..fdaf4a2c5 100644 --- 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 @@ -12,7 +12,7 @@ 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.description.PluginLoadPriority import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.utils.info import kotlin.reflect.KClass @@ -22,7 +22,7 @@ import kotlin.reflect.KClass * * 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器. * - * 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 + * 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供 */ public interface SingletonExtensionSelector : FunctionExtension { 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 c40a49b7e..8f7c529c9 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,7 +33,9 @@ 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.extensions.BuiltInSingletonExtensionSelector import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl +import net.mamoe.mirai.console.internal.util.autoHexToBytes import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.PermissionService @@ -124,13 +126,22 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI val pluginLoadSession: PluginManagerImpl.PluginLoadSession - phase `load plugins`@{ + phase `load BEFORE_EXTENSIONS plugins`@{ PluginManager // init mainLogger.verbose { "Loading PluginLoader provider plugins..." } PluginManagerImpl.loadEnablePluginProviderPlugins() mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } + } + phase `load SingletonExtensionSelector`@{ + val instance = SingletonExtensionSelector.instance + if (instance is BuiltInSingletonExtensionSelector) { + ConsoleDataScope.addAndReloadConfig(instance.config) + } + } + + phase `load ON_EXTENSIONS plugins`@{ mainLogger.verbose { "Scanning high-priority extension and normal plugins..." } pluginLoadSession = PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider() mainLogger.verbose { "${pluginLoadSession.allKindsOfPlugins.size} plugin(s) found." } @@ -140,8 +151,6 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } } - SingletonExtensionSelector.instance // init - phase `load PermissionService`@{ mainLogger.verbose { "Loading PermissionService..." } PermissionService.INSTANCE.let { ps -> @@ -162,7 +171,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI CommandManagerImpl.commandListener // start } - phase `load normal plugins`@{ + phase `load AFTER_EXTENSION plugins`@{ mainLogger.verbose { "Loading normal plugins..." } val count = PluginManagerImpl.loadEnableNormalPlugins(pluginLoadSession) mainLogger.verbose { "$count normal plugin(s) loaded." } @@ -172,14 +181,19 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI phase `auto-login bots`@{ runBlocking { - for ((id, password) in AutoLoginConfig.plainPasswords) { + for ((id, password) in AutoLoginConfig.plainPasswords.filterNot { it.key == 123456654321L }) { mainLogger.info { "Auto-login $id" } MiraiConsole.addBot(id, password).alsoLogin() } - for ((id, password) in AutoLoginConfig.md5Passwords) { + for ((id, password) in AutoLoginConfig.md5Passwords.filterNot { it.key == 123456654321L }) { mainLogger.info { "Auto-login $id" } - MiraiConsole.addBot(id, password).alsoLogin() + val x = runCatching { + password.autoHexToBytes() + }.getOrElse { + error("Bad auto-login md5: '$password'") + } + MiraiConsole.addBot(id, x).alsoLogin() } } } 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 b427d982e..d7027b48b 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 @@ -18,8 +18,6 @@ import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip 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 @@ -53,7 +51,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission: owner: CommandOwner, names: Array, description: String = "", - parentPermission: PermissionId = owner.basePermission, + parentPermission: Permission = owner.parentPermission, prefixOptional: Boolean = false ) : Command, AbstractCommand( owner, @@ -79,7 +77,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission: internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { DefaultSubCommandDescriptor( "", - createCommandPermission(parentPermission), + createOrFindCommandPermission(parentPermission), onCommand = { sender: CommandSender, args: MessageChain -> sender.onDefault(args) } @@ -144,7 +142,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission: argsWithSubCommandNameNotRemoved: MessageChain, removeSubName: Boolean ) { - val args = parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) names.size else 0) + val args = parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) 1 else 0) if (!this.permission.testPermission(sender)) { sender.sendMessage(usage) // TODO: 2020/8/26 #127 return @@ -259,7 +257,7 @@ internal fun AbstractReflectionCommand.createSubCommand( context: CommandArgumentContext ): AbstractReflectionCommand.SubCommandDescriptor { val notStatic = !function.hasAnnotation() - val overridePermission = function.findAnnotation()//optional + //val overridePermission = null//function.findAnnotation()//optional val subDescription = function.findAnnotation()?.value ?: "" @@ -331,7 +329,7 @@ internal fun AbstractReflectionCommand.createSubCommand( commandName, params, subDescription, // overridePermission?.value - overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission, + permission,//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 1958f7c61..650a254f5 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 @@ -13,7 +13,6 @@ import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.Command.Companion.primaryName 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 @@ -143,8 +142,9 @@ internal fun Group.fuzzySearchMember( } @OptIn(ExperimentalPermission::class) -internal fun Command.createCommandPermission(parent: PermissionId): Permission { - return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, parent) +internal fun Command.createOrFindCommandPermission(parent: Permission): Permission { + val id = owner.permissionId(primaryName) + return PermissionService.INSTANCE[id] ?: PermissionService.INSTANCE.register(id, description, parent) } //// internal diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt index 0e435677c..3fee86f5b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/builtins/AutoLoginConfig.kt @@ -3,6 +3,8 @@ package net.mamoe.mirai.console.internal.data.builtins import net.mamoe.mirai.console.data.AutoSavePluginConfig import net.mamoe.mirai.console.data.ValueDescription import net.mamoe.mirai.console.data.value +import net.mamoe.mirai.console.internal.util.md5 +import net.mamoe.mirai.console.internal.util.toUHexString internal object AutoLoginConfig : AutoSavePluginConfig() { override val saveName: String @@ -13,7 +15,7 @@ internal object AutoLoginConfig : AutoSavePluginConfig() { 账号和明文密码列表 """ ) - val plainPasswords: MutableMap by value(mutableMapOf()) + val plainPasswords: MutableMap by value(mutableMapOf(123456654321L to "example")) @ValueDescription( @@ -21,5 +23,9 @@ internal object AutoLoginConfig : AutoSavePluginConfig() { 账号和 MD5 密码列表 """ ) - val md5Passwords: MutableMap by value(mutableMapOf()) + val md5Passwords: MutableMap by value( + mutableMapOf( + 123456654321L to "example".toByteArray().md5().toUHexString() + ) + ) } \ No newline at end of file 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 index 5d84aabdf..8621e0c01 100644 --- 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 @@ -2,7 +2,7 @@ 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.AutoSavePluginConfig import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.extension.Extension import net.mamoe.mirai.console.extension.ExtensionRegistry @@ -15,9 +15,9 @@ import kotlin.reflect.KClass internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector { - private val config: SaveData = SaveData() + internal val config: SaveData = SaveData() - private class SaveData : AutoSavePluginData() { + internal class SaveData : AutoSavePluginConfig() { override val saveName: String get() = "ExtensionSelector" val value: MutableMap by value() 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 061bf6a15..80129ef53 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,6 +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.Permission import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService.Companion.allocatePermissionIdForPlugin @@ -44,11 +45,11 @@ internal abstract class JvmPluginInternal( ) : JvmPlugin, CoroutineScope { @OptIn(ExperimentalPermission::class) - final override val basePermission: PermissionId by lazy { + final override val parentPermission: Permission by lazy { PermissionService.INSTANCE.register( PermissionService.INSTANCE.allocatePermissionIdForPlugin(name, "*"), "The base permission" - ).id + ) } final override var isEnabled: Boolean = false @@ -110,7 +111,7 @@ internal abstract class JvmPluginInternal( } internal fun internalOnEnable(): Boolean { - basePermission + parentPermission if (!firstRun) refreshCoroutineContext() kotlin.runCatching { onEnable() 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 bdef81153..160df275a 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 @@ -22,7 +22,7 @@ import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.plugin.* 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 net.mamoe.mirai.console.plugin.description.PluginLoadPriority import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.utils.info @@ -131,7 +131,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol internal fun loadEnableHighPriorityExtensionPlugins(session: PluginLoadSession): Int { loadersLock.withLock { session.allKindsOfPlugins.flatMap { it.second } - .filter { it.kind == PluginKind.HIGH_PRIORITY_EXTENSIONS } + .filter { it.loadPriority == PluginLoadPriority.ON_EXTENSIONS } .sortByDependencies() .also { it.loadAndEnableAllInOrder() } .let { return it.size } @@ -142,7 +142,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol internal fun loadEnableNormalPlugins(session: PluginLoadSession): Int { loadersLock.withLock { session.allKindsOfPlugins.flatMap { it.second } - .filter { it.kind == PluginKind.NORMAL } + .filter { it.loadPriority == PluginLoadPriority.AFTER_EXTENSIONS } .sortByDependencies() .also { it.loadAndEnableAllInOrder() } .let { return it.size } @@ -191,7 +191,8 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol loader as PluginLoader descriptions.forEach(PluginManagerImpl::checkPluginDescription) - descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies().loadAndEnableAllInOrder() + descriptions.filter { it.loadPriority == PluginLoadPriority.BEFORE_EXTENSIONS }.sortByDependencies() + .loadAndEnableAllInOrder() } .flatMap { it.second.asSequence() } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt new file mode 100644 index 000000000..e72dba09f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/ByteUtils.kt @@ -0,0 +1,54 @@ +/* + * 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.internal.util + +import java.security.MessageDigest + + +@Suppress("DuplicatedCode") // false positive. `this` is not the same for `List` and `ByteArray` +internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { + require(offset >= 0) { "offset shouldn't be negative: $offset" } + require(length >= 0) { "length shouldn't be negative: $length" } + require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" } +} + +internal fun String.autoHexToBytes(): ByteArray = + this.trim(Char::isWhitespace).asSequence().chunked(2).map { + (it[0].toString() + it[1]).toUByte(16).toByte() + }.toList().toByteArray() + +internal fun ByteArray.md5(offset: Int = 0, length: Int = this.size - offset): ByteArray { + this.checkOffsetAndLength(offset, length) + return MessageDigest.getInstance("MD5").apply { update(this@md5, offset, length) }.digest() +} + +@JvmOverloads +@Suppress("DuplicatedCode") // false positive. foreach is not common to UByteArray and ByteArray +internal fun ByteArray.toUHexString( + separator: String = " ", + offset: Int = 0, + length: Int = this.size - offset +): String { + this.checkOffsetAndLength(offset, length) + if (length == 0) { + return "" + } + val lastIndex = offset + length + return buildString(length * 2) { + this@toUHexString.forEachIndexed { index, it -> + if (index in offset until lastIndex) { + var ret = it.toUByte().toString(16).toUpperCase() + if (ret.length == 1) ret = "0$ret" + append(ret) + if (index < lastIndex - 1) append(separator) + } + } + } +} 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 12baee7b6..21563301a 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 @@ -9,28 +9,27 @@ package net.mamoe.mirai.console.permission +import net.mamoe.mirai.console.data.PluginDataExtensions import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith -import java.util.concurrent.CopyOnWriteArrayList /** * */ @ExperimentalPermission -public abstract class AbstractConcurrentPermissionService

: PermissionService

{ +internal abstract class AbstractConcurrentPermissionService

: PermissionService

{ protected abstract val permissions: MutableMap - protected abstract val grantedPermissionsMap: MutableMap> + protected abstract val grantedPermissionsMap: PluginDataExtensions.NotNullMutableMap> protected abstract fun createPermission( id: PermissionId, description: String, - base: PermissionId = RootPermission.id + parent: Permission ): 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) + override fun register(id: PermissionId, description: String, parent: Permission): P { + val instance = createPermission(id, description, parent) val old = permissions.putIfAbsent(id, instance) if (old != null) throw DuplicatedPermissionRegistrationException(instance, old) return instance @@ -38,16 +37,15 @@ public abstract class AbstractConcurrentPermissionService

: Perm override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) { val id = permission.id - grantedPermissionsMap[id]?.add(permissibleIdentifier) - ?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.") + grantedPermissionsMap[id].add(permissibleIdentifier) } override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) { - grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) + grantedPermissionsMap[permission.id].remove(permissibleIdentifier) } override fun getRegisteredPermissions(): Sequence

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

= sequence

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

= sequence

{ for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { val granted = 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 de50373cb..c1c3918e2 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,28 +9,28 @@ package net.mamoe.mirai.console.permission +import kotlinx.serialization.Serializable import net.mamoe.mirai.console.data.AutoSavePluginConfig +import net.mamoe.mirai.console.data.PluginDataExtensions 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 +import java.util.concurrent.CopyOnWriteArraySet import kotlin.reflect.KClass -import kotlin.reflect.KType -import kotlin.reflect.full.createType @ExperimentalPermission -public object AllGrantPermissionService : PermissionService { +internal object AllGrantPermissionService : PermissionService { private val all = ConcurrentHashMap() - override val permissionType: KClass - get() = PermissionImpl::class + override val permissionType: KClass get() = PermissionImpl::class + override val rootPermission: PermissionImpl get() = RootPermissionImpl.also { all[it.id] = it } override fun register( id: PermissionId, description: String, - base: PermissionId + parent: Permission ): PermissionImpl { - val new = PermissionImpl(id, description, base) + val new = PermissionImpl(id, description, parent) val old = all.putIfAbsent(id, new) if (old != null) throw DuplicatedPermissionRegistrationException(new, old) return new @@ -51,18 +51,23 @@ public object AllGrantPermissionService : PermissionService { } } +@Suppress("DEPRECATION") +@OptIn(ExperimentalPermission::class) +private val RootPermissionImpl = PermissionImpl(PermissionId("*", "*"), "The root permission").also { it.parent = it } + @ExperimentalPermission -public object AllDenyPermissionService : PermissionService { +internal object AllDenyPermissionService : PermissionService { private val all = ConcurrentHashMap() override val permissionType: KClass get() = PermissionImpl::class + override val rootPermission: PermissionImpl = RootPermissionImpl.also { all[it.id] = it } override fun register( id: PermissionId, description: String, - base: PermissionId + parent: Permission ): PermissionImpl { - val new = PermissionImpl(id, description, base) + val new = PermissionImpl(id, description, parent) val old = all.putIfAbsent(id, new) if (old != null) throw DuplicatedPermissionRegistrationException(new, old) return new @@ -90,41 +95,75 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService

get() = PermissionImpl::class - override val permissions: MutableMap = ConcurrentHashMap() + override val permissions: ConcurrentHashMap = ConcurrentHashMap() + override val rootPermission: PermissionImpl = RootPermissionImpl.also { permissions[it.id] = it } @Suppress("UNCHECKED_CAST") - override val grantedPermissionsMap: MutableMap> - get() = config.grantedPermissionMap as MutableMap> + override val grantedPermissionsMap: PluginDataExtensions.NotNullMutableMap> + get() = config.grantedPermissionMap as PluginDataExtensions.NotNullMutableMap> - override fun createPermission(id: PermissionId, description: String, base: PermissionId): PermissionImpl = - PermissionImpl(id, description, base) + override fun createPermission(id: PermissionId, description: String, parent: Permission): PermissionImpl = + PermissionImpl(id, description, parent) - internal val config: ConcurrentSaveData = - ConcurrentSaveData( - PermissionImpl::class.createType(), - "PermissionService", - - ) + internal val config: ConcurrentSaveData = + ConcurrentSaveData("PermissionService") @Suppress("RedundantVisibilityModifier") @ExperimentalPermission - internal class ConcurrentSaveData

private constructor( - permissionType: KType, + internal class ConcurrentSaveData private constructor( public override val saveName: String, - // delegate: PluginConfig, @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? ) : AutoSavePluginConfig() { - public val grantedPermissionMap: MutableMap> - by value>>(ConcurrentHashMap()) - .withDefault { CopyOnWriteArrayList() } + public val grantedPermissionMap: PluginDataExtensions.NotNullMutableMap> + by value>>(ConcurrentHashMap()) + .withDefault { CopyOnWriteArraySet() } public companion object { @JvmStatic - public operator fun

invoke( - permissionType: KType, + public operator fun invoke( saveName: String, // delegate: PluginConfig, - ): ConcurrentSaveData

= ConcurrentSaveData(permissionType, saveName, null) + ): ConcurrentSaveData = ConcurrentSaveData(saveName, null) } } +} + +/** + * [Permission] 的简单实现 + */ +@Serializable +@ExperimentalPermission +internal data class PermissionImpl @Deprecated("Only for Root") constructor( + override val id: PermissionId, + override val description: String, +) : Permission { + override lateinit var parent: Permission + + @Suppress("DEPRECATION") + constructor(id: PermissionId, description: String, parent: Permission) : this(id, description) { + this.parent = parent + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PermissionImpl + + if (id != other.id) return false + if (description != other.description) return false + if (parent !== other.parent) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + description.hashCode() + result = 31 * result + if (parent == this) 1 else parent.hashCode() + return result + } + + override fun toString(): String = + "PermissionImpl(id=$id, description='$description', parent=${if (parent === this) "" else parent.toString()})" } \ 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 index e65719740..695b8cc97 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 @@ -46,36 +46,67 @@ public sealed class AbstractPermissibleIdentifier( 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) + val str = string.trim { it.isWhitespace() }.toLowerCase() + if (str == "console") return Console + if (str.isNotEmpty()) { + when (str[0]) { + 'g' -> { + val arg = str.substring(1) + if (arg == "*") return AnyGroup + else arg.toLongOrNull()?.let(::ExactGroup)?.let { return it } + } + 'f' -> { + val arg = str.substring(1) + if (arg == "*") return AnyFriend + else arg.toLongOrNull()?.let(::ExactFriend)?.let { return it } + } + 'u' -> { + val arg = str.substring(1) + if (arg == "*") return AnyUser + else arg.toLongOrNull()?.let(::ExactUser)?.let { return it } + } + 'c' -> { + val arg = str.substring(1) + if (arg == "*") return AnyContact + } + 'm' -> kotlin.run { + val arg = str.substring(1) + if (arg == "*") return AnyMemberFromAnyGroup + else { + val components = arg.split('.') + + if (components.size == 2) { + val groupId = components[0].toLongOrNull() ?: return@run + + if (components[1] == "*") return AnyMember(groupId) + else { + val memberId = components[1].toLongOrNull() ?: return@run + return ExactMember(groupId, memberId) + } + } + } + } + 't' -> kotlin.run { + val arg = str.substring(1) + if (arg == "*") return AnyTempFromAnyGroup + else { + val components = arg.split('.') + + if (components.size == 2) { + val groupId = components[0].toLongOrNull() ?: return@run + + if (components[1] == "*") return AnyTemp(groupId) + else { + val memberId = components[1].toLongOrNull() ?: return@run + return ExactTemp(groupId, memberId) + } + } + } + } + } } error("Cannot deserialize '$str' as AbstractPermissibleIdentifier") } - - internal val objects by lazy { - // https://youtrack.jetbrains.com/issue/KT-41782 - AbstractPermissibleIdentifier::class.nestedClasses.mapNotNull { it.objectInstance } - } - - 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()) }, - 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 @@ -86,54 +117,70 @@ public sealed class AbstractPermissibleIdentifier( ) public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) { - override fun toString(): String = "AnyGroup" + override fun toString(): String = "g*" } - public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) + public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup) { + override fun toString(): String = "g$groupId" + } - public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) + public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup) { + override fun toString(): String = "m$groupId.*" + } public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyMemberFromAnyGroup" + override fun toString(): String = "m*" + } + + public object AnyTempFromAnyGroup : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "t*" } public data class ExactMember( public val groupId: Long, public val memberId: Long - ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) + ) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId)) { + override fun toString(): String = "m$groupId.$memberId" + } public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyFriend" + override fun toString(): String = "f*" } public data class ExactFriend( public val id: Long ) : AbstractPermissibleIdentifier(ExactUser(id)) { - override fun toString(): String = "ExactFriend" + override fun toString(): String = "f$id" } - public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) { - override fun toString(): String = "AnyTemp" + public data class AnyTemp( + public val groupId: Long, + ) : AbstractPermissibleIdentifier(AnyUser, AnyMember(groupId)) { + override fun toString(): String = "t$groupId.*" } public data class ExactTemp( public val groupId: Long, - public val id: Long - ) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? + public val memberId: Long + ) : AbstractPermissibleIdentifier(ExactUser(groupId), ExactMember(groupId, memberId)) { + override fun toString(): String = "t$groupId.$memberId" + } public object AnyUser : AbstractPermissibleIdentifier(AnyContact) { - override fun toString(): String = "AnyUser" + override fun toString(): String = "u*" } public data class ExactUser( public val id: Long - ) : AbstractPermissibleIdentifier(AnyUser) + ) : AbstractPermissibleIdentifier(AnyUser) { + override fun toString(): String = "u$id" + } public object AnyContact : AbstractPermissibleIdentifier() { - override fun toString(): String = "AnyContact" + override fun toString(): String = "*" } public object Console : AbstractPermissibleIdentifier() { - override fun toString(): String = "Console" + override fun toString(): String = "console" } } \ 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 79ca00b77..78cb76515 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,8 +9,6 @@ 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 @@ -25,22 +23,29 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI public interface Permission { public val id: PermissionId public val description: String - public val parentId: PermissionId -} -@OptIn(ExperimentalPermission::class) -private val ROOT_PERMISSION_ID = PermissionId("*", "*") + /** + * [RootPermission] 的 parent 为自身 + */ + public val parent: Permission +} /** * 所有权限的父权限. */ @get:JvmName("getRootPermission") @ExperimentalPermission -public val RootPermission: Permission by lazy { +public val RootPermission: Permission + get() = PermissionService.INSTANCE.rootPermission + +/** + * 所有内建指令的权限 + */ +@ExperimentalPermission +public val RootConsoleBuiltInPermission: Permission by lazy { PermissionService.INSTANCE.register( - ROOT_PERMISSION_ID, - "The parent of any permission", - ROOT_PERMISSION_ID + PermissionId("console", "*"), + "The parent of any built-in commands" ) } @@ -48,16 +53,5 @@ public val RootPermission: Permission by lazy { @ExperimentalPermission public fun Permission.parentsWithSelfSequence(): Sequence = generateSequence(this) { p -> - p.parentId.findCorrespondingPermission()?.takeIf { parent -> parent != p } - } - -/** - * [Permission] 的简单实现 - */ -@Serializable -@ExperimentalPermission -public class PermissionImpl( - override val id: PermissionId, - override val description: String, - override val parentId: PermissionId = RootPermission.id -) : Permission \ No newline at end of file + p.parent.takeIf { parent -> parent != p } + } \ 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 42e30cb20..0b935e6f5 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 @@ -23,6 +23,7 @@ import kotlin.reflect.full.isSuperclassOf public interface PermissionService

{ @ExperimentalPermission public val permissionType: KClass

+ public val rootPermission: P /////////////////////////////////////////////////////////////////////////// @@ -46,7 +47,7 @@ public interface PermissionService

{ public fun register( id: PermissionId, description: String, - base: PermissionId = RootPermission.id + parent: Permission = RootPermission ): P /////////////////////////////////////////////////////////////////////////// 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 a9248bb7d..17155dcd3 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 @@ -18,7 +18,7 @@ import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader 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 net.mamoe.mirai.console.plugin.description.PluginLoadPriority import net.mamoe.mirai.console.plugin.jvm.JvmPlugin /** @@ -65,9 +65,9 @@ public inline val Plugin.name: String get() = this.description.name public inline val Plugin.version: Semver get() = this.description.version /** - * 获取 [PluginDescription.kind] + * 获取 [PluginDescription.loadPriority] */ -public inline val Plugin.kind: PluginKind get() = this.description.kind +public inline val Plugin.loadPriority: PluginLoadPriority get() = this.description.loadPriority /** * 获取 [PluginDescription.info] 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 6ef60f81a..c11f4d565 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 @@ -23,9 +23,9 @@ public interface PluginDescription { /** * 插件类型. 将会决定加载顺序 * - * @see PluginKind + * @see PluginLoadPriority */ - public val kind: PluginKind + public val loadPriority: PluginLoadPriority /** * 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'. 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/PluginLoadPriority.kt similarity index 56% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginKind.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginLoadPriority.kt index fce47b97b..8f49d63e8 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/PluginLoadPriority.kt @@ -14,39 +14,46 @@ 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.extensions.PluginLoaderProvider +import net.mamoe.mirai.console.extensions.SingletonExtensionSelector import net.mamoe.mirai.console.internal.data.map -import net.mamoe.mirai.console.plugin.PluginLoader -import net.mamoe.mirai.console.plugin.description.PluginKind.* +import net.mamoe.mirai.console.plugin.description.PluginLoadPriority.* /** * 插件类型. * - * 插件类型将影响加载顺序: [LOADER] -> [HIGH_PRIORITY_EXTENSIONS] -> [NORMAL]. + * 插件类型将影响加载顺序: [BEFORE_EXTENSIONS] -> [ON_EXTENSIONS] -> [AFTER_EXTENSIONS]. * - * 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [LOADER] 不允许依赖一个 [NORMAL] 类型的插件. + * 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [BEFORE_EXTENSIONS] 不允许依赖一个 [AFTER_EXTENSIONS] 类型的插件. */ -public enum class PluginKind { - /** 表示此插件提供一个 [PluginLoader], 也可以同时提供其他 [Extension] 应最早被加载 */ - LOADER, +public enum class PluginLoadPriority { + /** + * 表示此插件最早被加载. 在 Console 启动时的第一初始化阶段就会加载这些插件. + * + * 一般只有提供 [PluginLoaderProvider] 或 [SingletonExtensionSelector] 的插件才需要在此阶段加载. + */ + BEFORE_EXTENSIONS, /** - * 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [NORMAL] 类型插件前加载 + * 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [AFTER_EXTENSIONS] 类型插件前加载 * * 高优先级的 [Extension] 通常是覆盖 Console 内置的部分服务的扩展. 如 [PermissionServiceProvider]. * - * 一些普通的 [Extension], 如 [BotConfigurationAlterer], 也可以使用 [NORMAL] 类型插件注册. + * 一些普通的 [Extension], 如 [BotConfigurationAlterer], 也可以使用 [AFTER_EXTENSIONS] 类型插件注册. */ - HIGH_PRIORITY_EXTENSIONS, + ON_EXTENSIONS, - /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ - NORMAL; + /** + * 表示此插件为一个通常的插件, 在扩展处理完毕后加载. + */ + AFTER_EXTENSIONS; - public object AsStringSerializer : KSerializer by String.serializer().map( + public object AsStringSerializer : KSerializer by String.serializer().map( serializer = { it.name }, deserializer = { str -> values().firstOrNull { it.name.equals(str, ignoreCase = true) - } ?: NORMAL + } ?: AFTER_EXTENSIONS } ) } \ No newline at end of file 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 363f356df..64be7d2a9 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 @@ -14,7 +14,7 @@ package net.mamoe.mirai.console.plugin.jvm 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 net.mamoe.mirai.console.plugin.description.PluginLoadPriority import kotlin.internal.LowPriorityInOverloadResolution /** @@ -82,7 +82,7 @@ public class JvmPluginDescriptionBuilder( private var author: String = "" private var info: String = "" private var dependencies: MutableSet = mutableSetOf() - private var kind: PluginKind = PluginKind.NORMAL + private var loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS @ILoveKuriyamaMiraiForever public fun name(value: String): JvmPluginDescriptionBuilder = apply { this.name = value.trim() } @@ -104,17 +104,19 @@ public class JvmPluginDescriptionBuilder( public fun info(value: String): JvmPluginDescriptionBuilder = apply { this.info = value.trimIndent() } @ILoveKuriyamaMiraiForever - public fun kind(value: PluginKind): JvmPluginDescriptionBuilder = apply { this.kind = value } + public fun kind(value: PluginLoadPriority): JvmPluginDescriptionBuilder = apply { this.loadPriority = value } @ILoveKuriyamaMiraiForever - public fun normalPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.NORMAL } + public fun normalPlugin(): JvmPluginDescriptionBuilder = + apply { this.loadPriority = PluginLoadPriority.AFTER_EXTENSIONS } @ILoveKuriyamaMiraiForever - public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.LOADER } + public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder = + apply { this.loadPriority = PluginLoadPriority.BEFORE_EXTENSIONS } @ILoveKuriyamaMiraiForever public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder = - apply { this.kind = PluginKind.HIGH_PRIORITY_EXTENSIONS } + apply { this.loadPriority = PluginLoadPriority.ON_EXTENSIONS } @ILoveKuriyamaMiraiForever public fun dependsOn( @@ -152,7 +154,7 @@ public class JvmPluginDescriptionBuilder( @Suppress("DEPRECATION_ERROR") public fun build(): JvmPluginDescription = - SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) + SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority) @Retention(AnnotationRetention.SOURCE) @DslMarker @@ -192,7 +194,7 @@ public data class SimpleJvmPluginDescription public override val author: String = "", public override val info: String = "", public override val dependencies: Set = setOf(), - public override val kind: PluginKind = PluginKind.NORMAL, + public override val loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS, ) : JvmPluginDescription { @Deprecated( @@ -214,8 +216,8 @@ public data class SimpleJvmPluginDescription author: String = "", info: String = "", dependencies: Set = setOf(), - kind: PluginKind = PluginKind.NORMAL, - ) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, kind) + loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS, + ) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, loadPriority) init { require(!name.contains(':')) { "':' is forbidden in plugin name" } @@ -240,8 +242,8 @@ public fun JvmPluginDescription( author: String = "", info: String = "", dependencies: Set = setOf(), - kind: PluginKind = PluginKind.NORMAL -): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) + loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority) @Deprecated( "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", @@ -260,5 +262,5 @@ public fun JvmPluginDescription( author: String = "", info: String = "", dependencies: Set = setOf(), - kind: PluginKind = PluginKind.NORMAL -): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) + loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS +): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/MessageScope.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/MessageScope.kt index cda7b83fb..eb95259d4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/MessageScope.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/MessageScope.kt @@ -22,7 +22,6 @@ import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.User import net.mamoe.mirai.message.data.Message -import kotlin.internal.InlineOnly import kotlin.internal.LowPriorityInOverloadResolution /** @@ -38,10 +37,10 @@ import kotlin.internal.LowPriorityInOverloadResolution * - `C.toMessageScope()`. 其中 `C` 表示 `Iterable`, `Sequence`, `Flow`, `Array` 其中任一. * * ## 连接 [MessageScope] - * - `A.scopeWith(vararg B)`. - * - `A.scopeWith(vararg A)`. - * - `A.scopeWithNotNull(vararg B?)`. 类似 [listOfNotNull]. - * - `A.scopeWithNotNull(vararg A?)`. 类似 [listOfNotNull]. + * - `A?.scopeWith(vararg B?)`. + * - `A?.scopeWith(vararg A?)`. + * + * `null` 项将会被过滤. * * ## 自动去重 * 在连接时, [MessageScope] 会自动根据真实的 [收信对象][CommandSender.subject] 去重. @@ -83,12 +82,12 @@ import kotlin.internal.LowPriorityInOverloadResolution * * // 使用 MessageScope, 清晰逻辑 * // 表示至少发送给 `this`, 当 `this` 的真实发信对象与 `target.group` 不同时, 还额外发送给 `target.group` - * this.scopeWithNotNull(target.group) { + * this.scopeWith(target.group) { * sendMessage("${name} 禁言了 ${target.nameCardOrNick} $duration 秒") * } * * // 同样地, 可以扩展用法, 同时私聊指令执行者: - * // this.scopeWithNotNull( + * // this.scopeWith( * // target, * // target.group * // ) { ... } @@ -143,132 +142,51 @@ public fun Contact.asMessageScope(): MessageScope = createScopeDelegate(this) public fun CommandSender.asMessageScope(): MessageScope = createScopeDelegate(this) @LowPriorityInOverloadResolution -public fun Contact.scopeWith(vararg others: Contact): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun Contact?.scopeWith(vararg others: Contact?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun Contact.scopeWith(vararg others: CommandSender): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun Contact?.scopeWith(vararg others: CommandSender?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun Contact.scopeWith(vararg others: MessageScope): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun Contact?.scopeWith(vararg others: MessageScope?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun CommandSender.scopeWith(vararg others: Contact): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun CommandSender?.scopeWith(vararg others: Contact?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun CommandSender.scopeWith(vararg others: CommandSender): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun CommandSender?.scopeWith(vararg others: CommandSender?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun CommandSender.scopeWith(vararg others: MessageScope): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun CommandSender?.scopeWith(vararg others: MessageScope?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun MessageScope.scopeWith(vararg others: Contact): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun MessageScope?.scopeWith(vararg others: Contact?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun MessageScope.scopeWith(vararg others: CommandSender): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun MessageScope?.scopeWith(vararg others: CommandSender?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } @LowPriorityInOverloadResolution -public fun MessageScope.scopeWith(vararg others: MessageScope): MessageScope { - return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } +public fun MessageScope?.scopeWith(vararg others: MessageScope?): MessageScope { + return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) } } -@LowPriorityInOverloadResolution -public fun Contact?.scopeWithNotNull(vararg others: Contact?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun Contact?.scopeWithNotNull(vararg others: CommandSender?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun Contact?.scopeWithNotNull(vararg others: MessageScope?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun CommandSender?.scopeWithNotNull(vararg others: Contact?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun CommandSender?.scopeWithNotNull(vararg others: CommandSender?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun CommandSender?.scopeWithNotNull(vararg others: MessageScope?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun MessageScope?.scopeWithNotNull(vararg others: Contact?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun MessageScope?.scopeWithNotNull(vararg others: CommandSender?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -@LowPriorityInOverloadResolution -public fun MessageScope?.scopeWithNotNull(vararg others: MessageScope?): MessageScope { - return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWithNotNull(other?.asMessageScope()) } -} - -public fun Contact.scopeWith(other: Contact): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun Contact.scopeWith(other: CommandSender): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun Contact.scopeWith(other: MessageScope): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun CommandSender.scopeWith(other: Contact): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun CommandSender.scopeWith(other: CommandSender): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun CommandSender.scopeWith(other: MessageScope): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun MessageScope.scopeWith(other: Contact): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun MessageScope.scopeWith(other: CommandSender): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun MessageScope.scopeWith(other: MessageScope): MessageScope { - return CombinedScope(asMessageScope(), other.asMessageScope()) -} - -public fun Contact?.scopeWithNotNull(other: Contact?): MessageScope { +public fun Contact?.scopeWith(other: Contact?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -279,7 +197,7 @@ public fun Contact?.scopeWithNotNull(other: Contact?): MessageScope { } } -public fun Contact?.scopeWithNotNull(other: CommandSender?): MessageScope { +public fun Contact?.scopeWith(other: CommandSender?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -290,7 +208,7 @@ public fun Contact?.scopeWithNotNull(other: CommandSender?): MessageScope { } } -public fun Contact?.scopeWithNotNull(other: MessageScope?): MessageScope { +public fun Contact?.scopeWith(other: MessageScope?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -301,7 +219,7 @@ public fun Contact?.scopeWithNotNull(other: MessageScope?): MessageScope { } } -public fun CommandSender?.scopeWithNotNull(other: Contact?): MessageScope { +public fun CommandSender?.scopeWith(other: Contact?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -312,7 +230,7 @@ public fun CommandSender?.scopeWithNotNull(other: Contact?): MessageScope { } } -public fun CommandSender?.scopeWithNotNull(other: CommandSender?): MessageScope { +public fun CommandSender?.scopeWith(other: CommandSender?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -323,7 +241,7 @@ public fun CommandSender?.scopeWithNotNull(other: CommandSender?): MessageScope } } -public fun CommandSender?.scopeWithNotNull(other: MessageScope?): MessageScope { +public fun CommandSender?.scopeWith(other: MessageScope?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -334,7 +252,7 @@ public fun CommandSender?.scopeWithNotNull(other: MessageScope?): MessageScope { } } -public fun MessageScope?.scopeWithNotNull(other: Contact?): MessageScope { +public fun MessageScope?.scopeWith(other: Contact?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -345,7 +263,7 @@ public fun MessageScope?.scopeWithNotNull(other: Contact?): MessageScope { } } -public fun MessageScope?.scopeWithNotNull(other: CommandSender?): MessageScope { +public fun MessageScope?.scopeWith(other: CommandSender?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -356,7 +274,7 @@ public fun MessageScope?.scopeWithNotNull(other: CommandSender?): MessageScope { } } -public fun MessageScope?.scopeWithNotNull(other: MessageScope?): MessageScope { +public fun MessageScope?.scopeWith(other: MessageScope?): MessageScope { @Suppress("DuplicatedCode") return when { this == null && other == null -> NoopMessageScope @@ -367,78 +285,42 @@ public fun MessageScope?.scopeWithNotNull(other: MessageScope?): MessageScope { } } -public inline fun Contact.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { +public inline fun Contact?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun Contact.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { +public inline fun Contact?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun Contact.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { +public inline fun Contact?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun CommandSender.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { +public inline fun CommandSender?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun CommandSender.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { +public inline fun CommandSender?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun CommandSender.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { +public inline fun CommandSender?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun MessageScope.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { +public inline fun MessageScope?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun MessageScope.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { +public inline fun MessageScope?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun MessageScope.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { +public inline fun MessageScope?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R { return scopeWith(*others).invoke(action) } -public inline fun Contact?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun Contact?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun Contact?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun CommandSender?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun CommandSender?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun CommandSender?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun MessageScope?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun MessageScope?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - -public inline fun MessageScope?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R { - return scopeWithNotNull(*others).invoke(action) -} - @Deprecated( "Senseless scopeWith. Use asMessageScope.", ReplaceWith("this.asMessageScope()", "net.mamoe.mirai.console.util.asMessageScope") @@ -623,27 +505,23 @@ public suspend fun Flow.toMessageScope(): MessageScope { // Flow { +internal fun MessageScope.asSequence(): Sequence { return if (this is CombinedScope) { - sequenceOf(this.first.asSequence(), this.second.asSequence()).flatten() + val a = this.first.asSequence() + val b = this.second.asSequence() // don't inline. fuck compilers + sequenceOf(a, b).flatten() } else sequenceOf(this) } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a93801e38..abb6b862a 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -23,8 +23,8 @@ object Versions { const val consoleTerminal = "0.1.0" const val consolePure = console - const val kotlinCompiler = "1.4.0" - const val kotlinStdlib = "1.4.0" + const val kotlinCompiler = "1.4.10" + const val kotlinStdlib = kotlinCompiler const val coroutines = "1.3.9" const val collectionsImmutable = "0.3.2" @@ -37,5 +37,5 @@ object Versions { const val bintray = "1.8.5" const val blockingBridge = "1.0.5" - const val yamlkt = "0.5.1" + const val yamlkt = "0.5.2" } \ No newline at end of file diff --git a/docs/Plugins.md b/docs/Plugins.md index 5d35b26fb..9e7a7a467 100644 --- a/docs/Plugins.md +++ b/docs/Plugins.md @@ -26,7 +26,6 @@ [`RawCommand`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt [`CommandManager`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt -[`BotManager`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt [`Annotations`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt [`ConsoleInput`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt [`JavaPluginScheduler`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt @@ -75,9 +74,9 @@ Mirai Console 提供一些基础的实现,即 [`AbstractJvmPlugin`],并将 [ #### 描述 插件描述需要在主类构造器传递给 `super`。因此插件不需要 `plugin.yml`, `plugin.xml` 等配置文件来指示信息。 -Mirai Console 使用 `ServiceLoader` 加载插件。 +Mirai Console 使用类似 `ServiceLoader` 的机制加载插件。 在 Kotlin,可([使用 AutoService])自动配置 service 信息。 -在 Kotlin 或其他语言,手动创建 service 文件: 在 `jar` 内 `META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin` 文件内存放插件主类全名(以纯文本 UTF-8 存储,文件内容只包含一行插件主类全名). +在 Kotlin 或其他语言,可手动创建 service 文件: 在 `jar` 内 `META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin` 文件内存放插件主类全名(以纯文本 UTF-8 存储,文件内容只包含一行插件主类全名). 有关插件版本号的限制: @@ -94,13 +93,16 @@ Mirai Console 使用 `ServiceLoader` 加载插件。 - 访问权限为 `public` 或默认 (不指定) ```kotlin -@AutoService(JvmPlugin::class) // 如果选用上述自动配置的方法 object SchedulePlugin : KotlinPlugin( - SimpleJvmPluginDescription( // 插件的描述, name 和 version 是必须的 - name = "Schedule", + JvmPluginDescription( + id = "org.example.my-schedule-plugin", version = "1.0.0", - // author, description, ... - ) + ) { + name("Schedule") + + // author("...") + // dependsOn("...") + } ) { // ... } @@ -117,10 +119,14 @@ object SchedulePlugin : KotlinPlugin( public final class JExample extends JavaPlugin { public static final JExample INSTANCE = new JExample(); // 可以像 Kotlin 一样静态初始化单例 private JExample() { - super(new SimpleJvmPluginDescription( + super(new JvmPluginDescriptionBuilder( "JExample", // name "1.0.0" // version - )); + ) + // .author("...") + // .info("...") + .build() + ); } } ``` @@ -133,10 +139,14 @@ public final class JExample extends JavaPlugin { return instance; } public JExample() { // 此时必须 public - super(new SimpleJvmPluginDescription( + super(new JvmPluginDescriptionBuilder( "JExample", // name "1.0.0" // version - )); + ) + // .author("...") + // .info("...") + .build() + ); instance = this; } } @@ -248,5 +258,31 @@ Java: **仅可在插件 onEnable() 时及其之后才能使用这些方法。** **在插件 onDisable() 之后不能使用这些方法。** +#### 使用示例 + +```kotlin +object SchedulePlugin : KotlinPlugin( + JvmPluginDescription( + id = "org.example.my-schedule-plugin", + version = "1.0.0", + ) { + name("Schedule") + + // author("...") + // dependsOn("...") + } +) { + // ... + + override fun onEnable() { + MyData.reload() // 仅需此行,保证启动时更新数据,在之后自动存储数据。 + } +} + +object MyData : AutoSavePluginData() { + val value: Map by value() +} +``` + ### 附录:Java 插件的多线程调度器 - [`JavaPluginScheduler`] -拥有生命周期管理的简单 Java 线程池。 \ No newline at end of file +拥有生命周期管理的简单 Java 线程池。其中所有的任务都会在插件被关闭时自动停止。 \ No newline at end of file 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 66408bb71..80751f6cd 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 @@ -35,7 +35,7 @@ internal fun startupConsoleThread() { val next = MiraiConsole.requestInput("").let { when { it.startsWith(CommandManager.commandPrefix) -> it - it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName + it == "?" -> CommandManager.commandPrefix + BuiltInCommands.HelpCommand.primaryName else -> CommandManager.commandPrefix + it } }