From 55e70bd3c3002850df1388163abd9488e9829049 Mon Sep 17 00:00:00 2001
From: "jiahua.liu" <n@mamoe.net>
Date: Thu, 13 Feb 2020 00:10:59 +0800
Subject: [PATCH] smart config

---
 mirai-console/build.gradle.kts                |  1 +
 mirai-console/src/main/kotlin/MiraiConsole.kt | 21 ++++++--
 .../net/mamoe/mirai/plugin/ConfigSection.kt   | 52 +++++++++++++++----
 .../net/mamoe/mirai/plugin/PluginBase.kt      | 11 +---
 .../net/mamoe/mirai/utils/PlatformUtilsJvm.kt |  5 --
 5 files changed, 61 insertions(+), 29 deletions(-)

diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts
index 34c4dc570..9793ad677 100644
--- a/mirai-console/build.gradle.kts
+++ b/mirai-console/build.gradle.kts
@@ -29,5 +29,6 @@ dependencies {
     runtimeOnly(files("../mirai-core-qqandroid/build/classes/kotlin/jvm/main"))
     runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main"))
     api(kotlin("serialization"))
+    api(group = "com.alibaba", name = "fastjson", version = "1.2.62")
     // classpath is not set correctly by IDE
 }
\ No newline at end of file
diff --git a/mirai-console/src/main/kotlin/MiraiConsole.kt b/mirai-console/src/main/kotlin/MiraiConsole.kt
index 1cd97a72e..20c177d7d 100644
--- a/mirai-console/src/main/kotlin/MiraiConsole.kt
+++ b/mirai-console/src/main/kotlin/MiraiConsole.kt
@@ -45,9 +45,11 @@ object MiraiConsole {
         logger("\"/login qqnumber qqpassword \" to login a bot")
         logger("\"/login qq号 qq密码 \" 来登陆一个BOT")
 
+        MiraiProperties()
         CommandManager.register(DefaultCommands.DefaultLoginCommand())
         pluginManager.loadPlugins()
         CommandListener.start()
+        println(MiraiProperties.HTTP_API_ENABLE)
     }
 
     fun stop() {
@@ -80,6 +82,7 @@ object MiraiConsole {
                 return true
             }
         }
