Introduce JvmPluginDescriptionBuilder, add checks

This commit is contained in:
Him188 2020-09-10 10:30:00 +08:00
parent a70e00464f
commit c76a6dacc9
11 changed files with 340 additions and 118 deletions

View File

@ -42,7 +42,6 @@ import net.mamoe.mirai.console.permission.RootPermission
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.center.PluginCenter import net.mamoe.mirai.console.plugin.center.PluginCenter
import net.mamoe.mirai.console.util.BotManager
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.ConsoleInput import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -121,8 +120,6 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
phase `load configurations`@{ phase `load configurations`@{
mainLogger.verbose { "Loading configurations..." } mainLogger.verbose { "Loading configurations..." }
ConsoleDataScope.reloadAll() ConsoleDataScope.reloadAll()
BotManager
} }
val pluginLoadSession: PluginManagerImpl.PluginLoadSession val pluginLoadSession: PluginManagerImpl.PluginLoadSession

View File

@ -1,53 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.internal.data.builtins
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.data.AutoSavePluginConfig
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
import net.mamoe.mirai.console.data.ValueDescription
import net.mamoe.mirai.console.data.value
import net.mamoe.mirai.console.util.BotManager
import net.mamoe.mirai.contact.User
internal object BotManagerImpl : BotManager {
override val User.isManager: Boolean get() = this.id in ManagersConfig[this.bot]
override fun Bot.removeManager(id: Long): Boolean {
return ManagersConfig[this].remove(id)
}
override val Bot.managers: List<Long>
get() = ManagersConfig[this].toList()
override fun Bot.addManager(id: Long): Boolean {
return ManagersConfig[this].add(id)
}
}
internal object ManagersConfig : AutoSavePluginConfig() {
override val saveName: String
get() = "Managers"
@ValueDescription(
"""
管理员列表
"""
)
private val managers
by value<MutableMap<Long, MutableSet<Long>>>()
.withEmptyDefault()
.mapKeys(Bot::getInstance, Bot::id)
internal operator fun get(bot: Bot): MutableSet<Long> = managers[bot]
}

View File

