Explicit APIs

This commit is contained in:
Him188 2020-07-11 19:26:31 +08:00
parent 55ec375e09
commit 58187f95f0
43 changed files with 651 additions and 584 deletions

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("PRE_RELEASE_CLASS", "ClassName") @file:Suppress("PRE_RELEASE_CLASS", "ClassName", "RedundantVisibilityModifier")
package net.mamoe.mirai.console.codegen package net.mamoe.mirai.console.codegen
@ -15,7 +15,7 @@ import kotlin.reflect.full.functions
import kotlin.reflect.full.hasAnnotation import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
object ValueSettingCodegen { internal object ValueSettingCodegen {
/** /**
* The interface * The interface
*/ */
@ -31,7 +31,7 @@ object ValueSettingCodegen {
/** /**
* Represents a non-null [$ktType] value. * Represents a non-null [$ktType] value.
*/ */
interface ${ktType}Value : PrimitiveValue<$ktType> public interface ${ktType}Value : PrimitiveValue<$ktType>
""" """
) )
} }
@ -47,7 +47,7 @@ object ValueSettingCodegen {
kCode( kCode(
""" """
@JvmStatic @JvmStatic
val ${ktType.standardName}SerializerDescriptor = ${ktType.standardName}.serializer().descriptor internal val ${ktType.standardName}SerializerDescriptor = ${ktType.standardName}.serializer().descriptor
""" """
).lines().joinToString("\n") { " $it" } ).lines().joinToString("\n") { " $it" }
) )
@ -145,7 +145,7 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${
@Suppress("unused") @Suppress("unused")
appendKCode( appendKCode(
""" """
fun Setting.value(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> = valueImpl(default) public fun Setting.value(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> = valueImpl(default)
""" """
) )
} }

View File

@ -23,6 +23,8 @@ tasks.withType(JavaCompile::class.java) {
} }
kotlin { kotlin {
explicitApiWarning()
sourceSets.all { sourceSets.all {
target.compilations.all { target.compilations.all {
kotlinOptions { kotlinOptions {
@ -46,12 +48,17 @@ kotlin {
} }
} }
sourceSets { sourceSets.invoke {
getByName("test") { getByName("test") {
languageSettings.apply { languageSettings.apply {
languageVersion = "1.4" languageVersion = "1.4"
} }
} }
getByName("main") {
languageSettings.apply {
languageVersion = "1.3"
}
}
} }
} }
@ -71,7 +78,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.2.0") testImplementation("org.junit.jupiter:junit-jupiter-api:5.2.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0")
} }
ext { ext.apply {
// 傻逼 compileAndRuntime 没 exclude 掉 // 傻逼 compileAndRuntime 没 exclude 掉
// 傻逼 gradle 第二次配置 task 会覆盖掉第一次的配置 // 傻逼 gradle 第二次配置 task 会覆盖掉第一次的配置
val x: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.() -> Unit = { val x: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.() -> Unit = {
@ -83,7 +90,7 @@ ext {
} }
} }
} }
this.set("shadowJar", x) set("shadowJar", x)
} }
tasks { tasks {

View File

@ -23,6 +23,7 @@ import net.mamoe.mirai.console.command.internal.InternalCommandManager
import net.mamoe.mirai.console.command.primaryName import net.mamoe.mirai.console.command.primaryName
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.PluginManagerImpl
import net.mamoe.mirai.console.plugin.center.CuiPluginCenter import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
import net.mamoe.mirai.console.plugin.center.PluginCenter import net.mamoe.mirai.console.plugin.center.PluginCenter
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
@ -45,43 +46,47 @@ import kotlin.coroutines.CoroutineContext
* *
* @see INSTANCE * @see INSTANCE
*/ */
interface MiraiConsole : CoroutineScope { public interface MiraiConsole : CoroutineScope {
/** /**
* Console 运行路径 * Console 运行路径
*/ */
val rootDir: File public val rootDir: File
/** /**
* Console 前端接口 * Console 前端接口
*/ */
val frontEnd: MiraiConsoleFrontEnd public val frontEnd: MiraiConsoleFrontEnd
/** /**
* 与前端交互所使用的 Logger * 与前端交互所使用的 Logger
*/ */
val mainLogger: MiraiLogger public val mainLogger: MiraiLogger
/** /**
* 内建加载器列表, 一般需要包含 [JarPluginLoader] * 内建加载器列表, 一般需要包含 [JarPluginLoader]
*/ */
val builtInPluginLoaders: List<PluginLoader<*, *>> public val builtInPluginLoaders: List<PluginLoader<*, *>>
val buildDate: Date public val buildDate: Date
val version: String public val version: String
val pluginCenter: PluginCenter public val pluginCenter: PluginCenter
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
fun newLogger(identity: String?): MiraiLogger public fun newLogger(identity: String?): MiraiLogger
companion object INSTANCE : MiraiConsole by MiraiConsoleInternal public companion object INSTANCE : MiraiConsole by MiraiConsoleInternal
} }
public class IllegalMiraiConsoleImplementationError(
override val message: String?
) : Error()
/** /**
* 获取 [MiraiConsole] [Job] * 获取 [MiraiConsole] [Job]
*/ */
val MiraiConsole.job: Job public val MiraiConsole.job: Job
get() = this.coroutineContext[Job] ?: error("Internal error: Job not found in MiraiConsole.coroutineContext") get() = this.coroutineContext[Job] ?: error("Internal error: Job not found in MiraiConsole.coroutineContext")
//// internal //// internal
@ -149,17 +154,13 @@ internal object MiraiConsoleInternal : CoroutineScope, IMiraiConsole, MiraiConso
InternalCommandManager.commandListener // start InternalCommandManager.commandListener // start
mainLogger.info { "Loading plugins..." } mainLogger.info { "Loading plugins..." }
PluginManager.loadEnablePlugins() PluginManagerImpl.loadEnablePlugins()
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." } mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
mainLogger.info { "mirai-console started successfully." } mainLogger.info { "mirai-console started successfully." }
// Only for initialize // Only for initialize
} }
} }
class IllegalMiraiConsoleImplementationError(
override val message: String?
) : Error()
// 前端使用 // 前端使用
internal interface IMiraiConsole : CoroutineScope { internal interface IMiraiConsole : CoroutineScope {

View File

@ -17,33 +17,33 @@ import net.mamoe.mirai.utils.MiraiLogger
* 只需要实现一个这个传入 MiraiConsole 就可以绑定 UI 层与 Console * 只需要实现一个这个传入 MiraiConsole 就可以绑定 UI 层与 Console
* 需要保证线程安全 * 需要保证线程安全
*/ */
interface MiraiConsoleFrontEnd { public interface MiraiConsoleFrontEnd {
/** /**
* 名称 * 名称
*/ */
val name: String public val name: String
/** /**
* 版本 * 版本
*/ */
val version: String public val version: String
fun loggerFor(identity: String?): MiraiLogger public fun loggerFor(identity: String?): MiraiLogger
/** /**
* UI 层接受一个新的bot * UI 层接受一个新的bot
* */ * */
fun pushBot( public fun pushBot(
bot: Bot bot: Bot
) )
/** /**
* UI 层提供一个输入, 相当于 [readLine] * UI 层提供一个输入, 相当于 [readLine]
*/ */
suspend fun requestInput(hint: String): String public suspend fun requestInput(hint: String): String
/** /**
* UI 层创建一个 [LoginSolver] * UI 层创建一个 [LoginSolver]
*/ */
fun createLoginSolver(): LoginSolver public fun createLoginSolver(): LoginSolver
} }

View File

@ -29,7 +29,7 @@ import kotlin.system.exitProcess
/** /**
* 添加一个 [Bot] 实例到全局 Bot 列表, 但不登录. * 添加一个 [Bot] 实例到全局 Bot 列表, 但不登录.
*/ */
fun MiraiConsole.addBot(id: Long, password: String): Bot { public fun MiraiConsole.addBot(id: Long, password: String): Bot {
return Bot(id, password) { return Bot(id, password) {
/** /**
@ -65,15 +65,15 @@ fun MiraiConsole.addBot(id: Long, password: String): Bot {
} }
@Suppress("EXPOSED_SUPER_INTERFACE") @Suppress("EXPOSED_SUPER_INTERFACE")
interface BuiltInCommand : Command, BuiltInCommandInternal public interface BuiltInCommand : Command, BuiltInCommandInternal
// for identification // for identification
internal interface BuiltInCommandInternal : Command internal interface BuiltInCommandInternal : Command
@Suppress("unused") @Suppress("unused")
object BuiltInCommands { public object BuiltInCommands {
val all: Array<out Command> by lazy { public val all: Array<out Command> by lazy {
this::class.nestedClasses.mapNotNull { it.objectInstance as? Command }.toTypedArray() this::class.nestedClasses.mapNotNull { it.objectInstance as? Command }.toTypedArray()
} }
@ -83,7 +83,7 @@ object BuiltInCommands {
} }
} }
object Help : SimpleCommand( public object Help : SimpleCommand(
ConsoleCommandOwner, "help", ConsoleCommandOwner, "help",
description = "Gets help about the console." description = "Gets help about the console."
), BuiltInCommand { ), BuiltInCommand {
@ -94,13 +94,13 @@ object BuiltInCommands {
} }
@Handler @Handler
suspend fun CommandSender.handle() { public suspend fun CommandSender.handle() {
sendMessage("现在有指令: ${allRegisteredCommands.joinToString { it.primaryName }}") sendMessage("现在有指令: ${allRegisteredCommands.joinToString { it.primaryName }}")
sendMessage("帮助还没写, 将就一下") sendMessage("帮助还没写, 将就一下")
} }
} }
object Stop : SimpleCommand( public object Stop : SimpleCommand(
ConsoleCommandOwner, "stop", "shutdown", "exit", ConsoleCommandOwner, "stop", "shutdown", "exit",
description = "Stop the whole world." description = "Stop the whole world."
), BuiltInCommand { ), BuiltInCommand {
@ -116,7 +116,7 @@ object BuiltInCommands {
private val closingLock = Mutex() private val closingLock = Mutex()
@Handler @Handler
suspend fun CommandSender.handle(): Unit = closingLock.withLock { public suspend fun CommandSender.handle(): Unit = closingLock.withLock {
sendMessage("Stopping mirai-console") sendMessage("Stopping mirai-console")
kotlin.runCatching { kotlin.runCatching {
MiraiConsole.job.cancelAndJoin() MiraiConsole.job.cancelAndJoin()
@ -131,27 +131,28 @@ object BuiltInCommands {
} }
} }
object Login : SimpleCommand( public object Login : SimpleCommand(
ConsoleCommandOwner, "login", ConsoleCommandOwner, "login",
description = "Log in a bot account." description = "Log in a bot account."
), BuiltInCommand { ), BuiltInCommand {
@Handler @Handler
suspend fun CommandSender.handle(id: Long, password: String) { public suspend fun CommandSender.handle(id: Long, password: String) {
kotlin.runCatching { kotlin.runCatching {
MiraiConsole.addBot(id, password).alsoLogin() MiraiConsole.addBot(id, password).alsoLogin()
}.fold( }.fold(
onSuccess = { sendMessage("${it.nick} ($id) Login succeed") }, onSuccess = { sendMessage("${it.nick} ($id) Login succeed") },
onFailure = { throwable -> onFailure = { throwable ->
sendMessage("Login failed: ${throwable.localizedMessage ?: throwable.message ?: throwable.toString()}" + sendMessage(
if (this is MessageEventContextAware<*>) { "Login failed: ${throwable.localizedMessage ?: throwable.message ?: throwable.toString()}" +
this.fromEvent.selectMessagesUnit { if (this is MessageEventContextAware<*>) {
"stacktrace" reply { this.fromEvent.selectMessagesUnit {
throwable.stacktraceString "stacktrace" reply {
throwable.stacktraceString
}
} }
} "test"
"test" } else "")
} else "")
throw throwable throw throwable
} }

View File

@ -22,55 +22,58 @@ import net.mamoe.mirai.message.data.SingleMessage
* @see RawCommand * @see RawCommand
* @see CompositeCommand * @see CompositeCommand
*/ */
interface Command { public interface Command {
/** /**
* 指令名. 需要至少有一个元素. 所有元素都不能带有空格 * 指令名. 需要至少有一个元素. 所有元素都不能带有空格
*/ */
val names: Array<out String> public val names: Array<out String>
val usage: String public val usage: String
val description: String public val description: String
/** /**
* 指令权限 * 指令权限
*/ */
val permission: CommandPermission public val permission: CommandPermission
/** /**
* `true` 时表示 [指令前缀][CommandPrefix] 可选 * `true` 时表示 [指令前缀][CommandPrefix] 可选
*/ */
val prefixOptional: Boolean public val prefixOptional: Boolean
val owner: CommandOwner public val owner: CommandOwner
/** /**
* @param args 指令参数. 可能是 [SingleMessage] [String]. 且已经以 ' ' 分割. * @param args 指令参数. 可能是 [SingleMessage] [String]. 且已经以 ' ' 分割.
*
* @see Command.execute
*/ // TODO: 2020/6/28 Java-friendly bridges */ // TODO: 2020/6/28 Java-friendly bridges
suspend fun CommandSender.onCommand(args: Array<out Any>) public suspend fun CommandSender.onCommand(args: Array<out Any>)
} }
/** /**
* [Command] 的基础实现 * [Command] 的基础实现
*/ */
abstract class AbstractCommand @JvmOverloads constructor( public abstract class AbstractCommand @JvmOverloads constructor(
final override val owner: CommandOwner, public final override val owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "<no description available>", description: String = "<no description available>",
final override val permission: CommandPermission = CommandPermission.Default, public final override val permission: CommandPermission = CommandPermission.Default,
final override val prefixOptional: Boolean = false public final override val prefixOptional: Boolean = false
) : Command { ) : Command {
final override val description = description.trimIndent() public final override val description: String = description.trimIndent()
final override val names: Array<out String> = public final override val names: Array<out String> =
names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).also { list -> names.map(String::trim).filterNot(String::isEmpty).map(String::toLowerCase).also { list ->
list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") } list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") }
}.toTypedArray() }.toTypedArray()
} }
suspend inline fun Command.onCommand(sender: CommandSender, args: Array<out Any>) = sender.run { onCommand(args) } public suspend inline fun Command.onCommand(sender: CommandSender, args: Array<out Any>): Unit =
sender.run { onCommand(args) }
/** /**
* 主要指令名. [Command.names] 的第一个元素. * 主要指令名. [Command.names] 的第一个元素.
*/ */
val Command.primaryName: String get() = names[0] public val Command.primaryName: String get() = names[0]

View File

@ -20,94 +20,94 @@ import kotlin.contracts.contract
* *
* @see CommandExecuteStatus * @see CommandExecuteStatus
*/ */
sealed class CommandExecuteResult { public sealed class CommandExecuteResult {
/** 指令最终执行状态 */ /** 指令最终执行状态 */
abstract val status: CommandExecuteStatus public abstract val status: CommandExecuteStatus
/** 指令执行时发生的错误 (如果有) */ /** 指令执行时发生的错误 (如果有) */
abstract val exception: Throwable? public abstract val exception: Throwable?
/** 尝试执行的指令 (如果匹配到) */ /** 尝试执行的指令 (如果匹配到) */
abstract val command: Command? public abstract val command: Command?
/** 尝试执行的指令名 (如果匹配到) */ /** 尝试执行的指令名 (如果匹配到) */
abstract val commandName: String? public abstract val commandName: String?
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
abstract val args: Array<out Any>? public abstract val args: Array<out Any>?
// abstract val to allow smart casting // abstract val to allow smart casting
/** 指令执行成功 */ /** 指令执行成功 */
class Success( public class Success(
/** 尝试执行的指令 */ /** 尝试执行的指令 */
override val command: Command, public override val command: Command,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
override val commandName: String, public override val commandName: String,
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
override val args: Array<out Any> public override val args: Array<out Any>
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
override val exception: Nothing? get() = null public override val exception: Nothing? get() = null
/** 指令最终执行状态, 总是 [CommandExecuteStatus.SUCCESSFUL] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.SUCCESSFUL] */
override val status: CommandExecuteStatus get() = CommandExecuteStatus.SUCCESSFUL public override val status: CommandExecuteStatus get() = CommandExecuteStatus.SUCCESSFUL
} }
/** 指令执行过程出现了错误 */ /** 指令执行过程出现了错误 */
class ExecutionException( public class ExecutionException(
/** 指令执行时发生的错误 */ /** 指令执行时发生的错误 */
override val exception: Throwable, public override val exception: Throwable,
/** 尝试执行的指令 */ /** 尝试执行的指令 */
override val command: Command, public override val command: Command,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
override val commandName: String, public override val commandName: String,
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
override val args: Array<out Any> public override val args: Array<out Any>
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.EXECUTION_EXCEPTION] */
override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION public override val status: CommandExecuteStatus get() = CommandExecuteStatus.EXECUTION_EXCEPTION
} }
/** 没有匹配的指令 */ /** 没有匹配的指令 */
class CommandNotFound( public class CommandNotFound(
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
override val commandName: String public override val commandName: String
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
override val exception: Nothing? get() = null public override val exception: Nothing? get() = null
/** 尝试执行的指令, 总是 `null` */ /** 尝试执行的指令, 总是 `null` */
override val command: Nothing? get() = null public override val command: Nothing? get() = null
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
override val args: Nothing? get() = null public override val args: Nothing? get() = null
/** 指令最终执行状态, 总是 [CommandExecuteStatus.COMMAND_NOT_FOUND] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.COMMAND_NOT_FOUND] */
override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND public override val status: CommandExecuteStatus get() = CommandExecuteStatus.COMMAND_NOT_FOUND
} }
/** 权限不足 */ /** 权限不足 */
class PermissionDenied( public class PermissionDenied(
/** 尝试执行的指令 */ /** 尝试执行的指令 */
override val command: Command, public override val command: Command,
/** 尝试执行的指令名 */ /** 尝试执行的指令名 */
override val commandName: String public override val commandName: String
) : CommandExecuteResult() { ) : CommandExecuteResult() {
/** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */ /** 基础分割后的实际参数列表, 元素类型可能为 [Message] 或 [String] */
override val args: Nothing? get() = null public override val args: Nothing? get() = null
/** 指令执行时发生的错误, 总是 `null` */ /** 指令执行时发生的错误, 总是 `null` */
override val exception: Nothing? get() = null public override val exception: Nothing? get() = null
/** 指令最终执行状态, 总是 [CommandExecuteStatus.PERMISSION_DENIED] */ /** 指令最终执行状态, 总是 [CommandExecuteStatus.PERMISSION_DENIED] */
override val status: CommandExecuteStatus get() = CommandExecuteStatus.PERMISSION_DENIED public override val status: CommandExecuteStatus get() = CommandExecuteStatus.PERMISSION_DENIED
} }
/** /**
* 指令的执行状态 * 指令的执行状态
*/ */
enum class CommandExecuteStatus { public enum class CommandExecuteStatus {
/** 指令执行成功 */ /** 指令执行成功 */
SUCCESSFUL, SUCCESSFUL,
@ -124,13 +124,13 @@ sealed class CommandExecuteResult {
@Suppress("RemoveRedundantQualifierName") @Suppress("RemoveRedundantQualifierName")
typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus public typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus
/** /**
* [this] [CommandExecuteResult.Success] 时返回 `true` * [this] [CommandExecuteResult.Success] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
fun CommandExecuteResult.isSuccess(): Boolean { public fun CommandExecuteResult.isSuccess(): Boolean {
contract { contract {
returns(true) implies (this@isSuccess is CommandExecuteResult.Success) returns(true) implies (this@isSuccess is CommandExecuteResult.Success)
returns(false) implies (this@isSuccess !is CommandExecuteResult.Success) returns(false) implies (this@isSuccess !is CommandExecuteResult.Success)
@ -142,7 +142,7 @@ fun CommandExecuteResult.isSuccess(): Boolean {
* [this] [CommandExecuteResult.ExecutionException] 时返回 `true` * [this] [CommandExecuteResult.ExecutionException] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
fun CommandExecuteResult.isExecutionException(): Boolean { public fun CommandExecuteResult.isExecutionException(): Boolean {
contract { contract {
returns(true) implies (this@isExecutionException is CommandExecuteResult.ExecutionException) returns(true) implies (this@isExecutionException is CommandExecuteResult.ExecutionException)
returns(false) implies (this@isExecutionException !is CommandExecuteResult.ExecutionException) returns(false) implies (this@isExecutionException !is CommandExecuteResult.ExecutionException)
@ -154,7 +154,7 @@ fun CommandExecuteResult.isExecutionException(): Boolean {
* [this] [CommandExecuteResult.ExecutionException] 时返回 `true` * [this] [CommandExecuteResult.ExecutionException] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
fun CommandExecuteResult.isPermissionDenied(): Boolean { public fun CommandExecuteResult.isPermissionDenied(): Boolean {
contract { contract {
returns(true) implies (this@isPermissionDenied is CommandExecuteResult.PermissionDenied) returns(true) implies (this@isPermissionDenied is CommandExecuteResult.PermissionDenied)
returns(false) implies (this@isPermissionDenied !is CommandExecuteResult.PermissionDenied) returns(false) implies (this@isPermissionDenied !is CommandExecuteResult.PermissionDenied)
@ -166,7 +166,7 @@ fun CommandExecuteResult.isPermissionDenied(): Boolean {
* [this] [CommandExecuteResult.ExecutionException] 时返回 `true` * [this] [CommandExecuteResult.ExecutionException] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
fun CommandExecuteResult.isCommandNotFound(): Boolean { public fun CommandExecuteResult.isCommandNotFound(): Boolean {
contract { contract {
returns(true) implies (this@isCommandNotFound is CommandExecuteResult.CommandNotFound) returns(true) implies (this@isCommandNotFound is CommandExecuteResult.CommandNotFound)
returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.CommandNotFound) returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.CommandNotFound)
@ -178,7 +178,7 @@ fun CommandExecuteResult.isCommandNotFound(): Boolean {
* [this] [CommandExecuteResult.ExecutionException] [CommandExecuteResult.CommandNotFound] 时返回 `true` * [this] [CommandExecuteResult.ExecutionException] [CommandExecuteResult.CommandNotFound] 时返回 `true`
*/ */
@JvmSynthetic @JvmSynthetic
fun CommandExecuteResult.isFailure(): Boolean { public fun CommandExecuteResult.isFailure(): Boolean {
contract { contract {
returns(true) implies (this@isFailure !is CommandExecuteResult.Success) returns(true) implies (this@isFailure !is CommandExecuteResult.Success)
returns(false) implies (this@isFailure is CommandExecuteResult.Success) returns(false) implies (this@isFailure is CommandExecuteResult.Success)

View File

@ -14,21 +14,21 @@ package net.mamoe.mirai.console.command
/** /**
* [executeCommand] , [Command.onCommand] 抛出异常时包装的异常. * [executeCommand] , [Command.onCommand] 抛出异常时包装的异常.
*/ */
class CommandExecutionException( public class CommandExecutionException(
/** /**
* 执行过程发生异常的指令 * 执行过程发生异常的指令
*/ */
val command: Command, public val command: Command,
/** /**
* 匹配到的指令名 * 匹配到的指令名
*/ */
val name: String, public val name: String,
cause: Throwable cause: Throwable
) : RuntimeException( ) : RuntimeException(
"Exception while executing command '${command.primaryName}'", "Exception while executing command '${command.primaryName}'",
cause cause
) { ) {
override fun toString(): String = public override fun toString(): String =
"CommandExecutionException(command=$command, name='$name')" "CommandExecutionException(command=$command, name='$name')"
} }

View File

@ -27,12 +27,12 @@ import net.mamoe.mirai.message.data.MessageChain
* 指令的所有者. * 指令的所有者.
* @see PluginCommandOwner * @see PluginCommandOwner
*/ */
sealed class CommandOwner public sealed class CommandOwner
/** /**
* 插件指令所有者. 插件只能通过 [PluginCommandOwner] 管理指令. * 插件指令所有者. 插件只能通过 [PluginCommandOwner] 管理指令.
*/ */
abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner() { public abstract class PluginCommandOwner(public val plugin: Plugin) : CommandOwner() {
init { init {
if (plugin is CoroutineScope) { // JVM Plugin if (plugin is CoroutineScope) { // JVM Plugin
plugin.coroutineContext[Job]?.invokeOnCompletion { plugin.coroutineContext[Job]?.invokeOnCompletion {
@ -45,33 +45,33 @@ abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner() {
/** /**
* 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner].
*/ */
object ConsoleCommandOwner : CommandOwner() public object ConsoleCommandOwner : CommandOwner()
/** /**
* 获取已经注册了的属于这个 [CommandOwner] 的指令列表. * 获取已经注册了的属于这个 [CommandOwner] 的指令列表.
* @see JCommandManager.getRegisteredCommands Java 方法 * @see JCommandManager.getRegisteredCommands Java 方法
*/ */
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this } public val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
/** /**
* 获取所有已经注册了指令列表. * 获取所有已经注册了指令列表.
* @see JCommandManager.getRegisteredCommands Java 方法 * @see JCommandManager.getRegisteredCommands Java 方法
*/ */
val allRegisteredCommands: List<Command> get() = InternalCommandManager.registeredCommands.toList() // copy public val allRegisteredCommands: List<Command> get() = InternalCommandManager.registeredCommands.toList() // copy
/** /**
* 指令前缀, '/' * 指令前缀, '/'
* @see JCommandManager.getCommandPrefix Java 方法 * @see JCommandManager.getCommandPrefix Java 方法
*/ */
@get:JvmName("getCommandPrefix") @get:JvmName("getCommandPrefix")
val CommandPrefix: String public val CommandPrefix: String
get() = InternalCommandManager.COMMAND_PREFIX get() = InternalCommandManager.COMMAND_PREFIX
/** /**
* 取消注册所有属于 [this] 的指令 * 取消注册所有属于 [this] 的指令
* @see JCommandManager.unregisterAllCommands Java 方法 * @see JCommandManager.unregisterAllCommands Java 方法
*/ */
fun CommandOwner.unregisterAllCommands() { public fun CommandOwner.unregisterAllCommands() {
for (registeredCommand in registeredCommands) { for (registeredCommand in registeredCommands) {
registeredCommand.unregister() registeredCommand.unregister()
} }
@ -94,7 +94,7 @@ fun CommandOwner.unregisterAllCommands() {
* @see JCommandManager.register Java 方法 * @see JCommandManager.register Java 方法
*/ */
@JvmOverloads @JvmOverloads
fun Command.register(override: Boolean = false): Boolean { public fun Command.register(override: Boolean = false): Boolean {
if (this is CompositeCommand) this.subCommands // init if (this is CompositeCommand) this.subCommands // init
InternalCommandManager.modifyLock.withLock { InternalCommandManager.modifyLock.withLock {
@ -124,7 +124,7 @@ fun Command.register(override: Boolean = false): Boolean {
* *
* @see JCommandManager.findDuplicate Java 方法 * @see JCommandManager.findDuplicate Java 方法
*/ */
fun Command.findDuplicate(): Command? = public fun Command.findDuplicate(): Command? =
InternalCommandManager.registeredCommands.firstOrNull { it.names intersectsIgnoringCase this.names } InternalCommandManager.registeredCommands.firstOrNull { it.names intersectsIgnoringCase this.names }
/** /**
@ -132,7 +132,7 @@ fun Command.findDuplicate(): Command? =
* *
* @see JCommandManager.unregister Java 方法 * @see JCommandManager.unregister Java 方法
*/ */
fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock { public fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
if (this.prefixOptional) { if (this.prefixOptional) {
this.names.forEach { this.names.forEach {
InternalCommandManager.optionalPrefixCommandMap.remove(it) InternalCommandManager.optionalPrefixCommandMap.remove(it)
@ -147,7 +147,7 @@ fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
/** /**
* [this] 已经 [注册][register] 后返回 `true` * [this] 已经 [注册][register] 后返回 `true`
*/ */
fun Command.isRegistered(): Boolean = this in InternalCommandManager.registeredCommands public fun Command.isRegistered(): Boolean = this in InternalCommandManager.registeredCommands
//// executing without detailed result (faster) //// executing without detailed result (faster)
@ -161,7 +161,7 @@ fun Command.isRegistered(): Boolean = this in InternalCommandManager.registeredC
* *
* @see JCommandManager.executeCommand Java 方法 * @see JCommandManager.executeCommand Java 方法
*/ */
suspend fun CommandSender.executeCommand(vararg messages: Any): Command? { public suspend fun CommandSender.executeCommand(vararg messages: Any): Command? {
if (messages.isEmpty()) return null if (messages.isEmpty()) return null
return matchAndExecuteCommandInternal(messages, messages[0].toString().substringBefore(' ')) return matchAndExecuteCommandInternal(messages, messages[0].toString().substringBefore(' '))
} }
@ -175,7 +175,7 @@ suspend fun CommandSender.executeCommand(vararg messages: Any): Command? {
* @see JCommandManager.executeCommand Java 方法 * @see JCommandManager.executeCommand Java 方法
*/ */
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
suspend fun CommandSender.executeCommand(message: MessageChain): Command? { public suspend fun CommandSender.executeCommand(message: MessageChain): Command? {
if (message.isEmpty()) return null if (message.isEmpty()) return null
return matchAndExecuteCommandInternal(message, message[0].toString().substringBefore(' ')) return matchAndExecuteCommandInternal(message, message[0].toString().substringBefore(' '))
} }
@ -190,7 +190,7 @@ suspend fun CommandSender.executeCommand(message: MessageChain): Command? {
*/ */
@JvmOverloads @JvmOverloads
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPermission: Boolean = true) { public suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPermission: Boolean = true) {
sender.executeCommandInternal( sender.executeCommandInternal(
this, this,
args.flattenCommandComponents().toTypedArray(), args.flattenCommandComponents().toTypedArray(),
@ -209,7 +209,7 @@ suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPerm
*/ */
@JvmOverloads @JvmOverloads
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean = true) { public suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean = true) {
sender.executeCommandInternal( sender.executeCommandInternal(
this, this,
args.flattenCommandComponents().toTypedArray(), args.flattenCommandComponents().toTypedArray(),
@ -229,7 +229,7 @@ suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermis
* *
* @see JCommandManager.executeCommandDetailed Java 方法 * @see JCommandManager.executeCommandDetailed Java 方法
*/ */
suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult { public suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult {
if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("") if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
return executeCommandDetailedInternal(messages, messages[0].toString().substringBefore(' ')) return executeCommandDetailedInternal(messages, messages[0].toString().substringBefore(' '))
} }
@ -243,7 +243,7 @@ suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandE
* *
* @see JCommandManager.executeCommandDetailed Java 方法 * @see JCommandManager.executeCommandDetailed Java 方法
*/ */
suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult { public suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult {
if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("") if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
return executeCommandDetailedInternal(messages, messages[0].toString()) return executeCommandDetailedInternal(messages, messages[0].toString())
} }

View File

@ -22,48 +22,48 @@ import net.mamoe.mirai.contact.isOwner
* *
* @see AnonymousCommandPermission * @see AnonymousCommandPermission
*/ */
interface CommandPermission { public interface CommandPermission {
/** /**
* 判断 [this] 是否拥有这个指令的权限 * 判断 [this] 是否拥有这个指令的权限
* *
* @see CommandSender.hasPermission * @see CommandSender.hasPermission
* @see CommandPermission.testPermission * @see CommandPermission.testPermission
*/ */
fun CommandSender.hasPermission(): Boolean public fun CommandSender.hasPermission(): Boolean
/** /**
* 满足两个权限其中一个即可使用指令 * 满足两个权限其中一个即可使用指令
*/ // no extension for Java */ // no extension for Java
@JvmDefault @JvmDefault
infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another) public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
/** /**
* 同时拥有两个权限才能使用指令 * 同时拥有两个权限才能使用指令
*/ // no extension for Java */ // no extension for Java
@JvmDefault @JvmDefault
infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another) public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
/** /**
* 任何人都可以使用这个指令 * 任何人都可以使用这个指令
*/ */
object Any : CommandPermission { public object Any : CommandPermission {
override fun CommandSender.hasPermission(): Boolean = true public override fun CommandSender.hasPermission(): Boolean = true
} }
/** /**
* 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行. * 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行.
*/ */
object None : CommandPermission { public object None : CommandPermission {
override fun CommandSender.hasPermission(): Boolean = false public override fun CommandSender.hasPermission(): Boolean = false
} }
/** /**
* 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令 * 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令
*/ */
object Operator : CommandPermission { public object Operator : CommandPermission {
override fun CommandSender.hasPermission(): Boolean { public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isOperator() return this is MemberCommandSender && this.user.isOperator()
} }
} }
@ -71,8 +71,8 @@ interface CommandPermission {
/** /**
* 来自任何 [Bot] 的任何一个群主都可以使用这个指令 * 来自任何 [Bot] 的任何一个群主都可以使用这个指令
*/ */
object GroupOwner : CommandPermission { public object GroupOwner : CommandPermission {
override fun CommandSender.hasPermission(): Boolean { public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isOwner() return this is MemberCommandSender && this.user.isOwner()
} }
} }
@ -80,8 +80,8 @@ interface CommandPermission {
/** /**
* 管理员 (不包含群主) 可以使用这个指令 * 管理员 (不包含群主) 可以使用这个指令
*/ */
object Administrator : CommandPermission { public object Administrator : CommandPermission {
override fun CommandSender.hasPermission(): Boolean { public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isAdministrator() return this is MemberCommandSender && this.user.isAdministrator()
} }
} }
@ -89,8 +89,8 @@ interface CommandPermission {
/** /**
* 任何 [Bot] manager 都可以使用这个指令 * 任何 [Bot] manager 都可以使用这个指令
*/ */
object Manager : CommandPermission { public object Manager : CommandPermission {
override fun CommandSender.hasPermission(): Boolean { public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isManager return this is MemberCommandSender && this.user.isManager
} }
} }
@ -98,11 +98,11 @@ interface CommandPermission {
/** /**
* 仅控制台能使用和这个指令 * 仅控制台能使用和这个指令
*/ */
object Console : CommandPermission { public object Console : CommandPermission {
override fun CommandSender.hasPermission(): Boolean = this is ConsoleCommandSender public override fun CommandSender.hasPermission(): Boolean = this is ConsoleCommandSender
} }
object Default : CommandPermission by (Manager or Console) public object Default : CommandPermission by (Manager or Console)
} }
/** /**
@ -110,19 +110,19 @@ interface CommandPermission {
*/ */
@JvmSynthetic @JvmSynthetic
@Suppress("FunctionName") @Suppress("FunctionName")
inline fun AnonymousCommandPermission(crossinline block: CommandSender.() -> Boolean): CommandPermission { public inline fun AnonymousCommandPermission(crossinline block: CommandSender.() -> Boolean): CommandPermission {
return object : CommandPermission { return object : CommandPermission {
override fun CommandSender.hasPermission(): Boolean = block() override fun CommandSender.hasPermission(): Boolean = block()
} }
} }
inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean = public inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
permission.run { this@hasPermission.hasPermission() } permission.run { this@hasPermission.hasPermission() }
inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() } public inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission) public inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission)
internal class OrCommandPermission( internal class OrCommandPermission(
private val first: CommandPermission, private val first: CommandPermission,

View File

@ -14,12 +14,12 @@ package net.mamoe.mirai.console.command
* *
* 总是作为 [CommandExecutionException.cause]. * 总是作为 [CommandExecutionException.cause].
*/ */
class CommandPermissionDeniedException( public class CommandPermissionDeniedException(
/** /**
* 执行过程发生异常的指令 * 执行过程发生异常的指令
*/ */
val command: Command public val command: Command
) : RuntimeException("Permission denied while executing command '${command.primaryName}'") { ) : RuntimeException("Permission denied while executing command '${command.primaryName}'") {
override fun toString(): String = public override fun toString(): String =
"CommandPermissionDeniedException(command=$command)" "CommandPermissionDeniedException(command=$command)"
} }

View File

@ -28,55 +28,55 @@ import net.mamoe.mirai.message.data.PlainText
* @see UserCommandSender * @see UserCommandSender
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
interface CommandSender { public interface CommandSender {
/** /**
* 与这个 [CommandSender] 相关的 [Bot]. 当通过控制台执行时为 null. * 与这个 [CommandSender] 相关的 [Bot]. 当通过控制台执行时为 null.
*/ */
val bot: Bot? public val bot: Bot?
/** /**
* 立刻发送一条消息 * 立刻发送一条消息
*/ */
@JvmSynthetic @JvmSynthetic
suspend fun sendMessage(message: Message) public suspend fun sendMessage(message: Message)
@JvmDefault @JvmDefault
@JavaFriendlyAPI @JavaFriendlyAPI
@JvmName("sendMessage") @JvmName("sendMessage")
fun __sendMessageBlocking(messageChain: Message) = runBlocking { sendMessage(messageChain) } public fun __sendMessageBlocking(messageChain: Message): Unit = runBlocking { sendMessage(messageChain) }
@JvmDefault @JvmDefault
@JavaFriendlyAPI @JavaFriendlyAPI
@JvmName("sendMessage") @JvmName("sendMessage")
fun __sendMessageBlocking(message: String) = runBlocking { sendMessage(message) } public fun __sendMessageBlocking(message: String): Unit = runBlocking { sendMessage(message) }
} }
/** /**
* 可以知道其 [Bot] [CommandSender] * 可以知道其 [Bot] [CommandSender]
*/ */
interface BotAwareCommandSender : CommandSender { public interface BotAwareCommandSender : CommandSender {
override val bot: Bot public override val bot: Bot
} }
suspend inline fun CommandSender.sendMessage(message: String) = sendMessage(PlainText(message)) public suspend inline fun CommandSender.sendMessage(message: String): Unit = sendMessage(PlainText(message))
/** /**
* 控制台指令执行者. 代表由控制台执行指令 * 控制台指令执行者. 代表由控制台执行指令
*/ */
// 前端实现 // 前端实现
abstract class ConsoleCommandSender internal constructor() : CommandSender { public abstract class ConsoleCommandSender internal constructor() : CommandSender {
final override val bot: Nothing? get() = null public final override val bot: Nothing? get() = null
companion object { public companion object {
internal val instance get() = MiraiConsoleInternal.consoleCommandSender internal val instance get() = MiraiConsoleInternal.consoleCommandSender
} }
} }
fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this) public fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this)
fun Member.asCommandSender(): MemberCommandSender = MemberCommandSender(this) public fun Member.asCommandSender(): MemberCommandSender = MemberCommandSender(this)
fun User.asCommandSender(): UserCommandSender { public fun User.asCommandSender(): UserCommandSender {
return when (this) { return when (this) {
is Friend -> this.asCommandSender() is Friend -> this.asCommandSender()
is Member -> this.asCommandSender() is Member -> this.asCommandSender()
@ -87,28 +87,28 @@ fun User.asCommandSender(): UserCommandSender {
/** /**
* 表示由 [MessageEvent] 触发的指令 * 表示由 [MessageEvent] 触发的指令
*/ */
interface MessageEventContextAware<E : MessageEvent> : MessageEventExtensions<User, Contact> { public interface MessageEventContextAware<E : MessageEvent> : MessageEventExtensions<User, Contact> {
val fromEvent: E public val fromEvent: E
} }
/** /**
* 代表一个用户私聊机器人执行指令 * 代表一个用户私聊机器人执行指令
* @see User.asCommandSender * @see User.asCommandSender
*/ */
sealed class UserCommandSender : CommandSender, BotAwareCommandSender { public sealed class UserCommandSender : CommandSender, BotAwareCommandSender {
/** /**
* @see MessageEvent.sender * @see MessageEvent.sender
*/ */
abstract val user: User public abstract val user: User
/** /**
* @see MessageEvent.subject * @see MessageEvent.subject
*/ */
abstract val subject: Contact public abstract val subject: Contact
override val bot: Bot get() = user.bot public override val bot: Bot get() = user.bot
final override suspend fun sendMessage(message: Message) { public final override suspend fun sendMessage(message: Message) {
subject.sendMessage(message) subject.sendMessage(message)
} }
} }
@ -117,37 +117,39 @@ sealed class UserCommandSender : CommandSender, BotAwareCommandSender {
* 代表一个用户私聊机器人执行指令 * 代表一个用户私聊机器人执行指令
* @see Friend.asCommandSender * @see Friend.asCommandSender
*/ */
open class FriendCommandSender(final override val user: Friend) : UserCommandSender() { public open class FriendCommandSender(final override val user: Friend) : UserCommandSender() {
override val subject: Contact get() = user public override val subject: Contact get() = user
} }
/** /**
* 代表一个用户私聊机器人执行指令 * 代表一个用户私聊机器人执行指令
* @see Friend.asCommandSender * @see Friend.asCommandSender
*/ */
class FriendCommandSenderOnMessage(override val fromEvent: FriendMessageEvent) : FriendCommandSender(fromEvent.sender), public class FriendCommandSenderOnMessage(override val fromEvent: FriendMessageEvent) :
FriendCommandSender(fromEvent.sender),
MessageEventContextAware<FriendMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent { MessageEventContextAware<FriendMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject public override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot public override val bot: Bot get() = super.bot
} }
/** /**
* 代表一个群成员执行指令. * 代表一个群成员执行指令.
* @see Member.asCommandSender * @see Member.asCommandSender
*/ */
open class MemberCommandSender(final override val user: Member) : UserCommandSender() { public open class MemberCommandSender(final override val user: Member) : UserCommandSender() {
inline val group: Group get() = user.group public inline val group: Group get() = user.group
override val subject: Contact get() = group public override val subject: Contact get() = group
} }
/** /**
* 代表一个群成员在群内执行指令. * 代表一个群成员在群内执行指令.
* @see Member.asCommandSender * @see Member.asCommandSender
*/ */
class MemberCommandSenderOnMessage(override val fromEvent: GroupMessageEvent) : MemberCommandSender(fromEvent.sender), public class MemberCommandSenderOnMessage(override val fromEvent: GroupMessageEvent) :
MemberCommandSender(fromEvent.sender),
MessageEventContextAware<GroupMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent { MessageEventContextAware<GroupMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject public override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot public override val bot: Bot get() = super.bot
} }
/** /**
@ -155,8 +157,9 @@ class MemberCommandSenderOnMessage(override val fromEvent: GroupMessageEvent) :
* @see Member.asCommandSender * @see Member.asCommandSender
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
class TempCommandSenderOnMessage(override val fromEvent: TempMessageEvent) : MemberCommandSender(fromEvent.sender), public class TempCommandSenderOnMessage(override val fromEvent: TempMessageEvent) :
MemberCommandSender(fromEvent.sender),
MessageEventContextAware<TempMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent { MessageEventContextAware<TempMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject public override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot public override val bot: Bot get() = super.bot
} }

View File

@ -11,8 +11,8 @@
"EXPOSED_SUPER_CLASS", "EXPOSED_SUPER_CLASS",
"NOTHING_TO_INLINE", "NOTHING_TO_INLINE",
"unused", "unused",
"WRONG_MODIFIER_TARGET", "WRONG_MODIFIER_TARGET", "CANNOT_WEAKEN_ACCESS_PRIVILEGE",
"WRONG_MODIFIER_CONTAINING_DECLARATION" "WRONG_MODIFIER_CONTAINING_DECLARATION", "RedundantVisibilityModifier"
) )
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
@ -29,7 +29,7 @@ import kotlin.reflect.KClass
* 复合指令. * 复合指令.
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
abstract class CompositeCommand @JvmOverloads constructor( public abstract class CompositeCommand @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
@ -41,7 +41,7 @@ abstract class CompositeCommand @JvmOverloads constructor(
/** /**
* [CommandArgParser] 的环境 * [CommandArgParser] 的环境
*/ */
final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext public final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
/** /**
* 标记一个函数为子指令, [value] 为空时使用函数名. * 标记一个函数为子指令, [value] 为空时使用函数名.
@ -66,16 +66,17 @@ abstract class CompositeCommand @JvmOverloads constructor(
@Target(AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.VALUE_PARAMETER)
protected annotation class Name(val value: String) protected annotation class Name(val value: String)
override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) { public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
sendMessage(usage)
}
final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
matchSubCommand(args)?.parseAndExecute(this, args, true) ?: kotlin.run { matchSubCommand(args)?.parseAndExecute(this, args, true) ?: kotlin.run {
defaultSubCommand.onCommand(this, args) defaultSubCommand.onCommand(this, args)
} }
} }
final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
internal override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) {
sendMessage(usage)
}
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
get() = CompositeCommandSubCommandAnnotationResolver get() = CompositeCommandSubCommandAnnotationResolver
} }

View File

@ -1,12 +1,12 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
abstract class RawCommand( public abstract class RawCommand(
override val owner: CommandOwner, public override val owner: CommandOwner,
override vararg val names: String, public override vararg val names: String,
override val usage: String = "<no usages given>", public override val usage: String = "<no usages given>",
override val description: String = "<no descriptions given>", public override val description: String = "<no descriptions given>",
override val permission: CommandPermission = CommandPermission.Default, public override val permission: CommandPermission = CommandPermission.Default,
override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
abstract override suspend fun CommandSender.onCommand(args: Array<out Any>) public abstract override suspend fun CommandSender.onCommand(args: Array<out Any>)
} }

View File

@ -11,8 +11,8 @@
"EXPOSED_SUPER_CLASS", "EXPOSED_SUPER_CLASS",
"NOTHING_TO_INLINE", "NOTHING_TO_INLINE",
"unused", "unused",
"WRONG_MODIFIER_TARGET", "WRONG_MODIFIER_TARGET", "CANNOT_WEAKEN_ACCESS_PRIVILEGE",
"WRONG_MODIFIER_CONTAINING_DECLARATION" "WRONG_MODIFIER_CONTAINING_DECLARATION", "RedundantVisibilityModifier"
) )
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
@ -24,7 +24,7 @@ import net.mamoe.mirai.console.command.description.plus
import net.mamoe.mirai.console.command.internal.AbstractReflectionCommand import net.mamoe.mirai.console.command.internal.AbstractReflectionCommand
import net.mamoe.mirai.console.command.internal.SimpleCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.command.internal.SimpleCommandSubCommandAnnotationResolver
abstract class SimpleCommand @JvmOverloads constructor( public abstract class SimpleCommand @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
@ -34,7 +34,7 @@ abstract class SimpleCommand @JvmOverloads constructor(
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional), ) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
CommandParserContextAware { CommandParserContextAware {
override val usage: String public override val usage: String
get() = super.usage get() = super.usage
/** /**
@ -42,20 +42,20 @@ abstract class SimpleCommand @JvmOverloads constructor(
*/ */
protected annotation class Handler protected annotation class Handler
final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext public final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
override fun checkSubCommand(subCommands: Array<SubCommandDescriptor>) { internal override fun checkSubCommand(subCommands: Array<SubCommandDescriptor>) {
super.checkSubCommand(subCommands) super.checkSubCommand(subCommands)
check(subCommands.size == 1) { "There can only be exactly one function annotated with Handler at this moment as overloading is not yet supported." } check(subCommands.size == 1) { "There can only be exactly one function annotated with Handler at this moment as overloading is not yet supported." }
} }
@Deprecated("prohibited", level = DeprecationLevel.HIDDEN) @Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = sendMessage(usage) internal override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = sendMessage(usage)
final override suspend fun CommandSender.onCommand(args: Array<out Any>) { public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
subCommands.single().parseAndExecute(this, args, false) subCommands.single().parseAndExecute(this, args, false)
} }
final override val subCommandAnnotationResolver: SubCommandAnnotationResolver internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
get() = SimpleCommandSubCommandAnnotationResolver get() = SimpleCommandSubCommandAnnotationResolver
} }

View File

@ -20,14 +20,14 @@ import kotlin.contracts.contract
* this output type of that arg * this output type of that arg
* input is always String * input is always String
*/ */
interface CommandArgParser<out T : Any> { public interface CommandArgParser<out T : Any> {
fun parse(raw: String, sender: CommandSender): T public fun parse(raw: String, sender: CommandSender): T
@JvmDefault @JvmDefault
fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender) public fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
} }
fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T { public fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T {
contract { contract {
returns() implies (raw is String || raw is SingleMessage) returns() implies (raw is String || raw is SingleMessage)
} }
@ -41,12 +41,12 @@ fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T {
@Suppress("unused") @Suppress("unused")
@JvmSynthetic @JvmSynthetic
inline fun CommandArgParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing { public inline fun CommandArgParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing {
throw ParserException(message, cause) throw ParserException(message, cause)
} }
@JvmSynthetic @JvmSynthetic
inline fun CommandArgParser<*>.checkArgument( public inline fun CommandArgParser<*>.checkArgument(
condition: Boolean, condition: Boolean,
crossinline message: () -> String = { "Check failed." } crossinline message: () -> String = { "Check failed." }
) { ) {
@ -61,7 +61,7 @@ inline fun CommandArgParser<*>.checkArgument(
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
@JvmSynthetic @JvmSynthetic
inline fun <T : Any> CommandArgParser( public inline fun <T : Any> CommandArgParser(
crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
): CommandArgParser<T> = object : CommandArgParser<T> { ): CommandArgParser<T> = object : CommandArgParser<T> {
override fun parse(raw: String, sender: CommandSender): T = stringParser(raw, sender) override fun parse(raw: String, sender: CommandSender): T = stringParser(raw, sender)
@ -72,7 +72,7 @@ inline fun <T : Any> CommandArgParser(
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
@JvmSynthetic @JvmSynthetic
inline fun <T : Any> CommandArgParser( public inline fun <T : Any> CommandArgParser(
crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T, crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T,
crossinline messageParser: CommandArgParser<T>.(m: SingleMessage, sender: CommandSender) -> T crossinline messageParser: CommandArgParser<T>.(m: SingleMessage, sender: CommandSender) -> T
): CommandArgParser<T> = object : CommandArgParser<T> { ): CommandArgParser<T> = object : CommandArgParser<T> {
@ -84,4 +84,4 @@ inline fun <T : Any> CommandArgParser(
/** /**
* 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范. * 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范.
*/ */
class ParserException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) public class ParserException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)

View File

@ -23,44 +23,44 @@ import net.mamoe.mirai.message.data.SingleMessage
import net.mamoe.mirai.message.data.content import net.mamoe.mirai.message.data.content
object IntArgParser : CommandArgParser<Int> { public object IntArgParser : CommandArgParser<Int> {
override fun parse(raw: String, sender: CommandSender): Int = public override fun parse(raw: String, sender: CommandSender): Int =
raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数") raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数")
} }
object LongArgParser : CommandArgParser<Long> { public object LongArgParser : CommandArgParser<Long> {
override fun parse(raw: String, sender: CommandSender): Long = public override fun parse(raw: String, sender: CommandSender): Long =
raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数") raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数")
} }
object ShortArgParser : CommandArgParser<Short> { public object ShortArgParser : CommandArgParser<Short> {
override fun parse(raw: String, sender: CommandSender): Short = public override fun parse(raw: String, sender: CommandSender): Short =
raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数") raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数")
} }
object ByteArgParser : CommandArgParser<Byte> { public object ByteArgParser : CommandArgParser<Byte> {
override fun parse(raw: String, sender: CommandSender): Byte = public override fun parse(raw: String, sender: CommandSender): Byte =
raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节") raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节")
} }
object DoubleArgParser : CommandArgParser<Double> { public object DoubleArgParser : CommandArgParser<Double> {
override fun parse(raw: String, sender: CommandSender): Double = public override fun parse(raw: String, sender: CommandSender): Double =
raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数") raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数")
} }
object FloatArgParser : CommandArgParser<Float> { public object FloatArgParser : CommandArgParser<Float> {
override fun parse(raw: String, sender: CommandSender): Float = public override fun parse(raw: String, sender: CommandSender): Float =
raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数") raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数")
} }
object StringArgParser : CommandArgParser<String> { public object StringArgParser : CommandArgParser<String> {
override fun parse(raw: String, sender: CommandSender): String { public override fun parse(raw: String, sender: CommandSender): String {
return raw return raw
} }
} }
object BooleanArgParser : CommandArgParser<Boolean> { public object BooleanArgParser : CommandArgParser<Boolean> {
override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str -> public override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str ->
str.equals("true", ignoreCase = true) str.equals("true", ignoreCase = true)
|| str.equals("yes", ignoreCase = true) || str.equals("yes", ignoreCase = true)
|| str.equals("enabled", ignoreCase = true) || str.equals("enabled", ignoreCase = true)
@ -73,18 +73,18 @@ object BooleanArgParser : CommandArgParser<Boolean> {
* output: Bot * output: Bot
* errors: String->Int convert, Bot Not Exist * errors: String->Int convert, Bot Not Exist
*/ */
object ExistBotArgParser : CommandArgParser<Bot> { public object ExistBotArgParser : CommandArgParser<Bot> {
override fun parse(raw: String, sender: CommandSender): Bot { public override fun parse(raw: String, sender: CommandSender): Bot {
val uin = raw.toLongOrNull() ?: illegalArgument("无法识别 QQ ID: $raw") val uin = raw.toLongOrNull() ?: illegalArgument("无法识别 QQ ID: $raw")
return Bot.getInstanceOrNull(uin) ?: illegalArgument("无法找到 Bot $uin") return Bot.getInstanceOrNull(uin) ?: illegalArgument("无法找到 Bot $uin")
} }
} }
object ExistFriendArgParser : CommandArgParser<Friend> { public object ExistFriendArgParser : CommandArgParser<Friend> {
//Bot.friend //Bot.friend
//friend //friend
//~ = self //~ = self
override fun parse(raw: String, sender: CommandSender): Friend { public override fun parse(raw: String, sender: CommandSender): Friend {
if (raw == "~") { if (raw == "~") {
if (sender !is BotAwareCommandSender) { if (sender !is BotAwareCommandSender) {
illegalArgument("无法解析~作为默认") illegalArgument("无法解析~作为默认")
@ -115,7 +115,7 @@ object ExistFriendArgParser : CommandArgParser<Friend> {
} }
} }
override fun parse(raw: SingleMessage, sender: CommandSender): Friend { public override fun parse(raw: SingleMessage, sender: CommandSender): Friend {
if (raw is At) { if (raw is At) {
assert(sender is MemberCommandSender) assert(sender is MemberCommandSender)
return (sender as BotAwareCommandSender).bot.friends.getOrNull(raw.target) ?: illegalArgument("At的对象非Bot好友") return (sender as BotAwareCommandSender).bot.friends.getOrNull(raw.target) ?: illegalArgument("At的对象非Bot好友")
@ -125,8 +125,8 @@ object ExistFriendArgParser : CommandArgParser<Friend> {
} }
} }
object ExistGroupArgParser : CommandArgParser<Group> { public object ExistGroupArgParser : CommandArgParser<Group> {
override fun parse(raw: String, sender: CommandSender): Group { public override fun parse(raw: String, sender: CommandSender): Group {
//by default //by default
if ((raw == "" || raw == "~") && sender is MemberCommandSender) { if ((raw == "" || raw == "~") && sender is MemberCommandSender) {
return sender.group return sender.group
@ -160,12 +160,12 @@ object ExistGroupArgParser : CommandArgParser<Group> {
} }
} }
object ExistMemberArgParser : CommandArgParser<Member> { public object ExistMemberArgParser : CommandArgParser<Member> {
//后台: Bot.Group.Member[QQ/名片] //后台: Bot.Group.Member[QQ/名片]
//私聊: Group.Member[QQ/名片] //私聊: Group.Member[QQ/名片]
//群内: Q号 //群内: Q号
//群内: 名片 //群内: 名片
override fun parse(raw: String, sender: CommandSender): Member { public override fun parse(raw: String, sender: CommandSender): Member {
if (sender !is BotAwareCommandSender) { if (sender !is BotAwareCommandSender) {
with(raw.split(".")) { with(raw.split(".")) {
checkArgument(this.size >= 3) { checkArgument(this.size >= 3) {
@ -226,7 +226,7 @@ object ExistMemberArgParser : CommandArgParser<Member> {
} }
} }
override fun parse(raw: SingleMessage, sender: CommandSender): Member { public override fun parse(raw: SingleMessage, sender: CommandSender): Member {
return if (raw is At) { return if (raw is At) {
checkArgument(sender is MemberCommandSender) checkArgument(sender is MemberCommandSender)
(sender.group).members[raw.target] (sender.group).members[raw.target]

View File

@ -27,20 +27,20 @@ import kotlin.reflect.full.isSubclassOf
* [KClass] [CommandArgParser] 的匹配 * [KClass] [CommandArgParser] 的匹配
* @see CustomCommandParserContext 自定义 * @see CustomCommandParserContext 自定义
*/ */
interface CommandParserContext { public interface CommandParserContext {
data class ParserPair<T : Any>( public data class ParserPair<T : Any>(
val klass: KClass<T>, val klass: KClass<T>,
val parser: CommandArgParser<T> val parser: CommandArgParser<T>
) )
operator fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? public operator fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>?
fun toList(): List<ParserPair<*>> public fun toList(): List<ParserPair<*>>
/** /**
* 内建的默认 [CommandArgParser] * 内建的默认 [CommandArgParser]
*/ */
object Builtins : CommandParserContext by (CommandParserContext { public object Builtins : CommandParserContext by (CommandParserContext {
Int::class with IntArgParser Int::class with IntArgParser
Byte::class with ByteArgParser Byte::class with ByteArgParser
Short::class with ShortArgParser Short::class with ShortArgParser
@ -60,19 +60,19 @@ interface CommandParserContext {
/** /**
* 拥有 [CommandParserContext] 的类 * 拥有 [CommandParserContext] 的类
*/ */
interface CommandParserContextAware { public interface CommandParserContextAware {
/** /**
* [CommandArgParser] 的环境 * [CommandArgParser] 的环境
*/ */
val context: CommandParserContext public val context: CommandParserContext
} }
object EmptyCommandParserContext : CommandParserContext by CustomCommandParserContext(listOf()) public object EmptyCommandParserContext : CommandParserContext by CustomCommandParserContext(listOf())
/** /**
* 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser. * 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
*/ */
operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext { public operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext {
if (replacer == EmptyCommandParserContext) return this if (replacer == EmptyCommandParserContext) return this
if (this == EmptyCommandParserContext) return replacer if (this == EmptyCommandParserContext) return replacer
return object : CommandParserContext { return object : CommandParserContext {
@ -84,7 +84,7 @@ operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandP
/** /**
* 合并 [this] [replacer], [replacer] 将会替换 [this] 中重复的 parser. * 合并 [this] [replacer], [replacer] 将会替换 [this] 中重复的 parser.
*/ */
operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext { public operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext {
if (replacer.isEmpty()) return this if (replacer.isEmpty()) return this
if (this == EmptyCommandParserContext) return CustomCommandParserContext(replacer) if (this == EmptyCommandParserContext) return CustomCommandParserContext(replacer)
return object : CommandParserContext { return object : CommandParserContext {
@ -97,7 +97,7 @@ operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandPa
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class CustomCommandParserContext(val list: List<ParserPair<*>>) : CommandParserContext { public open class CustomCommandParserContext(public val list: List<ParserPair<*>>) : CommandParserContext {
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? = override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>? this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>?
@ -125,16 +125,16 @@ open class CustomCommandParserContext(val list: List<ParserPair<*>>) : CommandPa
*/ */
@Suppress("FunctionName") @Suppress("FunctionName")
@JvmSynthetic @JvmSynthetic
inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit): CommandParserContext { public inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit): CommandParserContext {
return CustomCommandParserContext(CommandParserContextBuilder().apply(block).distinctByReversed { it.klass }) return CustomCommandParserContext(CommandParserContextBuilder().apply(block).distinctByReversed { it.klass })
} }
/** /**
* @see CommandParserContext * @see CommandParserContext
*/ */
class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf() { public class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
@JvmName("add") @JvmName("add")
inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): ParserPair<*> = public inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): ParserPair<*> =
ParserPair(this, parser).also { add(it) } ParserPair(this, parser).also { add(it) }
/** /**
@ -142,7 +142,7 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
*/ */
@JvmSynthetic @JvmSynthetic
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
inline infix fun <T : Any> KClass<T>.with( public inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
): ParserPair<*> = ParserPair(this, CommandArgParser(parser)).also { add(it) } ): ParserPair<*> = ParserPair(this, CommandArgParser(parser)).also { add(it) }
@ -150,12 +150,12 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
* 添加一个指令解析器 * 添加一个指令解析器
*/ */
@JvmSynthetic @JvmSynthetic
inline infix fun <T : Any> KClass<T>.with( public inline infix fun <T : Any> KClass<T>.with(
crossinline parser: CommandArgParser<T>.(s: String) -> T crossinline parser: CommandArgParser<T>.(s: String) -> T
): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) }).also { add(it) } ): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) }).also { add(it) }
@JvmSynthetic @JvmSynthetic
inline fun <reified T : Any> add(parser: CommandArgParser<T>): ParserPair<*> = public inline fun <reified T : Any> add(parser: CommandArgParser<T>): ParserPair<*> =
ParserPair(T::class, parser).also { add(it) } ParserPair(T::class, parser).also { add(it) }
/** /**
@ -163,7 +163,7 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
@JvmSynthetic @JvmSynthetic
inline infix fun <reified T : Any> add( public inline infix fun <reified T : Any> add(
crossinline parser: CommandArgParser<*>.(s: String) -> T crossinline parser: CommandArgParser<*>.(s: String) -> T
): ParserPair<*> = T::class with CommandArgParser { s: String, _: CommandSender -> parser(s) } ): ParserPair<*> = T::class with CommandArgParser { s: String, _: CommandSender -> parser(s) }
@ -173,7 +173,7 @@ class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf(
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
@JvmSynthetic @JvmSynthetic
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
inline infix fun <reified T : Any> add( public inline infix fun <reified T : Any> add(
crossinline parser: CommandArgParser<*>.(s: String, sender: CommandSender) -> T crossinline parser: CommandArgParser<*>.(s: String, sender: CommandSender) -> T
): ParserPair<*> = T::class with CommandArgParser(parser) ): ParserPair<*> = T::class with CommandArgParser(parser)
} }

View File

@ -14,4 +14,4 @@ import net.mamoe.mirai.event.Event
/** /**
* 表示来自 mirai-console 的事件 * 表示来自 mirai-console 的事件
*/ */
interface ConsoleEvent : Event public interface ConsoleEvent : Event

View File

@ -22,15 +22,16 @@ import java.io.File
* *
* @see PluginLoader 插件加载器 * @see PluginLoader 插件加载器
*/ */
interface Plugin { public interface Plugin {
/** /**
* 所属插件加载器实例, 此加载器必须能加载这个 [Plugin]. * 所属插件加载器实例, 此加载器必须能加载这个 [Plugin].
*/ */
val loader: PluginLoader<*, *> public val loader: PluginLoader<*, *>
} }
@get:JvmSynthetic
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription> public inline val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription>
get() = this.loader as PluginLoader<P, PluginDescription> get() = this.loader as PluginLoader<P, PluginDescription>
/** /**
@ -39,15 +40,17 @@ inline val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription>
* @see JvmPlugin * @see JvmPlugin
*/ */
@ConsoleExperimentalAPI("classname is subject to change") @ConsoleExperimentalAPI("classname is subject to change")
interface PluginFileExtensions { public interface PluginFileExtensions {
/** /**
* 数据目录 * 数据目录
*/ */
val dataFolder: File public val dataFolder: File
/** /**
* 从数据目录获取一个文件, 若不存在则创建文件. * 从数据目录获取一个文件, 若不存在则创建文件.
*/ */
@JvmDefault @JvmDefault
fun file(relativePath: String): File = File(dataFolder, relativePath).apply { createNewFile() } public fun file(relativePath: String): File = File(dataFolder, relativePath).apply { createNewFile() }
// TODO: 2020/7/11 add `fun path(...): Path` ?
} }

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", "INAPPLICABLE_JVM_NAME") @file:Suppress("unused", "INAPPLICABLE_JVM_NAME", "NOTHING_TO_INLINE")
package net.mamoe.mirai.console.plugin package net.mamoe.mirai.console.plugin
@ -23,11 +23,11 @@ import java.io.File
* *
* @see JarPluginLoader Jar 插件加载器 * @see JarPluginLoader Jar 插件加载器
*/ */
interface PluginLoader<P : Plugin, D : PluginDescription> { public interface PluginLoader<P : Plugin, D : PluginDescription> {
/** /**
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次 * 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次
*/ */
fun listPlugins(): List<D> public fun listPlugins(): List<D>
/** /**
* 获取此插件的描述 * 获取此插件的描述
@ -36,7 +36,7 @@ interface PluginLoader<P : Plugin, D : PluginDescription> {
*/ */
@get:JvmName("getPluginDescription") @get:JvmName("getPluginDescription")
@get:Throws(PluginLoadException::class) @get:Throws(PluginLoadException::class)
val P.description: D public val P.description: D // Java signature: `public P getDescription(P)`
/** /**
* 加载一个插件 (实例), 但不 [启用][enable] . 返回加载成功的主类实例 * 加载一个插件 (实例), 但不 [启用][enable] . 返回加载成功的主类实例
@ -44,19 +44,20 @@ interface PluginLoader<P : Plugin, D : PluginDescription> {
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等). * @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
*/ */
@Throws(PluginLoadException::class) @Throws(PluginLoadException::class)
fun load(description: D): P public fun load(description: D): P
fun enable(plugin: P) public fun enable(plugin: P)
fun disable(plugin: P) public fun disable(plugin: P)
} }
fun <D : PluginDescription, P : Plugin> PluginLoader<P, D>.getDescription(plugin: P): D = plugin.description public inline fun <D : PluginDescription, P : Plugin> PluginLoader<P, D>.getDescription(plugin: P): D =
plugin.description
open class PluginLoadException : RuntimeException { public open class PluginLoadException : RuntimeException {
constructor() : super() public constructor() : super()
constructor(message: String?) : super(message) public constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause) public constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause) public constructor(cause: Throwable?) : super(cause)
} }
/** /**
@ -65,18 +66,18 @@ open class PluginLoadException : RuntimeException {
* @see AbstractFilePluginLoader 默认基础实现 * @see AbstractFilePluginLoader 默认基础实现
* @see JarPluginLoader 内建的 Jar (JVM) 插件加载器. * @see JarPluginLoader 内建的 Jar (JVM) 插件加载器.
*/ */
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> { public interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
/** /**
* 所支持的插件文件后缀, '.'. [JarPluginLoader] ".jar" * 所支持的插件文件后缀, '.'. [JarPluginLoader] ".jar"
*/ */
val fileSuffix: String public val fileSuffix: String
} }
/** /**
* [FilePluginLoader] 的默认基础实现 * [FilePluginLoader] 的默认基础实现
*/ */
abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>( public abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
override val fileSuffix: String public override val fileSuffix: String
) : FilePluginLoader<P, D> { ) : FilePluginLoader<P, D> {
private fun pluginsFilesSequence(): Sequence<File> = private fun pluginsFilesSequence(): Sequence<File> =
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) } PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
@ -86,7 +87,7 @@ abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
*/ */
protected abstract fun Sequence<File>.mapToDescription(): List<D> protected abstract fun Sequence<File>.mapToDescription(): List<D>
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription() public final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
} }

View File

@ -18,18 +18,68 @@ import net.mamoe.mirai.utils.info
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
val Plugin.description: PluginDescription // TODO: 2020/7/11 top-level or in PluginManager.companion?
get() = PluginManager.resolvedPlugins.firstOrNull { it == this }?.loader?.cast<PluginLoader<Plugin, PluginDescription>>() public val Plugin.description: PluginDescription
?.getDescription( get() = PluginManagerImpl.resolvedPlugins.firstOrNull { it == this }
this ?.loader?.cast<PluginLoader<Plugin, PluginDescription>>()
) ?: error("Plugin is unloaded") ?.getDescription(this)
?: error("Plugin is unloaded")
inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this) @JvmSynthetic
inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this) public inline fun PluginLoader<*, *>.register(): Boolean = PluginManager.registerPluginLoader(this)
object PluginManager { @JvmSynthetic
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } public inline fun PluginLoader<*, *>.unregister(): Boolean = PluginManager.unregisterPluginLoader(this)
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
// TODO: 2020/7/11 document
public interface PluginManager {
public val pluginsDir: File
public val pluginsDataFolder: File
/**
* 已加载的插件列表
*/
public val plugins: List<Plugin>
/**
* 内建的插件加载器列表. [MiraiConsole] 初始化
*/
public val builtInLoaders: List<PluginLoader<*, *>>
/**
* 由插件创建的 [PluginLoader]
*/
public val pluginLoaders: List<PluginLoader<*, *>>
public fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean
public fun unregisterPluginLoader(loader: PluginLoader<*, *>): Boolean
public companion object INSTANCE : PluginManager by PluginManagerImpl
}
public class PluginMissingDependencyException : PluginResolutionException {
public constructor() : super()
public constructor(message: String?) : super(message)
public constructor(message: String?, cause: Throwable?) : super(message, cause)
public constructor(cause: Throwable?) : super(cause)
}
public open class PluginResolutionException : Exception {
public constructor() : super()
public constructor(message: String?) : super(message)
public constructor(message: String?, cause: Throwable?) : super(message, cause)
public constructor(cause: Throwable?) : super(cause)
}
// internal
internal object PluginManagerImpl : PluginManager {
override val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
override val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf() private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
@ -38,38 +88,21 @@ object PluginManager {
@JvmField @JvmField
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf() internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
override val plugins: List<Plugin>
/**
* 已加载的插件列表
*/
@JvmStatic
val plugins: List<Plugin>
get() = resolvedPlugins.toList() get() = resolvedPlugins.toList()
override val builtInLoaders: List<PluginLoader<*, *>>
/**
* 内建的插件加载器列表. [MiraiConsole] 初始化
*/
@JvmStatic
val builtInLoaders: List<PluginLoader<*, *>>
get() = MiraiConsole.builtInPluginLoaders get() = MiraiConsole.builtInPluginLoaders
override val pluginLoaders: List<PluginLoader<*, *>>
/**
* 由插件创建的 [PluginLoader]
*/
@JvmStatic
val pluginLoaders: List<PluginLoader<*, *>>
get() = _pluginLoaders.toList() get() = _pluginLoaders.toList()
@JvmStatic override fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
if (_pluginLoaders.any { it::class == loader }) { if (_pluginLoaders.any { it::class == loader }) {
return false return false
} }
_pluginLoaders.add(loader) _pluginLoaders.add(loader)
} }
@JvmStatic override fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
_pluginLoaders.remove(loader) _pluginLoaders.remove(loader)
} }
@ -177,8 +210,10 @@ object PluginManager {
this.consumeLoadable().also { resultPlugins -> this.consumeLoadable().also { resultPlugins ->
check(resultPlugins.size < beforeSize) { check(resultPlugins.size < beforeSize) {
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin -> throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${badPlugin.dependencies.filterIsMissing() "Cannot load plugin ${badPlugin.name}, missing dependencies: ${
.joinToString()}" badPlugin.dependencies.filterIsMissing()
.joinToString()
}"
}) })
} }
}.doSort() }.doSort()
@ -191,16 +226,6 @@ object PluginManager {
// endregion // endregion
} }
class PluginMissingDependencyException(message: String?) : PluginResolutionException(message)
open class PluginResolutionException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
internal data class PluginDescriptionWithLoader( internal data class PluginDescriptionWithLoader(
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type @JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
@JvmField val delegate: PluginDescription @JvmField val delegate: PluginDescription

View File

@ -15,10 +15,11 @@ import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
import java.io.File import java.io.File
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
interface PluginCenter { public interface PluginCenter {
@Serializable @Serializable
data class PluginInsight( @ConsoleExperimentalAPI
public data class PluginInsight(
val name: String, val name: String,
val version: String, val version: String,
@SerialName("core") @SerialName("core")
@ -31,8 +32,9 @@ interface PluginCenter {
val commands: List<String> val commands: List<String>
) )
@ConsoleExperimentalAPI
@Serializable @Serializable
data class PluginInfo( public data class PluginInfo(
val name: String, val name: String,
val version: String, val version: String,
@SerialName("core") @SerialName("core")
@ -54,24 +56,24 @@ interface PluginCenter {
* 能获取到多少由实际的 [PluginCenter] 决定 * 能获取到多少由实际的 [PluginCenter] 决定
* 返回 插件名->Insight * 返回 插件名->Insight
*/ */
suspend fun fetchPlugin(page: Int): Map<String, PluginInsight> public suspend fun fetchPlugin(page: Int): Map<String, PluginInsight>
/** /**
* 尝试获取到某个插件 by 全名, case sensitive * 尝试获取到某个插件 by 全名, case sensitive
* null 则没有 * null 则没有
*/ */
suspend fun findPlugin(name: String): PluginInfo? public suspend fun findPlugin(name: String): PluginInfo?
suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File public suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File
suspend fun downloadPlugin(name: String, progressListener: PluginCenter.(Float) -> Unit): File = public suspend fun downloadPlugin(name: String, progressListener: PluginCenter.(Float) -> Unit): File =
downloadPlugin<PluginCenter>(name, progressListener) downloadPlugin<PluginCenter>(name, progressListener)
/** /**
* 刷新 * 刷新
*/ */
suspend fun refresh() public suspend fun refresh()
val name: String public val name: String
} }

View File

@ -9,7 +9,8 @@
package net.mamoe.mirai.console.plugin package net.mamoe.mirai.console.plugin
import kotlinx.serialization.* import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.setting.internal.map import net.mamoe.mirai.console.setting.internal.map
import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.Yaml
@ -19,14 +20,14 @@ import java.io.File
/** 插件类型 */ /** 插件类型 */
@Serializable(with = PluginKind.AsStringSerializer::class) @Serializable(with = PluginKind.AsStringSerializer::class)
enum class PluginKind { public enum class PluginKind {
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
LOADER, LOADER,
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
NORMAL; NORMAL;
object AsStringSerializer : KSerializer<PluginKind> by String.serializer().map( public object AsStringSerializer : KSerializer<PluginKind> by String.serializer().map(
serializer = { it.name }, serializer = { it.name },
deserializer = { str -> deserializer = { str ->
values().firstOrNull { values().firstOrNull {
@ -39,38 +40,38 @@ enum class PluginKind {
/** /**
* 插件描述 * 插件描述
*/ */
interface PluginDescription { public interface PluginDescription {
val kind: PluginKind public val kind: PluginKind
val name: String public val name: String
val author: String public val author: String
val version: String public val version: String
val info: String public val info: String
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> public val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency>
} }
/** 插件的一个依赖的信息 */ /** 插件的一个依赖的信息 */
@Serializable @Serializable
data class PluginDependency( public data class PluginDependency(
/** 依赖插件名 */ /** 依赖插件名 */
val name: String, public val name: String,
/** /**
* 依赖版本号. null 时则为不限制版本. * 依赖版本号. null 时则为不限制版本.
* @see versionKind 版本号类型 * @see versionKind 版本号类型
*/ */
val version: String? = null, public val version: String? = null,
/** 版本号类型 */ /** 版本号类型 */
val versionKind: VersionKind = VersionKind.AT_LEAST, public val versionKind: VersionKind = VersionKind.AT_LEAST,
/** /**
* 若为 `false`, 插件在找不到此依赖时也能正常加载. * 若为 `false`, 插件在找不到此依赖时也能正常加载.
*/ */
val isOptional: Boolean = false public val isOptional: Boolean = false
) { ) {
/** 版本号类型 */ /** 版本号类型 */
@Serializable(with = VersionKind.AsStringSerializer::class) @Serializable(with = VersionKind.AsStringSerializer::class)
enum class VersionKind( public enum class VersionKind(
private vararg val serialNames: String private vararg val serialNames: String
) { ) {
/** 要求依赖精确的版本 */ /** 要求依赖精确的版本 */
@ -82,7 +83,7 @@ data class PluginDependency(
/** 要求依赖最高版本 */ /** 要求依赖最高版本 */
AT_MOST("at_most", "AtMost", "most", "highest", "-"); AT_MOST("at_most", "AtMost", "most", "highest", "-");
object AsStringSerializer : KSerializer<VersionKind> by String.serializer().map( public object AsStringSerializer : KSerializer<VersionKind> by String.serializer().map(
serializer = { it.serialNames.first() }, serializer = { it.serialNames.first() },
deserializer = { str -> deserializer = { str ->
values().firstOrNull { values().firstOrNull {
@ -92,7 +93,7 @@ data class PluginDependency(
) )
} }
override fun toString(): String { public override fun toString(): String {
return "$name ${versionKind.toEnglishString()}v$version" return "$name ${versionKind.toEnglishString()}v$version"
} }
@ -100,7 +101,7 @@ data class PluginDependency(
/** /**
* 可支持解析 [String] 作为 [PluginDependency.version] 或单个 [PluginDependency] * 可支持解析 [String] 作为 [PluginDependency.version] 或单个 [PluginDependency]
*/ */
object SmartSerializer : KSerializer<PluginDependency> by YamlDynamicSerializer.map( public object SmartSerializer : KSerializer<PluginDependency> by YamlDynamicSerializer.map(
serializer = { it }, serializer = { it },
deserializer = { any -> deserializer = { any ->
when (any) { when (any) {
@ -112,10 +113,10 @@ data class PluginDependency(
} }
/** /**
* 基于文件的插件的描述 * 基于文件的插件 的描述
*/ */
interface FilePluginDescription : PluginDescription { public interface FilePluginDescription : PluginDescription {
val file: File public val file: File
} }
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) { internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {

View File

@ -39,8 +39,8 @@ internal abstract class JvmPluginInternal(
) : JvmPlugin, ) : JvmPlugin,
CoroutineScope { CoroutineScope {
private val resourceConsoleDelegate by lazy { this::class.asResourceContainer() } private val resourceContainerDelegate by lazy { this::class.asResourceContainer() }
override fun getResourceAsStream(name: String): InputStream = resourceConsoleDelegate.getResourceAsStream(name) override fun getResourceAsStream(name: String): InputStream = resourceContainerDelegate.getResourceAsStream(name)
// region JvmPlugin // region JvmPlugin
/** /**

View File

@ -22,10 +22,10 @@ import kotlin.coroutines.EmptyCoroutineContext
* @see JavaPlugin * @see JavaPlugin
* @see KotlinPlugin * @see KotlinPlugin
*/ */
abstract class AbstractJvmPlugin @JvmOverloads constructor( public abstract class AbstractJvmPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginInternal(parentCoroutineContext) { ) : JvmPlugin, JvmPluginInternal(parentCoroutineContext) {
final override val name: String get() = this.description.name public final override val name: String get() = this.description.name
override fun <T : Setting> getSetting(clazz: Class<T>): T = loader.settingStorage.load(this, clazz) public override fun <T : Setting> getSetting(clazz: Class<T>): T = loader.settingStorage.load(this, clazz)
} }

View File

@ -13,6 +13,7 @@ import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.MiraiConsoleInternal import net.mamoe.mirai.console.MiraiConsoleInternal
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.FilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.internal.JvmPluginInternal import net.mamoe.mirai.console.plugin.internal.JvmPluginInternal
import net.mamoe.mirai.console.plugin.internal.PluginsLoader import net.mamoe.mirai.console.plugin.internal.PluginsLoader
@ -28,11 +29,24 @@ import kotlin.reflect.full.createInstance
/** /**
* 内建的 Jar (JVM) 插件加载器 * 内建的 Jar (JVM) 插件加载器
*/ */
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"), CoroutineScope { public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
@ConsoleExperimentalAPI
public val settingStorage: SettingStorage
public companion object INSTANCE : JarPluginLoader by JarPluginLoaderImpl
}
internal object JarPluginLoaderImpl :
AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"),
CoroutineScope,
JarPluginLoader {
private val logger: MiraiLogger = MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!) private val logger: MiraiLogger = MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
val settingStorage: SettingStorage = MiraiConsoleInternal.settingStorage override val settingStorage: SettingStorage
get() = MiraiConsoleInternal.settingStorage
override val coroutineContext: CoroutineContext = override val coroutineContext: CoroutineContext =
MiraiConsole.coroutineContext + MiraiConsole.coroutineContext +

View File

@ -18,14 +18,12 @@ import kotlin.coroutines.EmptyCoroutineContext
/** /**
* Java 插件的父类 * Java 插件的父类
*/ */
abstract class JavaPlugin @JvmOverloads constructor( public abstract class JavaPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext) { ) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext) {
/** /**
* Java API Scheduler * Java API Scheduler
*/ */
val scheduler: JavaPluginScheduler = public val scheduler: JavaPluginScheduler = JavaPluginScheduler(this.coroutineContext)
JavaPluginScheduler(this.coroutineContext)
} }

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
package net.mamoe.mirai.console.plugin.jvm package net.mamoe.mirai.console.plugin.jvm
@ -32,34 +32,39 @@ import kotlin.reflect.KClass
* @see JvmPlugin 支持文件系统扩展 * @see JvmPlugin 支持文件系统扩展
* @see ResourceContainer 支持资源获取 ( Jar 中的资源文件) * @see ResourceContainer 支持资源获取 ( Jar 中的资源文件)
*/ */
interface JvmPlugin : Plugin, CoroutineScope, PluginFileExtensions, ResourceContainer, AutoSaveSettingHolder { public interface JvmPlugin : Plugin, CoroutineScope,
PluginFileExtensions, ResourceContainer, AutoSaveSettingHolder {
/** 日志 */ /** 日志 */
val logger: MiraiLogger public val logger: MiraiLogger
/** 插件描述 */ /** 插件描述 */
val description: JvmPluginDescription public val description: JvmPluginDescription
/** 所属插件加载器实例 */ /** 所属插件加载器实例 */
override val loader: JarPluginLoader get() = JarPluginLoader public override val loader: JarPluginLoader get() = JarPluginLoader
/** /**
* 获取一个 [Setting] 实例 * 获取一个 [Setting] 实例
*/ */
fun <T : Setting> getSetting(clazz: Class<T>): T public fun <T : Setting> getSetting(clazz: Class<T>): T
// TODO: 2020/7/11 document onLoad, onEnable, onDisable
@JvmDefault @JvmDefault
fun onLoad() { public fun onLoad() {
} }
@JvmDefault @JvmDefault
fun onEnable() { public fun onEnable() {
} }
@JvmDefault @JvmDefault
fun onDisable() { public fun onDisable() {
} }
} }
fun <T : Setting> JvmPlugin.getSetting(clazz: KClass<T>) = this.getSetting(clazz.java) @JvmSynthetic
inline fun <reified T : Setting> JvmPlugin.getSetting() = this.getSetting(T::class) public inline fun <T : Setting> JvmPlugin.getSetting(clazz: KClass<T>): T = this.getSetting(clazz.java)
@JvmSynthetic
public inline fun <reified T : Setting> JvmPlugin.getSetting(): T = this.getSetting(T::class)

View File

@ -19,23 +19,23 @@ import net.mamoe.mirai.console.plugin.PluginKind
import java.io.File import java.io.File
@Serializable @Serializable
class JvmPluginDescription internal constructor( public class JvmPluginDescription internal constructor(
override val kind: PluginKind = PluginKind.NORMAL, public override val kind: PluginKind = PluginKind.NORMAL,
override val name: String, public override val name: String,
@SerialName("main") @SerialName("main")
val mainClassName: String, public val mainClassName: String,
override val author: String = "", public override val author: String = "",
override val version: String, public override val version: String,
override val info: String = "", public override val info: String = "",
@SerialName("depends") @SerialName("depends")
override val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> = listOf() public override val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> = listOf()
) : PluginDescription, FilePluginDescription { ) : PluginDescription, FilePluginDescription {
/** /**
* 在手动实现时使用这个构造器. * 在手动实现时使用这个构造器.
*/ */
@Suppress("unused") @Suppress("unused")
constructor( public constructor(
kind: PluginKind, name: String, mainClassName: String, author: String, kind: PluginKind, name: String, mainClassName: String, author: String,
version: String, info: String, depends: List<PluginDependency>, version: String, info: String, depends: List<PluginDependency>,
file: File file: File
@ -43,7 +43,7 @@ class JvmPluginDescription internal constructor(
this._file = file this._file = file
} }
override val file: File public override val file: File
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null") get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
@ -52,7 +52,7 @@ class JvmPluginDescription internal constructor(
@JvmField @JvmField
internal var _file: File? = null internal var _file: File? = null
override fun toString(): String { public override fun toString(): String {
return "JvmPluginDescription(kind=$kind, name='$name', mainClassName='$mainClassName', author='$author', version='$version', info='$info', dependencies=$dependencies, _file=$_file)" return "JvmPluginDescription(kind=$kind, name='$name', mainClassName='$mainClassName', author='$author', version='$version', info='$info', dependencies=$dependencies, _file=$_file)"
} }
} }

View File

@ -7,13 +7,10 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS", "RedundantVisibilityModifier")
package net.mamoe.mirai.console.plugin.jvm package net.mamoe.mirai.console.plugin.jvm
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.getValue
import net.mamoe.mirai.console.setting.value
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
@ -23,7 +20,7 @@ import kotlin.coroutines.EmptyCoroutineContext
* *
* 必须通过 "plugin.yml" 指定主类并由 [JarPluginLoader] 加载. * 必须通过 "plugin.yml" 指定主类并由 [JarPluginLoader] 加载.
*/ */
abstract class KotlinPlugin @JvmOverloads constructor( public abstract class KotlinPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext) ) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext)
@ -32,11 +29,11 @@ abstract class KotlinPlugin @JvmOverloads constructor(
* 在内存动态加载的插件. * 在内存动态加载的插件.
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
abstract class KotlinMemoryPlugin @JvmOverloads constructor( public abstract class KotlinMemoryPlugin @JvmOverloads constructor(
description: JvmPluginDescription, description: JvmPluginDescription,
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext) { ) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext) {
final override var _description: JvmPluginDescription internal final override var _description: JvmPluginDescription
get() = super._description get() = super._description
set(value) { set(value) {
super._description = value super._description = value
@ -47,8 +44,11 @@ abstract class KotlinMemoryPlugin @JvmOverloads constructor(
} }
} }
object MyPlugin : KotlinPlugin() /*
object AccountSetting : Setting by MyPlugin.getSetting() { public object MyPlugin : KotlinPlugin()
val s by value(1)
} public object AccountSetting : Setting by MyPlugin.getSetting() {
public val s by value(1)
}
*/

View File

@ -29,11 +29,11 @@ import kotlin.reflect.KType
* ``` * ```
*/ */
// TODO: 2020/6/26 document // TODO: 2020/6/26 document
typealias SerialName = kotlinx.serialization.SerialName public typealias SerialName = kotlinx.serialization.SerialName
// TODO: 2020/6/26 document // TODO: 2020/6/26 document
abstract class AbstractSetting : Setting, SettingImpl() { public abstract class AbstractSetting : Setting, SettingImpl() {
final override operator fun <T> SerializerAwareValue<T>.provideDelegate( public final override operator fun <T> SerializerAwareValue<T>.provideDelegate(
thisRef: Any?, thisRef: Any?,
property: KProperty<*> property: KProperty<*>
): SerializerAwareValue<T> { ): SerializerAwareValue<T> {
@ -42,34 +42,34 @@ abstract class AbstractSetting : Setting, SettingImpl() {
return this return this
} }
final override val updaterSerializer: KSerializer<Unit> get() = super.updaterSerializer public final override val updaterSerializer: KSerializer<Unit> get() = super.updaterSerializer
} }
// TODO: 2020/6/26 document // TODO: 2020/6/26 document
interface Setting { public interface Setting {
// TODO: 2020/6/26 document // TODO: 2020/6/26 document
operator fun <T> SerializerAwareValue<T>.provideDelegate( public operator fun <T> SerializerAwareValue<T>.provideDelegate(
thisRef: Any?, thisRef: Any?,
property: KProperty<*> property: KProperty<*>
): SerializerAwareValue<T> ): SerializerAwareValue<T>
// TODO: 2020/6/26 document // TODO: 2020/6/26 document
val updaterSerializer: KSerializer<Unit> public val updaterSerializer: KSerializer<Unit>
fun onValueChanged(value: Value<*>) public fun onValueChanged(value: Value<*>)
} }
//// region Setting_value_primitives CODEGEN //// //// region Setting_value_primitives CODEGEN ////
fun Setting.value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default) public fun Setting.value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default)
fun Setting.value(default: Short): SerializerAwareValue<Short> = valueImpl(default) public fun Setting.value(default: Short): SerializerAwareValue<Short> = valueImpl(default)
fun Setting.value(default: Int): SerializerAwareValue<Int> = valueImpl(default) public fun Setting.value(default: Int): SerializerAwareValue<Int> = valueImpl(default)
fun Setting.value(default: Long): SerializerAwareValue<Long> = valueImpl(default) public fun Setting.value(default: Long): SerializerAwareValue<Long> = valueImpl(default)
fun Setting.value(default: Float): SerializerAwareValue<Float> = valueImpl(default) public fun Setting.value(default: Float): SerializerAwareValue<Float> = valueImpl(default)
fun Setting.value(default: Double): SerializerAwareValue<Double> = valueImpl(default) public fun Setting.value(default: Double): SerializerAwareValue<Double> = valueImpl(default)
fun Setting.value(default: Char): SerializerAwareValue<Char> = valueImpl(default) public fun Setting.value(default: Char): SerializerAwareValue<Char> = valueImpl(default)
fun Setting.value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(default) public fun Setting.value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(default)
fun Setting.value(default: String): SerializerAwareValue<String> = valueImpl(default) public fun Setting.value(default: String): SerializerAwareValue<String> = valueImpl(default)
//// endregion Setting_value_primitives CODEGEN //// //// endregion Setting_value_primitives CODEGEN ////
@ -84,7 +84,7 @@ fun Setting.value(default: String): SerializerAwareValue<String> = valueImpl(def
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
inline fun <reified T> Setting.value(default: T): SerializerAwareValue<T> = valueFromKType(typeOf0<T>(), default) public inline fun <reified T> Setting.value(default: T): SerializerAwareValue<T> = valueFromKType(typeOf0<T>(), default)
/** /**
* Creates a [Value] with reified type, and set default value by reflection to its no-arg public constructor. * Creates a [Value] with reified type, and set default value by reflection to its no-arg public constructor.
@ -95,13 +95,13 @@ inline fun <reified T> Setting.value(default: T): SerializerAwareValue<T> = valu
* (typically annotated with [kotlinx.serialization.Serializable]) * (typically annotated with [kotlinx.serialization.Serializable])
*/ */
@LowPriorityInOverloadResolution @LowPriorityInOverloadResolution
inline fun <reified T> Setting.value(): SerializerAwareValue<T> = value(T::class.createInstance() as T) public inline fun <reified T> Setting.value(): SerializerAwareValue<T> = value(T::class.createInstance() as T)
/** /**
* Creates a [Value] with specified [KType], and set default value. * Creates a [Value] with specified [KType], and set default value.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T> Setting.valueFromKType(type: KType, default: T): SerializerAwareValue<T> = public fun <T> Setting.valueFromKType(type: KType, default: T): SerializerAwareValue<T> =
(valueFromKTypeImpl(type) as SerializerAwareValue<Any?>).apply { this.value = default } as SerializerAwareValue<T> (valueFromKTypeImpl(type) as SerializerAwareValue<Any?>).apply { this.value = default } as SerializerAwareValue<T>
// TODO: 2020/6/24 Introduce class TypeToken for compound types for Java. // TODO: 2020/6/24 Introduce class TypeToken for compound types for Java.

View File

@ -1,3 +1,5 @@
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.console.setting package net.mamoe.mirai.console.setting
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
@ -22,19 +24,20 @@ import kotlin.reflect.full.findAnnotation
/** /**
* [Setting] 存储容器 * [Setting] 存储容器
*/ */
interface SettingStorage { public interface SettingStorage {
/** /**
* 读取一个实例 * 读取一个实例
*/ */
fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T public fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T
/** /**
* 保存一个实例 * 保存一个实例
*/ */
fun store(holder: SettingHolder, setting: Setting) public fun store(holder: SettingHolder, setting: Setting)
} }
fun <T : Setting> SettingStorage.load(holder: SettingHolder, settingClass: KClass<T>): T = // TODO: 2020/7/11 here or companion?
public inline fun <T : Setting> SettingStorage.load(holder: SettingHolder, settingClass: KClass<T>): T =
this.load(holder, settingClass.java) this.load(holder, settingClass.java)
/** /**
@ -45,17 +48,19 @@ fun <T : Setting> SettingStorage.load(holder: SettingHolder, settingClass: KClas
* *
* @see AutoSaveSettingHolder 自动保存 * @see AutoSaveSettingHolder 自动保存
*/ */
interface SettingHolder { public interface SettingHolder {
/** /**
* 保存时使用的分类名 * 保存时使用的分类名
*/ */
val name: String public val name: String
} }
/** /**
* 可以持有相关 [AutoSaveSetting] 的对象. * 可以持有相关 [AutoSaveSetting] 的对象.
*
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
*/ */
interface AutoSaveSettingHolder : SettingHolder, CoroutineScope { public interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
/** /**
* [AutoSaveSetting] 每次自动保存时间间隔 * [AutoSaveSetting] 每次自动保存时间间隔
* *
@ -63,8 +68,11 @@ interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
* - 区间的右端点为最大间隔, 一个 [Value] 被修改后, 最多不超过这个时间段后就会被保存. * - 区间的右端点为最大间隔, 一个 [Value] 被修改后, 最多不超过这个时间段后就会被保存.
* *
* [coroutineContext] 含有 [Job], [AutoSaveSetting] 会通过 [Job.invokeOnCompletion] Job 完结时触发自动保存. * [coroutineContext] 含有 [Job], [AutoSaveSetting] 会通过 [Job.invokeOnCompletion] Job 完结时触发自动保存.
*
* @see LongRange Java 用户使用 [LongRange] 的构造器创建
* @see Long.rangeTo Kotlin 用户使用 [Long.rangeTo] 创建, `3000..50000`
*/ */
val autoSaveIntervalMillis: LongRange public val autoSaveIntervalMillis: LongRange
get() = 30.secondsToMillis..10.minutesToSeconds get() = 30.secondsToMillis..10.minutesToSeconds
/** /**
@ -75,7 +83,7 @@ interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
* *
* @see getSetting * @see getSetting
*/ */
open class AutoSaveSetting(private val owner: AutoSaveSettingHolder, private val storage: SettingStorage) : public open class AutoSaveSetting(private val owner: AutoSaveSettingHolder, private val storage: SettingStorage) :
AbstractSetting() { AbstractSetting() {
@Volatile @Volatile
internal var lastAutoSaveJob: Job? = null internal var lastAutoSaveJob: Job? = null
@ -111,7 +119,26 @@ interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
} }
object MemorySettingStorage : SettingStorage { // TODO: 2020/7/11 document
public interface MemorySettingStorage : SettingStorage {
public companion object INSTANCE : MemorySettingStorage by MemorySettingStorageImpl
}
// TODO: 2020/7/11 document
public interface MultiFileSettingStorage : SettingStorage {
public val directory: File
public companion object {
@JvmStatic
@JvmName("create")
public operator fun invoke(directory: File): MultiFileSettingStorage = MultiFileSettingStorageImpl(directory)
}
}
// internal
internal object MemorySettingStorageImpl : SettingStorage, MemorySettingStorage {
private val list = mutableMapOf<Class<out Setting>, Setting>() private val list = mutableMapOf<Class<out Setting>, Setting>()
internal class MemorySettingImpl : AbstractSetting() { internal class MemorySettingImpl : AbstractSetting() {
@ -146,9 +173,9 @@ object MemorySettingStorage : SettingStorage {
} }
} }
class MultiFileSettingStorage( internal class MultiFileSettingStorageImpl(
private val directory: File override val directory: File
) : SettingStorage { ) : SettingStorage, MultiFileSettingStorage {
override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T = with(settingClass.kotlin) { override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T = with(settingClass.kotlin) {
val file = settingFile(holder, settingClass::class) val file = settingFile(holder, settingClass::class)
@ -161,7 +188,7 @@ class MultiFileSettingStorage(
) )
} }
if (holder is AutoSaveSettingHolder) { if (holder is AutoSaveSettingHolder) {
AutoSaveSetting(holder, this@MultiFileSettingStorage) as T? AutoSaveSetting(holder, this@MultiFileSettingStorageImpl) as T?
} else null } else null
} ?: throw IllegalArgumentException( } ?: throw IllegalArgumentException(
"Cannot create Setting instance. Make sure 'holder' is a AutoSaveSettingHolder, " + "Cannot create Setting instance. Make sure 'holder' is a AutoSaveSettingHolder, " +
@ -189,7 +216,7 @@ class MultiFileSettingStorage(
} }
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
override fun store(holder: SettingHolder, setting: Setting) = with(setting::class) { override fun store(holder: SettingHolder, setting: Setting): Unit = with(setting::class) {
val file = settingFile(holder, this) val file = settingFile(holder, this)
if (file.exists() && file.isFile && file.canRead()) { if (file.exists() && file.isFile && file.canRead()) {

View File

@ -29,14 +29,14 @@ import kotlin.reflect.KProperty
* @see PrimitiveValue * @see PrimitiveValue
* @see CompositeValue * @see CompositeValue
*/ */
interface Value<T> { public interface Value<T> {
var value: T public var value: T
} }
/** /**
* Typically returned by [Setting.value] functions. * Typically returned by [Setting.value] functions.
*/ */
class SerializableValue<T>( public class SerializableValue<T>(
private val delegate: Value<T>, private val delegate: Value<T>,
/** /**
* The serializer used to update and dump [delegate] * The serializer used to update and dump [delegate]
@ -46,7 +46,7 @@ class SerializableValue<T>(
override fun toString(): String = delegate.toString() override fun toString(): String = delegate.toString()
} }
fun <T> Value<T>.serializableValueWith( public fun <T> Value<T>.serializableValueWith(
serializer: KSerializer<T> serializer: KSerializer<T>
): SerializableValue<T> { ): SerializableValue<T> {
return SerializableValue( return SerializableValue(
@ -58,27 +58,30 @@ fun <T> Value<T>.serializableValueWith(
/** /**
* @see SerializableValue * @see SerializableValue
*/ */
interface SerializerAwareValue<T> : Value<T> { public interface SerializerAwareValue<T> : Value<T> {
val serializer: KSerializer<Unit> public val serializer: KSerializer<Unit>
} }
fun <T> SerializerAwareValue<T>.serialize(format: StringFormat): String { public fun <T> SerializerAwareValue<T>.serialize(format: StringFormat): String {
return format.stringify(this.serializer, Unit) return format.stringify(this.serializer, Unit)
} }
fun <T> SerializerAwareValue<T>.serialize(format: BinaryFormat): ByteArray { public fun <T> SerializerAwareValue<T>.serialize(format: BinaryFormat): ByteArray {
return format.dump(this.serializer, Unit) return format.dump(this.serializer, Unit)
} }
inline operator fun <T> Value<T>.getValue(mySetting: Any?, property: KProperty<*>): T = value @JvmSynthetic
inline operator fun <T> Value<T>.setValue(mySetting: Any?, property: KProperty<*>, value: T) { public inline operator fun <T> Value<T>.getValue(mySetting: Any?, property: KProperty<*>): T = value
@JvmSynthetic
public inline operator fun <T> Value<T>.setValue(mySetting: Any?, property: KProperty<*>, value: T) {
this.value = value this.value = value
} }
/** /**
* The serializer for a specific kind of [Value]. * The serializer for a specific kind of [Value].
*/ */
typealias ValueSerializer<T> = KSerializer<Value<T>> public typealias ValueSerializer<T> = KSerializer<Value<T>>
/** /**
* Represents a observable *primitive* value wrapping. * Represents a observable *primitive* value wrapping.
@ -92,7 +95,7 @@ typealias ValueSerializer<T> = KSerializer<Value<T>>
* Note: The values are actually *boxed* because of the generic type T. * Note: The values are actually *boxed* because of the generic type T.
* *Primitive* indicates only it is one of the 9 types mentioned above. * *Primitive* indicates only it is one of the 9 types mentioned above.
*/ */
interface PrimitiveValue<T> : Value<T> public interface PrimitiveValue<T> : Value<T>
//// region PrimitiveValues CODEGEN //// //// region PrimitiveValues CODEGEN ////
@ -100,77 +103,77 @@ interface PrimitiveValue<T> : Value<T>
/** /**
* Represents a non-null [Byte] value. * Represents a non-null [Byte] value.
*/ */
interface ByteValue : PrimitiveValue<Byte> public interface ByteValue : PrimitiveValue<Byte>
/** /**
* Represents a non-null [Short] value. * Represents a non-null [Short] value.
*/ */
interface ShortValue : PrimitiveValue<Short> public interface ShortValue : PrimitiveValue<Short>
/** /**
* Represents a non-null [Int] value. * Represents a non-null [Int] value.
*/ */
interface IntValue : PrimitiveValue<Int> public interface IntValue : PrimitiveValue<Int>
/** /**
* Represents a non-null [Long] value. * Represents a non-null [Long] value.
*/ */
interface LongValue : PrimitiveValue<Long> public interface LongValue : PrimitiveValue<Long>
/** /**
* Represents a non-null [Float] value. * Represents a non-null [Float] value.
*/ */
interface FloatValue : PrimitiveValue<Float> public interface FloatValue : PrimitiveValue<Float>
/** /**
* Represents a non-null [Double] value. * Represents a non-null [Double] value.
*/ */
interface DoubleValue : PrimitiveValue<Double> public interface DoubleValue : PrimitiveValue<Double>
/** /**
* Represents a non-null [Char] value. * Represents a non-null [Char] value.
*/ */
interface CharValue : PrimitiveValue<Char> public interface CharValue : PrimitiveValue<Char>
/** /**
* Represents a non-null [Boolean] value. * Represents a non-null [Boolean] value.
*/ */
interface BooleanValue : PrimitiveValue<Boolean> public interface BooleanValue : PrimitiveValue<Boolean>
/** /**
* Represents a non-null [String] value. * Represents a non-null [String] value.
*/ */
interface StringValue : PrimitiveValue<String> public interface StringValue : PrimitiveValue<String>
//// endregion PrimitiveValues CODEGEN //// //// endregion PrimitiveValues CODEGEN ////
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
interface CompositeValue<T> : Value<T> public interface CompositeValue<T> : Value<T>
/** /**
* Superclass of [CompositeListValue], [PrimitiveListValue]. * Superclass of [CompositeListValue], [PrimitiveListValue].
*/ */
interface ListValue<E> : CompositeValue<List<E>> public interface ListValue<E> : CompositeValue<List<E>>
/** /**
* Elements can by anything, wrapped as [Value]. * Elements can by anything, wrapped as [Value].
* @param E is not primitive types. * @param E is not primitive types.
*/ */
interface CompositeListValue<E> : ListValue<E> public interface CompositeListValue<E> : ListValue<E>
/** /**
* Elements can only be primitives, not wrapped. * Elements can only be primitives, not wrapped.
* @param E is not primitive types. * @param E is not primitive types.
*/ */
interface PrimitiveListValue<E> : ListValue<E> public interface PrimitiveListValue<E> : ListValue<E>
//// region PrimitiveListValue CODEGEN //// //// region PrimitiveListValue CODEGEN ////
interface PrimitiveIntListValue : PrimitiveListValue<Int> public interface PrimitiveIntListValue : PrimitiveListValue<Int>
interface PrimitiveLongListValue : PrimitiveListValue<Long> public interface PrimitiveLongListValue : PrimitiveListValue<Long>
// TODO + codegen // TODO + codegen
//// endregion PrimitiveListValue CODEGEN //// //// endregion PrimitiveListValue CODEGEN ////
@ -179,25 +182,25 @@ interface PrimitiveLongListValue : PrimitiveListValue<Long>
/** /**
* Superclass of [CompositeSetValue], [PrimitiveSetValue]. * Superclass of [CompositeSetValue], [PrimitiveSetValue].
*/ */
interface SetValue<E> : CompositeValue<Set<E>> public interface SetValue<E> : CompositeValue<Set<E>>
/** /**
* Elements can by anything, wrapped as [Value]. * Elements can by anything, wrapped as [Value].
* @param E is not primitive types. * @param E is not primitive types.
*/ */
interface CompositeSetValue<E> : SetValue<E> public interface CompositeSetValue<E> : SetValue<E>
/** /**
* Elements can only be primitives, not wrapped. * Elements can only be primitives, not wrapped.
* @param E is not primitive types. * @param E is not primitive types.
*/ */
interface PrimitiveSetValue<E> : SetValue<E> public interface PrimitiveSetValue<E> : SetValue<E>
//// region PrimitiveSetValue CODEGEN //// //// region PrimitiveSetValue CODEGEN ////
interface PrimitiveIntSetValue : PrimitiveSetValue<Int> public interface PrimitiveIntSetValue : PrimitiveSetValue<Int>
interface PrimitiveLongSetValue : PrimitiveSetValue<Long> public interface PrimitiveLongSetValue : PrimitiveSetValue<Long>
// TODO + codegen // TODO + codegen
//// endregion PrimitiveSetValue CODEGEN //// //// endregion PrimitiveSetValue CODEGEN ////
@ -206,17 +209,17 @@ interface PrimitiveLongSetValue : PrimitiveSetValue<Long>
/** /**
* Superclass of [CompositeMapValue], [PrimitiveMapValue]. * Superclass of [CompositeMapValue], [PrimitiveMapValue].
*/ */
interface MapValue<K, V> : CompositeValue<Map<K, V>> public interface MapValue<K, V> : CompositeValue<Map<K, V>>
interface CompositeMapValue<K, V> : MapValue<K, V> public interface CompositeMapValue<K, V> : MapValue<K, V>
interface PrimitiveMapValue<K, V> : MapValue<K, V> public interface PrimitiveMapValue<K, V> : MapValue<K, V>
//// region PrimitiveMapValue CODEGEN //// //// region PrimitiveMapValue CODEGEN ////
interface PrimitiveIntIntMapValue : PrimitiveMapValue<Int, Int> public interface PrimitiveIntIntMapValue : PrimitiveMapValue<Int, Int>
interface PrimitiveIntLongMapValue : PrimitiveMapValue<Int, Long> public interface PrimitiveIntLongMapValue : PrimitiveMapValue<Int, Long>
// TODO + codegen // TODO + codegen
//// endregion PrimitiveSetValue CODEGEN //// //// endregion PrimitiveSetValue CODEGEN ////

View File

@ -98,7 +98,6 @@ internal abstract class ShortValueImpl : ShortValue, SerializerAwareValue<Short>
else value.hashCode() * 31 else value.hashCode() * 31
} }
} }
internal abstract class IntValueImpl : IntValue, SerializerAwareValue<Int>, KSerializer<Unit>, AbstractValueImpl<Int> { internal abstract class IntValueImpl : IntValue, SerializerAwareValue<Int>, KSerializer<Unit>, AbstractValueImpl<Int> {
constructor() constructor()
constructor(default: Int) { constructor(default: Int) {

View File

@ -19,31 +19,31 @@ internal object BuiltInSerializerConstants {
//// region BuiltInSerializerConstantsPrimitives CODEGEN //// //// region BuiltInSerializerConstantsPrimitives CODEGEN ////
@JvmStatic @JvmStatic
val ByteSerializerDescriptor = Byte.serializer().descriptor internal val ByteSerializerDescriptor = Byte.serializer().descriptor
@JvmStatic @JvmStatic
val ShortSerializerDescriptor = Short.serializer().descriptor internal val ShortSerializerDescriptor = Short.serializer().descriptor
@JvmStatic @JvmStatic
val IntSerializerDescriptor = Int.serializer().descriptor internal val IntSerializerDescriptor = Int.serializer().descriptor
@JvmStatic @JvmStatic
val LongSerializerDescriptor = Long.serializer().descriptor internal val LongSerializerDescriptor = Long.serializer().descriptor
@JvmStatic @JvmStatic
val FloatSerializerDescriptor = Float.serializer().descriptor internal val FloatSerializerDescriptor = Float.serializer().descriptor
@JvmStatic @JvmStatic
val DoubleSerializerDescriptor = Double.serializer().descriptor internal val DoubleSerializerDescriptor = Double.serializer().descriptor
@JvmStatic @JvmStatic
val CharSerializerDescriptor = Char.serializer().descriptor internal val CharSerializerDescriptor = Char.serializer().descriptor
@JvmStatic @JvmStatic
val BooleanSerializerDescriptor = Boolean.serializer().descriptor internal val BooleanSerializerDescriptor = Boolean.serializer().descriptor
@JvmStatic @JvmStatic
val StringSerializerDescriptor = String.serializer().descriptor internal val StringSerializerDescriptor = String.serializer().descriptor
//// endregion BuiltInSerializerConstantsPrimitives CODEGEN //// //// endregion BuiltInSerializerConstantsPrimitives CODEGEN ////
} }
@ -76,103 +76,86 @@ internal fun Setting.valueImpl(default: Byte): SerializerAwareValue<Byte> {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.byteValueImpl(): SerializerAwareValue<Byte> { internal fun Setting.byteValueImpl(): SerializerAwareValue<Byte> {
return object : ByteValueImpl() { return object : ByteValueImpl() {
override fun onChanged() = this@byteValueImpl.onValueChanged(this) override fun onChanged() = this@byteValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Short): SerializerAwareValue<Short> { internal fun Setting.valueImpl(default: Short): SerializerAwareValue<Short> {
return object : ShortValueImpl(default) { return object : ShortValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.shortValueImpl(): SerializerAwareValue<Short> { internal fun Setting.shortValueImpl(): SerializerAwareValue<Short> {
return object : ShortValueImpl() { return object : ShortValueImpl() {
override fun onChanged() = this@shortValueImpl.onValueChanged(this) override fun onChanged() = this@shortValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Int): SerializerAwareValue<Int> { internal fun Setting.valueImpl(default: Int): SerializerAwareValue<Int> {
return object : IntValueImpl(default) { return object : IntValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.intValueImpl(): SerializerAwareValue<Int> { internal fun Setting.intValueImpl(): SerializerAwareValue<Int> {
return object : IntValueImpl() { return object : IntValueImpl() {
override fun onChanged() = this@intValueImpl.onValueChanged(this) override fun onChanged() = this@intValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Long): SerializerAwareValue<Long> { internal fun Setting.valueImpl(default: Long): SerializerAwareValue<Long> {
return object : LongValueImpl(default) { return object : LongValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.longValueImpl(): SerializerAwareValue<Long> { internal fun Setting.longValueImpl(): SerializerAwareValue<Long> {
return object : LongValueImpl() { return object : LongValueImpl() {
override fun onChanged() = this@longValueImpl.onValueChanged(this) override fun onChanged() = this@longValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Float): SerializerAwareValue<Float> { internal fun Setting.valueImpl(default: Float): SerializerAwareValue<Float> {
return object : FloatValueImpl(default) { return object : FloatValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.floatValueImpl(): SerializerAwareValue<Float> { internal fun Setting.floatValueImpl(): SerializerAwareValue<Float> {
return object : FloatValueImpl() { return object : FloatValueImpl() {
override fun onChanged() = this@floatValueImpl.onValueChanged(this) override fun onChanged() = this@floatValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Double): SerializerAwareValue<Double> { internal fun Setting.valueImpl(default: Double): SerializerAwareValue<Double> {
return object : DoubleValueImpl(default) { return object : DoubleValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.doubleValueImpl(): SerializerAwareValue<Double> { internal fun Setting.doubleValueImpl(): SerializerAwareValue<Double> {
return object : DoubleValueImpl() { return object : DoubleValueImpl() {
override fun onChanged() = this@doubleValueImpl.onValueChanged(this) override fun onChanged() = this@doubleValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Char): SerializerAwareValue<Char> { internal fun Setting.valueImpl(default: Char): SerializerAwareValue<Char> {
return object : CharValueImpl(default) { return object : CharValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.charValueImpl(): SerializerAwareValue<Char> { internal fun Setting.charValueImpl(): SerializerAwareValue<Char> {
return object : CharValueImpl() { return object : CharValueImpl() {
override fun onChanged() = this@charValueImpl.onValueChanged(this) override fun onChanged() = this@charValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: Boolean): SerializerAwareValue<Boolean> { internal fun Setting.valueImpl(default: Boolean): SerializerAwareValue<Boolean> {
return object : BooleanValueImpl(default) { return object : BooleanValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.booleanValueImpl(): SerializerAwareValue<Boolean> { internal fun Setting.booleanValueImpl(): SerializerAwareValue<Boolean> {
return object : BooleanValueImpl() { return object : BooleanValueImpl() {
override fun onChanged() = this@booleanValueImpl.onValueChanged(this) override fun onChanged() = this@booleanValueImpl.onValueChanged(this)
} }
} }
internal fun Setting.valueImpl(default: String): SerializerAwareValue<String> { internal fun Setting.valueImpl(default: String): SerializerAwareValue<String> {
return object : StringValueImpl(default) { return object : StringValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this) override fun onChanged() = this@valueImpl.onValueChanged(this)
} }
} }
internal fun Setting.stringValueImpl(): SerializerAwareValue<String> { internal fun Setting.stringValueImpl(): SerializerAwareValue<String> {
return object : StringValueImpl() { return object : StringValueImpl() {
override fun onChanged() = this@stringValueImpl.onValueChanged(this) override fun onChanged() = this@stringValueImpl.onValueChanged(this)

View File

@ -17,7 +17,7 @@ import net.mamoe.mirai.contact.User
/** /**
* 判断此用户是否为 console 管理员 * 判断此用户是否为 console 管理员
*/ */
val User.isManager: Boolean public val User.isManager: Boolean
get() = this.bot.managers.contains(this.id) get() = this.bot.managers.contains(this.id)
internal fun Bot.addManager(long: Long): Boolean { internal fun Bot.addManager(long: Long): Boolean {
@ -25,9 +25,9 @@ internal fun Bot.addManager(long: Long): Boolean {
return true return true
} }
fun Bot.removeManager(long: Long) { public fun Bot.removeManager(long: Long) {
TODO() TODO()
} }
val Bot.managers: List<Long> public val Bot.managers: List<Long>
get() = TODO() get() = TODO()

View File

@ -7,47 +7,45 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("INAPPLICABLE_JVM_NAME", "unused")
package net.mamoe.mirai.console.utils package net.mamoe.mirai.console.utils
import kotlinx.coroutines.* import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import java.util.concurrent.Executors
@Suppress("unused") /**
object ConsoleInput { * Console 输入. 由于 console 接管了 stdin, [readLine] 等操作需要在这里进行.
private val inputDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher() */
public interface ConsoleInput {
/**
* [提示][hint] 向用户索要一个输入
*/
@JvmSynthetic
public suspend fun requestInput(hint: String): String
/** /**
* 向用户索要一个Input * [提示][hint] 向用户索要一个输入. 仅供 Java 调用者使用
* 你需要提供一个hint提示并等待获取一个结果
* 具体索要方式将根据frontend不同而不同
* 如弹出框或一行字
*/ */
suspend fun requestInput( @JvmName("requestInput")
hint: String @JavaFriendlyAPI
): String { public fun requestInputBlocking(hint: String): String
return withContext(inputDispatcher) {
MiraiConsole.frontEnd.requestInput(hint) public companion object INSTANCE : ConsoleInput by ConsoleInputImpl {
} public suspend inline fun MiraiConsole.requestInput(hint: String): String = ConsoleInput.requestInput(hint)
} }
fun requestInputBlocking(hint: String): String = runBlocking { requestInput(hint) }
/**
* asnyc获取
*/
fun requestInputAsync(
scope: CoroutineScope,
hint: String
): Deferred<String> {
return scope.async {
requestInput(hint)
}
}
suspend fun MiraiConsole.requestInput(hint: String): String = requestInput(hint)
} }
@Suppress("unused")
internal object ConsoleInputImpl : ConsoleInput {
private val inputLock = Mutex()
override suspend fun requestInput(
hint: String
): String = inputLock.withLock { MiraiConsole.frontEnd.requestInput(hint) }
@JavaFriendlyAPI
override fun requestInputBlocking(hint: String): String = runBlocking { requestInput(hint) }
}

View File

@ -19,7 +19,7 @@ internal annotation class JavaFriendlyAPI
@RequiresOptIn(level = RequiresOptIn.Level.ERROR) @RequiresOptIn(level = RequiresOptIn.Level.ERROR)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR, CLASS, FUNCTION, PROPERTY) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR, CLASS, FUNCTION, PROPERTY)
@MustBeDocumented @MustBeDocumented
annotation class ConsoleInternalAPI( public annotation class ConsoleInternalAPI(
val message: String = "" val message: String = ""
) )
@ -33,6 +33,6 @@ annotation class ConsoleInternalAPI(
@RequiresOptIn(level = RequiresOptIn.Level.WARNING) @RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
@MustBeDocumented @MustBeDocumented
annotation class ConsoleExperimentalAPI( public annotation class ConsoleExperimentalAPI(
val message: String = "" val message: String = ""
) )

View File

@ -25,8 +25,8 @@ import kotlin.coroutines.CoroutineContext
* *
* @see JavaPlugin.scheduler 获取实例 * @see JavaPlugin.scheduler 获取实例
*/ */
class JavaPluginScheduler internal constructor(parentCoroutineContext: CoroutineContext) : CoroutineScope { public class JavaPluginScheduler internal constructor(parentCoroutineContext: CoroutineContext) : CoroutineScope {
override val coroutineContext: CoroutineContext = public override val coroutineContext: CoroutineContext =
parentCoroutineContext + SupervisorJob(parentCoroutineContext[Job]) parentCoroutineContext + SupervisorJob(parentCoroutineContext[Job])
/** /**
@ -36,7 +36,7 @@ class JavaPluginScheduler internal constructor(parentCoroutineContext: Coroutine
* *
* @see Future.cancel 取消这个任务 * @see Future.cancel 取消这个任务
*/ */
fun repeating(intervalMs: Long, runnable: Runnable): Future<Void?> { public fun repeating(intervalMs: Long, runnable: Runnable): Future<Void?> {
return this.future { return this.future {
while (isActive) { while (isActive) {
withContext(Dispatchers.IO) { runnable.run() } withContext(Dispatchers.IO) { runnable.run() }
@ -51,7 +51,7 @@ class JavaPluginScheduler internal constructor(parentCoroutineContext: Coroutine
* *
* 在延迟 [delayMillis] 后执行 [runnable] * 在延迟 [delayMillis] 后执行 [runnable]
*/ */
fun delayed(delayMillis: Long, runnable: Runnable): CompletableFuture<Void?> { public fun delayed(delayMillis: Long, runnable: Runnable): CompletableFuture<Void?> {
return future { return future {
delay(delayMillis) delay(delayMillis)
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -66,7 +66,7 @@ class JavaPluginScheduler internal constructor(parentCoroutineContext: Coroutine
* *
* 在延迟 [delayMillis] 后执行 [runnable] * 在延迟 [delayMillis] 后执行 [runnable]
*/ */
fun <R> delayed(delayMillis: Long, runnable: Callable<R>): CompletableFuture<Void?> { public fun <R> delayed(delayMillis: Long, runnable: Callable<R>): CompletableFuture<Void?> {
return future { return future {
delay(delayMillis) delay(delayMillis)
withContext(Dispatchers.IO) { runnable.call() } withContext(Dispatchers.IO) { runnable.call() }
@ -77,7 +77,7 @@ class JavaPluginScheduler internal constructor(parentCoroutineContext: Coroutine
/** /**
* 异步执行一个任务, 最终返回 [Future], Java 使用方法无异, 但效率更高且可以在插件关闭时停止 * 异步执行一个任务, 最终返回 [Future], Java 使用方法无异, 但效率更高且可以在插件关闭时停止
*/ */
fun <R> async(supplier: Callable<R>): Future<R> { public fun <R> async(supplier: Callable<R>): Future<R> {
return future { return future {
withContext(Dispatchers.IO) { supplier.call() } withContext(Dispatchers.IO) { supplier.call() }
} }
@ -86,7 +86,7 @@ class JavaPluginScheduler internal constructor(parentCoroutineContext: Coroutine
/** /**
* 异步执行一个任务, 没有返回 * 异步执行一个任务, 没有返回
*/ */
fun async(runnable: Runnable): Future<Void?> { public fun async(runnable: Runnable): Future<Void?> {
return future { return future {
withContext(Dispatchers.IO) { runnable.run() } withContext(Dispatchers.IO) { runnable.run() }
null null

View File

@ -10,26 +10,26 @@ import kotlin.reflect.KClass
/** /**
* 资源容器. * 资源容器.
*/ */
interface ResourceContainer { public interface ResourceContainer {
/** /**
* 获取一个资源文件 * 获取一个资源文件
*/ */
fun getResourceAsStream(name: String): InputStream public fun getResourceAsStream(name: String): InputStream
/** /**
* 读取一个资源文件并以 [Charsets.UTF_8] 编码为 [String] * 读取一个资源文件并以 [Charsets.UTF_8] 编码为 [String]
*/ */
@JvmDefault @JvmDefault
fun getResource(name: String): String = getResource(name, Charsets.UTF_8) public fun getResource(name: String): String = getResource(name, Charsets.UTF_8)
/** /**
* 读取一个资源文件并以 [charset] 编码为 [String] * 读取一个资源文件并以 [charset] 编码为 [String]
*/ */
@JvmDefault @JvmDefault
fun getResource(name: String, charset: Charset): String = public fun getResource(name: String, charset: Charset): String =
this.getResourceAsStream(name).use { it.readBytes() }.encodeToString() this.getResourceAsStream(name).use { it.readBytes() }.encodeToString()
companion object { public companion object {
/** /**
* 使用 [Class.getResourceAsStream] 读取资源文件 * 使用 [Class.getResourceAsStream] 读取资源文件
* *
@ -37,22 +37,23 @@ interface ResourceContainer {
*/ */
@JvmStatic @JvmStatic
@JavaFriendlyAPI @JavaFriendlyAPI
fun byClass(clazz: Class<*>): ResourceContainer = clazz.asResourceContainer() public fun byClass(clazz: Class<*>): ResourceContainer = clazz.asResourceContainer()
} }
} }
/**
* 使用 [Class.getResourceAsStream] 读取资源文件
*/
public fun KClass<*>.asResourceContainer(): ResourceContainer = ClassAsResourceContainer(this.java)
/**
* 使用 [Class.getResourceAsStream] 读取资源文件
*/
public fun Class<*>.asResourceContainer(): ResourceContainer = ClassAsResourceContainer(this)
internal class ClassAsResourceContainer( internal class ClassAsResourceContainer(
private val clazz: Class<*> private val clazz: Class<*>
) : ResourceContainer { ) : ResourceContainer {
override fun getResourceAsStream(name: String): InputStream = clazz.getResourceAsStream(name) override fun getResourceAsStream(name: String): InputStream = clazz.getResourceAsStream(name)
} }
/**
* 使用 [Class.getResourceAsStream] 读取资源文件
*/
fun KClass<*>.asResourceContainer(): ResourceContainer = ClassAsResourceContainer(this.java)
/**
* 使用 [Class.getResourceAsStream] 读取资源文件
*/
fun Class<*>.asResourceContainer(): ResourceContainer = ClassAsResourceContainer(this)

View File

@ -14,34 +14,25 @@ package net.mamoe.mirai.console.utils
import org.jetbrains.annotations.Range import org.jetbrains.annotations.Range
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.internal.InlineOnly
/** /**
* 执行 [n] [block], 在第一次成功时返回执行结果, 在捕获到异常时返回异常. * 执行 [n] [block], 在第一次成功时返回执行结果, 在捕获到异常时返回异常.
*/ */
@kotlin.internal.InlineOnly @InlineOnly
inline fun <R> retryCatching(n: @Range(from = 1, to = Long.MAX_VALUE) Int, block: () -> R): Result<R> { public inline fun <R> retryCatching(n: @Range(from = 1, to = Int.MAX_VALUE.toLong()) Int, block: () -> R): Result<R> {
contract { contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE) callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
} }
require(n >= 0) { "param n for retryCatching must not be negative" } require(n >= 1) { "param n for retryCatching must not be negative" }
var exception: Throwable? = null var exception: Throwable? = null
repeat(n) { repeat(n) {
try { try {
return Result.success(block()) return Result.success(block())
} catch (e: Throwable) { } catch (e: Throwable) {
exception?.addSuppressedMirai(e) exception?.addSuppressed(e)
exception = e exception = e
} }
} }
return Result.failure(exception!!) return Result.failure(exception!!)
}
@PublishedApi
internal fun Throwable.addSuppressedMirai(e: Throwable) {
if (e === this) {
return
}
kotlin.runCatching {
this.addSuppressed(e)
}
} }

View File

@ -14,7 +14,7 @@ object Versions {
const val consoleTerminal = "0.1.0" const val consoleTerminal = "0.1.0"
const val consolePure = "0.1-dev-1" const val consolePure = "0.1-dev-1"
const val kotlin = "1.3.72" const val kotlin = "1.4-M3"
const val coroutines = "1.3.7" const val coroutines = "1.3.7"
const val serialization = "0.20.0" const val serialization = "0.20.0"
const val ktor = "1.3.2" const val ktor = "1.3.2"