Merge remote-tracking branch 'origin/reborn' into reborn

# Conflicts:
#	backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/internal/PluginsLoader.kt
This commit is contained in:
jiahua.liu 2020-06-22 14:38:02 +08:00
commit 26ebd03300
40 changed files with 1765 additions and 471 deletions

View File

@ -1,8 +1,6 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("kotlin")
kotlin("plugin.serialization")
kotlin("jvm") version "1.4-M2"
kotlin("plugin.serialization") version "1.4-M2"
id("java")
}
@ -10,8 +8,10 @@ kotlin {
sourceSets {
all {
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.progressiveMode = true
languageSettings.languageVersion = "1.4"
languageSettings.apiVersion = "1.4"
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
@ -21,21 +21,5 @@ kotlin {
}
dependencies {
api(kotlin("stdlib"))
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
api(kotlin("stdlib-jdk8"))
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("FunctionName", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.console.codegen
import org.intellij.lang.annotations.Language
abstract class Replacer(private val name: String) : (String) -> String {
override fun toString(): String {
return name
}
}
fun Codegen.Replacer(block: (String) -> String): Replacer {
return object : Replacer(this@Replacer::class.simpleName ?: "<unnamed>") {
override fun invoke(p1: String): String = block(p1)
}
}
class CodegenScope : MutableList<Replacer> by mutableListOf() {
fun applyTo(fileContent: String): String {
return this.fold(fileContent) { acc, replacer -> replacer(acc) }
}
@CodegenDsl
operator fun Codegen.invoke(vararg ktTypes: KtType) {
if (ktTypes.isEmpty() && this is DefaultInvoke) {
invoke(defaultInvokeArgs)
}
invoke(ktTypes.toList())
}
@CodegenDsl
operator fun Codegen.invoke(ktTypes: Collection<KtType>) {
add(Replacer {
it + buildString {
ktTypes.forEach { applyTo(this, it) }
}
})
}
@RegionCodegenDsl
operator fun RegionCodegen.invoke(vararg ktTypes: KtType) = invoke(ktTypes.toList())
@RegionCodegenDsl
operator fun RegionCodegen.invoke(ktTypes: Collection<KtType>) {
add(Replacer {
it.replace(Regex("""//// region $regionName CODEGEN ////([\s\S]*?)//// endregion $regionName CODEGEN ////""")) {
val code = CodegenScope().apply { (this@invoke as Codegen).invoke(*ktTypes.toTypedArray()) }.applyTo("")
"""
|//// region $regionName CODEGEN ////
|
|$code
|
|//// endregion $regionName CODEGEN ////
""".trimMargin()
}
})
}
@DslMarker
annotation class CodegenDsl
}
@DslMarker
annotation class RegionCodegenDsl
interface DefaultInvoke {
val defaultInvokeArgs: List<KtType>
}
abstract class Codegen {
fun applyTo(stringBuilder: StringBuilder, ktType: KtType) = this.run { stringBuilder.apply(ktType) }
protected abstract fun StringBuilder.apply(ktType: KtType)
}
abstract class RegionCodegen(regionName: String? = null) : Codegen() {
val regionName: String by lazy {
regionName ?: this::class.simpleName!!.substringBefore("Codegen")
}
}
abstract class PrimitiveCodegen : Codegen() {
protected abstract fun StringBuilder.apply(ktType: KtPrimitive)
fun StringBuilder.apply(ktType: List<KtPrimitive>) = ktType.forEach { apply(it) }
}
fun StringBuilder.appendKCode(@Language("kt") ktCode: String): StringBuilder = append(kCode(ktCode)).appendLine()

View File

@ -46,35 +46,37 @@ fun JClazz.getTemplate():String = """
fun main(){
println(buildString {
appendln(COPYRIGHT)
appendln()
appendln(FILE_SUPPRESS)
appendln()
appendln("/**\n" +
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
" */\n" +
"\"\"\"")
appendln()
appendln()
appendLine(COPYRIGHT)
appendLine()
appendLine(FILE_SUPPRESS)
appendLine()
appendLine(
"/**\n" +
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
" */\n" +
"\"\"\""
)
appendLine()
appendLine()
//do simplest
(J_EXTRA + J_NUMBERS).forEach {
appendln(it.getTemplate())
appendLine(it.getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {
appendln(JListClazz(it).getTemplate())
appendLine(JListClazz(it).getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {
appendln(JArrayClazz(it).getTemplate())
appendLine(JArrayClazz(it).getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {key ->
(J_EXTRA + J_NUMBERS).forEach { value ->
appendln(JMapClazz(key, value).getTemplate())
appendLine(JMapClazz(key, value).getTemplate())
}
}
})

View File

@ -19,19 +19,19 @@ fun main() {
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt").apply {
createNewFile()
}.writeText(buildString {
appendln(COPYRIGHT)
appendln()
appendln(FILE_SUPPRESS)
appendln()
appendln(PACKAGE)
appendln()
appendln(IMPORTS)
appendln()
appendln()
appendln(DO_NOT_MODIFY)
appendln()
appendln()
appendln(genAllValueUseSite())
appendLine(COPYRIGHT)
appendLine()
appendLine(FILE_SUPPRESS)
appendLine()
appendLine(PACKAGE)
appendLine()
appendLine(IMPORTS)
appendLine()
appendLine()
appendLine(DO_NOT_MODIFY)
appendLine()
appendLine()
appendLine(genAllValueUseSite())
})
}
@ -56,7 +56,7 @@ import kotlin.internal.LowPriorityInOverloadResolution
fun genAllValueUseSite(): String = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
// PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) {
@ -83,7 +83,7 @@ fun genAllValueUseSite(): String = buildString {
// MUTABLE LIST / MUTABLE SET
for (collectionName in listOf("List", "Set")) {
for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln()
appendLine()
appendln(
"""
@JvmName("valueMutable")
@ -94,7 +94,7 @@ fun genAllValueUseSite(): String = buildString {
}
// SPECIAL
appendln()
appendLine()
appendln(
"""
fun <T : Setting> Setting.value(default: T): Value<T> {

View File

@ -19,17 +19,17 @@ fun main() {
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt").apply {
createNewFile()
}.writeText(buildString {
appendln(COPYRIGHT)
appendln()
appendln(PACKAGE)
appendln()
appendln(IMPORTS)
appendln()
appendln()
appendln(DO_NOT_MODIFY)
appendln()
appendln()
appendln(genAllValueImpl())
appendLine(COPYRIGHT)
appendLine()
appendLine(PACKAGE)
appendLine()
appendLine(IMPORTS)
appendLine()
appendLine()
appendLine(DO_NOT_MODIFY)
appendLine()
appendLine()
appendLine(genAllValueImpl())
})
}
@ -52,19 +52,19 @@ import net.mamoe.mirai.console.setting.*
fun genAllValueImpl(): String = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
// PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln(genPrimitiveValueImpl(number, number, "$number.serializer()", false))
appendln()
appendLine()
}
// PRIMITIVE ARRAYS
for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) {
appendln(genPrimitiveValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true))
appendln()
appendLine()
}
// TYPED ARRAYS
@ -77,7 +77,7 @@ fun genAllValueImpl(): String = buildString {
true
)
)
appendln()
appendLine()
}
// PRIMITIVE LISTS / SETS
@ -92,11 +92,11 @@ fun genAllValueImpl(): String = buildString {
false
)
)
appendln()
appendLine()
}
}
appendln()
appendLine()
// MUTABLE LIST / MUTABLE SET
@ -141,11 +141,11 @@ fun genAllValueImpl(): String = buildString {
}
"""
)
appendln()
appendLine()
}
}
appendln()
appendLine()
appendln(

View File

@ -0,0 +1,41 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.codegen
object ValueKtCodegen {
object SettingCodegen {
object PrimitiveValuesCodegen : RegionCodegen(), DefaultInvoke {
override val defaultInvokeArgs: List<KtType>
get() = KtType.Primitives + KtString
override fun StringBuilder.apply(ktType: KtType) {
@Suppress("ClassName")
appendKCode(
"""
/**
* Represents a non-null [$ktType] value.
*/
interface ${ktType}Value : PrimitiveValue<$ktType>
"""
)
}
}
}
@JvmStatic
fun main(args: Array<String>) {
codegen("Value.kt") {
SettingCodegen.PrimitiveValuesCodegen()
}
}
}

View File

@ -56,11 +56,11 @@ internal val OTHER_PRIMITIVES = listOf(
fun genPublicApi() = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
appendln(COPYRIGHT.trim())
appendln()
appendLine()
appendln(
"""
package net.mamoe.mirai.console.setting
@ -70,7 +70,7 @@ fun genPublicApi() = buildString {
import kotlin.reflect.KProperty
"""
)
appendln()
appendLine()
appendln(
"""
/**
@ -80,7 +80,7 @@ fun genPublicApi() = buildString {
*/
"""
)
appendln()
appendLine()
appendln(
"""
@ -107,7 +107,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
// PRIMITIVES
@ -127,7 +127,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
for (number in OTHER_PRIMITIVES) {
val template = """
@ -137,7 +137,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
// ARRAYS
@ -154,7 +154,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>()
"""
)
appendln()
appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) {
appendln(
@ -164,10 +164,10 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
}
appendln()
appendLine()
// TYPED ARRAYS
@ -178,7 +178,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES)) {
appendln(
@ -188,7 +188,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
)
}
appendln()
appendLine()
// TYPED LISTS / SETS
for (collectionName in listOf("List", "Set")) {
@ -207,14 +207,14 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
// SETTING
appendln(
"""
abstract class Setting${collectionName}Value<T: Setting> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T>
"""
)
appendln()
appendLine()
}
// SETTING VALUE
@ -225,7 +225,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
"""
)
appendln()
appendLine()
// MUTABLE LIST / MUTABLE SET
for (collectionName in listOf("List", "Set")) {
@ -235,7 +235,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
"""
)
appendln()
appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES)) {
appendln(
@ -245,17 +245,17 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
)
}
appendln()
appendLine()
// SETTING
appendln(
"""
abstract class MutableSetting${collectionName}Value<T: Setting> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T>
"""
)
appendln()
appendLine()
}
appendln()
appendLine()
// DYNAMIC
appendln(

View File

@ -0,0 +1,122 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate", "unused")
package net.mamoe.mirai.console.codegen
import org.intellij.lang.annotations.Language
import java.io.File
typealias KtByte = KtType.KtPrimitive.KtByte
typealias KtShort = KtType.KtPrimitive.KtShort
typealias KtInt = KtType.KtPrimitive.KtInt
typealias KtLong = KtType.KtPrimitive.KtLong
typealias KtFloat = KtType.KtPrimitive.KtFloat
typealias KtDouble = KtType.KtPrimitive.KtDouble
typealias KtChar = KtType.KtPrimitive.KtChar
typealias KtBoolean = KtType.KtPrimitive.KtBoolean
typealias KtString = KtType.KtString
typealias KtCollection = KtType.KtCollection
typealias KtMap = KtType.KtMap
typealias KtPrimitive = KtType.KtPrimitive
sealed class KtType {
/**
* Its classname in standard library
*/
abstract val standardName: String
override fun toString(): String = standardName
/**
* Not Including [String]
*/
sealed class KtPrimitive(
override val standardName: String,
val jPrimitiveName: String = standardName.toLowerCase(),
val jObjectName: String = standardName
) : KtType() {
object KtByte : KtPrimitive("Byte")
object KtShort : KtPrimitive("Short")
object KtInt : KtPrimitive("Int", jObjectName = "Integer")
object KtLong : KtPrimitive("Long")
object KtFloat : KtPrimitive("Float")
object KtDouble : KtPrimitive("Double")
object KtChar : KtPrimitive("Char", jObjectName = "Character")
object KtBoolean : KtPrimitive("Boolean")
}
object KtString : KtType() {
override val standardName: String get() = "String"
}
/**
* [List], [Set]
*/
data class KtCollection(override val standardName: String) : KtType()
object KtMap : KtType() {
override val standardName: String get() = "Map"
}
companion object {
val PrimitiveIntegers = listOf(KtByte, KtShort, KtInt, KtLong)
val PrimitiveFloatings = listOf(KtFloat, KtDouble)
val PrimitiveNumbers = PrimitiveIntegers + PrimitiveFloatings
val PrimitiveNonNumbers = listOf(KtChar, KtBoolean)
val Primitives = PrimitiveNumbers + PrimitiveNonNumbers
}
}
operator fun KtType.plus(type: KtType): List<KtType> {
return listOf(this, type)
}
val KtType.lowerCaseName: String get() = this.standardName.toLowerCase()
inline fun kCode(@Language("kt") source: String) = source.trimIndent()
fun codegen(targetFile: String, block: CodegenScope.() -> Unit) {
//// region PrimitiveValue CODEGEN ////
//// region PrimitiveValue CODEGEN ////
targetFile.findFileSmart().also {
println("Codegen target: ${it.absolutePath}")
}.apply {
writeText(
CodegenScope().apply(block).also { list ->
list.forEach {
println("Applying replacement: $it")
}
}.applyTo(readText())
)
}
}
fun String.findFileSmart(): File = kotlin.run {
if (contains("/")) { // absolute
File(this)
} else {
val list = File(".").walk().filter { it.name == this }.toList()
if (list.isNotEmpty()) return list.single()
File(".").walk().filter { it.name.contains(this) }.single()
}
}.also {
require(it.exists()) { "file doesn't exist" }
}

View File

@ -1,17 +1,17 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import upload.Bintray
import java.text.SimpleDateFormat
import java.util.*
plugins {
id("kotlin")
kotlin("plugin.serialization")
kotlin("jvm") version Versions.kotlin
kotlin("plugin.serialization") version Versions.kotlin
id("java")
`maven-publish`
id("com.jfrog.bintray")
id("com.jfrog.bintray") version Versions.bintray
}
apply(plugin = "com.github.johnrengelman.shadow")
version = Versions.Mirai.console
version = Versions.console
description = "Console backend for mirai"
java {
@ -56,14 +56,14 @@ kotlin {
}
dependencies {
compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}")
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
compileAndRuntime(kotlin("stdlib"))
api("net.mamoe.yamlkt:yamlkt:0.3.1")
api("org.jetbrains:annotations:19.0.0")
api(kotlinx("coroutines-jdk8", Versions.Kotlin.coroutines))
api(kotlinx("coroutines-jdk8", Versions.coroutines))
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
testApi(kotlin("stdlib-jdk8"))
testApi(kotlin("test"))
testApi(kotlin("test-junit5"))
@ -76,6 +76,33 @@ tasks {
"test"(Test::class) {
useJUnitPlatform()
}
val compileKotlin by getting {}
val fillBuildConstants by registering {
doLast {
(compileKotlin as KotlinCompile).source.filter { it.name == "MiraiConsole.kt" }.single().let { file ->
file.writeText(file.readText()
.replace(Regex("""val buildDate: Date = Date\((.*)\) //(.*)""")) {
"""
val buildDate: Date = Date(${System.currentTimeMillis()}L) // ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").apply {
timeZone = TimeZone.getTimeZone("GMT+8")
}.format(Date())}
""".trimIndent()
}
.replace(Regex("""const val version: String = "(.*)"""")) {
"""
const val version: String = "${Versions.console}"
""".trimIndent()
}
)
}
}
}
"compileKotlin" {
dependsOn(fillBuildConstants)
}
}
// region PUBLISHING

View File

@ -105,10 +105,10 @@ public final class JCommandManager {
* 解析并执行一个指令
*
* @param args 接受 {@link String} {@link Message} , 其他对象将会被 {@link Object#toString()}
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
* @see CommandExecuteResult
* @see #executeCommandAsync(CoroutineScope, CommandSender, Object...)
*/
public static boolean executeCommand(final @NotNull CommandSender sender, final @NotNull Object... args) throws InterruptedException {
public static CommandExecuteResult executeCommand(final @NotNull CommandSender sender, final @NotNull Object... args) throws InterruptedException {
Objects.requireNonNull(sender, "sender");
Objects.requireNonNull(args, "args");
for (Object arg : args) {
@ -123,10 +123,10 @@ public final class JCommandManager {
*
* @param scope 协程作用域 (用于管理协程生命周期). 一般填入 {@link JavaPlugin} 实例.
* @param args 接受 {@link String} {@link Message} , 其他对象将会被 {@link Object#toString()}
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
* @see CommandExecuteResult
* @see #executeCommand(CommandSender, Object...)
*/
public static CompletableFuture<Boolean> executeCommandAsync(final @NotNull CoroutineScope scope, final @NotNull CommandSender sender, final @NotNull Object... args) {
public static CompletableFuture<CommandExecuteResult> executeCommandAsync(final @NotNull CoroutineScope scope, final @NotNull CommandSender sender, final @NotNull Object... args) {
Objects.requireNonNull(sender, "sender");
Objects.requireNonNull(args, "args");
Objects.requireNonNull(scope, "scope");

View File

@ -13,41 +13,58 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.io.charsets.Charset
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.center.CuiPluginCenter
import net.mamoe.mirai.console.center.PluginCenter
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.util.*
import kotlin.coroutines.CoroutineContext
internal object MiraiConsoleInitializer {
internal lateinit var instance: IMiraiConsole
/** 由前端调用 */
internal fun init(instance: IMiraiConsole) {
this.instance = instance
MiraiConsole.initialize()
}
}
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
@JvmStatic
val buildDate: Date = Date(1592799753404L) // 2020-06-22 12:22:33
const val version: String = "0.5.1"
}
/**
* mirai 控制台实例.
*/
object MiraiConsole : CoroutineScope, IMiraiConsole {
private lateinit var instance: IMiraiConsole
val pluginCenter: PluginCenter get() = CuiPluginCenter
/** 由前端调用 */
internal fun init(instance: IMiraiConsole) {
this.instance = instance
}
private val instance: IMiraiConsole
get() = MiraiConsoleInitializer.instance
/**
* `mirai-console` build
*
* UTC+8 时间
*/
@MiraiExperimentalAPI
override val build: String
get() = instance.build
@JvmStatic
val buildDate: Date
get() = MiraiConsoleBuildConstants.buildDate
/**
* `mirai-console` 版本
*/
@MiraiExperimentalAPI
override val version: String
get() = instance.version
const val version: String = MiraiConsoleBuildConstants.version
/**
* Console 运行路径
@ -79,14 +96,15 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
@MiraiExperimentalAPI
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
internal fun initialize() {
// Only for initialize
}
}
// 前端使用
internal interface IMiraiConsole : CoroutineScope {
val build: String
val version: String
/**
* Console 运行路径
*/

View File

@ -10,8 +10,6 @@
package net.mamoe.mirai.console
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.center.CuiPluginCenter
import net.mamoe.mirai.console.center.PluginCenter
import net.mamoe.mirai.utils.LoginSolver
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
@ -22,11 +20,6 @@ import net.mamoe.mirai.utils.MiraiLogger
*/
@MiraiInternalAPI
interface MiraiConsoleFrontEnd {
/**
* 提供 [PluginCenter]
*/
val pluginCenter: PluginCenter get() = CuiPluginCenter
fun loggerFor(identity: String?): MiraiLogger
/**
@ -43,12 +36,6 @@ interface MiraiConsoleFrontEnd {
bot: Bot
)
fun pushVersion(
consoleVersion: String,
consoleBuild: String,
coreVersion: String
)
/**
* UI 层提供一个输入, 相当于 [readLine]
*/

View File

@ -134,12 +134,14 @@ fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
* Java 调用方式: `<static> CommandManager.executeCommand(Command)`
*
* @param messages 接受 [String] [Message], 其他对象将会被 [Any.toString]
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
* @see CommandExecuteResult
*
* @see JCommandManager.executeCommand Java 方法
*/
suspend fun CommandSender.executeCommand(vararg messages: Any): Boolean {
if (messages.isEmpty()) return false
suspend fun CommandSender.executeCommand(vararg messages: Any): CommandExecuteResult {
if (messages.isEmpty()) return CommandExecuteResult(
status = CommandExecuteStatus.EMPTY_COMMAND
)
return executeCommandInternal(
messages,
messages[0].let { if (it is SingleMessage) it.toString() else it.toString().substringBefore(' ') })
@ -150,12 +152,14 @@ internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Arr
/**
* 解析并执行一个指令
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
* @see CommandExecuteResult
*
* @see JCommandManager.executeCommand Java 方法
*/
suspend fun CommandSender.executeCommand(message: MessageChain): Boolean {
if (message.isEmpty()) return false
suspend fun CommandSender.executeCommand(message: MessageChain): CommandExecuteResult {
if (message.isEmpty()) return CommandExecuteResult(
status = CommandExecuteStatus.EMPTY_COMMAND
)
return executeCommandInternal(message, message[0].toString())
}
@ -163,9 +167,63 @@ suspend fun CommandSender.executeCommand(message: MessageChain): Boolean {
internal suspend inline fun CommandSender.executeCommandInternal(
messages: Any,
commandName: String
): Boolean {
val command = InternalCommandManager.matchCommand(commandName) ?: return false
): CommandExecuteResult {
val command = InternalCommandManager.matchCommand(commandName) ?: return CommandExecuteResult(
status = CommandExecuteStatus.COMMAND_NOT_FOUND,
commandName = commandName
)
val rawInput = messages.flattenCommandComponents()
command.onCommand(this, rawInput.dropToTypedArray(1))
return true
}
kotlin.runCatching {
command.onCommand(this, rawInput.dropToTypedArray(1))
}.onFailure {
return CommandExecuteResult(
status = CommandExecuteStatus.FAILED,
commandName = commandName,
command = command,
exception = it
)
}
return CommandExecuteResult(
status = CommandExecuteStatus.SUCCESSFUL,
commandName = commandName,
command = command
)
}
/**
* 命令的执行返回
*
* @param status 命令最终执行状态
* @param exception 命令执行时发生的错误(如果有)
* @param command 尝试执行的命令 (status = SUCCESSFUL | FAILED)
* @param commandName 尝试执行的命令的名字 (status != EMPTY_COMMAND)
*
*
* @see CommandExecuteStatus
*/
class CommandExecuteResult(
val status: CommandExecuteStatus,
val exception: Throwable? = null,
val command: Command? = null,
val commandName: String? = null
) {
/**
* 命令的执行状态
*
* 当为 [SUCCESSFUL] 的时候代表命令执行成功
*
* 当为 [FAILED] 的时候, 代表命令执行出现了错误
*
* 当为 [COMMAND_NOT_FOUND] 的时候代表没有匹配的命令
*
* 当为 [EMPTY_COMMAND] 的时候, 代表尝试执行 ""
*
*/
enum class CommandExecuteStatus {
SUCCESSFUL, FAILED, COMMAND_NOT_FOUND, EMPTY_COMMAND
}
}
@Suppress("RemoveRedundantQualifierName")
typealias CommandExecuteStatus = CommandExecuteResult.CommandExecuteStatus

View File

@ -16,7 +16,7 @@ import java.net.URLClassLoader
internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
private val loggerName = "PluginsLoader"
private val pluginLoaders = linkedMapOf<String, PluginClassLoader>()
private val classesCache = mutableMapOf<String,Class<*>>()
private val classesCache = mutableMapOf<String, Class<*>>()
private val logger = MiraiConsole.newLogger(loggerName)
/**
@ -34,7 +34,7 @@ internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
} catch (e: Throwable) {
logger.error("Plugin(${plugin.key}) can't not close its ClassLoader(${cl})", e)
}
}
}
classesCache.clear()
}
@ -93,9 +93,9 @@ internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
* A Adapted URL Class Loader that supports Android and JVM for single URL(File) Class Load
*/
internal open class AdaptiveURLClassLoader(file: File, parent: ClassLoader):ClassLoader(){
internal open class AdaptiveURLClassLoader(file: File, parent: ClassLoader) : ClassLoader() {
private val internalClassLoader:ClassLoader by lazy {
private val internalClassLoader: ClassLoader by lazy {
kotlin.runCatching {
val loaderClass = Class.forName("dalvik.system.PathClassLoader")
loaderClass.getConstructor(String::class.java, ClassLoader::class.java)
@ -110,19 +110,19 @@ internal open class AdaptiveURLClassLoader(file: File, parent: ClassLoader):Clas
}
private val internalClassCache = mutableMapOf<String,Class<*>>()
private val internalClassCache = mutableMapOf<String, Class<*>>()
internal val classesCache:Map<String,Class<*>>
internal val classesCache: Map<String, Class<*>>
get() = internalClassCache
internal fun addClassCache(string: String, clazz: Class<*>){
synchronized(internalClassCache){
internal fun addClassCache(string: String, clazz: Class<*>) {
synchronized(internalClassCache) {
internalClassCache[string] = clazz
}
}
fun close(){
fun close() {
if (internalClassLoader is URLClassLoader) {
(internalClassLoader as URLClassLoader).close()
}
@ -135,24 +135,25 @@ internal class PluginClassLoader(
file: File,
private val pluginsLoader: PluginsLoader,
parent: ClassLoader
) :AdaptiveURLClassLoader(file,parent){
) : AdaptiveURLClassLoader(file, parent) {
override fun findClass(name: String): Class<*> {
return findClass(name,true)
return findClass(name, true)
}
fun findClass(name: String, global: Boolean = true): Class<*>{
return classesCache[name]?: kotlin.run {
fun findClass(name: String, global: Boolean = true): Class<*> {
return classesCache[name] ?: kotlin.run {
var clazz: Class<*>? = null
if (global) {
clazz = pluginsLoader.findClassByName(name)
}
if(clazz == null) {
if (clazz == null) {
clazz = loadClass(name)//这里应该是find, 如果不行就要改
}
pluginsLoader.addClassCache(name, clazz)
this.addClassCache(name, clazz)
clazz
this.addClassCache(name, clazz)
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
clazz!! // compiler bug
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS")
package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer
import net.mamoe.mirai.console.setting.internal.cast
import net.mamoe.mirai.console.setting.internal.valueFromKTypeImpl
import net.mamoe.mirai.console.setting.internal.valueImpl
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import java.util.*
import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KProperty
import kotlin.reflect.KType
import kotlin.reflect.typeOf
// TODO: 2020/6/21 move to JvmPlugin to inherit SettingStorage and CoroutineScope for saving
// Shows public APIs such as deciding when to auto-save.
abstract class Setting : SettingImpl() {
operator fun <T> SerializerAwareValue<T>.provideDelegate(
thisRef: Any?,
property: KProperty<*>
): SerializerAwareValue<T> {
@Suppress("UNCHECKED_CAST")
valueNodes.add(Node(property as KProperty<T>, this, this.serializer))
return this
}
}
/**
* Internal implementation for [Setting] including:
* - Reflection on Kotlin properties and Java fields
* - Auto-saving
*/
// TODO move to internal package.
internal abstract class SettingImpl {
internal class Node<T>(
val property: KProperty<T>,
val value: Value<T>,
val updaterSerializer: KSerializer<Unit>
)
internal val valueNodes: MutableList<Node<*>> = Collections.synchronizedList(mutableListOf())
/**
* flatten
*/
internal fun onValueChanged(value: Value<*>) {
// TODO: 2020/6/22
}
}
//// region Setting.value primitives CODEGEN ////
// TODO: 2020/6/19 CODEGEN
fun Setting.value(default: Int): SerializableValue<Int> = valueImpl(default)
//// endregion Setting.value primitives CODEGEN ////
/**
* Creates a [Value] with reified type.
*
* @param T reified param type T.
* Supports only primitives, Kotlin built-in collections,
* and classes that are serializable with Kotlinx.serialization
* (typically annotated with [kotlinx.serialization.Serializable])
*/
@LowPriorityInOverloadResolution
@OptIn(ExperimentalStdlibApi::class) // stable in 1.4
inline fun <reified T> Setting.valueReified(default: T): SerializableValue<T> = valueFromKTypeImpl(typeOf<T>()).cast()
@MiraiExperimentalAPI
fun <T> Setting.valueFromKType(type: KType): SerializableValue<T> = valueFromKTypeImpl(type).cast()

View File

@ -0,0 +1,216 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused", "NOTHING_TO_INLINE")
package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer
import net.mamoe.mirai.console.setting.internal.map
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.reflect.KProperty
/**
* Represents a observable, immutable value wrapping.
*
* The value can be modified by delegation just like Kotlin's `var`, however it can also be done by the user, e.g. changing using the UI frontend.
*
* Some frequently used types are specially treated with performance enhancement by codegen.
*
* @see PrimitiveValue
* @see CompositeValue
*/
interface Value<T> {
var value: T
}
/**
* Typically returned by [Setting.value] functions.
*/
class SerializableValue<T>(
delegate: Value<T>,
/**
* The serializer used to update and dump [delegate]
*/
override val serializer: KSerializer<Unit>
) : Value<T> by delegate, SerializerAwareValue<T>
fun <T> Value<T>.serializableValueWith(
serializer: KSerializer<T>
): SerializableValue<T> {
return SerializableValue(this, serializer.map(serializer = { this.value }, deserializer = { this.value = it }))
}
/**
* @see SerializableValue
*/
interface SerializerAwareValue<T> : Value<T> {
val serializer: KSerializer<Unit>
}
inline operator fun <T> Value<T>.getValue(mySetting: Any?, property: KProperty<*>): T = value
inline operator fun <T> Value<T>.setValue(mySetting: Any?, property: KProperty<*>, value: T) {
this.value = value
}
/**
* The serializer for a specific kind of [Value].
*/
typealias ValueSerializer<T> = KSerializer<Value<T>>
/**
* Represents a observable *primitive* value wrapping.
*
* 9 types that are considered *primitive*:
* - Integers: [Byte], [Short], [Int], [Long]
* - Floating: [Float], [Double]
* - [Boolean]
* - [Char], [String]
*
* Note: The values are actually *boxed* because of the generic type T.
* *Primitive* indicates only it is one of the 9 types mentioned above.
*/
interface PrimitiveValue<T> : Value<T>
//// region PrimitiveValues CODEGEN ////
/**
* Represents a non-null [Byte] value.
*/
interface ByteValue : PrimitiveValue<Byte>
/**
* Represents a non-null [Short] value.
*/
interface ShortValue : PrimitiveValue<Short>
/**
* Represents a non-null [Int] value.
*/
interface IntValue : PrimitiveValue<Int>
/**
* Represents a non-null [Long] value.
*/
interface LongValue : PrimitiveValue<Long>
/**
* Represents a non-null [Float] value.
*/
interface FloatValue : PrimitiveValue<Float>
/**
* Represents a non-null [Double] value.
*/
interface DoubleValue : PrimitiveValue<Double>
/**
* Represents a non-null [Char] value.
*/
interface CharValue : PrimitiveValue<Char>
/**
* Represents a non-null [Boolean] value.
*/
interface BooleanValue : PrimitiveValue<Boolean>
/**
* Represents a non-null [String] value.
*/
interface StringValue : PrimitiveValue<String>
//// endregion PrimitiveValues CODEGEN ////
@MiraiExperimentalAPI
interface CompositeValue<T> : Value<T>
/**
* Superclass of [CompositeListValue], [PrimitiveListValue].
*/
interface ListValue<E> : CompositeValue<List<E>>
/**
* Elements can by anything, wrapped as [Value].
* @param E is not primitive types.
*/
interface CompositeListValue<E> : ListValue<E>
/**
* Elements can only be primitives, not wrapped.
* @param E is not primitive types.
*/
interface PrimitiveListValue<E> : ListValue<E>
//// region PrimitiveListValue CODEGEN ////
interface PrimitiveIntListValue : PrimitiveListValue<Int>
interface PrimitiveLongListValue : PrimitiveListValue<Long>
// TODO + codegen
//// endregion PrimitiveListValue CODEGEN ////
/**
* Superclass of [CompositeSetValue], [PrimitiveSetValue].
*/
interface SetValue<E> : CompositeValue<Set<E>>
/**
* Elements can by anything, wrapped as [Value].
* @param E is not primitive types.
*/
interface CompositeSetValue<E> : SetValue<E>
/**
* Elements can only be primitives, not wrapped.
* @param E is not primitive types.
*/
interface PrimitiveSetValue<E> : SetValue<E>
//// region PrimitiveSetValue CODEGEN ////
interface PrimitiveIntSetValue : PrimitiveSetValue<Int>
interface PrimitiveLongSetValue : PrimitiveSetValue<Long>
// TODO + codegen
//// endregion PrimitiveSetValue CODEGEN ////
/**
* Superclass of [CompositeMapValue], [PrimitiveMapValue].
*/
interface MapValue<K, V> : CompositeValue<Map<K, V>>
interface CompositeMapValue<K, V> : MapValue<K, V>
interface PrimitiveMapValue<K, V> : MapValue<K, V>
//// region PrimitiveMapValue CODEGEN ////
interface PrimitiveIntIntMapValue : PrimitiveMapValue<Int, Int>
interface PrimitiveIntLongMapValue : PrimitiveMapValue<Int, Long>
// TODO + codegen
//// endregion PrimitiveSetValue CODEGEN ////

View File

@ -0,0 +1,104 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused")
package net.mamoe.mirai.console.setting.internal
import net.mamoe.mirai.console.setting.*
// type inference bug
internal fun <T> Setting.createCompositeSetValueImpl(tToValue: (T) -> Value<T>): CompositeSetValueImpl<T> {
return object : CompositeSetValueImpl<T>(tToValue) {
override fun onChanged() {
this@createCompositeSetValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeSetValueImpl<T>(
tToValue: (T) -> Value<T> // should override onChanged
) : CompositeSetValue<T> {
private val internalSet: MutableSet<Value<T>> = mutableSetOf()
private var _value: Set<T> = internalSet.shadowMap({ it.value }, tToValue).observable { onChanged() }
override var value: Set<T>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}
// type inference bug
internal fun <T> Setting.createCompositeListValueImpl(tToValue: (T) -> Value<T>): CompositeListValueImpl<T> {
return object : CompositeListValueImpl<T>(tToValue) {
override fun onChanged() {
this@createCompositeListValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeListValueImpl<T>(
tToValue: (T) -> Value<T> // should override onChanged
) : CompositeListValue<T> {
private val internalList: MutableList<Value<T>> = mutableListOf()
private var _value: List<T> = internalList.shadowMap({ it.value }, tToValue).observable { onChanged() }
override var value: List<T>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}
// workaround to a type inference bug
internal fun <K, V> Setting.createCompositeMapValueImpl(
kToValue: (K) -> Value<K>,
vToValue: (V) -> Value<V>
): CompositeMapValueImpl<K, V> {
return object : CompositeMapValueImpl<K, V>(kToValue, vToValue) {
override fun onChanged() {
this@createCompositeMapValueImpl.onValueChanged(this)
}
}
}
internal abstract class CompositeMapValueImpl<K, V>(
kToValue: (K) -> Value<K>, // should override onChanged
vToValue: (V) -> Value<V> // should override onChanged
) : CompositeMapValue<K, V> {
private val internalList: MutableMap<Value<K>, Value<V>> = mutableMapOf()
private var _value: Map<K, V> =
internalList.shadowMap({ it.value }, kToValue, { it.value }, vToValue).observable { onChanged() }
override var value: Map<K, V>
get() = _value
set(v) {
if (_value != v) {
onChanged()
_value = v
}
}
protected abstract fun onChanged()
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import net.mamoe.mirai.console.setting.SerializableValue
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.serializableValueWith
import net.mamoe.mirai.console.setting.valueFromKType
import net.mamoe.yamlkt.YamlDynamicSerializer
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
import kotlin.reflect.KClass
import kotlin.reflect.KType
@PublishedApi
@Suppress("UnsafeCall", "SMARTCAST_IMPOSSIBLE", "UNCHECKED_CAST")
internal fun Setting.valueFromKTypeImpl(type: KType): SerializableValue<*> {
val classifier = type.classifier
require(classifier is KClass<*>)
if (classifier.isPrimitiveOrBuiltInSerializableValue()) {
TODO("是基础类型, 可以直接创建 ValueImpl. ")
}
// 复合类型
when (classifier) {
Map::class -> {
val keyClass = type.arguments[0].type?.classifier
require(keyClass is KClass<*>)
val valueClass = type.arguments[1].type?.classifier
require(valueClass is KClass<*>)
if (keyClass.isPrimitiveOrBuiltInSerializableValue() && valueClass.isPrimitiveOrBuiltInSerializableValue()) {
// PrimitiveIntIntMap
// ...
TODO()
} else {
return createCompositeMapValueImpl<Any?, Any?>(
kToValue = { valueFromKType(type.arguments[0].type!!) },
vToValue = { valueFromKType(type.arguments[1].type!!) }
).serializableValueWith(serializerMirai(type) as KSerializer<Map<Any?, Any?>>) // erased
}
}
List::class -> {
val elementClass = type.arguments[0].type?.classifier
require(elementClass is KClass<*>)
if (elementClass.isPrimitiveOrBuiltInSerializableValue()) {
// PrimitiveIntList
// ...
TODO()
} else {
return createCompositeListValueImpl<Any?> { valueFromKType(type.arguments[0].type!!) }
.serializableValueWith(serializerMirai(type) as KSerializer<List<Any?>>)
}
}
Set::class -> {
val elementClass = type.arguments[0].type?.classifier
require(elementClass is KClass<*>)
if (elementClass.isPrimitiveOrBuiltInSerializableValue()) {
// PrimitiveIntSet
// ...
TODO()
} else {
return createCompositeSetValueImpl<Any?> { valueFromKType(type.arguments[0].type!!) }
.serializableValueWith(serializerMirai(type) as KSerializer<Set<Any?>>)
}
}
else -> error("Custom composite value is not supported yet (${classifier.qualifiedName})")
}
}
internal fun KClass<*>.isPrimitiveOrBuiltInSerializableValue(): Boolean {
return false // debug
when (this) {
Byte::class, Short::class, Int::class, Long::class,
Boolean::class,
Char::class, String::class,
Pair::class, Triple::class
-> return true
}
return false
}
@PublishedApi
@Suppress("UNCHECKED_CAST")
internal inline fun <R, T> T.cast(): R = this as R
/**
* Copied from kotlinx.serialization, modifications are marked with "/* mamoe modify */"
* Copyright 2017-2020 JetBrains s.r.o.
*/
@Suppress("UNCHECKED_CAST", "NO_REFLECTION_IN_CLASS_PATH", "UNSUPPORTED", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@OptIn(ImplicitReflectionSerializer::class)
internal fun serializerMirai(type: KType): KSerializer<Any?> {
fun serializerByKTypeImpl(type: KType): KSerializer<Any> {
val rootClass = when (val t = type.classifier) {
is KClass<*> -> t
else -> error("Only KClass supported as classifier, got $t")
} as KClass<Any>
val typeArguments = type.arguments
.map { requireNotNull(it.type) { "Star projections are not allowed, had $it instead" } }
return when {
typeArguments.isEmpty() -> rootClass.serializer()
else -> {
val serializers = typeArguments
.map(::serializer)
// Array is not supported, see KT-32839
when (rootClass) {
List::class, MutableList::class, ArrayList::class -> ListSerializer(serializers[0])
HashSet::class -> SetSerializer(serializers[0])
Set::class, MutableSet::class, LinkedHashSet::class -> SetSerializer(serializers[0])
HashMap::class -> MapSerializer(serializers[0], serializers[1])
Map::class, MutableMap::class, LinkedHashMap::class -> MapSerializer(serializers[0], serializers[1])
Map.Entry::class -> MapEntrySerializer(serializers[0], serializers[1])
Pair::class -> PairSerializer(serializers[0], serializers[1])
Triple::class -> TripleSerializer(serializers[0], serializers[1], serializers[2])
/* mamoe modify */ Any::class -> if (type.isMarkedNullable) YamlNullableDynamicSerializer else YamlDynamicSerializer
else -> {
if (isReferenceArray(type, rootClass)) {
return ArraySerializer(typeArguments[0].classifier as KClass<Any>, serializers[0]).cast()
}
requireNotNull(rootClass.constructSerializerForGivenTypeArgs(*serializers.toTypedArray())) {
"Can't find a method to construct serializer for type ${rootClass.simpleName()}. " +
"Make sure this class is marked as @Serializable or provide serializer explicitly."
}
}
}
}
}.cast()
}
val result = serializerByKTypeImpl(type)
return if (type.isMarkedNullable) result.nullable else result.cast()
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.setting.internal
import net.mamoe.mirai.console.setting.IntValue
//// region PrimitiveValues CODEGEN ////
// TODO: 2020/6/21 CODEGEN
internal abstract class IntValueImpl : IntValue {
constructor()
constructor(default: Int) {
_value = default
}
private var _value: Int? = null
override var value: Int
get() = _value ?: throw IllegalStateException("IntValue should be initialized before get.")
set(v) {
if (v != this._value) {
this._value = v
onChanged()
}
}
protected abstract fun onChanged()
}
//// endregion PrimitiveValues CODEGEN ////

View File

@ -0,0 +1,31 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.setting.SerializableValue
import net.mamoe.mirai.console.setting.Setting
//// region Setting.value primitives impl CODEGEN ////
// TODO: 2020/6/21 CODEGEN
internal fun Setting.valueImpl(default: Int): SerializableValue<Int> {
val instance = object : IntValueImpl(default) {
override fun onChanged() = this@valueImpl.onValueChanged(this)
}
return SerializableValue(instance, Int.serializer().map(
serializer = { instance.value },
deserializer = { instance.value = it }
))
}
//// endregion Setting.value primitives impl CODEGEN ////

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("DuplicatedCode")
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.ImplicitReflectionSerializer
@ -14,7 +16,89 @@ import kotlinx.serialization.serializer
import net.mamoe.yamlkt.Yaml
import kotlin.reflect.KClass
internal fun <E, R> MutableList<E>.shadowMap(transform: (E) -> R, transformBack: (R) -> E): MutableList<R> {
internal inline fun <K, V, KR, VR> MutableMap<K, V>.shadowMap(
crossinline kTransform: (K) -> KR,
crossinline kTransformBack: (KR) -> K,
crossinline vTransform: (V) -> VR,
crossinline vTransformBack: (VR) -> V
): MutableMap<KR, VR> {
return object : MutableMap<KR, VR> {
override val size: Int get() = this@shadowMap.size
override fun containsKey(key: KR): Boolean = this@shadowMap.containsKey(key.let(kTransformBack))
override fun containsValue(value: VR): Boolean = this@shadowMap.containsValue(value.let(vTransformBack))
override fun get(key: KR): VR? = this@shadowMap[key.let(kTransformBack)]?.let(vTransform)
override fun isEmpty(): Boolean = this@shadowMap.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<KR, VR>>
get() = this@shadowMap.entries.shadowMap(
transform = { entry: MutableMap.MutableEntry<K, V> ->
object : MutableMap.MutableEntry<KR, VR> {
override val key: KR get() = entry.key.let(kTransform)
override val value: VR get() = entry.value.let(vTransform)
override fun setValue(newValue: VR): VR =
entry.setValue(newValue.let(vTransformBack)).let(vTransform)
}
} as ((MutableMap.MutableEntry<K, V>) -> MutableMap.MutableEntry<KR, VR>), // type inference bug
transformBack = { entry ->
object : MutableMap.MutableEntry<K, V> {
override val key: K get() = entry.key.let(kTransformBack)
override val value: V get() = entry.value.let(vTransformBack)
override fun setValue(newValue: V): V =
entry.setValue(newValue.let(vTransform)).let(vTransformBack)
}
}
)
override val keys: MutableSet<KR>
get() = this@shadowMap.keys.shadowMap(kTransform, kTransformBack)
override val values: MutableCollection<VR>
get() = this@shadowMap.values.shadowMap(vTransform, vTransformBack)
override fun clear() = this@shadowMap.clear()
override fun put(key: KR, value: VR): VR? =
this@shadowMap.put(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform)
override fun putAll(from: Map<out KR, VR>) {
from.forEach { (kr, vr) ->
this@shadowMap[kr.let(kTransformBack)] = vr.let(vTransformBack)
}
}
override fun remove(key: KR): VR? = this@shadowMap.remove(key.let(kTransformBack))?.let(vTransform)
}
}
internal inline fun <E, R> MutableCollection<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableCollection<R> {
return object : MutableCollection<R> {
override val size: Int get() = this@shadowMap.size
override fun contains(element: R): Boolean = this@shadowMap.any { it.let(transform) == element }
override fun containsAll(elements: Collection<R>): Boolean = elements.all(::contains)
override fun isEmpty(): Boolean = this@shadowMap.isEmpty()
override fun iterator(): MutableIterator<R> = object : MutableIterator<R> {
private val delegate = this@shadowMap.iterator()
override fun hasNext(): Boolean = delegate.hasNext()
override fun next(): R = delegate.next().let(transform)
override fun remove() = delegate.remove()
}
override fun add(element: R): Boolean = this@shadowMap.add(element.let(transformBack))
override fun addAll(elements: Collection<R>): Boolean = this@shadowMap.addAll(elements.map(transformBack))
override fun clear() = this@shadowMap.clear()
override fun remove(element: R): Boolean = this@shadowMap.removeIf { it.let(transform) == element }
override fun removeAll(elements: Collection<R>): Boolean = elements.all(::remove)
override fun retainAll(elements: Collection<R>): Boolean = this@shadowMap.retainAll(elements.map(transformBack))
}
}
internal inline fun <E, R> MutableList<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableList<R> {
return object : MutableList<R> {
override val size: Int get() = this@shadowMap.size
@ -78,7 +162,10 @@ internal fun <E, R> MutableList<E>.shadowMap(transform: (E) -> R, transformBack:
}
internal fun <E, R> MutableSet<E>.shadowMap(transform: (E) -> R, transformBack: (R) -> E): MutableSet<R> {
internal inline fun <E, R> MutableSet<E>.shadowMap(
crossinline transform: (E) -> R,
crossinline transformBack: (R) -> E
): MutableSet<R> {
return object : MutableSet<R> {
override val size: Int get() = this@shadowMap.size
@ -102,7 +189,7 @@ internal fun <E, R> MutableSet<E>.shadowMap(transform: (E) -> R, transformBack:
}
}
internal fun <T> dynamicList(supplier: () -> List<T>): List<T> {
internal inline fun <T> dynamicList(crossinline supplier: () -> List<T>): List<T> {
return object : List<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -118,7 +205,7 @@ internal fun <T> dynamicList(supplier: () -> List<T>): List<T> {
}
}
internal fun <T> dynamicSet(supplier: () -> Set<T>): Set<T> {
internal inline fun <T> dynamicSet(crossinline supplier: () -> Set<T>): Set<T> {
return object : Set<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -129,7 +216,7 @@ internal fun <T> dynamicSet(supplier: () -> Set<T>): Set<T> {
}
internal fun <T> dynamicMutableList(supplier: () -> MutableList<T>): MutableList<T> {
internal inline fun <T> dynamicMutableList(crossinline supplier: () -> MutableList<T>): MutableList<T> {
return object : MutableList<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -156,7 +243,7 @@ internal fun <T> dynamicMutableList(supplier: () -> MutableList<T>): MutableList
}
internal fun <T> dynamicMutableSet(supplier: () -> MutableSet<T>): MutableSet<T> {
internal inline fun <T> dynamicMutableSet(crossinline supplier: () -> MutableSet<T>): MutableSet<T> {
return object : MutableSet<T> {
override val size: Int get() = supplier().size
override fun contains(element: T): Boolean = supplier().contains(element)
@ -172,6 +259,23 @@ internal fun <T> dynamicMutableSet(supplier: () -> MutableSet<T>): MutableSet<T>
}
}
@Suppress("UNCHECKED_CAST", "USELESS_CAST") // type inference bug
internal inline fun <K, V> MutableMap<K, V>.observable(crossinline onChanged: () -> Unit): MutableMap<K, V> {
return object : MutableMap<K, V>, Map<K, V> by (this as Map<K, V>) {
override val keys: MutableSet<K>
get() = this@observable.keys.observable(onChanged)
override val values: MutableCollection<V>
get() = this@observable.values.observable(onChanged)
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() = this@observable.entries.observable(onChanged)
override fun clear() = this@observable.clear().also { onChanged() }
override fun put(key: K, value: V): V? = this@observable.put(key, value).also { onChanged() }
override fun putAll(from: Map<out K, V>) = this@observable.putAll(from).also { onChanged() }
override fun remove(key: K): V? = this@observable.remove(key).also { onChanged() }
}
}
internal inline fun <T> MutableList<T>.observable(crossinline onChanged: () -> Unit): MutableList<T> {
return object : MutableList<T> {
override val size: Int get() = this@observable.size
@ -234,6 +338,31 @@ internal inline fun <T> MutableList<T>.observable(crossinline onChanged: () -> U
}
}
internal inline fun <T> MutableCollection<T>.observable(crossinline onChanged: () -> Unit): MutableCollection<T> {
return object : MutableCollection<T> {
override val size: Int get() = this@observable.size
override fun contains(element: T): Boolean = this@observable.contains(element)
override fun containsAll(elements: Collection<T>): Boolean = this@observable.containsAll(elements)
override fun isEmpty(): Boolean = this@observable.isEmpty()
override fun iterator(): MutableIterator<T> = object : MutableIterator<T> {
private val delegate = this@observable.iterator()
override fun hasNext(): Boolean = delegate.hasNext()
override fun next(): T = delegate.next()
override fun remove() = delegate.remove().also { onChanged() }
}
override fun add(element: T): Boolean = this@observable.add(element).also { onChanged() }
override fun addAll(elements: Collection<T>): Boolean = this@observable.addAll(elements).also { onChanged() }
override fun clear() = this@observable.clear().also { onChanged() }
override fun remove(element: T): Boolean = this@observable.remove(element).also { onChanged() }
override fun removeAll(elements: Collection<T>): Boolean =
this@observable.removeAll(elements).also { onChanged() }
override fun retainAll(elements: Collection<T>): Boolean =
this@observable.retainAll(elements).also { onChanged() }
}
}
internal inline fun <T> MutableSet<T>.observable(crossinline onChanged: () -> Unit): MutableSet<T> {
return object : MutableSet<T> {
override val size: Int get() = this@observable.size

View File

@ -10,16 +10,9 @@
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.yamlkt.Yaml
import net.mamoe.yamlkt.YamlConfiguration
import kotlin.reflect.KProperty
import kotlin.reflect.full.findAnnotation
internal object SettingSerializerMark
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
internal inline fun <E> KSerializer<E>.bind(

View File

@ -18,8 +18,9 @@ object TestCompositeCommand : CompositeCommand(
"groupManagement", "grpMgn"
) {
@SubCommand
suspend fun CommandSender.mute(image: Image, target: Member, seconds: Int) {
suspend fun CommandSender.mute(image: Image, target: Member, seconds: Int): Boolean {
target.mute(seconds)
return true
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.setting
import org.junit.jupiter.api.Test
internal class SettingTest {
class MySetting : Setting() {
val int by value(1)
val map by valueReified(mapOf("" to ""))
val map2 by valueReified(mapOf("" to mapOf("" to mapOf("" to ""))))
}
@Test
fun testPrimitive() {
}
}

View File

@ -1,33 +1,15 @@
@file:Suppress("UnstableApiUsage")
import kotlin.math.pow
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
}
buildscript {
repositories {
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
jcenter()
mavenCentral()
}
dependencies {
classpath("com.github.jengelman.gradle.plugins:shadow:5.2.0")
classpath("org.jetbrains.kotlin:kotlin-serialization:${Versions.Kotlin.stdlib}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Kotlin.stdlib}")
classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4") // don"t use any other.
}
}
allprojects {
group = "net.mamoe"
repositories {
mavenLocal()
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
jcenter()
mavenCentral()
}
@ -35,97 +17,8 @@ allprojects {
subprojects {
afterEvaluate {
apply(plugin = "com.github.johnrengelman.shadow")
val kotlin =
(this as ExtensionAware).extensions.getByName("kotlin") as? org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
?: return@afterEvaluate
apply<MiraiConsoleBuildPlugin>()
tasks.getByName("shadowJar") {
doLast {
this.outputs.files.forEach {
if (it.nameWithoutExtension.endsWith("-all")) {
val output = File(
it.path.substringBeforeLast(File.separator) + File.separator + it.nameWithoutExtension.substringBeforeLast(
"-all"
) + "." + it.extension
)
println("Renaming to ${output.path}")
if (output.exists()) {
output.delete()
}
it.renameTo(output)
}
}
}
}
val githubUpload by tasks.creating {
group = "mirai"
dependsOn(tasks.getByName("shadowJar"))
doFirst {
timeout.set(java.time.Duration.ofHours(3))
findLatestFile()?.let { (_, file) ->
val filename = file.name
println("Uploading file $filename")
runCatching {
upload.GitHub.upload(
file,
"https://api.github.com/repos/mamoe/mirai-repo/contents/shadow/${project.name}/$filename",
project
)
}.exceptionOrNull()?.let {
System.err.println("GitHub Upload failed")
it.printStackTrace() // force show stacktrace
throw it
}
}
}
}
val cuiCloudUpload by tasks.creating {
group = "mirai"
dependsOn(tasks.getByName("shadowJar"))
doFirst {
timeout.set(java.time.Duration.ofHours(3))
findLatestFile()?.let { (_, file) ->
val filename = file.name
println("Uploading file $filename")
runCatching {
upload.CuiCloud.upload(
file,
project
)
}.exceptionOrNull()?.let {
System.err.println("CuiCloud Upload failed")
it.printStackTrace() // force show stacktrace
throw it
}
}
}
}
setJavaCompileTarget()
}
}
fun Project.findLatestFile(): Map.Entry<String, File> {
return File(projectDir, "build/libs").walk()
.filter { it.isFile }
.onEach { println("all files=$it") }
.filter { it.name.matches(Regex("""${project.name}-[0-9][0-9]*(\.[0-9]*)*.*\.jar""")) }
.onEach { println("matched file: ${it.name}") }
.associateBy { it.nameWithoutExtension.substringAfterLast('-') }
.onEach { println("versions: $it") }
.maxBy { (version, file) ->
version.split('.').let {
if (it.size == 2) it + "0"
else it
}.reversed().foldIndexed(0) { index: Int, acc: Int, s: String ->
acc + 100.0.pow(index).toInt() * (s.toIntOrNull() ?: 0)
}
} ?: error("cannot find any file to upload")
}
}

View File

@ -3,7 +3,10 @@ plugins {
}
repositories {
mavenLocal()
jcenter()
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
mavenCentral()
}
kotlin {
@ -24,4 +27,11 @@ dependencies {
api(ktor("client-core", "1.3.2"))
api(ktor("client-cio", "1.3.2"))
api(ktor("client-json", "1.3.2"))
//api(gradleApi())
//compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
//runtimeOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
api("com.github.jengelman.gradle.plugins:shadow:6.0.0")
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("UnstableApiUsage")
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.creating
import java.io.File
import kotlin.math.pow
class MiraiConsoleBuildPlugin : Plugin<Project> {
override fun apply(target: Project) = target.run {
apply<ShadowPlugin>()
if (tasks.none { it.name == "shadowJar" }) {
return@run
}
tasks.getByName("shadowJar") {
doLast {
this.outputs.files.forEach {
if (it.nameWithoutExtension.endsWith("-all")) {
val output = File(
it.path.substringBeforeLast(File.separator) + File.separator + it.nameWithoutExtension.substringBeforeLast(
"-all"
) + "." + it.extension
)
println("Renaming to ${output.path}")
if (output.exists()) {
output.delete()
}
it.renameTo(output)
}
}
}
}
tasks.creating {
group = "mirai"
dependsOn(tasks.getByName("shadowJar"))
doFirst {
timeout.set(java.time.Duration.ofHours(3))
findLatestFile().let { (_, file) ->
val filename = file.name
println("Uploading file $filename")
runCatching {
upload.GitHub.upload(
file,
"https://api.github.com/repos/mamoe/mirai-repo/contents/shadow/${project.name}/$filename",
project
)
}.exceptionOrNull()?.let {
System.err.println("GitHub Upload failed")
it.printStackTrace() // force show stacktrace
throw it
}
}
}
}
tasks.creating {
group = "mirai"
dependsOn(tasks.getByName("shadowJar"))
doFirst {
timeout.set(java.time.Duration.ofHours(3))
findLatestFile().let { (_, file) ->
val filename = file.name
println("Uploading file $filename")
runCatching {
upload.CuiCloud.upload(
file,
project
)
}.exceptionOrNull()?.let {
System.err.println("CuiCloud Upload failed")
it.printStackTrace() // force show stacktrace
throw it
}
}
}
}
}
}
fun Project.findLatestFile(): Map.Entry<String, File> {
return File(projectDir, "build/libs").walk()
.filter { it.isFile }
.onEach { println("all files=$it") }
.filter { it.name.matches(Regex("""${project.name}-[0-9][0-9]*(\.[0-9]*)*.*\.jar""")) }
.onEach { println("matched file: ${it.name}") }
.associateBy { it.nameWithoutExtension.substringAfterLast('-') }
.onEach { println("versions: $it") }
.maxBy { (version, _) ->
version.split('.').let {
if (it.size == 2) it + "0"
else it
}.reversed().foldIndexed(0) { index: Int, acc: Int, s: String ->
acc + 100.0.pow(index).toInt() * (s.toIntOrNull() ?: 0)
}
} ?: error("cannot find any file to upload")
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.compile.JavaCompile
import java.lang.reflect.Method
import kotlin.reflect.KClass
fun Any.reflectMethod(name: String, vararg params: KClass<out Any>): Pair<Any, Method> {
return this to this::class.java.getMethod(name, *params.map { it.java }.toTypedArray())
}
operator fun Pair<Any, Method>.invoke(vararg args: Any?): Any? {
return second.invoke(first, *args)
}
@Suppress("NOTHING_TO_INLINE") // or error
fun Project.setJavaCompileTarget() {
tasks.filter { it.name in arrayOf("compileKotlin", "compileTestKotlin") }.forEach { task ->
task
.reflectMethod("getKotlinOptions")()!!
.reflectMethod("setJvmTarget", String::class)("1.8")
}
kotlin.runCatching { // apply only when java plugin is available
(extensions.getByName("java") as JavaPluginExtension).run {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
}
}
}

View File

@ -8,18 +8,18 @@
*/
object Versions {
object Mirai {
const val core = "1.0.0"
const val console = "0.5.1"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = "0.1.0"
}
const val core = "1.1-EA"
const val console = "0.5.1"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = "0.1.0"
object Kotlin {
const val stdlib = "1.3.72"
const val coroutines = "1.3.7"
const val serialization = "0.20.0"
const val ktor = "1.3.2"
}
const val kotlin = "1.3.72"
const val coroutines = "1.3.7"
const val serialization = "0.20.0"
const val ktor = "1.3.2"
const val androidGradle = "3.6.2"
const val bintray = "1.8.4"
}

View File

@ -5,7 +5,7 @@ import org.gradle.kotlin.dsl.DependencyHandlerScope
fun DependencyHandlerScope.kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
@Suppress("unused")
fun DependencyHandlerScope.ktor(id: String, version: String = Versions.Kotlin.ktor) = "io.ktor:ktor-$id:$version"
fun DependencyHandlerScope.ktor(id: String, version: String = Versions.ktor) = "io.ktor:ktor-$id:$version"
@Suppress("unused")
fun DependencyHandler.compileAndRuntime(any: Any) {

View File

@ -29,17 +29,17 @@ version = Versions.Mirai.consoleGraphical
description = "Graphical frontend for mirai-console"
dependencies {
compileOnly("net.mamoe:mirai-core:${Versions.Mirai.core}")
compileOnly("net.mamoe:mirai-core:${Versions.core}")
implementation(project(":mirai-console"))
api(group = "no.tornado", name = "tornadofx", version = "1.7.19")
api(group = "com.jfoenix", name = "jfoenix", version = "9.0.8")
testApi(project(":mirai-console"))
testApi(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
testApi(kotlinx("coroutines-core", Versions.coroutines))
testApi(group = "org.yaml", name = "snakeyaml", version = "1.25")
testApi("net.mamoe:mirai-core:${Versions.Mirai.core}")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
testApi("net.mamoe:mirai-core:${Versions.core}")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
}
kotlin {
@ -47,7 +47,7 @@ kotlin {
all {
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.progressiveMode = true
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
}

View File

@ -1,22 +1,18 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm")
kotlin("plugin.serialization")
kotlin("jvm") version Versions.kotlin
kotlin("plugin.serialization") version Versions.kotlin
id("java")
`maven-publish`
id("com.jfrog.bintray")
id("com.jfrog.bintray") version Versions.bintray
}
apply(plugin = "com.github.johnrengelman.shadow")
kotlin {
sourceSets {
all {
languageSettings.enableLanguageFeature("InlineClasses")
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.progressiveMode = true
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
@ -25,31 +21,19 @@ kotlin {
}
}
}
dependencies {
compileOnly(project(":mirai-console"))
compileOnly("net.mamoe:mirai-core:${Versions.Mirai.core}")
compileOnly(kotlin("stdlib")) // embedded by core
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
dependencies {
implementation("org.jline:jline:3.15.0")
implementation("org.fusesource.jansi:jansi:1.18")
compileAndRuntime(project(":mirai-console"))
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
compileAndRuntime(kotlin("stdlib")) // embedded by core
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
testApi(project(":mirai-console"))
}
version = Versions.Mirai.consolePure
version = Versions.consolePure
description = "Console Pure CLI frontend for mirai"
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
}
description = "Console Pure CLI frontend for mirai"

View File

@ -0,0 +1,37 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*
*/
package net.mamoe.mirai.console.pure
import org.jline.reader.LineReader
import org.jline.reader.LineReaderBuilder
import org.jline.reader.impl.completer.NullCompleter
import org.jline.terminal.Terminal
import org.jline.terminal.TerminalBuilder
object ConsoleUtils {
val lineReader: LineReader
val terminal: Terminal
init {
val dumb = System.getProperty("java.class.path")
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null
terminal = TerminalBuilder.builder()
.dumb(dumb)
.build()
lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.completer(NullCompleter())
.build()
}
}

View File

@ -9,20 +9,90 @@
package net.mamoe.mirai.console.pure
import kotlinx.coroutines.delay
//import net.mamoe.mirai.console.command.CommandManager
//import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
import net.mamoe.mirai.utils.DefaultLoginSolver
import net.mamoe.mirai.utils.LoginSolver
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.PlatformLogger
import org.fusesource.jansi.Ansi
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.thread
import java.util.concurrent.ConcurrentHashMap
private val ANSI_RESET = Ansi().reset().toString()
internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
PlatformLogger(identity = it, output = { line ->
ConsoleUtils.lineReader.printAbove(line + ANSI_RESET)
})
}
@Suppress("unused")
object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
private val globalLogger = LoggerCreator("Mirai")
private val cachedLoggers = ConcurrentHashMap<String, MiraiLogger>()
// companion object {
// ANSI color codes
const val COLOR_RED = "\u001b[38;5;196m"
const val COLOR_CYAN = "\u001b[38;5;87m"
const val COLOR_GREEN = "\u001b[38;5;82m"
// use a dark yellow(more like orange) instead of light one to save Solarized-light users
const val COLOR_YELLOW = "\u001b[38;5;220m"
const val COLOR_GREY = "\u001b[38;5;244m"
const val COLOR_BLUE = "\u001b[38;5;27m"
const val COLOR_NAVY = "\u001b[38;5;24m" // navy uniform blue
const val COLOR_PINK = "\u001b[38;5;207m"
const val COLOR_RESET = "\u001b[39;49m"
// }
val sdf by lazy {
SimpleDateFormat("HH:mm:ss")
}
override fun loggerFor(identity: String?): MiraiLogger {
identity?.apply {
return cachedLoggers.computeIfAbsent(this, LoggerCreator)
}
return globalLogger
}
override fun prePushBot(identity: Long) {
}
override fun pushBot(bot: Bot) {
}
override suspend fun requestInput(hint: String): String {
if (hint.isNotEmpty()) {
ConsoleUtils.lineReader.printAbove(
Ansi.ansi()
.fgCyan().a(sdf.format(Date()))
.fgMagenta().a(hint)
.toString()
)
}
return ConsoleUtils.lineReader.readLine("> ")
}
override fun pushBotAdminStatus(identity: Long, admins: List<Long>) {
}
override fun createLoginSolver(): LoginSolver {
return DefaultLoginSolver(
input = suspend {
requestInput("")
}
)
}
}
/*
class MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
private var requesting = false
private var requestStr = ""
@ -106,4 +176,4 @@ class MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
}
*/

View File

@ -0,0 +1,46 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*
*/
@file:Suppress(
"INVISIBLE_MEMBER",
"INVISIBLE_REFERENCE",
"CANNOT_OVERRIDE_INVISIBLE_MEMBER",
"INVISIBLE_SETTER",
"INVISIBLE_GETTER",
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPE_WARNING"
)
package net.mamoe.mirai.console.pure
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.console.IMiraiConsole
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
private val delegateScope = CoroutineScope(EmptyCoroutineContext)
object MiraiConsolePure : IMiraiConsole {
override val builtInPluginLoaders: List<PluginLoader<*, *>> = LinkedList()
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure
override val mainLogger: MiraiLogger = DefaultLogger("Console")
override val rootDir: File = File("./test/console").also {
it.mkdirs()
}
override val coroutineContext: CoroutineContext
get() = delegateScope.coroutineContext
}

View File

@ -7,68 +7,67 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress(
"INVISIBLE_MEMBER",
"INVISIBLE_REFERENCE",
"CANNOT_OVERRIDE_INVISIBLE_MEMBER",
"INVISIBLE_SETTER",
"INVISIBLE_GETTER",
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPE_WARNING"
)
package net.mamoe.mirai.console.pure
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.DefaultCommands
import net.mamoe.mirai.console.plugins.PluginManager
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
import net.mamoe.mirai.console.MiraiConsoleInitializer
import net.mamoe.mirai.console.command.CommandExecuteStatus
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.command.executeCommand
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.utils.DefaultLogger
import kotlin.concurrent.thread
class MiraiConsolePureLoader {
companion object {
@JvmStatic
fun load(
coreVersion: String,
consoleVersion: String
) {
start(
MiraiConsoleFrontEndPure(),
coreVersion,
consoleVersion
)
Runtime.getRuntime().addShutdownHook(thread(start = false) {
MiraiConsole.stop()
})
object MiraiConsolePureLoader {
@JvmStatic
fun main(args: Array<String>?) {
startup()
}
}
internal fun startup() {
MiraiConsoleInitializer.init(MiraiConsolePure)
startConsoleThread()
}
internal fun startConsoleThread() {
thread(name = "Console", isDaemon = false) {
val consoleLogger = DefaultLogger("Console")
kotlinx.coroutines.runBlocking {
while (true) {
val next = MiraiConsoleFrontEndPure.requestInput("")
consoleLogger.debug("INPUT> $next")
val result = ConsoleCS.executeCommand(PlainText(next))
when (result.status) {
CommandExecuteStatus.SUCCESSFUL -> {
}
CommandExecuteStatus.EMPTY_COMMAND -> {
}
CommandExecuteStatus.FAILED -> {
consoleLogger.error("An error occurred while executing the command: $next", result.exception)
}
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
consoleLogger.warning("Unknown command: ${result.commandName}")
}
}
}
}
}
}
/**
* 启动 Console
*/
@JvmOverloads
internal fun start(
frontEnd: MiraiConsoleFrontEnd,
coreVersion: String = "0.0.0",
consoleVersion: String = "0.0.0",
path: String = System.getProperty("user.dir")
) {
if (MiraiConsole.started) {
return
object ConsoleCS : ConsoleCommandSender() {
override suspend fun sendMessage(message: Message) {
ConsoleUtils.lineReader.printAbove(message.contentToString())
}
MiraiConsole.started = true
this.path = path
/* 初始化前端 */
this.version = consoleVersion
this.frontEnd = frontEnd
this.frontEnd.pushVersion(consoleVersion, MiraiConsole.build, coreVersion)
logger("Mirai-console now running under $path")
logger("Get news in github: https://github.com/mamoe/mirai")
logger("Mirai为开源项目请自觉遵守开源项目协议")
logger("Powered by Mamoe Technologies and contributors")
/* 依次启用功能 */
DefaultCommands()
PluginManager.loadPlugins()
CommandManager.start()
/* 通知启动完成 */
logger("Mirai-console 启动完成")
logger("\"login qqnumber qqpassword \" to login a bot")
logger("\"login qq号 qq密码 \" 来登录一个BOT")
/* 尝试从系统配置自动登录 */
DefaultCommands.tryLoginAuto()
}

View File

@ -20,7 +20,7 @@ kotlin {
all {
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.progressiveMode = true
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
}
@ -28,7 +28,7 @@ kotlin {
}
dependencies {
compileOnly("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
compileOnly("net.mamoe:mirai-core-qqandroid:${Versions.core}")
api(project(":mirai-console"))
api(group = "com.googlecode.lanterna", name = "lanterna", version = "3.0.2")
}

View File

@ -1,5 +1,5 @@
#Wed Mar 04 22:27:09 CST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists

View File

@ -1,72 +0,0 @@
pluginManagement {
resolutionStrategy {
eachPlugin {
switch (requested.id.id) {
case "org.jetbrains.kotlin.multiplatform": useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}"); break
case "com.android.library": useModule("com.android.tools.build:gradle:${requested.version}"); break
case "com.jfrog.bintray": useModule("com.jfrog.bintray.gradle:gradle-bintray-plugin:${requested.version}")
}
}
}
repositories {
mavenLocal()
jcenter()
google()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" }
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
maven { url 'https://plugins.gradle.org/m2/' }
}
}
rootProject.name = 'mirai-console'
def onlyBackEnd = true
include(':mirai-console')
project(':mirai-console').dir = file("backend/mirai-console")
include(':codegen')
project(':codegen').dir = file("backend/codegen")
if (!onlyBackEnd) {
include(':mirai-console-pure')
project(':mirai-console-pure').dir = file("frontend/mirai-console-pure")
include(':mirai-console-terminal')
project(':mirai-console-terminal').dir = file("frontend/mirai-console-terminal")
try {
def javaVersion = System.getProperty("java.version")
def versionPos = javaVersion.indexOf(".")
def javaVersionNum = javaVersion.substring(0, 1).toInteger()
if (javaVersion.startsWith("1.")) {
javaVersionNum = javaVersion.substring(2, 3).toInteger()
} else {
if (versionPos == -1) versionPos = javaVersion.indexOf("-")
if (versionPos == -1) {
println("jdk version unknown")
} else {
javaVersionNum = javaVersion.substring(0, versionPos).toInteger()
}
}
if (javaVersionNum >= 9) {
include(':mirai-console-graphical')
project(':mirai-console-graphical').dir = file("frontend/mirai-console-graphical")
} else {
println("jdk版本为 " + javaVersionNum)
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 请使用JDK 9以上版本引入模块 `:mirai-console-graphical`\n")
}
} catch (Exception ignored) {
println("无法确定 JDK 版本, 将不会引入 `:mirai-console-graphical`")
}
}
enableFeaturePreview('GRADLE_METADATA')

53
settings.gradle.kts Normal file
View File

@ -0,0 +1,53 @@
pluginManagement {
repositories {
mavenLocal()
jcenter()
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
mavenCentral()
}
resolutionStrategy {
eachPlugin {
val version = requested.version
when (requested.id.id) {
"org.jetbrains.kotlin.jvm" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${version}")
"org.jetbrains.kotlin.plugin.serialization" -> useModule("org.jetbrains.kotlin:kotlin-serialization:${version}")
"com.jfrog.bintray" -> useModule("com.jfrog.bintray.gradle:gradle-bintray-plugin:$version")
}
}
}
}
rootProject.name = "mirai-console"
val disableOldFrontEnds = true
fun includeProject(projectPath: String, path: String? = null) {
include(projectPath)
if (path != null) project(projectPath).projectDir = file(path)
}
includeProject(":mirai-console", "backend/mirai-console")
includeProject(":mirai-console.codegen", "backend/codegen")
includeProject(":mirai-console-pure", "frontend/mirai-console-pure")
@Suppress("ConstantConditionIf")
if (!disableOldFrontEnds) {
includeProject(":mirai-console-terminal", "frontend/mirai-console-terminal")
val jdkVersion = kotlin.runCatching {
System.getProperty("java.version").let { v ->
v.toIntOrNull() ?: v.removePrefix("1.").substringBefore("-").toIntOrNull()
}
}.getOrNull() ?: -1
println("JDK version: $jdkVersion")
if (jdkVersion >= 9) {
includeProject(":mirai-console-graphical", "frontend/mirai-console-graphical")
} else {
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 请使用 JDK 9 以上版本引入模块 `:mirai-console-graphical`\n")
}
}
enableFeaturePreview("GRADLE_METADATA")