+
     }
 
     object CommandListener {
@@ -107,17 +110,29 @@ object MiraiConsole {
 
     object DefaultLogger : MiraiConsoleLogger {
         override fun invoke(any: Any?) {
-            println("[Mirai${version} $build]: " + any?.toString())
+            if (any != null) {
+                println("[Mirai${version} $build]: " + any.toString())
+            }
         }
     }
 
-    @UnstableDefault
     object MiraiProperties {
+        val init = !File("$path/mirai.json").exists()
+
         var config = Config.load("$path/mirai.json")
 
         var HTTP_API_ENABLE: Boolean by config.withDefault { true }
         var HTTP_API_PORT: Int by config.withDefault { 8080 }
-        var HTTP_API_AUTH_KEY: String by config.withDefault { "INITKEY" + generateSessionKey() }
+        var HTTP_API_AUTH_KEY: String by config
+
+        operator fun invoke() {
+            if (init) {
+                HTTP_API_AUTH_KEY = "INITKEY" + generateSessionKey()
+                logger("Mirai HTTPAPI authkey 已随机生成, 请注意修改")
+            }
+        }
+
+
     }
 }
 
diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/ConfigSection.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/ConfigSection.kt
index d29736bc4..f361d7b67 100644
--- a/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/ConfigSection.kt
+++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/ConfigSection.kt
@@ -9,15 +9,21 @@
 
 package net.mamoe.mirai.plugin
 
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.UnstableDefault
+import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
+import com.alibaba.fastjson.TypeReference
+import com.alibaba.fastjson.parser.Feature
+import kotlinx.serialization.*
 import kotlinx.serialization.json.Json
+import net.mamoe.mirai.utils.cryptor.contentToString
 import java.io.File
 import java.util.concurrent.ConcurrentHashMap
+import java.util.function.BiConsumer
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
 import kotlin.reflect.full.isSubclassOf
 
+
 /**
  * TODO: support all config types
  * only JSON is now supported
@@ -31,6 +37,7 @@ interface Config {
     fun getFloat(key: String): Float
     fun getDouble(key: String): Double
     fun getLong(key: String): Long
+    fun getBoolean(key: String): Boolean
     fun getList(key: String): List<*>
     fun getStringList(key: String): List<String>
     fun getIntList(key: String): List<Int>
@@ -69,7 +76,7 @@ inline fun <reified T : Any> Config.withDefault(
             if (!this@withDefault.exist(property.name)) {
                 return defaultValue.invoke()
             }
-            return getValue(thisRef, property)
+            return smartCast(property)
         }
 
         override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
@@ -80,13 +87,14 @@ inline fun <reified T : Any> Config.withDefault(
 }
 
 @Suppress("IMPLICIT_CAST_TO_ANY")
-inline operator fun <reified T> Config.getValue(thisRef: Any?, property: KProperty<*>): T {
+inline fun <reified T> Config.smartCast(property: KProperty<*>): T {
     return when (T::class) {
         String::class -> this.getString(property.name)
         Int::class -> this.getInt(property.name)
         Float::class -> this.getFloat(property.name)
         Double::class -> this.getDouble(property.name)
         Long::class -> this.getLong(property.name)
+        Boolean::class -> this.getBoolean(property.name)
         else -> when {
             T::class.isSubclassOf(ConfigSection::class) -> this.getConfigSection(property.name)
             T::class == List::class || T::class == MutableList::class -> {
@@ -113,8 +121,13 @@ inline operator fun <reified T> Config.getValue(thisRef: Any?, property: KProper
     } as T
 }
 
+inline operator fun <reified T> Config.getValue(thisRef: Any?, property: KProperty<*>): T {
+    return smartCast(property)
+}
+
 inline operator fun <reified T> Config.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
     this[property.name] = value!!
+    this.save()
 }
 
 
@@ -135,6 +148,10 @@ interface ConfigSection : Config {
         return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toFloat()
     }
 
+    override fun getBoolean(key: String): Boolean {
+        return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toBoolean()
+    }
+
     override fun getDouble(key: String): Double {
         return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toDouble()
     }
@@ -175,7 +192,7 @@ interface ConfigSection : Config {
 @Serializable
 open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection {
     override fun set(key: String, value: Any) {
-        this[key] = value
+        this.put(key, value)
     }
 
     override operator fun get(key: String): Any? {
@@ -238,17 +255,30 @@ class JsonConfig internal constructor(file: File) : FileConfigImpl(file) {
         if (content.isEmpty() || content.isBlank() || content == "{}") {
             return ConfigSectionImpl()
         }
-        return Json.parse(
-            ConfigSectionImpl.serializer(),
-            content
+        val section = ConfigSectionImpl()
+        val map: LinkedHashMap<String, Any> = JSON.parseObject(
+            content,
+            object : TypeReference<LinkedHashMap<String, Any>>() {},
+            Feature.OrderedField
         )
+        map.forEach { (t, u) ->
+            section[t] = u
+        }
+        return section
     }
 
     @UnstableDefault
     override fun serialize(config: ConfigSectionImpl): String {
-        if (config.isEmpty()) {
-            return "{}"
+        return JSONObject.toJSONString(config)
+    }
+
+    internal class AnySerializer(override val descriptor: SerialDescriptor) : KSerializer<Any> {
+        override fun deserialize(decoder: Decoder): Any {
+            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+        }
+
+        override fun serialize(encoder: Encoder, obj: Any) {
+            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
         }
-        return Json.stringify(ConfigSectionImpl.serializer(), config)
     }
 }
\ No newline at end of file
diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/PluginBase.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/PluginBase.kt
index 0975192e3..d4cbc60ef 100644
--- a/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/PluginBase.kt
+++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/PluginBase.kt
@@ -67,18 +67,9 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope {
         this.onEnable()
     }
 
-    /**
-     * TODO: support all config types
-     */
 
-    @UnstableDefault
     fun loadConfig(fileName: String): Config {
-        return JsonConfig.load(File(fileName))
-    }
-
-    @UnstableDefault
-    fun saveConfig(config: Config, fileName: String = "config.json") {
-        JsonConfig.save(file = File(fileName), config = config as JsonConfig)
+        return Config.load(File(fileName))
     }
 
 
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
index 840072726..d952acdba 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
@@ -74,8 +74,3 @@ actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
         return output.toByteArray()
     }
 }
-
-/**
- * 时间戳
- */
-actual val currentTimeMillis: Long get() = System.currentTimeMillis()
\ No newline at end of file