Merge remote-tracking branch 'origin/master' into dependencies

This commit is contained in:
Karlatemp 2020-09-10 22:34:33 +08:00
commit 95e1486e3f
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
38 changed files with 544 additions and 649 deletions

View File

@ -84,46 +84,46 @@ internal object MessageScopeCodegen {
) )
appendLine() 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()) { for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) {
appendKCode( appendKCode(
""" """
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun ${a}.scopeWith(vararg others: ${b}): MessageScope { public fun ${a}?.scopeWith(vararg others: ${b}?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
""" """
) )
appendLine() 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()) { for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) {
appendKCode( appendKCode(
""" """
@LowPriorityInOverloadResolution public fun ${a}?.scopeWith(other: ${b}?): MessageScope {
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 {
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope this == null && other == null -> NoopMessageScope
@ -137,11 +137,22 @@ internal object MessageScopeCodegen {
) )
appendLine() appendLine()
} }
//
// for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) {
// appendKCode(
// """
// public inline fun <R> ${a}.scopeWith(vararg others: ${b}, action: MessageScope.() -> R): R {
// return scopeWith(*others).invoke(action)
// }
// """
// )
// appendLine()
// }
for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) { for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) {
appendKCode( appendKCode(
""" """
public inline fun <R> ${a}.scopeWith(vararg others: ${b}, action: MessageScope.() -> R): R { public inline fun <R> ${a}?.scopeWith(vararg others: ${b}?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
""" """
@ -149,17 +160,6 @@ internal object MessageScopeCodegen {
appendLine() appendLine()
} }
for ((a, b) in (TypeCandidatesForMessageScope + KtMessageScope).arrangements()) {
appendKCode(
"""
public inline fun <R> ${a}?.scopeWithNotNull(vararg others: ${b}?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
"""
)
appendLine()
}
for (a in (TypeCandidatesForMessageScope + KtMessageScope)) { for (a in (TypeCandidatesForMessageScope + KtMessageScope)) {
appendKCode( appendKCode(
""" """

View File

@ -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
<!--[MiraiConsoleFrontEnd]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.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 <T : PluginData> loadPluginData(clazz: Class<T>): T
fun <T : PluginConfig> loadPluginConfig(clazz: Class<T>): 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`]
... 待续

View File

@ -16,17 +16,13 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.description.PermissibleIdentifierArgumentParser import net.mamoe.mirai.console.command.description.*
import net.mamoe.mirai.console.command.description.PermissionIdArgumentParser
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.command.CommandManagerImpl
import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.*
import net.mamoe.mirai.console.permission.PermissibleIdentifier
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.permission.PermissionService.Companion.denyPermission import net.mamoe.mirai.console.permission.PermissionService.Companion.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.getGrantedPermissions
import net.mamoe.mirai.console.permission.PermissionService.Companion.grantPermission import net.mamoe.mirai.console.permission.PermissionService.Companion.grantPermission
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
@ -51,6 +47,7 @@ internal interface BuiltInCommandInternal : Command
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
@Suppress("unused") @Suppress("unused")
@OptIn(ExperimentalPermission::class)
public object BuiltInCommands { public object BuiltInCommands {
public val all: Array<out Command> by lazy { public val all: Array<out Command> by lazy {
@ -63,9 +60,10 @@ public object BuiltInCommands {
} }
} }
public object Help : SimpleCommand( public object HelpCommand : SimpleCommand(
ConsoleCommandOwner, "help", ConsoleCommandOwner, "help",
description = "Command list" description = "Command list",
parentPermission = RootConsoleBuiltInPermission,
), BuiltInCommand { ), BuiltInCommand {
@Handler @Handler
public suspend fun CommandSender.handle() { public suspend fun CommandSender.handle() {
@ -82,9 +80,10 @@ public object BuiltInCommands {
}) })
} }
public object Stop : SimpleCommand( public object StopCommand : SimpleCommand(
ConsoleCommandOwner, "stop", "shutdown", "exit", ConsoleCommandOwner, "stop", "shutdown", "exit",
description = "Stop the whole world." description = "Stop the whole world.",
parentPermission = RootConsoleBuiltInPermission,
), BuiltInCommand { ), BuiltInCommand {
private val closingLock = Mutex() private val closingLock = Mutex()
@ -117,9 +116,10 @@ public object BuiltInCommands {
} }
} }
public object Login : SimpleCommand( public object LoginCommand : SimpleCommand(
ConsoleCommandOwner, "login", "登录", ConsoleCommandOwner, "login", "登录",
description = "Log in a bot account." description = "Log in a bot account.",
parentPermission = RootConsoleBuiltInPermission,
), BuiltInCommand { ), BuiltInCommand {
@Handler @Handler
public suspend fun CommandSender.handle(id: Long, password: String) { public suspend fun CommandSender.handle(id: Long, password: String) {
@ -145,23 +145,28 @@ public object BuiltInCommands {
} }
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
public object Permission : CompositeCommand( public object PermissionCommand : CompositeCommand(
ConsoleCommandOwner, "permission", "权限", ConsoleCommandOwner, "permission", "权限", "perm",
description = "Manage permissions", description = "Manage permissions",
overrideContext = buildCommandArgumentContext { overrideContext = buildCommandArgumentContext {
PermissibleIdentifier::class with PermissibleIdentifierArgumentParser 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 { ), BuiltInCommand {
// TODO: 2020/9/10 improve Permission command // TODO: 2020/9/10 improve Permission command
@SubCommand @SubCommand
public suspend fun CommandSender.grant(target: PermissibleIdentifier, permission: PermissionId) { public suspend fun CommandSender.grant(target: PermissibleIdentifier, permission: Permission) {
target.grantPermission(permission) target.grantPermission(permission)
sendMessage("OK") sendMessage("OK")
} }
@SubCommand @SubCommand
public suspend fun CommandSender.deny(target: PermissibleIdentifier, permission: PermissionId) { public suspend fun CommandSender.deny(target: PermissibleIdentifier, permission: Permission) {
target.denyPermission(permission) target.denyPermission(permission)
sendMessage("OK") sendMessage("OK")
} }

View File

@ -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.executeCommand
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.java.JCommand 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.internal.command.isValidSubName
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission 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.MessageChain
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
@ -107,7 +106,7 @@ public abstract class AbstractCommand
public override val owner: CommandOwner, public override val owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "<no description available>", description: String = "<no description available>",
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
@ -118,5 +117,5 @@ public abstract class AbstractCommand
}.toTypedArray() }.toTypedArray()
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
public override val permission: Permission by lazy { createCommandPermission(parentPermission) } public override val permission: Permission by lazy { createOrFindCommandPermission(parentPermission) }
} }

View File

@ -10,10 +10,7 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.*
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.permission.PermissionIdNamespace
import net.mamoe.mirai.console.permission.RootPermission
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
/** /**
@ -27,12 +24,12 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
public interface CommandOwner : PermissionIdNamespace { public interface CommandOwner : PermissionIdNamespace {
/** /**
* [PermissionIdNamespace] 拥有的指令都默认将 [basePermission] 作为父权限. * [PermissionIdNamespace] 拥有的指令都默认将 [parentPermission] 作为父权限.
* *
* TODO document * TODO document
*/ */
@ExperimentalPermission @ExperimentalPermission
public val basePermission: PermissionId public val parentPermission: Permission
} }
/** /**
@ -40,8 +37,8 @@ public interface CommandOwner : PermissionIdNamespace {
*/ */
internal object ConsoleCommandOwner : CommandOwner { internal object ConsoleCommandOwner : CommandOwner {
@ExperimentalPermission @ExperimentalPermission
override val basePermission: PermissionId override val parentPermission: Permission
get() = RootPermission.id get() = RootPermission
@ExperimentalPermission @ExperimentalPermission
override fun permissionId(id: String): PermissionId = PermissionId("console", id) override fun permissionId(id: String): PermissionId = PermissionId("console", id)

View File

@ -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.AbstractReflectionCommand
import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
import net.mamoe.mirai.console.permission.ExperimentalPermission 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.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationRetention.RUNTIME
@ -86,7 +86,7 @@ public abstract class CompositeCommand @OptIn(ExperimentalPermission::class) con
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
prefixOptional: Boolean = false, prefixOptional: Boolean = false,
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
) : Command, AbstractReflectionCommand(owner, names, description, parentPermission, prefixOptional), ) : Command, AbstractReflectionCommand(owner, names, description, parentPermission, prefixOptional),
@ -110,12 +110,6 @@ public abstract class CompositeCommand @OptIn(ExperimentalPermission::class) con
@Target(FUNCTION) @Target(FUNCTION)
protected annotation class SubCommand(vararg val value: String) protected annotation class SubCommand(vararg val value: String)
/** 指定子指令要求的权限 */
@Retention(RUNTIME)
@Target(FUNCTION)
@ExperimentalPermission
protected annotation class Permission(val value: String)
/** 指令描述 */ /** 指令描述 */
@Retention(RUNTIME) @Retention(RUNTIME)
@Target(FUNCTION) @Target(FUNCTION)

View File

@ -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.execute
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.java.JRawCommand 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.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission 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.MessageChain
/** /**
@ -44,12 +43,12 @@ public abstract class RawCommand @OptIn(ExperimentalPermission::class) construct
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */ /** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public override val description: String = "<no descriptions given>", public override val description: String = "<no descriptions given>",
/** 指令父权限 */ /** 指令父权限 */
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
public override val permission: Permission by lazy { createCommandPermission(parentPermission) } public override val permission: Permission by lazy { createOrFindCommandPermission(parentPermission) }
/** /**
* 在指令被执行时调用. * 在指令被执行时调用.

View File

@ -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.AbstractReflectionCommand
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
import net.mamoe.mirai.console.permission.ExperimentalPermission 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 import net.mamoe.mirai.message.data.MessageChain
/** /**
@ -53,10 +53,10 @@ public abstract class SimpleCommand @OptIn(ExperimentalPermission::class) constr
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
basePermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
prefixOptional: Boolean = false, prefixOptional: Boolean = false,
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional), ) : Command, AbstractReflectionCommand(owner, names, description, parentPermission, prefixOptional),
CommandArgumentContextAware { CommandArgumentContextAware {
/** /**

View File

@ -81,6 +81,21 @@ public interface CommandArgumentParser<out T : Any> {
public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender) public fun parse(raw: MessageContent, sender: CommandSender): T = parse(raw.content, sender)
} }
/**
* 使用原 [this] 解析, 成功后使用 [mapper] 映射为另一个类型.
*/
public fun <T : Any, R : Any> CommandArgumentParser<T>.map(
mapper: CommandArgumentParser<R>.(T) -> R
): CommandArgumentParser<R> = MappingCommandArgumentParser(this, mapper)
private class MappingCommandArgumentParser<T : Any, R : Any>(
private val original: CommandArgumentParser<T>,
private val mapper: CommandArgumentParser<R>.(T) -> R
) : CommandArgumentParser<R> {
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] 类型参数 * 解析一个字符串或 [SingleMessage] [T] 类型参数
* *

View File

@ -321,7 +321,7 @@ public object PermissionIdArgumentParser : CommandArgumentParser<PermissionId> {
public object PermissibleIdentifierArgumentParser : CommandArgumentParser<PermissibleIdentifier> { public object PermissibleIdentifierArgumentParser : CommandArgumentParser<PermissibleIdentifier> {
override fun parse(raw: String, sender: CommandSender): PermissibleIdentifier { override fun parse(raw: String, sender: CommandSender): PermissibleIdentifier {
return kotlin.runCatching { AbstractPermissibleIdentifier.parseFromString(raw) }.getOrElse { return kotlin.runCatching { AbstractPermissibleIdentifier.parseFromString(raw) }.getOrElse {
illegalArgument("无法解析 $raw 为 PermissionId") illegalArgument("无法解析 $raw 为 PermissibleIdentifier")
} }
} }

View File

@ -15,7 +15,7 @@ import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.CompositeCommand import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
import net.mamoe.mirai.console.permission.ExperimentalPermission 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.console.util.ConsoleExperimentalAPI
/** /**
@ -73,14 +73,14 @@ public abstract class JCompositeCommand @OptIn(ExperimentalPermission::class)
@JvmOverloads constructor( @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
) : CompositeCommand(owner, *names, parentPermission = parentPermission) { ) : CompositeCommand(owner, *names, parentPermission = parentPermission) {
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */ /** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public final override var description: String = "<no descriptions given>" public final override var description: String = "<no descriptions given>"
protected set protected set
@OptIn(ExperimentalPermission::class) @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 protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */

View File

@ -13,10 +13,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute 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.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission 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.MessageChain
import net.mamoe.mirai.message.data.SingleMessage 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 val owner: CommandOwner,
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */ /** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
public override vararg val names: String, public override vararg val names: String,
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
) : Command { ) : Command {
/** 用法说明, 用于发送给用户 */ /** 用法说明, 用于发送给用户 */
public override var usage: String = "<no usages given>" public override var usage: String = "<no usages given>"
@ -67,7 +66,7 @@ public abstract class JRawCommand @OptIn(ExperimentalPermission::class)
/** 指令权限 */ /** 指令权限 */
@ExperimentalPermission @ExperimentalPermission
public final override var permission: Permission = createCommandPermission(parentPermission) public final override var permission: Permission = createOrFindCommandPermission(parentPermission)
protected set protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */

View File

@ -16,7 +16,6 @@ import net.mamoe.mirai.console.command.SimpleCommand
import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
/** /**
* Java 实现: * Java 实现:
@ -44,8 +43,8 @@ import net.mamoe.mirai.console.permission.PermissionId
public abstract class JSimpleCommand @OptIn(ExperimentalPermission::class) constructor( public abstract class JSimpleCommand @OptIn(ExperimentalPermission::class) constructor(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
basePermission: PermissionId, basePermission: Permission,
) : SimpleCommand(owner, *names, basePermission = basePermission) { ) : SimpleCommand(owner, *names, parentPermission = basePermission) {
public override var description: String = super.description public override var description: String = super.description
protected set protected set

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.console.extension
import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.extensions.PluginLoaderProvider import net.mamoe.mirai.console.extensions.PluginLoaderProvider
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
/** /**
@ -28,6 +29,8 @@ public interface FunctionExtension : Extension
/** /**
* 为某单例服务注册的 [Extension]. * 为某单例服务注册的 [Extension].
* *
* 若同时有多个实例可用, 将会使用 [SingletonExtensionSelector.selectSingleton] 选择
*
* @see PermissionServiceProvider * @see PermissionServiceProvider
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI

View File

@ -5,12 +5,14 @@ import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.extension.SingletonExtensionPoint import net.mamoe.mirai.console.extension.SingletonExtensionPoint
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.plugin.description.PluginKind import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
/** /**
* [权限服务][PermissionService] 提供器. * [权限服务][PermissionService] 提供器.
* *
* 此扩展可由 [PluginKind.LOADER] [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 * 当插件注册 [PermissionService] , 默认会使用插件的 [PermissionService].
*
* 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] [PluginLoadPriority.ON_EXTENSIONS] 插件提供
*/ */
@ExperimentalPermission @ExperimentalPermission
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> { public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {

View File

@ -3,12 +3,12 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.description.PluginKind import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
/** /**
* 提供扩展 [PluginLoader] * 提供扩展 [PluginLoader]
* *
* 此扩展可由 [PluginKind.LOADER] 插件提供 * 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供
*/ */
public interface PluginLoaderProvider : InstanceExtension<PluginLoader<*, *>> { public interface PluginLoaderProvider : InstanceExtension<PluginLoader<*, *>> {
public companion object ExtensionPoint : AbstractExtensionPoint<PluginLoaderProvider>(PluginLoaderProvider::class) public companion object ExtensionPoint : AbstractExtensionPoint<PluginLoaderProvider>(PluginLoaderProvider::class)

View File

@ -12,7 +12,7 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.extension.* import net.mamoe.mirai.console.extension.*
import net.mamoe.mirai.console.internal.extensions.BuiltInSingletonExtensionSelector 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.console.plugin.name
import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.info
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -22,7 +22,7 @@ import kotlin.reflect.KClass
* *
* 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器. * 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器.
* *
* 此扩展可由 [PluginKind.LOADER] [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供 * 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供
*/ */
public interface SingletonExtensionSelector : FunctionExtension { public interface SingletonExtensionSelector : FunctionExtension {

View File

@ -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.command.CommandManagerImpl
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope 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.plugin.PluginManagerImpl
import net.mamoe.mirai.console.internal.util.autoHexToBytes
import net.mamoe.mirai.console.permission.BuiltInPermissionService import net.mamoe.mirai.console.permission.BuiltInPermissionService
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService
@ -124,13 +126,22 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
val pluginLoadSession: PluginManagerImpl.PluginLoadSession val pluginLoadSession: PluginManagerImpl.PluginLoadSession
phase `load plugins`@{ phase `load BEFORE_EXTENSIONS plugins`@{
PluginManager // init PluginManager // init
mainLogger.verbose { "Loading PluginLoader provider plugins..." } mainLogger.verbose { "Loading PluginLoader provider plugins..." }
PluginManagerImpl.loadEnablePluginProviderPlugins() PluginManagerImpl.loadEnablePluginProviderPlugins()
mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." } 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..." } mainLogger.verbose { "Scanning high-priority extension and normal plugins..." }
pluginLoadSession = PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider() pluginLoadSession = PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider()
mainLogger.verbose { "${pluginLoadSession.allKindsOfPlugins.size} plugin(s) found." } 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." } mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." }
} }
SingletonExtensionSelector.instance // init
phase `load PermissionService`@{ phase `load PermissionService`@{
mainLogger.verbose { "Loading PermissionService..." } mainLogger.verbose { "Loading PermissionService..." }
PermissionService.INSTANCE.let { ps -> PermissionService.INSTANCE.let { ps ->
@ -162,7 +171,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
CommandManagerImpl.commandListener // start CommandManagerImpl.commandListener // start
} }
phase `load normal plugins`@{ phase `load AFTER_EXTENSION plugins`@{
mainLogger.verbose { "Loading normal plugins..." } mainLogger.verbose { "Loading normal plugins..." }
val count = PluginManagerImpl.loadEnableNormalPlugins(pluginLoadSession) val count = PluginManagerImpl.loadEnableNormalPlugins(pluginLoadSession)
mainLogger.verbose { "$count normal plugin(s) loaded." } mainLogger.verbose { "$count normal plugin(s) loaded." }
@ -172,14 +181,19 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
phase `auto-login bots`@{ phase `auto-login bots`@{
runBlocking { runBlocking {
for ((id, password) in AutoLoginConfig.plainPasswords) { for ((id, password) in AutoLoginConfig.plainPasswords.filterNot { it.key == 123456654321L }) {
mainLogger.info { "Auto-login $id" } mainLogger.info { "Auto-login $id" }
MiraiConsole.addBot(id, password).alsoLogin() 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" } 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()
} }
} }
} }

View File

@ -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.internal.data.kClassQualifiedNameOrTip
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission 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.console.permission.PermissionService.Companion.testPermission
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KAnnotatedElement
@ -53,7 +51,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission:
owner: CommandOwner, owner: CommandOwner,
names: Array<out String>, names: Array<out String>,
description: String = "<no description available>", description: String = "<no description available>",
parentPermission: PermissionId = owner.basePermission, parentPermission: Permission = owner.parentPermission,
prefixOptional: Boolean = false prefixOptional: Boolean = false
) : Command, AbstractCommand( ) : Command, AbstractCommand(
owner, owner,
@ -79,7 +77,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission:
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
DefaultSubCommandDescriptor( DefaultSubCommandDescriptor(
"", "",
createCommandPermission(parentPermission), createOrFindCommandPermission(parentPermission),
onCommand = { sender: CommandSender, args: MessageChain -> onCommand = { sender: CommandSender, args: MessageChain ->
sender.onDefault(args) sender.onDefault(args)
} }
@ -144,7 +142,7 @@ internal abstract class AbstractReflectionCommand @OptIn(ExperimentalPermission:
argsWithSubCommandNameNotRemoved: MessageChain, argsWithSubCommandNameNotRemoved: MessageChain,
removeSubName: Boolean 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)) { if (!this.permission.testPermission(sender)) {
sender.sendMessage(usage) // TODO: 2020/8/26 #127 sender.sendMessage(usage) // TODO: 2020/8/26 #127
return return
@ -259,7 +257,7 @@ internal fun AbstractReflectionCommand.createSubCommand(
context: CommandArgumentContext context: CommandArgumentContext
): AbstractReflectionCommand.SubCommandDescriptor { ): AbstractReflectionCommand.SubCommandDescriptor {
val notStatic = !function.hasAnnotation<JvmStatic>() val notStatic = !function.hasAnnotation<JvmStatic>()
val overridePermission = function.findAnnotation<CompositeCommand.Permission>()//optional //val overridePermission = null//function.findAnnotation<CompositeCommand.PermissionId>()//optional
val subDescription = val subDescription =
function.findAnnotation<CompositeCommand.Description>()?.value ?: "" function.findAnnotation<CompositeCommand.Description>()?.value ?: ""
@ -331,7 +329,7 @@ internal fun AbstractReflectionCommand.createSubCommand(
commandName, commandName,
params, params,
subDescription, // overridePermission?.value 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<out Any> -> onCommand = { sender: CommandSender, args: Array<out Any> ->
val result = if (notStatic) { val result = if (notStatic) {
if (hasSenderParam) { if (hasSenderParam) {

View File

@ -13,7 +13,6 @@ import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.Command.Companion.primaryName import net.mamoe.mirai.console.command.Command.Companion.primaryName
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission 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
import net.mamoe.mirai.console.permission.PermissionService.Companion.testPermission import net.mamoe.mirai.console.permission.PermissionService.Companion.testPermission
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
@ -143,8 +142,9 @@ internal fun Group.fuzzySearchMember(
} }
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
internal fun Command.createCommandPermission(parent: PermissionId): Permission { internal fun Command.createOrFindCommandPermission(parent: Permission): Permission {
return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, parent) val id = owner.permissionId(primaryName)
return PermissionService.INSTANCE[id] ?: PermissionService.INSTANCE.register(id, description, parent)
} }
//// internal //// internal

View File

@ -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.AutoSavePluginConfig
import net.mamoe.mirai.console.data.ValueDescription import net.mamoe.mirai.console.data.ValueDescription
import net.mamoe.mirai.console.data.value 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() { internal object AutoLoginConfig : AutoSavePluginConfig() {
override val saveName: String override val saveName: String
@ -13,7 +15,7 @@ internal object AutoLoginConfig : AutoSavePluginConfig() {
账号和明文密码列表 账号和明文密码列表
""" """
) )
val plainPasswords: MutableMap<Long, String> by value(mutableMapOf()) val plainPasswords: MutableMap<Long, String> by value(mutableMapOf(123456654321L to "example"))
@ValueDescription( @ValueDescription(
@ -21,5 +23,9 @@ internal object AutoLoginConfig : AutoSavePluginConfig() {
账号和 MD5 密码列表 账号和 MD5 密码列表
""" """
) )
val md5Passwords: MutableMap<Long, String> by value(mutableMapOf()) val md5Passwords: MutableMap<Long, String> by value(
mutableMapOf(
123456654321L to "example".toByteArray().md5().toUHexString()
)
)
} }

View File

@ -2,7 +2,7 @@ package net.mamoe.mirai.console.internal.extensions
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.MiraiConsole 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.data.value
import net.mamoe.mirai.console.extension.Extension import net.mamoe.mirai.console.extension.Extension
import net.mamoe.mirai.console.extension.ExtensionRegistry import net.mamoe.mirai.console.extension.ExtensionRegistry
@ -15,9 +15,9 @@ import kotlin.reflect.KClass
internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector { 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" override val saveName: String get() = "ExtensionSelector"
val value: MutableMap<String, String> by value() val value: MutableMap<String, String> by value()

View File

@ -16,6 +16,7 @@ import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.data.runCatchingLog import net.mamoe.mirai.console.data.runCatchingLog
import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.internal.data.mkdir
import net.mamoe.mirai.console.permission.ExperimentalPermission 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.PermissionId
import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.permission.PermissionService.Companion.allocatePermissionIdForPlugin import net.mamoe.mirai.console.permission.PermissionService.Companion.allocatePermissionIdForPlugin
@ -44,11 +45,11 @@ internal abstract class JvmPluginInternal(
) : JvmPlugin, CoroutineScope { ) : JvmPlugin, CoroutineScope {
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
final override val basePermission: PermissionId by lazy { final override val parentPermission: Permission by lazy {
PermissionService.INSTANCE.register( PermissionService.INSTANCE.register(
PermissionService.INSTANCE.allocatePermissionIdForPlugin(name, "*"), PermissionService.INSTANCE.allocatePermissionIdForPlugin(name, "*"),
"The base permission" "The base permission"
).id )
} }
final override var isEnabled: Boolean = false final override var isEnabled: Boolean = false
@ -110,7 +111,7 @@ internal abstract class JvmPluginInternal(
} }
internal fun internalOnEnable(): Boolean { internal fun internalOnEnable(): Boolean {
basePermission parentPermission
if (!firstRun) refreshCoroutineContext() if (!firstRun) refreshCoroutineContext()
kotlin.runCatching { kotlin.runCatching {
onEnable() onEnable()

View File

@ -22,7 +22,7 @@ import net.mamoe.mirai.console.internal.data.mkdir
import net.mamoe.mirai.console.plugin.* import net.mamoe.mirai.console.plugin.*
import net.mamoe.mirai.console.plugin.description.PluginDependency import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription 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.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.info
@ -131,7 +131,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
internal fun loadEnableHighPriorityExtensionPlugins(session: PluginLoadSession): Int { internal fun loadEnableHighPriorityExtensionPlugins(session: PluginLoadSession): Int {
loadersLock.withLock { loadersLock.withLock {
session.allKindsOfPlugins.flatMap { it.second } session.allKindsOfPlugins.flatMap { it.second }
.filter { it.kind == PluginKind.HIGH_PRIORITY_EXTENSIONS } .filter { it.loadPriority == PluginLoadPriority.ON_EXTENSIONS }
.sortByDependencies() .sortByDependencies()
.also { it.loadAndEnableAllInOrder() } .also { it.loadAndEnableAllInOrder() }
.let { return it.size } .let { return it.size }
@ -142,7 +142,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
internal fun loadEnableNormalPlugins(session: PluginLoadSession): Int { internal fun loadEnableNormalPlugins(session: PluginLoadSession): Int {
loadersLock.withLock { loadersLock.withLock {
session.allKindsOfPlugins.flatMap { it.second } session.allKindsOfPlugins.flatMap { it.second }
.filter { it.kind == PluginKind.NORMAL } .filter { it.loadPriority == PluginLoadPriority.AFTER_EXTENSIONS }
.sortByDependencies() .sortByDependencies()
.also { it.loadAndEnableAllInOrder() } .also { it.loadAndEnableAllInOrder() }
.let { return it.size } .let { return it.size }
@ -191,7 +191,8 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
loader as PluginLoader<Plugin, PluginDescription> loader as PluginLoader<Plugin, PluginDescription>
descriptions.forEach(PluginManagerImpl::checkPluginDescription) 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() } .flatMap { it.second.asSequence() }

View File

@ -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<Byte>` 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)
}
}
}
}

View File

@ -9,28 +9,27 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.data.PluginDataExtensions
import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith
import java.util.concurrent.CopyOnWriteArrayList
/** /**
* *
*/ */
@ExperimentalPermission @ExperimentalPermission
public abstract class AbstractConcurrentPermissionService<P : Permission> : PermissionService<P> { internal abstract class AbstractConcurrentPermissionService<P : Permission> : PermissionService<P> {
protected abstract val permissions: MutableMap<PermissionId, P> protected abstract val permissions: MutableMap<PermissionId, P>
protected abstract val grantedPermissionsMap: MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>> protected abstract val grantedPermissionsMap: PluginDataExtensions.NotNullMutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>
protected abstract fun createPermission( protected abstract fun createPermission(
id: PermissionId, id: PermissionId,
description: String, description: String,
base: PermissionId = RootPermission.id parent: Permission
): P ): P
override fun get(id: PermissionId): P? = permissions[id] override fun get(id: PermissionId): P? = permissions[id]
override fun register(id: PermissionId, description: String, base: PermissionId): P { override fun register(id: PermissionId, description: String, parent: Permission): P {
grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed val instance = createPermission(id, description, parent)
val instance = createPermission(id, description, base)
val old = permissions.putIfAbsent(id, instance) val old = permissions.putIfAbsent(id, instance)
if (old != null) throw DuplicatedPermissionRegistrationException(instance, old) if (old != null) throw DuplicatedPermissionRegistrationException(instance, old)
return instance return instance
@ -38,16 +37,15 @@ public abstract class AbstractConcurrentPermissionService<P : Permission> : Perm
override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) { override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) {
val id = permission.id val id = permission.id
grantedPermissionsMap[id]?.add(permissibleIdentifier) grantedPermissionsMap[id].add(permissibleIdentifier)
?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.")
} }
override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) { override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) {
grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier) grantedPermissionsMap[permission.id].remove(permissibleIdentifier)
} }
override fun getRegisteredPermissions(): Sequence<P> = permissions.values.asSequence() override fun getRegisteredPermissions(): Sequence<P> = permissions.values.asSequence()
public override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence<P> = sequence<P> { override fun getGrantedPermissions(permissibleIdentifier: PermissibleIdentifier): Sequence<P> = sequence<P> {
for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) { for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) {
val granted = val granted =

View File

@ -9,28 +9,28 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import kotlinx.serialization.Serializable
import net.mamoe.mirai.console.data.AutoSavePluginConfig 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.PluginDataExtensions.withDefault
import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.data.value
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArraySet
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.createType
@ExperimentalPermission @ExperimentalPermission
public object AllGrantPermissionService : PermissionService<PermissionImpl> { internal object AllGrantPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionId, PermissionImpl>() private val all = ConcurrentHashMap<PermissionId, PermissionImpl>()
override val permissionType: KClass<PermissionImpl> override val permissionType: KClass<PermissionImpl> get() = PermissionImpl::class
get() = PermissionImpl::class override val rootPermission: PermissionImpl get() = RootPermissionImpl.also { all[it.id] = it }
override fun register( override fun register(
id: PermissionId, id: PermissionId,
description: String, description: String,
base: PermissionId parent: Permission
): PermissionImpl { ): PermissionImpl {
val new = PermissionImpl(id, description, base) val new = PermissionImpl(id, description, parent)
val old = all.putIfAbsent(id, new) val old = all.putIfAbsent(id, new)
if (old != null) throw DuplicatedPermissionRegistrationException(new, old) if (old != null) throw DuplicatedPermissionRegistrationException(new, old)
return new return new
@ -51,18 +51,23 @@ public object AllGrantPermissionService : PermissionService<PermissionImpl> {
} }
} }
@Suppress("DEPRECATION")
@OptIn(ExperimentalPermission::class)
private val RootPermissionImpl = PermissionImpl(PermissionId("*", "*"), "The root permission").also { it.parent = it }
@ExperimentalPermission @ExperimentalPermission
public object AllDenyPermissionService : PermissionService<PermissionImpl> { internal object AllDenyPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionId, PermissionImpl>() private val all = ConcurrentHashMap<PermissionId, PermissionImpl>()
override val permissionType: KClass<PermissionImpl> override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class get() = PermissionImpl::class
override val rootPermission: PermissionImpl = RootPermissionImpl.also { all[it.id] = it }
override fun register( override fun register(
id: PermissionId, id: PermissionId,
description: String, description: String,
base: PermissionId parent: Permission
): PermissionImpl { ): PermissionImpl {
val new = PermissionImpl(id, description, base) val new = PermissionImpl(id, description, parent)
val old = all.putIfAbsent(id, new) val old = all.putIfAbsent(id, new)
if (old != null) throw DuplicatedPermissionRegistrationException(new, old) if (old != null) throw DuplicatedPermissionRegistrationException(new, old)
return new return new
@ -90,41 +95,75 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService<P
@ExperimentalPermission @ExperimentalPermission
override val permissionType: KClass<PermissionImpl> override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class get() = PermissionImpl::class
override val permissions: MutableMap<PermissionId, PermissionImpl> = ConcurrentHashMap() override val permissions: ConcurrentHashMap<PermissionId, PermissionImpl> = ConcurrentHashMap()
override val rootPermission: PermissionImpl = RootPermissionImpl.also { permissions[it.id] = it }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override val grantedPermissionsMap: MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>> override val grantedPermissionsMap: PluginDataExtensions.NotNullMutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>
get() = config.grantedPermissionMap as MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>> get() = config.grantedPermissionMap as PluginDataExtensions.NotNullMutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>
override fun createPermission(id: PermissionId, description: String, base: PermissionId): PermissionImpl = override fun createPermission(id: PermissionId, description: String, parent: Permission): PermissionImpl =
PermissionImpl(id, description, base) PermissionImpl(id, description, parent)
internal val config: ConcurrentSaveData<PermissionImpl> = internal val config: ConcurrentSaveData =
ConcurrentSaveData( ConcurrentSaveData("PermissionService")
PermissionImpl::class.createType(),
"PermissionService",
)
@Suppress("RedundantVisibilityModifier") @Suppress("RedundantVisibilityModifier")
@ExperimentalPermission @ExperimentalPermission
internal class ConcurrentSaveData<P : Permission> private constructor( internal class ConcurrentSaveData private constructor(
permissionType: KType,
public override val saveName: String, public override val saveName: String,
// delegate: PluginConfig,
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any? @Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
) : AutoSavePluginConfig() { ) : AutoSavePluginConfig() {
public val grantedPermissionMap: MutableMap<PermissionId, MutableList<AbstractPermissibleIdentifier>> public val grantedPermissionMap: PluginDataExtensions.NotNullMutableMap<PermissionId, MutableSet<AbstractPermissibleIdentifier>>
by value<MutableMap<PermissionId, MutableList<AbstractPermissibleIdentifier>>>(ConcurrentHashMap()) by value<MutableMap<PermissionId, MutableSet<AbstractPermissibleIdentifier>>>(ConcurrentHashMap())
.withDefault { CopyOnWriteArrayList() } .withDefault { CopyOnWriteArraySet() }
public companion object { public companion object {
@JvmStatic @JvmStatic
public operator fun <P : Permission> invoke( public operator fun invoke(
permissionType: KType,
saveName: String, saveName: String,
// delegate: PluginConfig, // delegate: PluginConfig,
): ConcurrentSaveData<P> = 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) "<self>" else parent.toString()})"
}

View File

@ -46,36 +46,67 @@ public sealed class AbstractPermissibleIdentifier(
public companion object { public companion object {
@JvmStatic @JvmStatic
public fun parseFromString(string: String): AbstractPermissibleIdentifier { public fun parseFromString(string: String): AbstractPermissibleIdentifier {
val str = string.trim() val str = string.trim { it.isWhitespace() }.toLowerCase()
objects.find { it.toString() == str }?.let { return it as AbstractPermissibleIdentifier } if (str == "console") return Console
for ((regex, block) in regexes) { if (str.isNotEmpty()) {
val result = regex.find(str) ?: continue when (str[0]) {
if (result.range.last != str.lastIndex) continue 'g' -> {
if (result.range.first != 0) continue val arg = str.substring(1)
return result.destructured.run(block) 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") 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<Pair<Regex, (matchGroup: MatchResult.Destructured) -> 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 @ConsoleExperimentalAPI
@ -86,54 +117,70 @@ public sealed class AbstractPermissibleIdentifier(
) )
public object AnyGroup : AbstractPermissibleIdentifier(AnyContact) { 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) { 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 data class ExactMember(
public val groupId: Long, public val groupId: Long,
public val memberId: 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) { public object AnyFriend : AbstractPermissibleIdentifier(AnyUser) {
override fun toString(): String = "AnyFriend" override fun toString(): String = "f*"
} }
public data class ExactFriend( public data class ExactFriend(
public val id: Long public val id: Long
) : AbstractPermissibleIdentifier(ExactUser(id)) { ) : AbstractPermissibleIdentifier(ExactUser(id)) {
override fun toString(): String = "ExactFriend" override fun toString(): String = "f$id"
} }
public object AnyTemp : AbstractPermissibleIdentifier(AnyUser) { public data class AnyTemp(
override fun toString(): String = "AnyTemp" public val groupId: Long,
) : AbstractPermissibleIdentifier(AnyUser, AnyMember(groupId)) {
override fun toString(): String = "t$groupId.*"
} }
public data class ExactTemp( public data class ExactTemp(
public val groupId: Long, public val groupId: Long,
public val id: Long public val memberId: Long
) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ? ) : AbstractPermissibleIdentifier(ExactUser(groupId), ExactMember(groupId, memberId)) {
override fun toString(): String = "t$groupId.$memberId"
}
public object AnyUser : AbstractPermissibleIdentifier(AnyContact) { public object AnyUser : AbstractPermissibleIdentifier(AnyContact) {
override fun toString(): String = "AnyUser" override fun toString(): String = "u*"
} }
public data class ExactUser( public data class ExactUser(
public val id: Long public val id: Long
) : AbstractPermissibleIdentifier(AnyUser) ) : AbstractPermissibleIdentifier(AnyUser) {
override fun toString(): String = "u$id"
}
public object AnyContact : AbstractPermissibleIdentifier() { public object AnyContact : AbstractPermissibleIdentifier() {
override fun toString(): String = "AnyContact" override fun toString(): String = "*"
} }
public object Console : AbstractPermissibleIdentifier() { public object Console : AbstractPermissibleIdentifier() {
override fun toString(): String = "Console" override fun toString(): String = "console"
} }
} }

View File

@ -9,8 +9,6 @@
package net.mamoe.mirai.console.permission 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 import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
@ -25,22 +23,29 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
public interface Permission { public interface Permission {
public val id: PermissionId public val id: PermissionId
public val description: String 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") @get:JvmName("getRootPermission")
@ExperimentalPermission @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( PermissionService.INSTANCE.register(
ROOT_PERMISSION_ID, PermissionId("console", "*"),
"The parent of any permission", "The parent of any built-in commands"
ROOT_PERMISSION_ID
) )
} }
@ -48,16 +53,5 @@ public val RootPermission: Permission by lazy {
@ExperimentalPermission @ExperimentalPermission
public fun Permission.parentsWithSelfSequence(): Sequence<Permission> = public fun Permission.parentsWithSelfSequence(): Sequence<Permission> =
generateSequence(this) { p -> generateSequence(this) { p ->
p.parentId.findCorrespondingPermission()?.takeIf { parent -> parent != p } p.parent.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

View File

@ -23,6 +23,7 @@ import kotlin.reflect.full.isSuperclassOf
public interface PermissionService<P : Permission> { public interface PermissionService<P : Permission> {
@ExperimentalPermission @ExperimentalPermission
public val permissionType: KClass<P> public val permissionType: KClass<P>
public val rootPermission: P
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -46,7 +47,7 @@ public interface PermissionService<P : Permission> {
public fun register( public fun register(
id: PermissionId, id: PermissionId,
description: String, description: String,
base: PermissionId = RootPermission.id parent: Permission = RootPermission
): P ): P
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@ -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.PluginManager.INSTANCE.safeLoader
import net.mamoe.mirai.console.plugin.description.PluginDependency import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription 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.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 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] * 获取 [PluginDescription.info]

View File

@ -23,9 +23,9 @@ public interface PluginDescription {
/** /**
* 插件类型. 将会决定加载顺序 * 插件类型. 将会决定加载顺序
* *
* @see PluginKind * @see PluginLoadPriority
*/ */
public val kind: PluginKind public val loadPriority: PluginLoadPriority
/** /**
* 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'. * 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'.

View File

@ -14,39 +14,46 @@ import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.extension.Extension import net.mamoe.mirai.console.extension.Extension
import net.mamoe.mirai.console.extensions.BotConfigurationAlterer import net.mamoe.mirai.console.extensions.BotConfigurationAlterer
import net.mamoe.mirai.console.extensions.PermissionServiceProvider 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.internal.data.map
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.description.PluginLoadPriority.*
import net.mamoe.mirai.console.plugin.description.PluginKind.*
/** /**
* 插件类型. * 插件类型.
* *
* 插件类型将影响加载顺序: [LOADER] -> [HIGH_PRIORITY_EXTENSIONS] -> [NORMAL]. * 插件类型将影响加载顺序: [BEFORE_EXTENSIONS] -> [ON_EXTENSIONS] -> [AFTER_EXTENSIONS].
* *
* 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [LOADER] 不允许依赖一个 [NORMAL] 类型的插件. * 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [BEFORE_EXTENSIONS] 不允许依赖一个 [AFTER_EXTENSIONS] 类型的插件.
*/ */
public enum class PluginKind { public enum class PluginLoadPriority {
/** 表示此插件提供一个 [PluginLoader], 也可以同时提供其他 [Extension] 应最早被加载 */ /**
LOADER, * 表示此插件最早被加载. Console 启动时的第一初始化阶段就会加载这些插件.
*
* 一般只有提供 [PluginLoaderProvider] [SingletonExtensionSelector] 的插件才需要在此阶段加载.
*/
BEFORE_EXTENSIONS,
/** /**
* 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [NORMAL] 类型插件前加载 * 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [AFTER_EXTENSIONS] 类型插件前加载
* *
* 高优先级的 [Extension] 通常是覆盖 Console 内置的部分服务的扩展. [PermissionServiceProvider]. * 高优先级的 [Extension] 通常是覆盖 Console 内置的部分服务的扩展. [PermissionServiceProvider].
* *
* 一些普通的 [Extension], [BotConfigurationAlterer], 也可以使用 [NORMAL] 类型插件注册. * 一些普通的 [Extension], [BotConfigurationAlterer], 也可以使用 [AFTER_EXTENSIONS] 类型插件注册.
*/ */
HIGH_PRIORITY_EXTENSIONS, ON_EXTENSIONS,
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ /**
NORMAL; * 表示此插件为一个通常的插件, 在扩展处理完毕后加载.
*/
AFTER_EXTENSIONS;
public object AsStringSerializer : KSerializer<PluginKind> by String.serializer().map( public object AsStringSerializer : KSerializer<PluginLoadPriority> by String.serializer().map(
serializer = { it.name }, serializer = { it.name },
deserializer = { str -> deserializer = { str ->
values().firstOrNull { values().firstOrNull {
it.name.equals(str, ignoreCase = true) it.name.equals(str, ignoreCase = true)
} ?: NORMAL } ?: AFTER_EXTENSIONS
} }
) )
} }

View File

@ -14,7 +14,7 @@ package net.mamoe.mirai.console.plugin.jvm
import com.vdurmont.semver4j.Semver import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.plugin.description.PluginDependency import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription 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 import kotlin.internal.LowPriorityInOverloadResolution
/** /**
@ -82,7 +82,7 @@ public class JvmPluginDescriptionBuilder(
private var author: String = "" private var author: String = ""
private var info: String = "" private var info: String = ""
private var dependencies: MutableSet<PluginDependency> = mutableSetOf() private var dependencies: MutableSet<PluginDependency> = mutableSetOf()
private var kind: PluginKind = PluginKind.NORMAL private var loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun name(value: String): JvmPluginDescriptionBuilder = apply { this.name = value.trim() } 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() } public fun info(value: String): JvmPluginDescriptionBuilder = apply { this.info = value.trimIndent() }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun kind(value: PluginKind): JvmPluginDescriptionBuilder = apply { this.kind = value } public fun kind(value: PluginLoadPriority): JvmPluginDescriptionBuilder = apply { this.loadPriority = value }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun normalPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.NORMAL } public fun normalPlugin(): JvmPluginDescriptionBuilder =
apply { this.loadPriority = PluginLoadPriority.AFTER_EXTENSIONS }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.LOADER } public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder =
apply { this.loadPriority = PluginLoadPriority.BEFORE_EXTENSIONS }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder = public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder =
apply { this.kind = PluginKind.HIGH_PRIORITY_EXTENSIONS } apply { this.loadPriority = PluginLoadPriority.ON_EXTENSIONS }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun dependsOn( public fun dependsOn(
@ -152,7 +154,7 @@ public class JvmPluginDescriptionBuilder(
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
public fun build(): JvmPluginDescription = public fun build(): JvmPluginDescription =
SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@DslMarker @DslMarker
@ -192,7 +194,7 @@ public data class SimpleJvmPluginDescription
public override val author: String = "", public override val author: String = "",
public override val info: String = "", public override val info: String = "",
public override val dependencies: Set<PluginDependency> = setOf(), public override val dependencies: Set<PluginDependency> = setOf(),
public override val kind: PluginKind = PluginKind.NORMAL, public override val loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS,
) : JvmPluginDescription { ) : JvmPluginDescription {
@Deprecated( @Deprecated(
@ -214,8 +216,8 @@ public data class SimpleJvmPluginDescription
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: Set<PluginDependency> = setOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL, loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS,
) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, kind) ) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, loadPriority)
init { init {
require(!name.contains(':')) { "':' is forbidden in plugin name" } require(!name.contains(':')) { "':' is forbidden in plugin name" }
@ -240,8 +242,8 @@ public fun JvmPluginDescription(
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: Set<PluginDependency> = setOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) ): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)
@Deprecated( @Deprecated(
"JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.",
@ -260,5 +262,5 @@ public fun JvmPluginDescription(
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: Set<PluginDependency> = setOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind) ): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)

View File

@ -22,7 +22,6 @@ import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.User import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import kotlin.internal.InlineOnly
import kotlin.internal.LowPriorityInOverloadResolution import kotlin.internal.LowPriorityInOverloadResolution
/** /**
@ -38,10 +37,10 @@ import kotlin.internal.LowPriorityInOverloadResolution
* - `C<A>.toMessageScope()`. 其中 `C` 表示 `Iterable`, `Sequence`, `Flow`, `Array` 其中任一. * - `C<A>.toMessageScope()`. 其中 `C` 表示 `Iterable`, `Sequence`, `Flow`, `Array` 其中任一.
* *
* ## 连接 [MessageScope] * ## 连接 [MessageScope]
* - `A.scopeWith(vararg B)`. * - `A?.scopeWith(vararg B?)`.
* - `A.scopeWith(vararg A)`. * - `A?.scopeWith(vararg A?)`.
* - `A.scopeWithNotNull(vararg B?)`. 类似 [listOfNotNull]. *
* - `A.scopeWithNotNull(vararg A?)`. 类似 [listOfNotNull]. * `null` 项将会被过滤.
* *
* ## 自动去重 * ## 自动去重
* 在连接时, [MessageScope] 会自动根据真实的 [收信对象][CommandSender.subject] 去重. * 在连接时, [MessageScope] 会自动根据真实的 [收信对象][CommandSender.subject] 去重.
@ -83,12 +82,12 @@ import kotlin.internal.LowPriorityInOverloadResolution
* *
* // 使用 MessageScope, 清晰逻辑 * // 使用 MessageScope, 清晰逻辑
* // 表示至少发送给 `this`, 当 `this` 的真实发信对象与 `target.group` 不同时, 还额外发送给 `target.group` * // 表示至少发送给 `this`, 当 `this` 的真实发信对象与 `target.group` 不同时, 还额外发送给 `target.group`
* this.scopeWithNotNull(target.group) { * this.scopeWith(target.group) {
* sendMessage("${name} 禁言了 ${target.nameCardOrNick} $duration") * sendMessage("${name} 禁言了 ${target.nameCardOrNick} $duration")
* } * }
* *
* // 同样地, 可以扩展用法, 同时私聊指令执行者: * // 同样地, 可以扩展用法, 同时私聊指令执行者:
* // this.scopeWithNotNull( * // this.scopeWith(
* // target, * // target,
* // target.group * // target.group
* // ) { ... } * // ) { ... }
@ -143,132 +142,51 @@ public fun Contact.asMessageScope(): MessageScope = createScopeDelegate(this)
public fun CommandSender.asMessageScope(): MessageScope = createScopeDelegate(this) public fun CommandSender.asMessageScope(): MessageScope = createScopeDelegate(this)
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun Contact.scopeWith(vararg others: Contact): MessageScope { public fun Contact?.scopeWith(vararg others: Contact?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun Contact.scopeWith(vararg others: CommandSender): MessageScope { public fun Contact?.scopeWith(vararg others: CommandSender?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun Contact.scopeWith(vararg others: MessageScope): MessageScope { public fun Contact?.scopeWith(vararg others: MessageScope?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun CommandSender.scopeWith(vararg others: Contact): MessageScope { public fun CommandSender?.scopeWith(vararg others: Contact?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun CommandSender.scopeWith(vararg others: CommandSender): MessageScope { public fun CommandSender?.scopeWith(vararg others: CommandSender?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun CommandSender.scopeWith(vararg others: MessageScope): MessageScope { public fun CommandSender?.scopeWith(vararg others: MessageScope?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun MessageScope.scopeWith(vararg others: Contact): MessageScope { public fun MessageScope?.scopeWith(vararg others: Contact?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun MessageScope.scopeWith(vararg others: CommandSender): MessageScope { public fun MessageScope?.scopeWith(vararg others: CommandSender?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
public fun MessageScope.scopeWith(vararg others: MessageScope): MessageScope { public fun MessageScope?.scopeWith(vararg others: MessageScope?): MessageScope {
return others.fold(this.asMessageScope()) { acc, other -> CombinedScope(acc, other.asMessageScope()) } return others.fold(this.asMessageScopeOrNoop()) { acc, other -> acc.scopeWith(other?.asMessageScope()) }
} }
@LowPriorityInOverloadResolution public fun Contact?.scopeWith(other: Contact?): MessageScope {
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 {
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope 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") @Suppress("DuplicatedCode")
return when { return when {
this == null && other == null -> NoopMessageScope this == null && other == null -> NoopMessageScope
@ -367,78 +285,42 @@ public fun MessageScope?.scopeWithNotNull(other: MessageScope?): MessageScope {
} }
} }
public inline fun <R> Contact.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { public inline fun <R> Contact?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> Contact.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { public inline fun <R> Contact?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> Contact.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { public inline fun <R> Contact?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> CommandSender.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { public inline fun <R> CommandSender?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> CommandSender.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { public inline fun <R> CommandSender?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> CommandSender.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { public inline fun <R> CommandSender?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> MessageScope.scopeWith(vararg others: Contact, action: MessageScope.() -> R): R { public inline fun <R> MessageScope?.scopeWith(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> MessageScope.scopeWith(vararg others: CommandSender, action: MessageScope.() -> R): R { public inline fun <R> MessageScope?.scopeWith(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> MessageScope.scopeWith(vararg others: MessageScope, action: MessageScope.() -> R): R { public inline fun <R> MessageScope?.scopeWith(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWith(*others).invoke(action) return scopeWith(*others).invoke(action)
} }
public inline fun <R> Contact?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> Contact?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> Contact?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> CommandSender?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> CommandSender?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> CommandSender?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> MessageScope?.scopeWithNotNull(vararg others: Contact?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> MessageScope?.scopeWithNotNull(vararg others: CommandSender?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
public inline fun <R> MessageScope?.scopeWithNotNull(vararg others: MessageScope?, action: MessageScope.() -> R): R {
return scopeWithNotNull(*others).invoke(action)
}
@Deprecated( @Deprecated(
"Senseless scopeWith. Use asMessageScope.", "Senseless scopeWith. Use asMessageScope.",
ReplaceWith("this.asMessageScope()", "net.mamoe.mirai.console.util.asMessageScope") ReplaceWith("this.asMessageScope()", "net.mamoe.mirai.console.util.asMessageScope")
@ -623,27 +505,23 @@ public suspend fun Flow<MessageScope>.toMessageScope(): MessageScope { // Flow<A
// [MessageScope] 实现 // [MessageScope] 实现
@PublishedApi @PublishedApi
@InlineOnly
internal inline fun MessageScope.asMessageScope(): MessageScope = this internal inline fun MessageScope.asMessageScope(): MessageScope = this
@InlineOnly
private inline fun MessageScope?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope private inline fun MessageScope?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope
@InlineOnly
private inline fun Contact?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope private inline fun Contact?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope
@InlineOnly
private inline fun CommandSender?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope private inline fun CommandSender?.asMessageScopeOrNoop(): MessageScope = this?.asMessageScope() ?: NoopMessageScope
@InlineOnly
private inline fun createScopeDelegate(o: CommandSender) = CommandSenderAsMessageScope(o) private inline fun createScopeDelegate(o: CommandSender) = CommandSenderAsMessageScope(o)
@InlineOnly
private inline fun createScopeDelegate(o: Contact) = ContactAsMessageScope(o) private inline fun createScopeDelegate(o: Contact) = ContactAsMessageScope(o)
private fun MessageScope.asSequence(): Sequence<MessageScope> { internal fun MessageScope.asSequence(): Sequence<MessageScope> {
return if (this is CombinedScope) { 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) } else sequenceOf(this)
} }

View File

@ -23,8 +23,8 @@ object Versions {
const val consoleTerminal = "0.1.0" const val consoleTerminal = "0.1.0"
const val consolePure = console const val consolePure = console
const val kotlinCompiler = "1.4.0" const val kotlinCompiler = "1.4.10"
const val kotlinStdlib = "1.4.0" const val kotlinStdlib = kotlinCompiler
const val coroutines = "1.3.9" const val coroutines = "1.3.9"
const val collectionsImmutable = "0.3.2" const val collectionsImmutable = "0.3.2"
@ -37,5 +37,5 @@ object Versions {
const val bintray = "1.8.5" const val bintray = "1.8.5"
const val blockingBridge = "1.0.5" const val blockingBridge = "1.0.5"
const val yamlkt = "0.5.1" const val yamlkt = "0.5.2"
} }

View File

@ -26,7 +26,6 @@
[`RawCommand`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt [`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 [`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 [`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 [`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 [`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` 等配置文件来指示信息。 插件描述需要在主类构造器传递给 `super`。因此插件不需要 `plugin.yml`, `plugin.xml` 等配置文件来指示信息。
Mirai Console 使用 `ServiceLoader` 加载插件。 Mirai Console 使用类似 `ServiceLoader` 的机制加载插件。
在 Kotlin[使用 AutoService])自动配置 service 信息。 在 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` 或默认 (不指定) - 访问权限为 `public` 或默认 (不指定)
```kotlin ```kotlin
@AutoService(JvmPlugin::class) // 如果选用上述自动配置的方法
object SchedulePlugin : KotlinPlugin( object SchedulePlugin : KotlinPlugin(
SimpleJvmPluginDescription( // 插件的描述, name 和 version 是必须的 JvmPluginDescription(
name = "Schedule", id = "org.example.my-schedule-plugin",
version = "1.0.0", version = "1.0.0",
// author, description, ... ) {
) name("Schedule")
// author("...")
// dependsOn("...")
}
) { ) {
// ... // ...
} }
@ -117,10 +119,14 @@ object SchedulePlugin : KotlinPlugin(
public final class JExample extends JavaPlugin { public final class JExample extends JavaPlugin {
public static final JExample INSTANCE = new JExample(); // 可以像 Kotlin 一样静态初始化单例 public static final JExample INSTANCE = new JExample(); // 可以像 Kotlin 一样静态初始化单例
private JExample() { private JExample() {
super(new SimpleJvmPluginDescription( super(new JvmPluginDescriptionBuilder(
"JExample", // name "JExample", // name
"1.0.0" // version "1.0.0" // version
)); )
// .author("...")
// .info("...")
.build()
);
} }
} }
``` ```
@ -133,10 +139,14 @@ public final class JExample extends JavaPlugin {
return instance; return instance;
} }
public JExample() { // 此时必须 public public JExample() { // 此时必须 public
super(new SimpleJvmPluginDescription( super(new JvmPluginDescriptionBuilder(
"JExample", // name "JExample", // name
"1.0.0" // version "1.0.0" // version
)); )
// .author("...")
// .info("...")
.build()
);
instance = this; instance = this;
} }
} }
@ -248,5 +258,31 @@ Java
**仅可在插件 onEnable() 时及其之后才能使用这些方法。** **仅可在插件 onEnable() 时及其之后才能使用这些方法。**
**在插件 onDisable() 之后不能使用这些方法。** **在插件 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<String, String> by value()
}
```
### 附录Java 插件的多线程调度器 - [`JavaPluginScheduler`] ### 附录Java 插件的多线程调度器 - [`JavaPluginScheduler`]
拥有生命周期管理的简单 Java 线程池。 拥有生命周期管理的简单 Java 线程池。其中所有的任务都会在插件被关闭时自动停止。

View File

@ -35,7 +35,7 @@ internal fun startupConsoleThread() {
val next = MiraiConsole.requestInput("").let { val next = MiraiConsole.requestInput("").let {
when { when {
it.startsWith(CommandManager.commandPrefix) -> it it.startsWith(CommandManager.commandPrefix) -> it
it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName it == "?" -> CommandManager.commandPrefix + BuiltInCommands.HelpCommand.primaryName
else -> CommandManager.commandPrefix + it else -> CommandManager.commandPrefix + it
} }
} }