mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Implement PluginLoaders and PluginManager
This commit is contained in:
parent
4de8a6b01c
commit
9c18c04466
@ -224,7 +224,8 @@ fun genCollectionValueImpl(
|
||||
"""
|
||||
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||
var internalValue: $kotlinTypeName = default
|
||||
return object : ${miraiValueName}Value(), $kotlinTypeName by dynamic$collectionName({ internalValue }) {
|
||||
val delegt = dynamic$collectionName { internalValue }
|
||||
return object : ${miraiValueName}Value(), $kotlinTypeName by delegt {
|
||||
override var value: $kotlinTypeName
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import upload.Bintray
|
||||
import java.util.*
|
||||
|
||||
@ -12,19 +11,46 @@ plugins {
|
||||
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
version = Versions.Mirai.console
|
||||
description = "Console backend for mirai"
|
||||
|
||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
|
||||
languageSettings.progressiveMode = true
|
||||
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
|
||||
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile::class.java) {
|
||||
options.encoding = "UTF8"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets.all {
|
||||
target.compilations.all {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=enable"
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
languageSettings.apply {
|
||||
enableLanguageFeature("InlineClasses")
|
||||
progressiveMode = true
|
||||
|
||||
useExperimentalAnnotation("kotlin.Experimental")
|
||||
useExperimentalAnnotation("kotlin.OptIn")
|
||||
|
||||
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
|
||||
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
|
||||
useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
|
||||
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("test") {
|
||||
languageSettings.apply {
|
||||
languageVersion = "1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,36 +60,15 @@ dependencies {
|
||||
compileAndRuntime(kotlin("stdlib"))
|
||||
|
||||
api("net.mamoe.yamlkt:yamlkt:0.3.1")
|
||||
|
||||
api("org.jetbrains:annotations:19.0.0")
|
||||
|
||||
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
|
||||
testApi(kotlin("stdlib"))
|
||||
testApi(kotlin("stdlib-jdk8"))
|
||||
testApi(kotlin("test"))
|
||||
testApi(kotlin("test-junit5"))
|
||||
}
|
||||
|
||||
version = Versions.Mirai.console
|
||||
|
||||
description = "Console backend for mirai"
|
||||
|
||||
val compileKotlin: KotlinCompile by tasks
|
||||
compileKotlin.kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
val compileTestKotlin: KotlinCompile by tasks
|
||||
compileTestKotlin.kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
tasks.withType(JavaCompile::class.java) {
|
||||
options.encoding = "UTF8"
|
||||
}
|
||||
|
||||
|
||||
// region PUBLISHING
|
||||
|
||||
tasks.register("ensureBintrayAvailable") {
|
||||
doLast {
|
||||
@ -128,4 +133,6 @@ if (Bintray.isBintrayAvailable(project)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
|
||||
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
|
||||
|
||||
// endregion
|
@ -13,33 +13,40 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.io.charsets.Charset
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
||||
import net.mamoe.mirai.console.plugins.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugins.PluginLoader
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
// 前端使用
|
||||
interface IMiraiConsole : CoroutineScope {
|
||||
internal interface IMiraiConsole : CoroutineScope {
|
||||
val build: String
|
||||
val version: String
|
||||
|
||||
/**
|
||||
* Console运行路径
|
||||
* Console 运行路径
|
||||
*/
|
||||
val path: String
|
||||
val rootDir: File
|
||||
|
||||
/**
|
||||
* Console前端接口
|
||||
* Console 前端接口
|
||||
*/
|
||||
val frontEnd: MiraiConsoleFrontEnd
|
||||
|
||||
/**
|
||||
* 与前端交互所使用的Logger
|
||||
* 与前端交互所使用的 Logger
|
||||
*/
|
||||
val mainLogger: MiraiLogger
|
||||
|
||||
/**
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
||||
*/
|
||||
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||
}
|
||||
|
||||
object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||
@ -52,19 +59,19 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||
|
||||
override val build: String get() = instance.build
|
||||
override val version: String get() = instance.version
|
||||
override val path: String get() = instance.path
|
||||
override val rootDir: File get() = instance.rootDir
|
||||
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
|
||||
override val mainLogger: MiraiLogger get() = instance.mainLogger
|
||||
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
||||
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
|
||||
|
||||
init {
|
||||
DefaultLogger = {
|
||||
this.newLogger(it)
|
||||
DefaultLogger = { identity ->
|
||||
this.newLogger(identity)
|
||||
}
|
||||
this.coroutineContext[Job]!!.invokeOnCompletion {
|
||||
Bot.botInstances.forEach {
|
||||
it.close()
|
||||
}
|
||||
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +79,9 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Included in kotlin stdlib 1.4
|
||||
*/
|
||||
internal val Throwable.stacktraceString: String
|
||||
get() =
|
||||
ByteArrayOutputStream().apply {
|
||||
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.utils
|
||||
package net.mamoe.mirai.console
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.center.CuiPluginCenter
|
@ -10,11 +10,7 @@
|
||||
package net.mamoe.mirai.console.command.description
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.command.BotAwareCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.MemberCommandSender
|
||||
import net.mamoe.mirai.console.command.UserCommandSender
|
||||
import net.mamoe.mirai.console.utils.fuzzySearchMember
|
||||
import net.mamoe.mirai.console.command.*
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
|
||||
@ -61,4 +63,97 @@ internal infix fun <T> Array<out T>.intersects(other: Array<out T>): Boolean {
|
||||
if (this[i] == other[i]) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal fun String.fuzzyCompare(target: String): Double {
|
||||
var step = 0
|
||||
if (this == target) {
|
||||
return 1.0
|
||||
}
|
||||
if (target.length > this.length) {
|
||||
return 0.0
|
||||
}
|
||||
for (i in this.indices) {
|
||||
if (target.length == i) {
|
||||
step--
|
||||
} else {
|
||||
if (this[i] != target[i]) {
|
||||
break
|
||||
}
|
||||
step++
|
||||
}
|
||||
}
|
||||
|
||||
if (step == this.length - 1) {
|
||||
return 1.0
|
||||
}
|
||||
return step.toDouble() / this.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
*/
|
||||
internal inline fun <T : Any> Collection<T>.fuzzySearch(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
this.forEach {
|
||||
val thisIndex = index(it)
|
||||
if (thisIndex == target) {
|
||||
return it
|
||||
}
|
||||
with(thisIndex.fuzzyCompare(target)) {
|
||||
if (this > rate) {
|
||||
rate = this
|
||||
potential = it
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
* 并且确保target是唯一的
|
||||
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
|
||||
*/
|
||||
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
var collide = 0
|
||||
this.forEach {
|
||||
with(index(it).fuzzyCompare(target)) {
|
||||
if (this > rate) {
|
||||
rate = this
|
||||
potential = it
|
||||
}
|
||||
if (this == 1.0) {
|
||||
collide++
|
||||
}
|
||||
if (collide > 1) {
|
||||
return null//collide
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
|
||||
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
|
||||
return this.members.fuzzySearchOnly(nameCardTarget) {
|
||||
it.nameCard
|
||||
}
|
||||
}
|
@ -6,50 +6,133 @@
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
|
||||
|
||||
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
sealed class JarPlugin : Plugin(), CoroutineScope {
|
||||
internal lateinit var _description: JarPluginDescription
|
||||
interface JvmPlugin : Plugin, CoroutineScope {
|
||||
val logger: MiraiLogger
|
||||
val description: JvmPluginDescription
|
||||
|
||||
final override val description: PluginDescription get() = _description
|
||||
final override val loader: JarPluginLoader get() = JarPluginLoader
|
||||
@JvmDefault
|
||||
fun onLoad() {
|
||||
}
|
||||
|
||||
@JvmDefault
|
||||
fun onEnable() {
|
||||
}
|
||||
|
||||
@JvmDefault
|
||||
fun onDisable() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class JavaPlugin @JvmOverloads constructor(
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
|
||||
// TODO: 2020/5/23 scheduler, event listener(?)
|
||||
}
|
||||
|
||||
abstract class KotlinPlugin @JvmOverloads constructor(
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
|
||||
// that's it
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal class JarPluginDescription(
|
||||
data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器
|
||||
override val kind: PluginKind,
|
||||
override val name: String,
|
||||
override val author: String,
|
||||
override val version: String,
|
||||
override val info: String,
|
||||
override val depends: List<String>
|
||||
) : PluginDescription
|
||||
override val loadBefore: List<String>,
|
||||
override val dependencies: List<PluginDependency>
|
||||
) : PluginDescription, FilePluginDescription {
|
||||
/**
|
||||
* 在手动实现时使用这个构造器.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
constructor(
|
||||
kind: PluginKind,
|
||||
name: String,
|
||||
author: String,
|
||||
version: String,
|
||||
info: String,
|
||||
loadBefore: List<String>,
|
||||
depends: List<PluginDependency>,
|
||||
file: File
|
||||
) : this(kind, name, author, version, info, loadBefore, depends) {
|
||||
this._file = file
|
||||
}
|
||||
|
||||
abstract class JavaPlugin : JarPlugin()
|
||||
@Suppress("PropertyName")
|
||||
@Transient
|
||||
internal var _file: File? = null
|
||||
|
||||
abstract class KotlinPlugin : JarPlugin()
|
||||
override val file: File
|
||||
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
|
||||
}
|
||||
|
||||
internal abstract class JvmPluginImpl(
|
||||
parentCoroutineContext: CoroutineContext
|
||||
) : JvmPlugin, CoroutineScope {
|
||||
/**
|
||||
* Initialized immediately after construction of [JvmPluginImpl] instance
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
internal lateinit var _description: JvmPluginDescription
|
||||
override val description: JvmPluginDescription get() = _description
|
||||
|
||||
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
|
||||
|
||||
final override val coroutineContext: CoroutineContext by lazy {
|
||||
SupervisorJob(parentCoroutineContext[Job]) + CoroutineExceptionHandler { _, throwable ->
|
||||
logger.error(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内建的 Jar (JVM) 插件加载器
|
||||
*/
|
||||
object JarPluginLoader : PluginLoader<JarPlugin> {
|
||||
override val list: List<JarPlugin>
|
||||
get() = TODO("Not yet implemented")
|
||||
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar") {
|
||||
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
||||
|
||||
override fun load(plugin: JarPlugin) {
|
||||
TODO("Not yet implemented")
|
||||
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
||||
TODO(
|
||||
"""
|
||||
CHECK IS JAR FILE AND CAN BE READ
|
||||
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
|
||||
SET JvmPluginDescription._file
|
||||
RETURN PLUGIN
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
override fun enable(plugin: JarPlugin) {
|
||||
TODO("Not yet implemented")
|
||||
@Throws(PluginLoadException::class)
|
||||
override fun load(description: JvmPluginDescription): JvmPlugin {
|
||||
TODO("FIND PLUGIN MAIN, THEN LOAD")
|
||||
// no need to check dependencies
|
||||
}
|
||||
|
||||
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
|
||||
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
|
||||
}
|
||||
|
||||
/*
|
@ -9,45 +9,82 @@
|
||||
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
|
||||
/** 插件类型 */
|
||||
enum class PluginKind {
|
||||
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
|
||||
LOADER,
|
||||
|
||||
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
|
||||
NORMAL
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件信息
|
||||
* 插件描述
|
||||
*/
|
||||
interface PluginDescription {
|
||||
val kind: PluginKind
|
||||
|
||||
val name: String
|
||||
val author: String
|
||||
val version: String
|
||||
val info: String
|
||||
val depends: List<String>
|
||||
|
||||
/** 指定此插件需要在这些插件之前加载 */
|
||||
val loadBefore: List<String>
|
||||
|
||||
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
|
||||
val dependencies: List<PluginDependency>
|
||||
}
|
||||
|
||||
/** 插件的一个依赖的信息 */
|
||||
@Serializable
|
||||
data class PluginDependency(
|
||||
/** 依赖插件名 */
|
||||
val name: String,
|
||||
/**
|
||||
* 依赖版本号
|
||||
* @see versionKind 版本号类型
|
||||
*/
|
||||
val version: String,
|
||||
/** 版本号类型 */
|
||||
val versionKind: VersionKind
|
||||
) {
|
||||
enum class VersionKind {
|
||||
/** 要求依赖精确的版本 */
|
||||
EXACT,
|
||||
|
||||
/** 要求依赖最低版本 */
|
||||
AT_LEAST,
|
||||
|
||||
/** 要求依赖最高版本 */
|
||||
AT_MOST
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$name ${versionKind.toEnglishString()}v$version"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
|
||||
PluginDependency.VersionKind.EXACT -> ""
|
||||
PluginDependency.VersionKind.AT_LEAST -> "at least "
|
||||
PluginDependency.VersionKind.AT_MOST -> "at most "
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件基类.
|
||||
*
|
||||
* 内建的插件类型:
|
||||
* - [JarPlugin]
|
||||
* 基于文件的插件的描述
|
||||
*/
|
||||
abstract class Plugin : CoroutineScope {
|
||||
abstract val description: PluginDescription
|
||||
abstract val loader: PluginLoader<*>
|
||||
interface FilePluginDescription : PluginDescription {
|
||||
val file: File
|
||||
}
|
||||
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
val logger: MiraiLogger by lazy { MiraiConsole.newLogger(description.name) }
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = SupervisorJob(MiraiConsole.coroutineContext[Job]) + CoroutineExceptionHandler { _, throwable ->
|
||||
logger.error(throwable)
|
||||
}
|
||||
|
||||
open fun onLoaded() {}
|
||||
open fun onDisabled() {}
|
||||
open fun onEnabled() {}
|
||||
}
|
||||
/**
|
||||
* 表示一个 mirai-console 插件.
|
||||
*
|
||||
* @see JvmPlugin
|
||||
*/
|
||||
interface Plugin
|
@ -1,29 +1,68 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 插件加载器
|
||||
* 插件加载器.
|
||||
*
|
||||
* 插件加载器只实现寻找插件列表, 加载插件, 启用插件, 关闭插件这四个功能.
|
||||
*
|
||||
* 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护.
|
||||
*/
|
||||
interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
/**
|
||||
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次
|
||||
*/
|
||||
fun listPlugins(): List<D>
|
||||
|
||||
/**
|
||||
* 获取此插件的描述
|
||||
*/
|
||||
@Throws(PluginLoadException::class)
|
||||
fun getPluginDescription(plugin: P): D
|
||||
|
||||
/**
|
||||
* 加载一个插件 (实例), 但不 [启用][enable] 它. 返回加载成功的实例
|
||||
*
|
||||
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
|
||||
*/
|
||||
@Throws(PluginLoadException::class)
|
||||
fun load(description: D): P
|
||||
|
||||
fun enable(plugin: P)
|
||||
fun disable(plugin: P)
|
||||
}
|
||||
|
||||
open class PluginLoadException : RuntimeException {
|
||||
constructor() : super()
|
||||
constructor(message: String?) : super(message)
|
||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
/**
|
||||
* '/plugins' 目录中的插件的加载器. 每个加载器需绑定一个后缀.
|
||||
*
|
||||
* @see AbstractFilePluginLoader
|
||||
* @see JarPluginLoader 内建的 Jar (JVM) 插件加载器.
|
||||
*/
|
||||
interface PluginLoader<P : Plugin> {
|
||||
val list: List<P>
|
||||
|
||||
fun loadAll() = list.forEach(::load)
|
||||
fun enableAll() = list.forEach(::enable)
|
||||
fun unloadAll() = list.forEach(::unload)
|
||||
fun reloadAll() = list.forEach(::reload)
|
||||
|
||||
val isUnloadSupported: Boolean
|
||||
get() = false
|
||||
|
||||
fun load(plugin: P)
|
||||
fun enable(plugin: P)
|
||||
fun unload(plugin: P) {
|
||||
error("NotImplemented")
|
||||
}
|
||||
|
||||
fun reload(plugin: P) {
|
||||
unload(plugin)
|
||||
load(plugin)
|
||||
}
|
||||
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
|
||||
/**
|
||||
* 所支持的插件文件后缀, 不含 '.'. 如 [JarPluginLoader] 为 "jar"
|
||||
*/
|
||||
val fileSuffix: String
|
||||
}
|
||||
|
||||
abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
|
||||
override val fileSuffix: String
|
||||
) : FilePluginLoader<P, D> {
|
||||
private fun pluginsFilesSequence(): Sequence<File> =
|
||||
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
|
||||
|
||||
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
||||
|
||||
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
||||
}
|
@ -1,24 +1,180 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
val Plugin.description: PluginDescription get() = TODO()
|
||||
val <P : Plugin> P.loader: PluginLoader<P, *> get() = TODO()
|
||||
|
||||
inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this)
|
||||
inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this)
|
||||
|
||||
object PluginManager {
|
||||
private val _loaders: MutableSet<PluginLoader<*>> = mutableSetOf()
|
||||
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||
|
||||
val loaders: Set<PluginLoader<*>> get() = _loaders
|
||||
class LoaderNode<P : Plugin, D : PluginDescription>(
|
||||
val loader: PluginLoader<P, D>,
|
||||
val loadedPlugins: MutableList<P> = mutableListOf()
|
||||
)
|
||||
|
||||
fun registerPluginLoader(loader: PluginLoader<*>) {
|
||||
_loaders.add(loader)
|
||||
private val _pluginLoaders: MutableSet<LoaderNode<*, *>> = mutableSetOf()
|
||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||
|
||||
private val resolvedPlugins: LinkedList<Plugin> = LinkedList()
|
||||
|
||||
@JvmStatic
|
||||
val plugins: List<Plugin>
|
||||
get() = _pluginLoaders.flatMap { it.loadedPlugins }
|
||||
|
||||
/**
|
||||
* 内建的插件加载器列表. 由 [MiraiConsole] 初始化
|
||||
*/
|
||||
@JvmStatic
|
||||
val builtInLoaders: List<PluginLoader<*, *>>
|
||||
get() = MiraiConsole.builtInPluginLoaders
|
||||
|
||||
/**
|
||||
* 由插件创建的 [PluginLoader]
|
||||
*/
|
||||
val pluginLoaders: List<PluginLoader<*, *>> get() = _pluginLoaders.map { it.loader }
|
||||
|
||||
@JvmStatic
|
||||
fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
|
||||
_pluginLoaders.add(LoaderNode(loader))
|
||||
}
|
||||
|
||||
fun unregisterPluginLoader(loader: PluginLoader<*>) {
|
||||
_loaders.remove(loader)
|
||||
@JvmStatic
|
||||
fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
|
||||
_pluginLoaders.removeAll { it.loader == loader }
|
||||
}
|
||||
|
||||
fun loadPlugins() {
|
||||
loaders.forEach(PluginLoader<*>::loadAll)
|
||||
|
||||
// region LOADING
|
||||
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
||||
return this.load(description).also { resolvedPlugins.add(it) }
|
||||
}
|
||||
|
||||
fun enablePlugins() {
|
||||
loaders.forEach(PluginLoader<*>::enableAll)
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginAndEnable(description: D) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return this.enable(loadPluginNoEnable(description.unwrap()))
|
||||
}
|
||||
|
||||
/**
|
||||
* STEPS:
|
||||
* 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件
|
||||
* 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件
|
||||
* 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表.
|
||||
* 4. 解决依赖并排序
|
||||
* 5. 依次 [PluginLoader.load]
|
||||
* 但不 [PluginLoader.enable]
|
||||
*
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
internal fun loadEnablePlugins() {
|
||||
val all = loadAndEnableLoaderProviders() + pluginLoaders.listAllPlugins().flatMap { it.second }
|
||||
|
||||
for ((loader, desc) in all.sortByDependencies()) {
|
||||
loader.loadPluginAndEnable(desc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
|
||||
val allDescriptions =
|
||||
this.builtInLoaders.listAllPlugins()
|
||||
.asSequence()
|
||||
.onEach { (loader, descriptions) ->
|
||||
loader as PluginLoader<Plugin, PluginDescription>
|
||||
|
||||
for (it in descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies()) {
|
||||
loader.loadPluginAndEnable(it)
|
||||
}
|
||||
}
|
||||
.flatMap { it.second.asSequence() }
|
||||
|
||||
return allDescriptions.toList()
|
||||
}
|
||||
|
||||
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
|
||||
return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList()
|
||||
}
|
||||
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
|
||||
val resolved = ArrayList<D>(this.size)
|
||||
|
||||
fun D.canBeLoad(): Boolean = this.dependencies.all { it in resolved }
|
||||
|
||||
fun List<D>.consumeLoadable(): List<D> {
|
||||
val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() }
|
||||
resolved.addAll(canBeLoad)
|
||||
return cannotBeLoad
|
||||
}
|
||||
|
||||
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> = this.filterNot { it in resolved }
|
||||
|
||||
tailrec fun List<D>.doSort() {
|
||||
if (this.isEmpty()) return
|
||||
|
||||
val beforeSize = this.size
|
||||
this.consumeLoadable().also { resultPlugins ->
|
||||
check(resultPlugins.size < beforeSize) {
|
||||
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
|
||||
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${badPlugin.dependencies.filterIsMissing()
|
||||
.joinToString()}"
|
||||
})
|
||||
}
|
||||
}.doSort()
|
||||
}
|
||||
|
||||
this.doSort()
|
||||
return resolved
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
internal data class PluginDescriptionWithLoader(
|
||||
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
|
||||
@JvmField val delegate: PluginDescription
|
||||
) : PluginDescription by delegate
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <D : PluginDescription> PluginDescription.unwrap(): D =
|
||||
if (this is PluginDescriptionWithLoader) this.delegate as D else this as D
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescriptionWithLoader =
|
||||
PluginDescriptionWithLoader(
|
||||
loader as PluginLoader<*, PluginDescription>, this
|
||||
)
|
||||
|
||||
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
|
||||
any { it.name == dependency.name }
|
||||
|
||||
class PluginMissingDependencyException : Exception {
|
||||
constructor() : super()
|
||||
constructor(message: String?) : super(message)
|
||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
open class PluginResolutionException : Exception {
|
||||
constructor() : super()
|
||||
constructor(message: String?) : super(message)
|
||||
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
constructor(cause: Throwable?) : super(cause)
|
||||
}
|
@ -9,10 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.setting.internal
|
||||
|
||||
import kotlinx.serialization.Decoder
|
||||
import kotlinx.serialization.Encoder
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialDescriptor
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.*
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
|
||||
@ -415,7 +412,8 @@ internal fun Setting.valueImpl(default: Array<String>): TypedStringArrayValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Int>): IntListValue {
|
||||
var internalValue: List<Int> = default
|
||||
return object : IntListValue(), List<Int> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : IntListValue(), List<Int> by delegt {
|
||||
override var value: List<Int>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -430,7 +428,8 @@ internal fun Setting.valueImpl(default: List<Int>): IntListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
|
||||
var internalValue: List<Short> = default
|
||||
return object : ShortListValue(), List<Short> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : ShortListValue(), List<Short> by delegt {
|
||||
override var value: List<Short>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -445,7 +444,8 @@ internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
|
||||
var internalValue: List<Byte> = default
|
||||
return object : ByteListValue(), List<Byte> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : ByteListValue(), List<Byte> by delegt {
|
||||
override var value: List<Byte>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -460,7 +460,8 @@ internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Long>): LongListValue {
|
||||
var internalValue: List<Long> = default
|
||||
return object : LongListValue(), List<Long> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : LongListValue(), List<Long> by delegt {
|
||||
override var value: List<Long>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -475,7 +476,8 @@ internal fun Setting.valueImpl(default: List<Long>): LongListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
|
||||
var internalValue: List<Float> = default
|
||||
return object : FloatListValue(), List<Float> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : FloatListValue(), List<Float> by delegt {
|
||||
override var value: List<Float>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -490,7 +492,8 @@ internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
|
||||
var internalValue: List<Double> = default
|
||||
return object : DoubleListValue(), List<Double> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : DoubleListValue(), List<Double> by delegt {
|
||||
override var value: List<Double>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -505,7 +508,8 @@ internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
|
||||
var internalValue: List<Boolean> = default
|
||||
return object : BooleanListValue(), List<Boolean> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : BooleanListValue(), List<Boolean> by delegt {
|
||||
override var value: List<Boolean>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -520,7 +524,8 @@ internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<Char>): CharListValue {
|
||||
var internalValue: List<Char> = default
|
||||
return object : CharListValue(), List<Char> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : CharListValue(), List<Char> by delegt {
|
||||
override var value: List<Char>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -535,7 +540,8 @@ internal fun Setting.valueImpl(default: List<Char>): CharListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: List<String>): StringListValue {
|
||||
var internalValue: List<String> = default
|
||||
return object : StringListValue(), List<String> by dynamicList({ internalValue }) {
|
||||
val delegt = dynamicList { internalValue }
|
||||
return object : StringListValue(), List<String> by delegt {
|
||||
override var value: List<String>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -550,7 +556,8 @@ internal fun Setting.valueImpl(default: List<String>): StringListValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
|
||||
var internalValue: Set<Int> = default
|
||||
return object : IntSetValue(), Set<Int> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : IntSetValue(), Set<Int> by delegt {
|
||||
override var value: Set<Int>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -565,7 +572,8 @@ internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
|
||||
var internalValue: Set<Short> = default
|
||||
return object : ShortSetValue(), Set<Short> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : ShortSetValue(), Set<Short> by delegt {
|
||||
override var value: Set<Short>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -580,7 +588,8 @@ internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
|
||||
var internalValue: Set<Byte> = default
|
||||
return object : ByteSetValue(), Set<Byte> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : ByteSetValue(), Set<Byte> by delegt {
|
||||
override var value: Set<Byte>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -595,7 +604,8 @@ internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
|
||||
var internalValue: Set<Long> = default
|
||||
return object : LongSetValue(), Set<Long> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : LongSetValue(), Set<Long> by delegt {
|
||||
override var value: Set<Long>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -610,7 +620,8 @@ internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
|
||||
var internalValue: Set<Float> = default
|
||||
return object : FloatSetValue(), Set<Float> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : FloatSetValue(), Set<Float> by delegt {
|
||||
override var value: Set<Float>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -625,7 +636,8 @@ internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
|
||||
var internalValue: Set<Double> = default
|
||||
return object : DoubleSetValue(), Set<Double> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : DoubleSetValue(), Set<Double> by delegt {
|
||||
override var value: Set<Double>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -640,7 +652,8 @@ internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
|
||||
var internalValue: Set<Boolean> = default
|
||||
return object : BooleanSetValue(), Set<Boolean> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : BooleanSetValue(), Set<Boolean> by delegt {
|
||||
override var value: Set<Boolean>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -655,7 +668,8 @@ internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
|
||||
var internalValue: Set<Char> = default
|
||||
return object : CharSetValue(), Set<Char> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : CharSetValue(), Set<Char> by delegt {
|
||||
override var value: Set<Char>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -670,7 +684,8 @@ internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
|
||||
|
||||
internal fun Setting.valueImpl(default: Set<String>): StringSetValue {
|
||||
var internalValue: Set<String> = default
|
||||
return object : StringSetValue(), Set<String> by dynamicSet({ internalValue }) {
|
||||
val delegt = dynamicSet { internalValue }
|
||||
return object : StringSetValue(), Set<String> by delegt {
|
||||
override var value: Set<String>
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
@ -690,7 +705,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableIntListValue {
|
||||
var internalValue: MutableList<Int> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableIntListValue(), MutableList<Int> by delegt {
|
||||
override var value: MutableList<Int>
|
||||
get() = internalValue
|
||||
@ -700,15 +715,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Int>> = object : KSerializer<MutableList<Int>> {
|
||||
private val delegate = ListSerializer(Int.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Int> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -726,7 +741,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableShortListValue {
|
||||
var internalValue: MutableList<Short> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableShortListValue(), MutableList<Short> by delegt {
|
||||
override var value: MutableList<Short>
|
||||
get() = internalValue
|
||||
@ -736,15 +751,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Short>> = object : KSerializer<MutableList<Short>> {
|
||||
private val delegate = ListSerializer(Short.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Short> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -762,7 +777,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableByteListValue {
|
||||
var internalValue: MutableList<Byte> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableByteListValue(), MutableList<Byte> by delegt {
|
||||
override var value: MutableList<Byte>
|
||||
get() = internalValue
|
||||
@ -772,15 +787,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Byte>> = object : KSerializer<MutableList<Byte>> {
|
||||
private val delegate = ListSerializer(Byte.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Byte> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -798,7 +813,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableLongListValue {
|
||||
var internalValue: MutableList<Long> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableLongListValue(), MutableList<Long> by delegt {
|
||||
override var value: MutableList<Long>
|
||||
get() = internalValue
|
||||
@ -808,15 +823,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Long>> = object : KSerializer<MutableList<Long>> {
|
||||
private val delegate = ListSerializer(Long.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Long> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -834,7 +849,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableFloatListValue {
|
||||
var internalValue: MutableList<Float> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableFloatListValue(), MutableList<Float> by delegt {
|
||||
override var value: MutableList<Float>
|
||||
get() = internalValue
|
||||
@ -844,15 +859,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Float>> = object : KSerializer<MutableList<Float>> {
|
||||
private val delegate = ListSerializer(Float.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Float> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -870,7 +885,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableDoubleListValue {
|
||||
var internalValue: MutableList<Double> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableDoubleListValue(), MutableList<Double> by delegt {
|
||||
override var value: MutableList<Double>
|
||||
get() = internalValue
|
||||
@ -880,15 +895,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Double>> = object : KSerializer<MutableList<Double>> {
|
||||
private val delegate = ListSerializer(Double.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Double> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -906,7 +921,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableBooleanListValue {
|
||||
var internalValue: MutableList<Boolean> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableBooleanListValue(), MutableList<Boolean> by delegt {
|
||||
override var value: MutableList<Boolean>
|
||||
get() = internalValue
|
||||
@ -916,15 +931,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Boolean>> = object : KSerializer<MutableList<Boolean>> {
|
||||
private val delegate = ListSerializer(Boolean.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Boolean> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -942,7 +957,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableCharListValue {
|
||||
var internalValue: MutableList<Char> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableCharListValue(), MutableList<Char> by delegt {
|
||||
override var value: MutableList<Char>
|
||||
get() = internalValue
|
||||
@ -952,15 +967,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<Char>> = object : KSerializer<MutableList<Char>> {
|
||||
private val delegate = ListSerializer(Char.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<Char> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -978,7 +993,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableStringListValue {
|
||||
var internalValue: MutableList<String> = default
|
||||
|
||||
val delegt = dynamicMutableList { internalValue }
|
||||
val delegt = dynamicMutableList{ internalValue }
|
||||
return object : MutableStringListValue(), MutableList<String> by delegt {
|
||||
override var value: MutableList<String>
|
||||
get() = internalValue
|
||||
@ -988,15 +1003,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableList<String>> = object : KSerializer<MutableList<String>> {
|
||||
private val delegate = ListSerializer(String.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableList<String> {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
return delegate.deserialize(decoder).toMutableList().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1014,7 +1029,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableIntSetValue {
|
||||
var internalValue: MutableSet<Int> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableIntSetValue(), MutableSet<Int> by delegt {
|
||||
override var value: MutableSet<Int>
|
||||
get() = internalValue
|
||||
@ -1024,15 +1039,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Int>> = object : KSerializer<MutableSet<Int>> {
|
||||
private val delegate = SetSerializer(Int.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Int> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1050,7 +1065,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableShortSetValue {
|
||||
var internalValue: MutableSet<Short> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableShortSetValue(), MutableSet<Short> by delegt {
|
||||
override var value: MutableSet<Short>
|
||||
get() = internalValue
|
||||
@ -1060,15 +1075,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Short>> = object : KSerializer<MutableSet<Short>> {
|
||||
private val delegate = SetSerializer(Short.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Short> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1086,7 +1101,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableByteSetValue {
|
||||
var internalValue: MutableSet<Byte> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableByteSetValue(), MutableSet<Byte> by delegt {
|
||||
override var value: MutableSet<Byte>
|
||||
get() = internalValue
|
||||
@ -1096,15 +1111,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Byte>> = object : KSerializer<MutableSet<Byte>> {
|
||||
private val delegate = SetSerializer(Byte.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Byte> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1122,7 +1137,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableLongSetValue {
|
||||
var internalValue: MutableSet<Long> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableLongSetValue(), MutableSet<Long> by delegt {
|
||||
override var value: MutableSet<Long>
|
||||
get() = internalValue
|
||||
@ -1132,15 +1147,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Long>> = object : KSerializer<MutableSet<Long>> {
|
||||
private val delegate = SetSerializer(Long.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Long> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1158,7 +1173,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableFloatSetValue {
|
||||
var internalValue: MutableSet<Float> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableFloatSetValue(), MutableSet<Float> by delegt {
|
||||
override var value: MutableSet<Float>
|
||||
get() = internalValue
|
||||
@ -1168,15 +1183,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Float>> = object : KSerializer<MutableSet<Float>> {
|
||||
private val delegate = SetSerializer(Float.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Float> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1194,7 +1209,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableDoubleSetValue {
|
||||
var internalValue: MutableSet<Double> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableDoubleSetValue(), MutableSet<Double> by delegt {
|
||||
override var value: MutableSet<Double>
|
||||
get() = internalValue
|
||||
@ -1204,15 +1219,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Double>> = object : KSerializer<MutableSet<Double>> {
|
||||
private val delegate = SetSerializer(Double.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Double> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1230,7 +1245,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableBooleanSetValue {
|
||||
var internalValue: MutableSet<Boolean> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableBooleanSetValue(), MutableSet<Boolean> by delegt {
|
||||
override var value: MutableSet<Boolean>
|
||||
get() = internalValue
|
||||
@ -1240,15 +1255,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Boolean>> = object : KSerializer<MutableSet<Boolean>> {
|
||||
private val delegate = SetSerializer(Boolean.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Boolean> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1266,7 +1281,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableCharSetValue {
|
||||
var internalValue: MutableSet<Char> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableCharSetValue(), MutableSet<Char> by delegt {
|
||||
override var value: MutableSet<Char>
|
||||
get() = internalValue
|
||||
@ -1276,15 +1291,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<Char>> = object : KSerializer<MutableSet<Char>> {
|
||||
private val delegate = SetSerializer(Char.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<Char> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1302,7 +1317,7 @@ internal fun Setting.valueImpl(
|
||||
): MutableStringSetValue {
|
||||
var internalValue: MutableSet<String> = default
|
||||
|
||||
val delegt = dynamicMutableSet { internalValue }
|
||||
val delegt = dynamicMutableSet{ internalValue }
|
||||
return object : MutableStringSetValue(), MutableSet<String> by delegt {
|
||||
override var value: MutableSet<String>
|
||||
get() = internalValue
|
||||
@ -1312,15 +1327,15 @@ internal fun Setting.valueImpl(
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val outerThis get() = this
|
||||
|
||||
|
||||
override val serializer: KSerializer<MutableSet<String>> = object : KSerializer<MutableSet<String>> {
|
||||
private val delegate = SetSerializer(String.serializer())
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): MutableSet<String> {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||
onElementChanged(outerThis)
|
||||
}
|
||||
}
|
||||
@ -1344,7 +1359,7 @@ internal fun <T : Setting> Setting.valueImpl(default: T): Value<T> {
|
||||
onElementChanged(this)
|
||||
}
|
||||
}
|
||||
override val serializer = object : KSerializer<T> {
|
||||
override val serializer = object : KSerializer<T>{
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = internalValue.updaterSerializer.descriptor
|
||||
|
||||
|
@ -22,13 +22,6 @@ import java.io.File
|
||||
val User.isManager: Boolean
|
||||
get() = this.bot.managers.contains(this.id)
|
||||
|
||||
@JvmName("addManager")
|
||||
@JvmSynthetic
|
||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
fun Bot.addManagerDeprecated(long: Long) {
|
||||
addManager(long)
|
||||
}
|
||||
|
||||
internal fun Bot.addManager(long: Long): Boolean {
|
||||
TODO()
|
||||
return true
|
||||
|
@ -1,161 +0,0 @@
|
||||
package net.mamoe.mirai.console.utils
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* 执行N次 builder
|
||||
* 成功一次就会结束
|
||||
* 否则就会throw
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
|
||||
@kotlin.internal.InlineOnly
|
||||
inline fun <R> retryCatching(n: Int, block: () -> R): Result<R> {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
require(n >= 0) { "param n for retryCatching must not be negative" }
|
||||
var exception: Throwable? = null
|
||||
repeat(n){
|
||||
try {
|
||||
return Result.success(block())
|
||||
} catch (e: Throwable) {
|
||||
exception?.addSuppressedMirai(e)
|
||||
exception = e
|
||||
}
|
||||
}
|
||||
return Result.failure(exception!!)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun <T> tryNTimes(n: Int = 2, block: () -> T):T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
require(n >= 0) { "param n for tryNTimes must not be negative" }
|
||||
var last:Exception? = null
|
||||
repeat(n){
|
||||
try {
|
||||
return block.invoke()
|
||||
}catch (e:Exception){
|
||||
last = e
|
||||
}
|
||||
}
|
||||
|
||||
//给我编译
|
||||
|
||||
throw last!!
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||
if (e === this) {
|
||||
return
|
||||
}
|
||||
kotlin.runCatching {
|
||||
this.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 两个字符串的近似值
|
||||
* 要求前面完全一致
|
||||
* 如
|
||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYY) = 0.8
|
||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYYZZ) = 0.8
|
||||
*/
|
||||
|
||||
internal fun String.fuzzyCompare(target: String): Double {
|
||||
var step = 0
|
||||
if (this == target) {
|
||||
return 1.0
|
||||
}
|
||||
if (target.length > this.length) {
|
||||
return 0.0
|
||||
}
|
||||
for (i in this.indices) {
|
||||
if (target.length == i) {
|
||||
step--
|
||||
}else {
|
||||
if (this[i] != target[i]) {
|
||||
break
|
||||
}
|
||||
step++
|
||||
}
|
||||
}
|
||||
|
||||
if(step == this.length-1){
|
||||
return 1.0
|
||||
}
|
||||
return step.toDouble()/this.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
*/
|
||||
internal inline fun <T : Any> Collection<T>.fuzzySearch(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
this.forEach {
|
||||
val thisIndex = index(it)
|
||||
if(thisIndex == target){
|
||||
return it
|
||||
}
|
||||
with(thisIndex.fuzzyCompare(target)) {
|
||||
if (this > rate) {
|
||||
rate = this
|
||||
potential = it
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
* 并且确保target是唯一的
|
||||
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
|
||||
*/
|
||||
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
var collide = 0
|
||||
this.forEach {
|
||||
with(index(it).fuzzyCompare(target)) {
|
||||
if (this > rate) {
|
||||
rate = this
|
||||
potential = it
|
||||
}
|
||||
if(this == 1.0){
|
||||
collide++
|
||||
}
|
||||
if(collide > 1){
|
||||
return null//collide
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
|
||||
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
|
||||
return this.members.fuzzySearchOnly(nameCardTarget) {
|
||||
it.nameCard
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package net.mamoe.mirai.console.utils
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
/**
|
||||
* A Value
|
||||
* the input type of this Value is T while the output is V
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
abstract class Value<T, V> {
|
||||
operator fun invoke(): V = get()
|
||||
|
||||
abstract fun get(): V
|
||||
|
||||
abstract fun set(t: T)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This value can be used as a Config Value
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
interface ConfigValue
|
||||
|
||||
|
||||
/**
|
||||
* A simple value
|
||||
* the input type is same as output value
|
||||
*/
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
open class SimpleValue<T>(
|
||||
var value: T
|
||||
) : Value<T, T>() {
|
||||
override fun get() = this.value
|
||||
|
||||
override fun set(t: T) {
|
||||
this.value = t
|
||||
}
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
open class NullableSimpleValue<T>(
|
||||
value: T? = null
|
||||
) : SimpleValue<T?>(
|
||||
value
|
||||
) {
|
||||
fun isNull() = value == null
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
|
||||
|
||||
package net.mamoe.mirai.console.utils
|
||||
|
||||
import org.jetbrains.annotations.Range
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* 执行 [n] 次 [block], 在第一次成功时返回执行结果, 在捕获到异常时返回异常.
|
||||
*/
|
||||
@kotlin.internal.InlineOnly
|
||||
inline fun <R> retryCatching(n: @Range(from = 1, to = Long.MAX_VALUE) Int, block: () -> R): Result<R> {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
require(n >= 0) { "param n for retryCatching must not be negative" }
|
||||
var exception: Throwable? = null
|
||||
repeat(n) {
|
||||
try {
|
||||
return Result.success(block())
|
||||
} catch (e: Throwable) {
|
||||
exception?.addSuppressedMirai(e)
|
||||
exception = e
|
||||
}
|
||||
}
|
||||
return Result.failure(exception!!)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||
if (e === this) {
|
||||
return
|
||||
}
|
||||
kotlin.runCatching {
|
||||
this.addSuppressed(e)
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import net.mamoe.mirai.console.utils.fuzzySearch
|
||||
import net.mamoe.mirai.console.utils.fuzzySearchOnly
|
||||
import net.mamoe.mirai.console.command.fuzzySearch
|
||||
import net.mamoe.mirai.console.command.fuzzySearchOnly
|
||||
|
||||
class Him188(val name:String){
|
||||
override fun toString(): String {
|
||||
|
Loading…
Reference in New Issue
Block a user