@ -22,7 +22,7 @@ import net.mamoe.mirai.utils.minutesToMillis
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") { internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") {
private val data: List<PluginData> = mutableListOf() private val data: List<PluginData> = mutableListOf()
private val configs: MutableList<PluginConfig> = mutableListOf(ManagersConfig, AutoLoginConfig) private val configs: MutableList<PluginConfig> = mutableListOf(AutoLoginConfig)
fun addAndReloadConfig(config: PluginConfig) { fun addAndReloadConfig(config: PluginConfig) {
configs.add(config) configs.add(config)

View File

@ -171,8 +171,10 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
@kotlin.jvm.Throws(PluginLoadException::class) @kotlin.jvm.Throws(PluginLoadException::class)
internal fun checkPluginDescription(description: PluginDescription) { internal fun checkPluginDescription(description: PluginDescription) {
when (description.name.toLowerCase()) { kotlin.runCatching {
"main", "console", "plugin", "config", "data" -> throw PluginLoadException("Plugin name '${description.name}' is forbidden.") PluginDescription.checkPluginDescription(description)
}.getOrElse {
throw PluginLoadException("PluginDescription check failed.", it)
} }
} }
@ -214,7 +216,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
return cannotBeLoad return cannotBeLoad
} }
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> = fun Collection<PluginDependency>.filterIsMissing(): List<PluginDependency> =
this.filterNot { it.isOptional || it in resolved } this.filterNot { it.isOptional || it in resolved }
tailrec fun List<D>.doSort() { tailrec fun List<D>.doSort() {
@ -257,4 +259,4 @@ internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>, plugin: Plug
) )
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean = internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
any { it.name == dependency.name } any { it.id == dependency.id }

View File

@ -82,4 +82,4 @@ public inline val Plugin.author: String get() = this.description.author
/** /**
* 获取 [PluginDescription.dependencies] * 获取 [PluginDescription.dependencies]
*/ */
public inline val Plugin.dependencies: List<PluginDependency> get() = this.description.dependencies public inline val Plugin.dependencies: Set<PluginDependency> get() = this.description.dependencies

View File

@ -0,0 +1,8 @@
package net.mamoe.mirai.console.plugin.description
public class IllegalPluginDescriptionException : RuntimeException {
public constructor() : super()
public constructor(message: String?) : super(message)
public constructor(message: String?, cause: Throwable?) : super(message, cause)
public constructor(cause: Throwable?) : super(cause)
}

View File

@ -18,9 +18,11 @@ import com.vdurmont.semver4j.Semver
* *
* @see PluginDescription.dependencies * @see PluginDescription.dependencies
*/ */
public data class PluginDependency( public data class PluginDependency @JvmOverloads constructor(
/** 依赖插件名 */ /**
public val name: String, * 依赖插件 ID, [PluginDescription.id]
*/
public val id: String,
/** /**
* 依赖版本号. null 时则为不限制版本. * 依赖版本号. null 时则为不限制版本.
* *
@ -34,6 +36,16 @@ public data class PluginDependency(
*/ */
public val isOptional: Boolean = false public val isOptional: Boolean = false
) { ) {
/**
* @see PluginDependency
*/
public constructor(name: String, isOptional: Boolean = false) : this(
name, null, isOptional
)
/**
* @see PluginDependency
*/
public constructor(name: String, version: String, isOptional: Boolean) : this( public constructor(name: String, version: String, isOptional: Boolean) : this(
name, name,
Semver(version, Semver.SemverType.IVY), Semver(version, Semver.SemverType.IVY),

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.console.plugin.description
import com.vdurmont.semver4j.Semver import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginLoadException
/** /**
@ -27,14 +28,44 @@ public interface PluginDescription {
public val kind: PluginKind public val kind: PluginKind
/** /**
* 插件名称. 不允许存在 ":", 推荐全英文. * 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'.
* *
* 插件名称不能完全是以下其中一种. * - 类似于 Java 包名, 插件 ID 需要 '域名.名称' 格式, `net.mamoe.mirai.example-plugin`
* - 域名和名称都是必须的
* - '.' 不允许位于首位或末尾
* - '-' '_' 仅允许存在于两个英文字母之间
*
* ID 在插件发布后就应该保持不变, 以便其他插件添加依赖.
*
* 插件 ID 的域名和名称都不能完全是以下其中一个 ([FORBIDDEN_ID_WORDS]).
* - "console"
* - "main"
* - "plugin"
* - "config"
* - "data"
*
*
* ID 用于指令权限等一些内部处理
*
* @see FORBIDDEN_ID_LETTERS
* @see FORBIDDEN_ID_WORDS
*/
public val id: String
/**
* 插件名称. 允许中文, 允许各类符号.
*
* 插件名称不能完全是以下其中一种 ([FORBIDDEN_ID_WORDS]).
* - console * - console
* - main * - main
* - plugin * - plugin
* - config * - config
* - data * - data
*
* 插件名称用于显示给用户.
*
* @see FORBIDDEN_ID_LETTERS
* @see FORBIDDEN_ID_WORDS
*/ */
public val name: String public val name: String
@ -46,7 +77,21 @@ public interface PluginDescription {
/** /**
* 插件版本. * 插件版本.
* *
* 语法参考: ([语义化版本 2.0.0](https://semver.org/lang/zh-CN/)) * 语法参考: ([语义化版本 2.0.0](https://semver.org/lang/zh-CN/)).
*
* 合法的版本号示例:
* - `1.0.0`
* - `1.0`
* - `1.0-M1`
* - `1.0.0-M1`
* - `1.0.0-M2-1`
* - `1` (尽管非常不建议这么做)
*
* 非法版本号实例:
* - `DEBUG-1`
* - `-1.0`
* - `v1.0` (不允许 "v")
* - `V1.0` (不允许 "V")
* *
* @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本. * @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本.
*/ */
@ -62,6 +107,77 @@ public interface PluginDescription {
* *
* @see PluginDependency * @see PluginDependency
*/ */
public val dependencies: List<PluginDependency> public val dependencies: Set<PluginDependency>
public companion object {
public val FORBIDDEN_ID_LETTERS: Array<String> = "~!@#$%^&*()+/*<>{}|[]\\?".map(Char::toString).toTypedArray()
public val FORBIDDEN_ID_WORDS: Array<String> = arrayOf("main", "console", "plugin", "config", "data")
/**
* 依次检查 [PluginDescription] [PluginDescription.id], [PluginDescription.name], [PluginDescription.dependencies] 的合法性
*
* @throws IllegalPluginDescriptionException 当不合法时抛出.
*/
@Throws(IllegalPluginDescriptionException::class)
public fun checkPluginDescription(instance: PluginDescription) {
kotlin.runCatching {
checkPluginId(instance.id)
checkPluginName(instance.name)
checkDependencies(instance.id, instance.dependencies)
}.getOrElse {
throw IllegalPluginDescriptionException(
"Illegal description. Plugin ${instance.name} (${instance.id})",
it
)
}
}
/**
* 检查 [PluginDescription.id] 的合法性.
*
* @throws IllegalPluginDescriptionException 当不合法时抛出.
*/
@Throws(IllegalPluginDescriptionException::class)
public fun checkPluginId(id: String) {
if (id.isBlank()) throw IllegalPluginDescriptionException("Plugin id cannot be blank")
if (id.count { it == '.' } < 2) throw IllegalPluginDescriptionException("'$id' is illegal. Plugin id must consist of both domain and name. ")
FORBIDDEN_ID_LETTERS.firstOrNull { it in id }?.let { illegal ->
throw IllegalPluginDescriptionException("Plugin id contains illegal char: $illegal.")
}
val idSections = id.split('.')
FORBIDDEN_ID_WORDS.firstOrNull { it in idSections }?.let { illegal ->
throw IllegalPluginDescriptionException("Plugin id contains illegal word: '$illegal'.")
}
}
/**
* 检查 [PluginDescription.name] 的合法性.
*
* @throws IllegalPluginDescriptionException 当不合法时抛出.
*/
@Throws(IllegalPluginDescriptionException::class)
public fun checkPluginName(name: String) {
if (name.isBlank()) throw IllegalPluginDescriptionException("Plugin name cannot be blank")
FORBIDDEN_ID_WORDS.firstOrNull { it in name }?.let { illegal ->
throw IllegalPluginDescriptionException("Plugin name is illegal: '$illegal'.")
}
}
/**
* 检查 [PluginDescription.dependencies] 的合法性.
*
* @throws IllegalPluginDescriptionException 当不合法时抛出.
*/
@Throws(IllegalPluginDescriptionException::class)
public fun checkDependencies(pluginId: String, dependencies: Set<PluginDependency>) {
if (dependencies.distinctBy { it.id }.size != dependencies.size)
throw PluginLoadException("Duplicated dependency detected: A plugin cannot depend on different versions of dependencies of the same id")
if (dependencies.any { it.id == pluginId })
throw PluginLoadException("Recursive dependency detected: A plugin cannot depend on itself")
}
}
} }

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("unused") @file:Suppress("unused", "INVISIBLE_REFERENCE", "INVISIBLE_member")
package net.mamoe.mirai.console.plugin.jvm package net.mamoe.mirai.console.plugin.jvm
@ -15,35 +15,207 @@ import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.plugin.description.PluginDependency import net.mamoe.mirai.console.plugin.description.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.PluginKind
import kotlin.internal.LowPriorityInOverloadResolution
/** /**
* JVM 插件的描述. 通常作为 `plugin.yml` * JVM 插件的描述. 通常作为 `plugin.yml`
*
* 请不要自行实现 [JvmPluginDescription] 接口. 它不具有继承稳定性.
*
* @see SimpleJvmPluginDescription * @see SimpleJvmPluginDescription
* @see JvmPluginDescriptionBuilder
*/ */
public interface JvmPluginDescription : PluginDescription public interface JvmPluginDescription : PluginDescription {
public companion object {
/**
* 构建 [JvmPluginDescription]
* @see JvmPluginDescriptionBuilder
*/
@JvmSynthetic
public operator fun invoke(
name: String,
version: String,
block: JvmPluginDescriptionBuilder.() -> Unit = {}
): JvmPluginDescription = JvmPluginDescriptionBuilder(name, version).apply(block).build()
/**
* 构建 [JvmPluginDescription]
* @see JvmPluginDescriptionBuilder
*/
@JvmSynthetic
public operator fun invoke(
name: String,
version: Semver,
block: JvmPluginDescriptionBuilder.() -> Unit = {}
): JvmPluginDescription = JvmPluginDescriptionBuilder(name, version).apply(block).build()
}
}
/** /**
* [JvmPluginDescription] 构建器.
*
* #### Kotlin Example
* ```
* val desc = JvmPluginDescription("org.example.example-plugin", "1.0.0") {
* info("This is an example plugin")
* dependsOn("org.example.another-plugin")
* }
* ```
*
* #### Java Example
* ```
* JvmPluginDescription desc = new JvmPluginDescriptionBuilder("org.example.example-plugin", "1.0.0")
* .info("This is an example plugin")
* .dependsOn("org.example.another-plugin")
* .build()
* ```
*
* @see [JvmPluginDescription.invoke]
*/
public class JvmPluginDescriptionBuilder(
private var id: String,
private var version: Semver,
) {
public constructor(name: String, version: String) : this(name, Semver(version, Semver.SemverType.LOOSE))
private var name: String = id
private var author: String = ""
private var info: String = ""
private var dependencies: MutableSet<PluginDependency> = mutableSetOf()
private var kind: PluginKind = PluginKind.NORMAL
@ILoveKuriyamaMiraiForever
public fun name(value: String): JvmPluginDescriptionBuilder = apply { this.name = value.trim() }
@ILoveKuriyamaMiraiForever
public fun version(value: String): JvmPluginDescriptionBuilder =
apply { this.version = Semver(value, Semver.SemverType.LOOSE) }
@ILoveKuriyamaMiraiForever
public fun version(value: Semver): JvmPluginDescriptionBuilder = apply { this.version = value }
@ILoveKuriyamaMiraiForever
public fun id(value: String): JvmPluginDescriptionBuilder = apply { this.id = value.trim() }
@ILoveKuriyamaMiraiForever
public fun author(value: String): JvmPluginDescriptionBuilder = apply { this.author = value.trim() }
@ILoveKuriyamaMiraiForever
public fun info(value: String): JvmPluginDescriptionBuilder = apply { this.info = value.trimIndent() }
@ILoveKuriyamaMiraiForever
public fun kind(value: PluginKind): JvmPluginDescriptionBuilder = apply { this.kind = value }
@ILoveKuriyamaMiraiForever
public fun normalPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.NORMAL }
@ILoveKuriyamaMiraiForever
public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder = apply { this.kind = PluginKind.LOADER }
@ILoveKuriyamaMiraiForever
public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder =
apply { this.kind = PluginKind.HIGH_PRIORITY_EXTENSIONS }
@ILoveKuriyamaMiraiForever
public fun dependsOn(
pluginId: String,
version: String? = null,
isOptional: Boolean = false
): JvmPluginDescriptionBuilder = apply {
if (version == null) this.dependencies.add(PluginDependency(pluginId, version, isOptional))
else this.dependencies.add(PluginDependency(pluginId, version, isOptional))
}
@ILoveKuriyamaMiraiForever
public fun setDependencies(
value: Set<PluginDependency>
): JvmPluginDescriptionBuilder = apply {
this.dependencies = value.toMutableSet()
}
@ILoveKuriyamaMiraiForever
public fun dependsOn(
vararg dependencies: PluginDependency
): JvmPluginDescriptionBuilder = apply {
for (dependency in dependencies) {
this.dependencies.add(dependency)
}
}
@ILoveKuriyamaMiraiForever
public fun dependsOn(
pluginId: String,
version: Semver? = null,
isOptional: Boolean = false
): JvmPluginDescriptionBuilder = apply { this.dependencies.add(PluginDependency(pluginId, version, isOptional)) }
@Suppress("DEPRECATION_ERROR")
public fun build(): JvmPluginDescription =
SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind)
@Retention(AnnotationRetention.SOURCE)
@DslMarker
private annotation class ILoveKuriyamaMiraiForever // https://zh.moegirl.org.cn/zh-cn/%E6%A0%97%E5%B1%B1%E6%9C%AA%E6%9D%A5
}
/**
* @constructor 推荐使用带名称的参数, 而不要按位置摆放.
*
* @see JvmPluginDescription * @see JvmPluginDescription
*/ */
@Deprecated(
"""
将在 1.0-RC 删除. 请使用 JvmPluginDescription.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription"
),
level = DeprecationLevel.ERROR
)
public data class SimpleJvmPluginDescription public data class SimpleJvmPluginDescription
@Deprecated(
"""
构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription(name, version) {}",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke"
),
level = DeprecationLevel.ERROR
)
@JvmOverloads public constructor( @JvmOverloads public constructor(
public override val name: String, public override val name: String,
public override val version: Semver, public override val version: Semver,
public override val id: String = name,
public override val author: String = "", public override val author: String = "",
public override val info: String = "", public override val info: String = "",
public override val dependencies: List<PluginDependency> = listOf(), public override val dependencies: Set<PluginDependency> = setOf(),
public override val kind: PluginKind = PluginKind.NORMAL, public override val kind: PluginKind = PluginKind.NORMAL,
) : JvmPluginDescription { ) : JvmPluginDescription {
@Deprecated(
"""
构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription.invoke(name, version) {}",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke"
),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
@JvmOverloads @JvmOverloads
public constructor( public constructor(
name: String, name: String,
version: String, version: String,
id: String = name,
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: List<PluginDependency> = listOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL, kind: PluginKind = PluginKind.NORMAL,
) : this(name, Semver(version, Semver.SemverType.LOOSE), author, info, dependencies, kind) ) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, kind)
init { init {
require(!name.contains(':')) { "':' is forbidden in plugin name" } require(!name.contains(':')) { "':' is forbidden in plugin name" }
@ -59,15 +231,17 @@ public data class SimpleJvmPluginDescription
), ),
level = DeprecationLevel.WARNING level = DeprecationLevel.WARNING
) )
@Suppress("FunctionName") @LowPriorityInOverloadResolution
@Suppress("DEPRECATION_ERROR", "FunctionName")
public fun JvmPluginDescription( public fun JvmPluginDescription(
name: String, name: String,
version: Semver, version: Semver,
id: String = name,
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: List<PluginDependency> = listOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL kind: PluginKind = PluginKind.NORMAL
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) ): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind)
@Deprecated( @Deprecated(
"JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.", "JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.",
@ -77,12 +251,14 @@ public fun JvmPluginDescription(
), ),
level = DeprecationLevel.WARNING level = DeprecationLevel.WARNING
) )
@Suppress("FunctionName") @LowPriorityInOverloadResolution
@Suppress("DEPRECATION_ERROR", "FunctionName")
public fun JvmPluginDescription( public fun JvmPluginDescription(
name: String, name: String,
version: String, version: String,
id: String = name,
author: String = "", author: String = "",
info: String = "", info: String = "",
dependencies: List<PluginDependency> = listOf(), dependencies: Set<PluginDependency> = setOf(),
kind: PluginKind = PluginKind.NORMAL kind: PluginKind = PluginKind.NORMAL
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, author, info, dependencies, kind) ): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, kind)

View File

@ -1,36 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE")
@file:JvmMultifileClass
@file:JvmName("ConsoleUtils")
package net.mamoe.mirai.console.util
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.internal.data.builtins.BotManagerImpl
import net.mamoe.mirai.contact.User
public interface BotManager {
/**
* 判断此用户是否为 console 管理员
*/
public val User.isManager: Boolean
public val Bot.managers: List<Long>
public fun Bot.removeManager(id: Long): Boolean
public fun Bot.addManager(id: Long): Boolean
public companion object INSTANCE : BotManager { // kotlin import handler doesn't recognize delegation.
override fun Bot.addManager(id: Long): Boolean = BotManagerImpl.run { addManager(id) }
override fun Bot.removeManager(id: Long): Boolean = BotManagerImpl.run { removeManager(id) }
override val User.isManager: Boolean get() = BotManagerImpl.run { isManager }
override val Bot.managers: List<Long> get() = BotManagerImpl.run { managers }
}
}

View File

@ -10,8 +10,8 @@
package net.mamoe.mirai.console.data package net.mamoe.mirai.console.data
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
import net.mamoe.mirai.console.plugin.jvm.SimpleJvmPluginDescription
import net.mamoe.mirai.console.util.ConsoleInternalAPI import net.mamoe.mirai.console.util.ConsoleInternalAPI
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -21,7 +21,7 @@ import kotlin.test.assertSame
internal class PluginDataTest { internal class PluginDataTest {
object MyPlugin : KotlinPlugin( object MyPlugin : KotlinPlugin(
SimpleJvmPluginDescription( JvmPluginDescription(
"1", "2" "1", "2"
) )
) )