Explorar o código

add permissionGroupCmd
use Kotson to handle json instead of FastJson

00ll00 %!s(int64=5) %!d(string=hai) anos
pai
achega
43bdcb1f98
Modificáronse 36 ficheiros con 612 adicións e 302 borrados
  1. 1 1
      build.gradle.kts
  2. 1 0
      src/main/kotlin/bot_luo_core/bot/BotLuo.kt
  3. 0 3
      src/main/kotlin/bot_luo_core/cli/Checker.kt
  4. 5 0
      src/main/kotlin/bot_luo_core/cli/CmdCatalog.kt
  5. 3 0
      src/main/kotlin/bot_luo_core/cli/CmdPermissionLevel.kt
  6. 0 16
      src/main/kotlin/bot_luo_core/cli/checkers/GroupCmdWorkingChecker.kt
  7. 5 5
      src/main/kotlin/bot_luo_core/cli/checkers/PermissionChecker.kt
  8. 0 16
      src/main/kotlin/bot_luo_core/cli/checkers/UserCmdWorkingChecker.kt
  9. 19 0
      src/main/kotlin/bot_luo_core/cli/checkers/addon/GroupOriginalPermissionChecker.kt
  10. 5 4
      src/main/kotlin/bot_luo_core/cli/checkers/addon/UserOriginalPermissionChecker.kt
  11. 9 9
      src/main/kotlin/bot_luo_core/cli/commands/AnnounceCmd.kt
  12. 58 60
      src/main/kotlin/bot_luo_core/cli/commands/DataCmd.kt
  13. 4 4
      src/main/kotlin/bot_luo_core/cli/commands/DebugCmd.kt
  14. 1 2
      src/main/kotlin/bot_luo_core/cli/commands/ExecuteCmd.kt
  15. 7 11
      src/main/kotlin/bot_luo_core/cli/commands/HelpCmd.kt
  16. 7 6
      src/main/kotlin/bot_luo_core/cli/commands/ListCmd.kt
  17. 18 7
      src/main/kotlin/bot_luo_core/cli/commands/PermissionCmd.kt
  18. 249 0
      src/main/kotlin/bot_luo_core/cli/commands/PermissionGroupCmd.kt
  19. 1 1
      src/main/kotlin/bot_luo_core/cli/commands/SayCmd.kt
  20. 7 13
      src/main/kotlin/bot_luo_core/cli/commands/StatCmd.kt
  21. 27 23
      src/main/kotlin/bot_luo_core/cli/commands/SwitchCmd.kt
  22. 15 15
      src/main/kotlin/bot_luo_core/cli/commands/TagCmd.kt
  23. 1 1
      src/main/kotlin/bot_luo_core/cli/handlers/PmsGroupArgHandler.kt
  24. 2 2
      src/main/kotlin/bot_luo_core/data/Config.kt
  25. 3 0
      src/main/kotlin/bot_luo_core/data/Data.kt
  26. 20 28
      src/main/kotlin/bot_luo_core/data/DataArray.kt
  27. 24 19
      src/main/kotlin/bot_luo_core/data/DataObject.kt
  28. 16 1
      src/main/kotlin/bot_luo_core/data/Group.kt
  29. 24 3
      src/main/kotlin/bot_luo_core/data/PmsGroup.kt
  30. 36 35
      src/main/kotlin/bot_luo_core/data/PmsGroups.kt
  31. 16 1
      src/main/kotlin/bot_luo_core/data/User.kt
  32. 13 0
      src/main/kotlin/bot_luo_core/util/GsonHelper.kt
  33. 5 5
      src/main/kotlin/bot_luo_core/util/JsonWorker.kt
  34. 3 3
      src/main/kotlin/bot_luo_core/util/TableBuilder.kt
  35. 5 5
      src/main/kotlin/bot_luo_core/util/Tag.kt
  36. 2 3
      src/main/kotlin/bot_luo_core/util/Text.kt

+ 1 - 1
build.gradle.kts

@@ -4,7 +4,7 @@ version = "1.4.0"
 dependencies {
     implementation(kotlin("stdlib"))
     api("net.mamoe:mirai-core:2.7.1-dev-1")
-    implementation("com.alibaba:fastjson:1.2.73")
+    api("com.github.salomonbrys.kotson:kotson:2.5.0")
     implementation("org.reflections:reflections:0.9.12")
     implementation ("org.apache.logging.log4j:log4j-api:2.14.0")
     implementation ("org.apache.logging.log4j:log4j-core:2.14.0")

+ 1 - 0
src/main/kotlin/bot_luo_core/bot/BotLuo.kt

@@ -106,6 +106,7 @@ object BotLuo {
         GlobalEventChannel.subscribeAlways<MessageEvent>(priority = EventPriority.NORMAL) {
             if (this.sender is AnonymousMember) return@subscribeAlways
             if (this is MessageSyncEvent) return@subscribeAlways
+//            if (this !is GroupAwareMessageEvent || this.group.id != 565056329L) return@subscribeAlways
             if (this is GroupAwareMessageEvent && !bot.isMainBotOf(this.group.id)) return@subscribeAlways
             CmdHandler.call(this)
             if (this.isIntercepted) Logger.log(this, Level.INFO) else Logger.log(this, Level.DEBUG)

+ 0 - 3
src/main/kotlin/bot_luo_core/cli/Checker.kt

@@ -3,7 +3,6 @@ package bot_luo_core.cli
 import bot_luo_core.cli.checkers.*
 import bot_luo_core.cli.exceptions.CheckerFatal
 import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.content
 import net.mamoe.mirai.message.data.toPlainText
 import kotlin.jvm.Throws
 import kotlin.reflect.KClass
@@ -31,11 +30,9 @@ interface Checker {
         val CHECKERS = arrayListOf(
             BotRunningChecker::class,
             GroupCDChecker::class,
-            GroupCmdWorkingChecker::class,
             GroupDayLimitChecker::class,
             PermissionChecker::class,
             UserCDChecker::class,
-            UserCmdWorkingChecker::class,
             UserDayLimitChecker::class,
             UserParallelExecutingChecker::class
         )

+ 5 - 0
src/main/kotlin/bot_luo_core/cli/CmdCatalog.kt

@@ -3,7 +3,9 @@ package bot_luo_core.cli
 import bot_luo_core.cli.annotation.Method
 import bot_luo_core.cli.commands.*
 import bot_luo_core.cli.exceptions.CliInternalError
+import bot_luo_core.util.Logger
 import bot_luo_core.util.Text.escapeRegex
+import org.apache.logging.log4j.Level
 import java.lang.IllegalArgumentException
 import java.util.*
 import kotlin.collections.ArrayList
@@ -22,6 +24,7 @@ object CmdCatalog {
 
     init {
         //注册内建命令
+        Logger.sysLog(Level.DEBUG, "开始注册内建命令……")
         registerCmd(
             AnnounceCmd::class,
             ControlCmd::class,
@@ -32,11 +35,13 @@ object CmdCatalog {
             HelpCmd::class,
             ListCmd::class,
             PermissionCmd::class,
+            PermissionGroupCmd::class,
             SayCmd::class,
             StatCmd::class,
             SwitchCmd::class,
             TagCmd::class
         )
+        Logger.sysLog(Level.DEBUG, "内建命令注册完毕")
     }
 
     /**

+ 3 - 0
src/main/kotlin/bot_luo_core/cli/CmdPermissionLevel.kt

@@ -2,6 +2,9 @@ package bot_luo_core.cli
 
 import java.util.*
 
+/**
+ * # 命令权限等级
+ */
 enum class CmdPermissionLevel {
     NONE,
     NORMAL,

+ 0 - 16
src/main/kotlin/bot_luo_core/cli/checkers/GroupCmdWorkingChecker.kt

@@ -1,16 +0,0 @@
-package bot_luo_core.cli.checkers
-
-import bot_luo_core.cli.Checker
-import bot_luo_core.cli.CmdContext
-import bot_luo_core.cli.CmdExecutable
-import net.mamoe.mirai.message.data.toPlainText
-
-class GroupCmdWorkingChecker: Checker {
-
-    override val name = "群组命令启用检定器"
-
-    override fun check(cmd: CmdExecutable, context: CmdContext) {
-        val data = context.group.readCmdData(cmd)
-        if (!data.working) throw CheckerFatal("此命令未在群组 ${context.group.name}(${context.group.id}) 启用")
-    }
-}

+ 5 - 5
src/main/kotlin/bot_luo_core/cli/checkers/PermissionChecker.kt

@@ -11,11 +11,11 @@ class PermissionChecker: Checker {
     override val name = "权限检定器"
 
     override fun check(cmd: CmdExecutable, context: CmdContext) {
-        val p1 = context.userF.pmsGroup.readPmsOn(cmd)
-        val p2 = context.groupF.pmsGroup.readPmsOn(cmd)
-        val b1 = p1 >= cmd.pmsLevel
-        val b2 = p2 >= cmd.pmsLevel
-        if (!b1 && !b2) throw CheckerFatal("权限不足,要求:${cmd.pmsLevel.name} 群组权限:${p2.name} 用户权限:${p1.name}")
+        val p1 = context.userF.realPmsGroup
+        val p2 = context.groupF.realPmsGroup
+        val i1 = p1.readPmsOn(cmd)
+        val i2 = p2.readPmsOn(cmd)
+        if (i1 + i2 <= 0) throw CheckerFatal("权限不足,要求:${cmd.pmsLevel.name} 群组权限:${p2.name}($i2) 用户权限:${p1.name}($i1)")
     }
 
 }

+ 0 - 16
src/main/kotlin/bot_luo_core/cli/checkers/UserCmdWorkingChecker.kt

@@ -1,16 +0,0 @@
-package bot_luo_core.cli.checkers
-
-import bot_luo_core.cli.Checker
-import bot_luo_core.cli.CmdContext
-import bot_luo_core.cli.CmdExecutable
-import net.mamoe.mirai.message.data.toPlainText
-
-class UserCmdWorkingChecker: Checker {
-
-    override val name = "用户命令启用检定器"
-
-    override fun check(cmd: CmdExecutable, context: CmdContext) {
-        val data = context.user.readCmdData(cmd)
-        if (!data.working) throw CheckerFatal("此命令已对用户 ${context.user.name}(${context.user.id}) 关闭")
-    }
-}

+ 19 - 0
src/main/kotlin/bot_luo_core/cli/checkers/addon/GroupOriginalPermissionChecker.kt

@@ -0,0 +1,19 @@
+package bot_luo_core.cli.checkers.addon
+
+import bot_luo_core.cli.Checker
+import bot_luo_core.cli.CmdContext
+import bot_luo_core.cli.CmdExecutable
+import bot_luo_core.data.PmsGroups.readPmsOn
+import net.mamoe.mirai.message.data.toPlainText
+
+class GroupOriginalPermissionChecker: Checker {
+
+    override val name = "群组原始权限检定器"
+
+    override fun check(cmd: CmdExecutable, context: CmdContext) {
+        val p = context.group.pmsGroup
+        val i = p.readPmsOn(cmd)
+        if (i < 1) throw CheckerFatal("群组原始权限不足,要求:${cmd.pmsLevel.name} 群组原始权限:${p.name}($i)")
+    }
+
+}

+ 5 - 4
src/main/kotlin/bot_luo_core/cli/checkers/addon/GroupPermissionChecker.kt → src/main/kotlin/bot_luo_core/cli/checkers/addon/UserOriginalPermissionChecker.kt

@@ -6,13 +6,14 @@ import bot_luo_core.cli.CmdExecutable
 import bot_luo_core.data.PmsGroups.readPmsOn
 import net.mamoe.mirai.message.data.toPlainText
 
-class GroupPermissionChecker: Checker {
+class UserOriginalPermissionChecker: Checker {
 
-    override val name = "群组权限检定器"
+    override val name = "用户原始权限检定器"
 
     override fun check(cmd: CmdExecutable, context: CmdContext) {
-        val p = context.group.pmsGroup.readPmsOn(cmd)
-        if (p < cmd.pmsLevel) throw CheckerFatal("群组权限不足,要求:${cmd.pmsLevel.name} 群组权限:${p.name}")
+        val p = context.user.pmsGroup
+        val i = p.readPmsOn(cmd)
+        if (i < 1) throw CheckerFatal("用户原始权限不足,要求:${cmd.pmsLevel.name} 用户原始权限:${p.name}($i)")
     }
 
 }

+ 9 - 9
src/main/kotlin/bot_luo_core/cli/commands/AnnounceCmd.kt

@@ -58,11 +58,11 @@ class AnnounceCmd(context: CmdContext) : Cmd(context) {
         val t = TableBuilder(4)
         t.th("公告预览 ——").br()
 
-        t.tr("置顶:").tb(builder.isPinned)
-        t.tr("弹窗:").tb(builder.showPopup)
-        t.tr("编辑名片:").tb(builder.showEditCard)
-        t.tr("需要确认:").tb(builder.requireConfirmation)
-        t.tr("仅新成员:").tb(builder.sendToNewMember)
+        t.tr("置顶:").td(builder.isPinned)
+        t.tr("弹窗:").td(builder.showPopup)
+        t.tr("编辑名片:").td(builder.showEditCard)
+        t.tr("需要确认:").td(builder.requireConfirmation)
+        t.tr("仅新成员:").td(builder.sendToNewMember)
         t.p("头图:").sp()
         if (image != null) t.p(image)
         t.br()
@@ -90,16 +90,16 @@ class AnnounceCmd(context: CmdContext) : Cmd(context) {
                     connection.disconnect()
                 }
                 contact.publishAnnouncement(content, builder.build())
-                table.tr("[√]").tb("${group.name}(${group.id})")
+                table.tr("[√]").td("${group.name}(${group.id})")
                 suc ++
                 delay((3000L..6000L).random())
             } catch (e: IOException) {
-                table.tr("[×]").tb("${group.name}(${group.id})").tb("网络异常")
+                table.tr("[×]").td("${group.name}(${group.id})").td("网络异常")
             } catch (e: IllegalStateException) {
-                table.tr("[×]").tb("${group.name}(${group.id})").tb("协议异常")
+                table.tr("[×]").td("${group.name}(${group.id})").td("协议异常")
             }
 
-        }.let { if (!it) table.tr("[×]").tb("${group.name}(${group.id})").tb("无权限") } }
+        }.let { if (!it) table.tr("[×]").td("${group.name}(${group.id})").td("无权限") } }
         context.print(table.toString())
         return if (suc > 0) SUCCESS else FATAL
     }

+ 58 - 60
src/main/kotlin/bot_luo_core/cli/commands/DataCmd.kt

@@ -4,15 +4,17 @@ import bot_luo_core.cli.*
 import bot_luo_core.cli.annotation.Argument
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.GroupCmdWorkingChecker
+import bot_luo_core.cli.checkers.addon.UserOriginalPermissionChecker
 import bot_luo_core.cli.exceptions.SyntaxError
 import bot_luo_core.cli.handlers.GroupArgHandler
 import bot_luo_core.cli.handlers.JsonPathArgHandler
 import bot_luo_core.cli.handlers.UserArgHandler
 import bot_luo_core.data.*
 import bot_luo_core.util.TableBuilder
-import com.alibaba.fastjson.JSONArray
-import com.alibaba.fastjson.JSONObject
+import bot_luo_core.util.set
+import com.github.salomonbrys.kotson.add
+import com.github.salomonbrys.kotson.keys
+import com.google.gson.*
 import kotlin.jvm.Throws
 
 @Command(
@@ -28,12 +30,12 @@ class DataCmd(context: CmdContext) : Cmd(context) {
 
     /*  ========================  get  ========================  */
 
-    fun get(dataObject: DataObject, objName: String, objId: String = "", path: ArrayList<String> = ArrayList()): CmdReceipt {
+    fun get(data: Data, objName: String, objId: String = "", path: ArrayList<String> = ArrayList()): CmdReceipt {
         val table = TableBuilder(4)
         table.th("${objName}数据 —— $objId")
         table.p("/"+path.joinToString("/")).br().br()
         try {
-            readByPath(dataObject.jsonObj, path, table)
+            readByPath(data.element, path, table)
             context.print(table.toString())
             return SUCCESS
         } catch (e: NullPointerException) {
@@ -50,7 +52,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         table.th("${arrayName}数据 —— $arrayId")
         table.p("/"+path.joinToString("/")).br().br()
         try {
-            readByPath(dataArray.jsonArray, path, table)
+            readByPath(dataArray.element, path, table)
             context.print(table.toString())
             return SUCCESS
         } catch (e: NullPointerException) {
@@ -62,7 +64,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         return FATAL
     }
 
-    @Method(name = "", alias = ["get-group","gg","g"], pmsLevel = CmdPermissionLevel.OP, ignoreCheckers = [GroupCmdWorkingChecker::class], title = "获取群组数据")
+    @Method(name = "", alias = ["get-group","gg","g"], pmsLevel = CmdPermissionLevel.OP, addonCheckers = [UserOriginalPermissionChecker::class], title = "获取群组数据")
     fun getGroup (
         @Argument(name = "群组", handler = GroupArgHandler::class)
         group: Group,
@@ -70,7 +72,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         pathIn: ArrayList<String>?
     ): CmdReceipt = get(group, "群组", "${group.name}(${group.id})", pathIn?: ArrayList())
 
-    @Method(name = "get-user", alias = ["gu","u"], pmsLevel = CmdPermissionLevel.OP, ignoreCheckers = [GroupCmdWorkingChecker::class], title = "获取用户数据")
+    @Method(name = "get-user", alias = ["gu","u"], pmsLevel = CmdPermissionLevel.OP, addonCheckers = [UserOriginalPermissionChecker::class], title = "获取用户数据")
     fun getUser (
         @Argument(name = "用户", handler = UserArgHandler::class)
         user: User,
@@ -86,8 +88,8 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         table.p("/"+path.joinToString("/")).br().br()
         try { withLockedAccessing(dataObject) {
             dataObject.markDirty()
-            val (old, new) = writeByPathTyped(dataObject.jsonObj, path, defaultCast(value))
-            table.tr(formatValue(old)).tb("->").tb(formatValue(new))
+            val (old, new) = writeByPathTyped(dataObject.element, path, defaultCast(value))
+            table.tr(formatValue(old)).td("->").td(formatValue(new))
             context.print(table.toString())
             return SUCCESS
         } } catch (e: NullPointerException) {
@@ -105,8 +107,8 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         table.p("/"+path.joinToString("/")).br().br()
         try { withLockedAccessing(dataArray) {
             dataArray.markDirty()
-            val (old, new) = writeByPathTyped(dataArray.jsonArray, path, defaultCast(value))
-            table.tr(formatValue(old)).tb("->").tb(formatValue(new))
+            val (old, new) = writeByPathTyped(dataArray.element, path, defaultCast(value))
+            table.tr(formatValue(old)).td("->").td(formatValue(new))
             context.print(table.toString())
             return SUCCESS
         } } catch (e: NullPointerException) {
@@ -124,8 +126,8 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         table.p("/"+path.joinToString("/")).br().br()
         try { withLockedAccessing(dataObject) {
             dataObject.markDirty()
-            val (old, new) = writeByPathTyped(dataObject.jsonObj, path, value castTo type)
-            table.tr(formatValue(old)).tb("->").tb(formatValue(new))
+            val (old, new) = writeByPathTyped(dataObject.element, path, value castTo type)
+            table.tr(formatValue(old)).td("->").td(formatValue(new))
             context.print(table.toString())
             return SUCCESS
         } } catch (e: NullPointerException) {
@@ -143,8 +145,8 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         table.p("/"+path.joinToString("/")).br().br()
         try { withLockedAccessing(dataArray) {
             dataArray.markDirty()
-            val (old, new) = writeByPathTyped(dataArray.jsonArray, path, value castTo type)
-            table.tr(formatValue(old)).tb("->").tb(formatValue(new))
+            val (old, new) = writeByPathTyped(dataArray.element, path, value castTo type)
+            table.tr(formatValue(old)).td("->").td(formatValue(new))
             context.print(table.toString())
             return SUCCESS
         } } catch (e: NullPointerException) {
@@ -156,7 +158,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         return FATAL
     }
 
-    @Method(name = "set-group", alias = ["sg"], pmsLevel = CmdPermissionLevel.DEBUG, ignoreCheckers = [GroupCmdWorkingChecker::class], order = 0, title = "修改群组数据")
+    @Method(name = "set-group", alias = ["sg"], pmsLevel = CmdPermissionLevel.DEBUG, addonCheckers = [UserOriginalPermissionChecker::class], order = 0, title = "修改群组数据")
     suspend fun setGroup (
         @Argument(name = "群组", handler = GroupArgHandler::class)
         group: Group,
@@ -166,7 +168,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         value: String
     ): CmdReceipt = set(group, "群组", "${group.name}(${group.id})", path, value)
 
-    @Method(name = "set-user", alias = ["su"], pmsLevel = CmdPermissionLevel.DEBUG, ignoreCheckers = [GroupCmdWorkingChecker::class], order = 0, title = "修改用户数据")
+    @Method(name = "set-user", alias = ["su"], pmsLevel = CmdPermissionLevel.DEBUG, addonCheckers = [UserOriginalPermissionChecker::class], order = 0, title = "修改用户数据")
     suspend fun setUser (
         @Argument(name = "用户", handler = UserArgHandler::class)
         user: User,
@@ -176,7 +178,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         value: String
     ): CmdReceipt = set(user, "用户", "${user.name}(${user.id})", path, value)
 
-    @Method(name = "set-group", alias = ["sg"], pmsLevel = CmdPermissionLevel.DEBUG, ignoreCheckers = [GroupCmdWorkingChecker::class], order = 1, title = "修改群组数据")
+    @Method(name = "set-group", alias = ["sg"], pmsLevel = CmdPermissionLevel.DEBUG, addonCheckers = [UserOriginalPermissionChecker::class], order = 1, title = "修改群组数据")
     suspend fun setGroup (
         @Argument(name = "群组", handler = GroupArgHandler::class)
         group: Group,
@@ -191,7 +193,7 @@ class DataCmd(context: CmdContext) : Cmd(context) {
         type: String
     ): CmdReceipt  = set(group, "群组", "${group.name}(${group.id})", path, value, type)
 
-    @Method(name = "set-user", alias = ["su"], pmsLevel = CmdPermissionLevel.DEBUG, ignoreCheckers = [GroupCmdWorkingChecker::class], order = 1, title = "修改用户数据")
+    @Method(name = "set-user", alias = ["su"], pmsLevel = CmdPermissionLevel.DEBUG, addonCheckers = [UserOriginalPermissionChecker::class], order = 1, title = "修改用户数据")
     suspend fun setUser (
         @Argument(name = "用户", handler = UserArgHandler::class)
         user: User,
@@ -216,8 +218,8 @@ class DataCmd(context: CmdContext) : Cmd(context) {
                 "str","string","charsequence" -> this
                 "bool","boolean" -> this.toBoolean()
                 "null" -> if (this == "null") return null else throw SyntaxError(-1, "无法将“$this”转换为Null")
-                "array","list" -> if (this == "[]") return JSONArray() else throw SyntaxError(-1, "无法将“$this”转换为Array")
-                "object","obj" -> if (this == "{}") return JSONObject() else throw SyntaxError(-1, "无法将“$this”转换为Object")
+                "array","list" -> if (this == "[]") return JsonArray() else throw SyntaxError(-1, "无法将“$this”转换为Array")
+                "object","obj" -> if (this == "{}") return JsonObject() else throw SyntaxError(-1, "无法将“$this”转换为Object")
                 else -> throw SyntaxError(-1, "未知的类型“$type”")
             }
         }
@@ -227,56 +229,54 @@ class DataCmd(context: CmdContext) : Cmd(context) {
                 input.toBigDecimalOrNull()?:
                 input.toBooleanStrictOrNull()?:
                 when (input) {
-                    "{}" -> JSONObject()
-                    "[]" -> JSONArray()
+                    "{}" -> JsonObject()
+                    "[]" -> JsonArray()
                     "null" -> null
                     else -> input
                 }
         }
 
-        private fun objectGet(obj: JSONObject, table: TableBuilder) {
-            for ((k, v) in obj.innerMap) {
-                table.tr("$k:").tb(formatValue(v))
+        private fun objectGet(obj: JsonObject, table: TableBuilder) {
+            for ((k, v) in obj.entrySet()) {
+                table.tr("$k:").td(formatValue(v))
             }
         }
 
-        private fun arrayGet(array: JSONArray, table: TableBuilder) {
-            for (i in array.indices) {
-                table.tr("[$i]").tb(formatValue(array[i]))
+        private fun arrayGet(array: JsonArray, table: TableBuilder) {
+            for (i in 0 until array.size()) {
+                table.tr("[$i]").td(formatValue(array[i]))
             }
         }
 
-        private fun formatValue(value: Any?): String {
+        private fun formatValue(value: JsonElement): String {
             return when (value) {
-                is Boolean -> value.toString()
-                is Number -> value.toString()
-                is String -> "\"$value\""
-                is Map<*, *> -> "<object: ${value.size}>"
-                is List<*> -> "<array: ${value.size}>"
-                null -> "null"
+                is JsonPrimitive -> value.asString
+                is JsonObject -> "<object: ${value.size()}>"
+                is JsonArray -> "<array: ${value.size()}>"
+                is JsonNull -> "null"
                 else -> "<${value::class.simpleName}>"
             }
         }
 
         @Throws(IndexOutOfBoundsException::class, NullPointerException::class)
-        private fun readByPath(obj: Any?, path: ArrayList<String> = ArrayList(), table: TableBuilder) {
+        private fun readByPath(obj: JsonElement, path: ArrayList<String> = ArrayList(), table: TableBuilder) {
             if (path.isEmpty()) {
                 when (obj) {
-                    is JSONObject -> objectGet(obj, table)
-                    is JSONArray -> arrayGet(obj, table)
-                    null -> throw NullPointerException()
+                    is JsonObject -> objectGet(obj, table)
+                    is JsonArray -> arrayGet(obj, table)
+                    is JsonNull -> throw NullPointerException()
                     else -> table.tr(formatValue(obj))
                 }
             } else {
                 when (obj) {
-                    is JSONObject -> {
+                    is JsonObject -> {
                         val key = path[0]
-                        if (!obj.containsKey(key)) throw NullPointerException()
+                        if (key !in obj.keys()) throw NullPointerException()
                         readByPath(obj[key], path.apply { removeAt(0) }, table)
                     }
-                    is JSONArray -> {
+                    is JsonArray -> {
                         val index = path[0].toIntOrNull() ?: throw NullPointerException()
-                        if (index !in obj.indices) throw IndexOutOfBoundsException()
+                        if (index !in 0 until obj.size()) throw IndexOutOfBoundsException()
                         readByPath(obj[index], path.apply { removeAt(0) }, table)
                     }
                     else -> throw NullPointerException()
@@ -292,13 +292,12 @@ class DataCmd(context: CmdContext) : Cmd(context) {
          * 返回 [Pair] (旧数据,新数据)
          */
         @Throws(NullPointerException::class)
-        private fun writeByPathTyped(obj: Any?, path: ArrayList<String>, value: Any?): Pair<Any?,Any?> {
+        private fun writeByPathTyped(obj: JsonElement, path: ArrayList<String>, value: Any?): Pair<JsonElement,JsonElement> {
             when (path.size) {
                 0 -> throw NullPointerException()
                 1 -> {
                     when (obj) {
-                        is MutableMap<*, *> -> {
-                            obj as MutableMap<String, Any?>
+                        is JsonObject -> {
                             val oldValue = obj[path[0]]
                             if (value == null)
                                 obj.remove(path[0])
@@ -306,37 +305,36 @@ class DataCmd(context: CmdContext) : Cmd(context) {
                                 obj[path[0]] = value
                             return oldValue to obj[path[0]]
                         }
-                        is MutableList<*> -> {
-                            obj as MutableList<Any>
+                        is JsonArray -> {
                             val index = path[0].toIntOrNull()?: throw NullPointerException()
-                            if (index !in 0..obj.size) throw IndexOutOfBoundsException()
-                            val oldValue: Any?
-                            if (index == obj.size) {
-                                oldValue = null
+                            if (index !in 0..obj.size()) throw IndexOutOfBoundsException()
+                            val oldValue: JsonElement
+                            if (index == obj.size()) {
+                                oldValue = JsonNull.INSTANCE
                                 if (value != null)
                                     obj.add(value)
                             } else {
                                 oldValue = obj[index]
                                 if (value == null)
-                                    obj.removeAt(index)
+                                    obj.remove(index)
                                 else
                                     obj[index] = value
                             }
-                            return oldValue to if (value == null) null else obj[index]
+                            return oldValue to if (value == null) JsonNull.INSTANCE else obj[index]
                         }
                         else -> throw NullPointerException()
                     }
                 }
                 else -> {
                     return when (obj) {
-                        is JSONObject -> {
+                        is JsonObject -> {
                             val key = path[0]
-                            if (!obj.containsKey(key)) throw NullPointerException()
+                            if (key !in obj.keys()) throw NullPointerException()
                             writeByPathTyped(obj[key], path.apply { removeAt(0) }, value)
                         }
-                        is JSONArray -> {
+                        is JsonArray -> {
                             val index = path[0].toIntOrNull() ?: throw NullPointerException()
-                            if (index !in obj.indices) throw IndexOutOfBoundsException()
+                            if (index !in 0 until obj.size()) throw IndexOutOfBoundsException()
                             writeByPathTyped(obj[index], path.apply { removeAt(0) }, value)
                         }
                         else -> throw NullPointerException()

+ 4 - 4
src/main/kotlin/bot_luo_core/cli/commands/DebugCmd.kt

@@ -22,10 +22,10 @@ class DebugCmd(context: CmdContext) : Cmd(context) {
     fun show (): CmdReceipt {
         val table = TableBuilder(4)
         table.th("调试信息 ——").br()
-        table.tr("start_at:").tb( BotLuo.startAt relativeTo context.time)
-        table.tr("active_groups:").tb(Groups.activeGroupsCount)
-        table.tr("active_users:").tb(Users.activeUsersCount)
-        table.tr("save_jobs:").tb(Data.savingJobs.size)
+        table.tr("start_at:").td( BotLuo.startAt relativeTo context.time)
+        table.tr("active_groups:").td(Groups.activeGroupsCount)
+        table.tr("active_users:").td(Users.activeUsersCount)
+        table.tr("save_jobs:").td(Data.savingJobs.size)
         context.print(table.toString())
         return SUCCESS
     }

+ 1 - 2
src/main/kotlin/bot_luo_core/cli/commands/ExecuteCmd.kt

@@ -4,7 +4,6 @@ import bot_luo_core.cli.*
 import bot_luo_core.cli.annotation.Argument
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.GroupCmdWorkingChecker
 import bot_luo_core.cli.checkers.UserParallelExecutingChecker
 import bot_luo_core.cli.exceptions.*
 import bot_luo_core.cli.handlers.MultiGroupArgHandler
@@ -29,7 +28,7 @@ import kotlin.reflect.KType
 )
 class ExecuteCmd(context: CmdContext) : Cmd(context) {
 
-    @Method(name = "", alias = [], pmsLevel = CmdPermissionLevel.OP, ignoreCheckers = [UserParallelExecutingChecker::class, GroupCmdWorkingChecker::class])
+    @Method(name = "", alias = [], pmsLevel = CmdPermissionLevel.OP, ignoreCheckers = [UserParallelExecutingChecker::class])
     fun run (
         @Argument("子命令", handler = ExecuteArgHandler::class)
         contexts: ArrayList<CmdContext>

+ 7 - 11
src/main/kotlin/bot_luo_core/cli/commands/HelpCmd.kt

@@ -4,9 +4,7 @@ import bot_luo_core.cli.*
 import bot_luo_core.cli.annotation.Argument
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.GroupCmdWorkingChecker
-import bot_luo_core.cli.checkers.addon.GroupPermissionChecker
-import bot_luo_core.cli.checkers.UserCmdWorkingChecker
+import bot_luo_core.cli.checkers.addon.GroupOriginalPermissionChecker
 import bot_luo_core.cli.exceptions.CheckerFatal
 import bot_luo_core.cli.handlers.CmdExArgHandler
 import bot_luo_core.util.TableBuilder
@@ -73,9 +71,7 @@ class HelpCmd(context: CmdContext) : Cmd(context) {
     companion object {
 
         val justCheck = arrayOf(
-            GroupPermissionChecker::class,
-            GroupCmdWorkingChecker::class,
-            UserCmdWorkingChecker::class
+            GroupOriginalPermissionChecker::class
         )
 
         fun cmdHelpGen(cmds: List<CmdExecutable>): String? {
@@ -95,17 +91,17 @@ class HelpCmd(context: CmdContext) : Cmd(context) {
                 table.p(v[0].usage).br().br()
                 map.forEach { (_ ,value ) ->
                     table.prettyLines(value[0].subTitle, value) { item, builder ->
-                        builder.tb(item.headPara).tb(item.getParasFormat())
-                    }.tb(value[0].subUsage)
+                        builder.td(item.headPara).td(item.getParasFormat())
+                    }.td(value[0].subUsage)
                 }
                 table.th().prettyLines("示例:",spl) { item, builder ->
-                    builder.tb(item)
+                    builder.td(item)
                 }
                 table.th().prettyLines("说明:",v[0].caption.toList()) { item, builder ->
-                    builder.tb(item)
+                    builder.td(item)
                 }
                 table.th().prettyLines("注意:",v[0].notice.toList()) { item, builder ->
-                    builder.tb(item)
+                    builder.td(item)
                 }
             }
             return table.toString()

+ 7 - 6
src/main/kotlin/bot_luo_core/cli/commands/ListCmd.kt

@@ -3,8 +3,9 @@ package bot_luo_core.cli.commands
 import bot_luo_core.cli.*
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.addon.GroupPermissionChecker
+import bot_luo_core.cli.checkers.addon.GroupOriginalPermissionChecker
 import bot_luo_core.cli.exceptions.CheckerFatal
+import bot_luo_core.data.PmsGroups.readPmsOn
 import bot_luo_core.util.TableBuilder
 import kotlin.reflect.full.createInstance
 
@@ -26,11 +27,11 @@ class ListCmd(context: CmdContext) : Cmd(context) {
             if (map[cmd.cmdName]?.first == true) continue
             try {
                 justCheck.forEach { it.createInstance().check(cmd, context) }
-                map[cmd.cmdName] = group.readCmdData(cmd).working to cmd
+                map[cmd.cmdName] = (group.realPmsGroup.readPmsOn(cmd) >= 0) to cmd
             } catch (ignore: CheckerFatal) {}
         }
         map.values.forEach { (working, cmd) ->
-            table.tr(if (working) "[√]" else "[×]").tb(cmd.display).tb("(${cmd.cmdName})")
+            table.tr(if (working) "[√]" else "[×]").td(cmd.display).td("(${cmd.cmdName})")
         }
 
         context.print(table.toString())
@@ -46,7 +47,7 @@ class ListCmd(context: CmdContext) : Cmd(context) {
         for (cex in CmdCatalog.COMMANDS.distinctBy { it.id }.sortedBy { it.id }) {
             try {
                 justCheck.forEach { it.createInstance().check(cex, context) }
-                table.tr(if (group.readCmdData(cex).working) "[√]" else "[×]").tb(cex.idFixed).tb(cex.subTitle)
+                table.tr(if (group.realPmsGroup.readPmsOn(cex) >= 0) "[√]" else "[×]").td(cex.idFixed).td(cex.subTitle)
             } catch (ignore: CheckerFatal) {}
         }
 
@@ -61,7 +62,7 @@ class ListCmd(context: CmdContext) : Cmd(context) {
         table.th("命令列表 —— 群组 ${group.name}(${group.id})").br()
 
         for (cex in CmdCatalog.COMMANDS.distinctBy { it.id }.sortedBy { it.id }) {
-            table.tr(if (group.readCmdData(cex).working) "[√]" else "[×]").tb(cex.idFixed).tb(cex.subTitle)
+            table.tr(if (group.realPmsGroup.readPmsOn(cex) >= 0) "[√]" else "[×]").td(cex.idFixed).td(cex.subTitle)
         }
 
         context.print(table.toString())
@@ -71,7 +72,7 @@ class ListCmd(context: CmdContext) : Cmd(context) {
 
     companion object{
         val justCheck = arrayOf(
-            GroupPermissionChecker::class
+            GroupOriginalPermissionChecker::class
         )
     }
 }

+ 18 - 7
src/main/kotlin/bot_luo_core/cli/commands/PermissionCmd.kt

@@ -18,7 +18,10 @@ import bot_luo_core.util.TableBuilder
     name = "permission",
     display = "权限",
     alias = ["pms"],
-    usage = "查看或设置权限"
+    usage = "查看或设置权限",
+    caption = [
+        "使用pg命令查看或编辑权限组"
+    ]
 )
 class PermissionCmd(context: CmdContext) : Cmd(context) {
 
@@ -31,8 +34,12 @@ class PermissionCmd(context: CmdContext) : Cmd(context) {
     ): CmdReceipt {
         val group = groupIn?: context.group
         val table = TableBuilder(4)
-        table.th("权限组 —— 群组 ${group.name}(${group.id})").br()
-        table.tr(group.pmsGroup.name)
+        table.th("权限 —— 群组 ${group.name}(${group.id})").br()
+        table.tr("权限组:").td(group.realPmsGroup.name)
+        table.tr("描述:").td(group.realPmsGroup.description)
+        table.prettyLines("修改项:", group.pmsModify.entries) { item, builder ->
+            builder.td("${item.key}:").td(item.value)
+        }
         context.print(table.toString())
         return SUCCESS
     }
@@ -44,8 +51,12 @@ class PermissionCmd(context: CmdContext) : Cmd(context) {
     ): CmdReceipt {
         val user = userIn?: context.user
         val table = TableBuilder(4)
-        table.th("权限组 —— 用户 ${user.name}(${user.id})").br()
-        table.tr(user.pmsGroup.name)
+        table.th("权限 —— 用户 ${user.name}(${user.id})").br()
+        table.tr("权限组:").td(user.realPmsGroup.name)
+        table.tr("描述:").td(user.realPmsGroup.description)
+        table.prettyLines("修改项:", user.pmsModify.entries) { item, builder ->
+            builder.td("${item.key}:").td(item.value)
+        }
         context.print(table.toString())
         return SUCCESS
     }
@@ -63,7 +74,7 @@ class PermissionCmd(context: CmdContext) : Cmd(context) {
         val table = TableBuilder(4)
         table.th("更改权限组 —— 群组").br()
         for (group in groups) { withLockedAccessing(group) {
-            table.tr("${group.name}(${group.id})").tb(group.pmsGroup.name).tb("->").tb(pms.name)
+            table.tr("${group.name}(${group.id})").td(group.pmsGroup.name).td("->").td(pms.name)
             group.pmsGroup = pms
         } }
         context.print(table.toString())
@@ -81,7 +92,7 @@ class PermissionCmd(context: CmdContext) : Cmd(context) {
         val table = TableBuilder(4)
         table.th("更改权限组 —— 用户").br()
         for (user in users) { withLockedAccessing(user) {
-            table.tr("${user.name}(${user.id})").tb(user.pmsGroup.name).tb("->").tb(pms.name)
+            table.tr("${user.name}(${user.id})").td(user.pmsGroup.name).td("->").td(pms.name)
             user.pmsGroup = pms
         } }
         context.print(table.toString())

+ 249 - 0
src/main/kotlin/bot_luo_core/cli/commands/PermissionGroupCmd.kt

@@ -0,0 +1,249 @@
+package bot_luo_core.cli.commands
+
+import bot_luo_core.cli.*
+import bot_luo_core.cli.annotation.Argument
+import bot_luo_core.cli.annotation.Command
+import bot_luo_core.cli.annotation.Method
+import bot_luo_core.cli.exceptions.HandlerFatal
+import bot_luo_core.cli.exceptions.SyntaxError
+import bot_luo_core.cli.handlers.CmdExArgHandler
+import bot_luo_core.cli.handlers.GreedyStringArgHandler
+import bot_luo_core.cli.handlers.PmsGroupArgHandler
+import bot_luo_core.data.PmsGroup
+import bot_luo_core.data.PmsGroups
+import bot_luo_core.data.PmsGroups.readPmsOn
+import bot_luo_core.data.withLockedAccessing
+import bot_luo_core.util.TableBuilder
+import bot_luo_core.util.Text.limitEnd
+import com.github.salomonbrys.kotson.keys
+import kotlin.reflect.KType
+
+@Command(
+    name = "pmsGroup",
+    display = "权限组",
+    alias = ["pg"],
+    usage = "编辑权限组"
+)
+class PermissionGroupCmd(context: CmdContext) : Cmd(context) {
+
+    /*  ========================  get  ========================  */
+
+    @Method(name = "", alias = ["get","g"], pmsLevel = CmdPermissionLevel.OP, order = 0, title = "查看")
+    fun get (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup
+    ): CmdReceipt {
+        val table = TableBuilder(4)
+        table.th("权限组 —— ${pmsGroup.name}")
+        table.tr("继承于:").td(pmsGroup.inherit)
+        table.prettyLines("修改项:", pmsGroup.modify?.entries?: ArrayList()) { item, builder ->
+            builder.td("${item.key}:").td(item.value)
+        }
+        table.tr("描述:").sp().p(pmsGroup.description)
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    @Method(name = "", alias = ["get","g"], pmsLevel = CmdPermissionLevel.OP, order = 1, title = "查看")
+    fun get (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup,
+        @Argument(name = "命令", handler = PmsGroupArgHandler::class)
+        cmds: List<CmdExecutable>
+    ): CmdReceipt {
+        val table = TableBuilder(4)
+        table.th("权限组 —— ${pmsGroup.name}").br()
+        for (cmd in cmds) {
+            table.tr("${cmd.id}:").td(pmsGroup.readPmsOn(cmd))
+        }
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    /*  ========================  add  ========================  */
+
+    @Method(name = "add", alias = ["a"], pmsLevel = CmdPermissionLevel.OP, order = 0, title = "新建")
+    suspend fun add (
+        @Argument(name = "权限组名", handler = PmsGroupNameArgHandler::class)
+        name: String,
+        @Argument(name = "继承权限组", handler = PmsGroupArgHandler::class)
+        inherit: PmsGroup?,
+        @Argument(name = "权限组说明", handler = GreedyStringArgHandler::class)
+        description: String,
+    ): CmdReceipt {
+        if (PmsGroups.getOrNull(name) != null) {
+            context.print("权限组“$name”已存在")
+            return FATAL
+        }
+        val new = PmsGroup(
+            name = name,
+            description = description,
+            inherit = inherit?.name,
+            modify = HashMap()
+        )
+        if (PmsGroups.isCyclingRef(new)) {
+            context.print("权限组继承出现循环")
+            return FATAL
+        }
+        withLockedAccessing(PmsGroups) {
+            PmsGroups[name] = new
+        }
+        val table = TableBuilder(4)
+        table.th("新建权限组 —— $name").br()
+        table.tr("继承于:").td(new.inherit)
+        table.tr("描述:").sp().p(description)
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    @Method(name = "add", alias = ["a"], pmsLevel = CmdPermissionLevel.OP, order = 1, title = "新建")
+    suspend fun add (
+        @Argument(name = "权限组名", handler = PmsGroupNameArgHandler::class)
+        name: String,
+        @Argument(name = "权限组说明", handler = GreedyStringArgHandler::class)
+        description: String,
+    ): CmdReceipt  = add(name, null, description)
+
+    /*  ========================  modify  ========================  */
+
+    @Method(name = "modify", alias = ["m"], pmsLevel = CmdPermissionLevel.OP, title = "设置修改")
+    suspend fun modify (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup,
+        @Argument(name = "命令", handler = CmdExArgHandler::class)
+        cmds: List<CmdExecutable>,
+        @Argument(name = "权限值", handler = PmsValueArgHandler::class)
+        value: Int
+    ): CmdReceipt {
+        if (pmsGroup.name in PmsGroups.BUILTIN_PMS_GROUPS) {
+            context.print("不允许修改内建权限组")
+            return FATAL
+        }
+        val table = TableBuilder(4)
+        table.th("权限组设置修改 —— ${pmsGroup.name}").br()
+        if (pmsGroup.modify == null) pmsGroup.modify = HashMap()
+        for (cmd in cmds) {
+            table.tr(cmd.id).td(pmsGroup.modify!![cmd.id]).td("->").td(value)
+            pmsGroup.modify!![cmd.id] = value
+        }
+        withLockedAccessing(PmsGroups) {
+            PmsGroups[pmsGroup.name] = pmsGroup
+        }
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    /*  ========================  removeModify  ========================  */
+
+    @Method(name = "remove-modify", alias = ["rm"], pmsLevel = CmdPermissionLevel.OP, title = "移除修改")
+    suspend fun removeModify (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup,
+        @Argument(name = "命令", handler = CmdExArgHandler::class)
+        cmds: List<CmdExecutable>
+    ): CmdReceipt {
+        if (pmsGroup.name in PmsGroups.BUILTIN_PMS_GROUPS) {
+            context.print("不允许修改内建权限组")
+            return FATAL
+        }
+        val table = TableBuilder(4)
+        table.th("权限组移除修改 —— ${pmsGroup.name}").br()
+        if (pmsGroup.modify == null) {
+            context.print("权限组“${pmsGroup.name}”未设置任何修改")
+            return FATAL
+        }
+        for (cmd in cmds) {
+            table.tr(cmd.id).td(pmsGroup.modify!![cmd.id]).td("->").td("null")
+            pmsGroup.modify!!.remove(cmd.id)
+        }
+        withLockedAccessing(PmsGroups) {
+            PmsGroups[pmsGroup.name] = pmsGroup
+        }
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    /*  ========================  modifyInherit  ========================  */
+
+    @Method(name = "modify-inherit", alias = ["mi"], pmsLevel = CmdPermissionLevel.OP, title = "修改继承")
+    suspend fun modifyInherit (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup,
+        @Argument(name = "继承权限组", required = false, handler = PmsGroupArgHandler::class)
+        inherit: PmsGroup?,
+    ): CmdReceipt {
+        if (pmsGroup.name in PmsGroups.BUILTIN_PMS_GROUPS) {
+            context.print("不允许修改内建权限组")
+            return FATAL
+        }
+        val table = TableBuilder(4)
+        table.th("权限组修改继承 —— ${pmsGroup.name}").br()
+        table.tr(pmsGroup.inherit).td("->").td(inherit?.name)
+        pmsGroup.inherit = inherit?.name
+        withLockedAccessing(PmsGroups) {
+            PmsGroups[pmsGroup.name] = pmsGroup
+        }
+        context.print(table.toString())
+        return SUCCESS
+    }
+
+    /*  ========================  remove  ========================  */
+
+    @Method(name = "remove", alias = ["r"], pmsLevel = CmdPermissionLevel.OP, title = "删除")
+    suspend fun remove (
+        @Argument(name = "权限组", handler = PmsGroupArgHandler::class)
+        pmsGroup: PmsGroup
+    ): CmdReceipt {
+        if (pmsGroup.name in PmsGroups.BUILTIN_PMS_GROUPS) {
+            context.print("不允许修改内建权限组")
+            return FATAL
+        }
+        context.sendOutputWithLog("是否要删除权限组“${pmsGroup.name}”?\n注:不会检查权限组引用情况,无效权限组会视为“BLOCK”\n(y/n)")
+        if (context.nextYorN(timeoutMillis = 15000L) {
+                context.print("操作超时,")
+                false
+        }) {
+            context.print("已放弃删除")
+            return FATAL
+        }
+        withLockedAccessing(PmsGroups) {
+            PmsGroups[pmsGroup.name] = null
+        }
+        context.print("已删除权限组“${pmsGroup.name}”")
+        return SUCCESS
+    }
+
+    /*  ========================  list  ========================  */
+
+    @Method(name = "list", alias = ["l"], pmsLevel = CmdPermissionLevel.OP, title = "列表")
+    fun list (): CmdReceipt {
+        val table = TableBuilder(4)
+        table.th("权限组列表 ——").br()
+        for (p in PmsGroups.element.keys()) {
+            table.tr(p)
+            if (p in PmsGroups.BUILTIN_PMS_GROUPS) table.td("*") else table.td()
+            table.td(PmsGroups[p].description.limitEnd(20))
+        }
+        context.print(table.toString())
+        return SUCCESS
+    }
+}
+
+class PmsGroupNameArgHandler: ArgHandler<String> {
+    override val name = "权限组名参数解析器"
+    override fun handle(reader: MessageReader, pos: Int, argName: String?, type: KType?, context: CmdContext?): String {
+        val input = reader.readString().uppercase()
+        if (Regex("[A-Z0-9_]+").matches(input)) return input
+        else throw HandlerFatal(input, argName, pos, type)
+    }
+}
+
+class PmsValueArgHandler: ArgHandler<Int> {
+    override val name = "权限值参数解析器"
+    override fun handle(reader: MessageReader, pos: Int, argName: String?, type: KType?, context: CmdContext?): Int {
+        val input = reader.readString()
+        val value = input.toIntOrNull() ?: throw SyntaxError(pos, "无法将“$input”解析为整数值")
+        if (value !in -1..1) throw HandlerFatal(input, argName, pos, type)
+        return value
+    }
+}

+ 1 - 1
src/main/kotlin/bot_luo_core/cli/commands/SayCmd.kt

@@ -23,7 +23,7 @@ import net.mamoe.mirai.message.data.MessageChain
 )
 class SayCmd(context: CmdContext) : Cmd(context) {
 
-    @Method(name = "", alias = [], pmsLevel = CmdPermissionLevel.OP )
+    @Method(name = "", alias = [], pmsLevel = CmdPermissionLevel.OP)
     suspend fun say (
         @Argument(name = "消息", handler = GreedyMessageArgHandler::class)
         msg: MessageChain

+ 7 - 13
src/main/kotlin/bot_luo_core/cli/commands/StatCmd.kt

@@ -5,17 +5,13 @@ import bot_luo_core.cli.CmdCatalog.COMMANDS
 import bot_luo_core.cli.annotation.Argument
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.GroupCmdWorkingChecker
-import bot_luo_core.cli.checkers.UserCmdWorkingChecker
-import bot_luo_core.cli.checkers.addon.GroupPermissionChecker
+import bot_luo_core.cli.checkers.addon.GroupOriginalPermissionChecker
 import bot_luo_core.cli.exceptions.CheckerFatal
 import bot_luo_core.cli.handlers.CmdExArgHandler
 import bot_luo_core.data.CmdDataObj
 import bot_luo_core.data.Cmds
 import bot_luo_core.util.TableBuilder
 import bot_luo_core.util.Time.relativeToE
-import bot_luo_core.util.Time.spanFromE
-import net.mamoe.mirai.message.data.MessageChain
 import kotlin.reflect.full.createInstance
 
 @Command(
@@ -47,9 +43,9 @@ class StatCmd(context: CmdContext) : Cmd(context) {
                     "${k}:${v.sumOf { carrier.readCmdData(it).totalCount }}次",
                     v.groupBy { it.methName }.entries
                 ) { (key, value), builder ->
-                    builder.tb("-$key:")
-                        .tb("${value.sumOf { carrier.readCmdData(it).totalCount }}次")
-                        .tb(value.maxOf { carrier.readCmdData(it).lastTime } relativeToE context.time)
+                    builder.td("-$key:")
+                        .td("${value.sumOf { carrier.readCmdData(it).totalCount }}次")
+                        .td(value.maxOf { carrier.readCmdData(it).lastTime } relativeToE context.time)
                 }
             }
             context.sendMessageWithLog(table.toString())
@@ -75,8 +71,8 @@ class StatCmd(context: CmdContext) : Cmd(context) {
             table.th("${name}统计 —— $id").br()
             filtered.groupBy { it.cmdName }.forEach { (k, v) ->
                 table.tr("$k:")
-                    .tb("${v.sumOf { carrier.readCmdData(it).totalCount }}次")
-                    .tb(v.maxOf { carrier.readCmdData(it).lastTime } relativeToE context.time)
+                    .td("${v.sumOf { carrier.readCmdData(it).totalCount }}次")
+                    .td(v.maxOf { carrier.readCmdData(it).lastTime } relativeToE context.time)
             }
             context.sendMessageWithLog(table.toString())
             SUCCESS
@@ -119,9 +115,7 @@ class StatCmd(context: CmdContext) : Cmd(context) {
     companion object {
 
         val justCheck = arrayOf(
-            GroupPermissionChecker::class,
-            GroupCmdWorkingChecker::class,
-            UserCmdWorkingChecker::class
+            GroupOriginalPermissionChecker::class
         )
     }
 }

+ 27 - 23
src/main/kotlin/bot_luo_core/cli/commands/SwitchCmd.kt

@@ -7,10 +7,12 @@ import bot_luo_core.cli.CmdPermissionLevel
 import bot_luo_core.cli.annotation.Argument
 import bot_luo_core.cli.annotation.Command
 import bot_luo_core.cli.annotation.Method
-import bot_luo_core.cli.checkers.GroupCmdWorkingChecker
+import bot_luo_core.cli.checkers.PermissionChecker
+import bot_luo_core.cli.checkers.addon.UserOriginalPermissionChecker
 import bot_luo_core.cli.handlers.CmdIdArgHandler
 import bot_luo_core.cli.handlers.GroupArgHandler
 import bot_luo_core.data.Group
+import bot_luo_core.data.withLockedAccessing
 import bot_luo_core.util.TableBuilder
 
 @Command(
@@ -23,7 +25,7 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
 
     /*  ========================  show  ========================  */
 
-    @Method(name = "",alias = ["show"],pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class],
+    @Method(name = "", alias = ["show"], pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class],
     title = "查看", usage = "获取命令是否开启")
     fun show(
         @Argument(name = "命令", handler = CmdIdArgHandler::class, multiValued = true)
@@ -35,14 +37,14 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
         val table = TableBuilder(4)
         table.th("电闸 —— 群组(${group.id})").br()
         for (id in ids) {
-            val data = group.readCmdData(id)
-            table.tr(id).tb(":").tb(if (data.working) "ON" else "OFF")
+            val i = group.pmsModify[id]?: 0
+            table.tr(id).td(":").td(if (i >= 0) "ON" else "OFF")
         }
         context.println(table.toString())
         return SUCCESS
     }
 
-    @Method(name = "",alias = ["show"],pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class])
+    @Method(name = "", alias = ["show"], pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class])
     fun show(
         @Argument(name = "群组", handler = GroupArgHandler::class)
         groupIn: Group,
@@ -52,8 +54,8 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
 
     /*  ========================  on  ========================  */
 
-    @Method(name = "on",pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class])
-    fun on(
+    @Method(name = "on", pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class])
+    suspend fun on(
         @Argument(name = "命令", handler = CmdIdArgHandler::class, multiValued = true)
         ids: ArrayList<String>,
         @Argument(name = "群组", handler = GroupArgHandler::class, required = false)
@@ -62,18 +64,19 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
         val group = groupIn?: context.group
         val table = TableBuilder(4)
         table.th("电闸开启 —— 群组(${group.id})").br()
-        for (id in ids) {
-            val data = group.readCmdData(id)
-            table.tr(id).tb(":").tb(if (data.working) "ON" else "OFF").tb("->").tb("ON")
-            data.working = true
-            group.writeCmdData(id, data)
+        withLockedAccessing(group) {
+            for (id in ids) {
+                val i = group.pmsModify[id] ?: 0
+                table.tr(id).td(":").td(if (i >= 0) "ON" else "OFF").td("->").td("ON")
+                if (i == -1) group.pmsModify = group.pmsModify.apply { remove(id) }
+            }
         }
         context.println(table.toString())
         return SUCCESS
     }
 
-    @Method(name = "on",pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class])
-    fun on(
+    @Method(name = "on", pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class])
+    suspend fun on(
         @Argument(name = "群组", handler = GroupArgHandler::class)
         groupIn: Group,
         @Argument(name = "命令", handler = CmdIdArgHandler::class, multiValued = true)
@@ -82,8 +85,8 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
 
     /*  ========================  off  ========================  */
 
-    @Method(name = "off",pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class])
-    fun off(
+    @Method(name = "off", pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class])
+    suspend fun off(
         @Argument(name = "命令", handler = CmdIdArgHandler::class, multiValued = true)
         ids: ArrayList<String>,
         @Argument(name = "群组", handler = GroupArgHandler::class, required = false)
@@ -92,18 +95,19 @@ class SwitchCmd(context: CmdContext) : Cmd(context) {
         val group = groupIn?: context.group
         val table = TableBuilder(4)
         table.th("电闸关闭 —— 群组(${group.id})").br()
-        for (id in ids) {
-            val data = group.readCmdData(id)
-            table.tr(id).tb(":").tb(if (data.working) "ON" else "OFF").tb("->").tb("OFF")
-            data.working = false
-            group.writeCmdData(id, data)
+        withLockedAccessing(group) {
+            for (id in ids) {
+                val i = group.pmsModify[id] ?: 0
+                table.tr(id).td(":").td(if (i >= 0) "ON" else "OFF").td("->").td("OFF")
+                group.pmsModify = group.pmsModify.apply { set(id, -1) }
+            }
         }
         context.println(table.toString())
         return SUCCESS
     }
 
-    @Method(name = "off",pmsLevel = CmdPermissionLevel.OP,order = 0,ignoreCheckers = [GroupCmdWorkingChecker::class])
-    fun off(
+    @Method(name = "off", pmsLevel = CmdPermissionLevel.OP, order = 0, ignoreCheckers = [PermissionChecker::class], addonCheckers = [UserOriginalPermissionChecker::class])
+    suspend fun off(
         @Argument(name = "群组", handler = GroupArgHandler::class)
         groupIn: Group,
         @Argument(name = "命令", handler = CmdIdArgHandler::class, multiValued = true)

+ 15 - 15
src/main/kotlin/bot_luo_core/cli/commands/TagCmd.kt

@@ -55,10 +55,10 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(GroupTags) {
             for (t in tagIn) {
                 if (addToGroupTag(tag, t)) {
-                    table.tr().tb(t).tb("[√] success added")
+                    table.tr().td(t).td("[√] success added")
                     suc++
                 } else {
-                    table.tr().tb(t).tb("[×] duplicated item or circulated tag")
+                    table.tr().td(t).td("[×] duplicated item or circulated tag")
                 }
             }
         }
@@ -85,10 +85,10 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(GroupTags) {
             for (g in groupIn) {
                 if (addToGroupTag(tag, g.id.toString())) {
-                    table.tr(g.name).tb("(${g.id})").tb("[√] success added")
+                    table.tr(g.name).td("(${g.id})").td("[√] success added")
                     suc++
                 } else {
-                    table.tr(g.name).tb("(${g.id})").tb("[×] duplicated item")
+                    table.tr(g.name).td("(${g.id})").td("[×] duplicated item")
                 }
             }
         }
@@ -115,10 +115,10 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(UserTags) {
             for (t in tagIn) {
                 if (addToUserTag(tag, t)) {
-                    table.tr().tb(t).tb("[√] success added")
+                    table.tr().td(t).td("[√] success added")
                     suc++
                 } else {
-                    table.tr().tb(t).tb("[×] duplicated item or circulated tag")
+                    table.tr().td(t).td("[×] duplicated item or circulated tag")
                 }
             }
         }
@@ -145,10 +145,10 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(UserTags) {
             for (u in userIn) {
                 if (addToUserTag(tag, u.id.toString())) {
-                    table.tr(u.name).tb("(${u.id})").tb("[√] success added")
+                    table.tr(u.name).td("(${u.id})").td("[√] success added")
                     suc++
                 } else {
-                    table.tr(u.name).tb("(${u.id})").tb("[×] duplicated item")
+                    table.tr(u.name).td("(${u.id})").td("[×] duplicated item")
                 }
             }
         }
@@ -170,14 +170,14 @@ class TagCmd(context: CmdContext) : Cmd(context) {
                 table.tr(t)
             }
             for (t in BUILTIN_GROUP_TAGS) {
-                table.tr(t).tb("*")
+                table.tr(t).td("*")
             }
             context.print(table.toString())
             return SUCCESS
         } else {
             table.th("群组标签 —— 标签($tag)").br()
             for (g in readGroupTag(tag, context)!!) {
-                table.tr("G(${g.id})").tb(g.name)
+                table.tr("G(${g.id})").td(g.name)
             }
             context.print(table.toString())
             return SUCCESS
@@ -196,7 +196,7 @@ class TagCmd(context: CmdContext) : Cmd(context) {
                 table.tr(t)
             }
             for (t in BUILTIN_USER_TAGS) {
-                table.tr(t).tb("*")
+                table.tr(t).td("*")
             }
             context.print(table.toString())
             return SUCCESS
@@ -308,9 +308,9 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(GroupTags) {
             for (i in itemsIn) {
                 if (remFromGroupTag(tag, i.toLowercase()))
-                    table.tr().tb(i).tb("[√] success removed")
+                    table.tr().td(i).td("[√] success removed")
                 else
-                    table.tr().tb(i).tb("[×] tag or item not found")
+                    table.tr().td(i).td("[×] tag or item not found")
             }
         }
         context.print(table.toString())
@@ -333,9 +333,9 @@ class TagCmd(context: CmdContext) : Cmd(context) {
         withLockedAccessing(UserTags) {
             for (i in itemsIn) {
                 if (remFromUserTag(tag, i.toLowercase()))
-                    table.tr().tb(i).tb("[√] success removed")
+                    table.tr().td(i).td("[√] success removed")
                 else
-                    table.tr().tb(i).tb("[×] tag or item not found")
+                    table.tr().td(i).td("[×] tag or item not found")
             }
         }
         context.print(table.toString())

+ 1 - 1
src/main/kotlin/bot_luo_core/cli/handlers/PmsGroupArgHandler.kt

@@ -20,6 +20,6 @@ class PmsGroupArgHandler: ArgHandler<PmsGroup> {
         context: CmdContext?
     ): PmsGroup {
         val str = reader.readString()
-        return PmsGroups.getPmsGroupOrNull(str)?: throw HandlerFatal(str, argName, pos, type)
+        return PmsGroups.getOrNull(str)?: throw HandlerFatal(str, argName, pos, type)
     }
 }

+ 2 - 2
src/main/kotlin/bot_luo_core/data/Config.kt

@@ -9,12 +9,12 @@ object Config {
     /**
      * 回复-确认
      */
-    val REPL_YES = arrayOf("y","ok","yes","好","是","好的","是的")
+    val REPL_YES = arrayOf("y","ok","yes","true","好","是","对","行","中","成","好的","是的","对的","没错","确认")
 
     /**
      * 回复-否认
      */
-    val REPL_NO = arrayOf("n","no","nop","不","否","不是","不要")
+    val REPL_NO = arrayOf("n","no","nope","false","不","否","孬","不是","不要","爪巴","不对","不好","不行","否认")
 
     /**
      * 输出消息最大长度

+ 3 - 0
src/main/kotlin/bot_luo_core/data/Data.kt

@@ -1,5 +1,6 @@
 package bot_luo_core.data
 
+import com.google.gson.JsonElement
 import kotlinx.atomicfu.AtomicInt
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.*
@@ -34,6 +35,8 @@ abstract class Data(
 
     private var saveJob: Job? = null
 
+    abstract val element: JsonElement
+
     /**
      * 访问数量
      *

+ 20 - 28
src/main/kotlin/bot_luo_core/data/DataArray.kt

@@ -1,18 +1,17 @@
 package bot_luo_core.data
 
-import com.alibaba.fastjson.JSON
-import com.alibaba.fastjson.JSONArray
-import com.alibaba.fastjson.JSONObject
-import com.alibaba.fastjson.TypeReference
-import com.alibaba.fastjson.parser.ParserConfig
-import com.alibaba.fastjson.util.TypeUtils
+import com.github.salomonbrys.kotson.removeAll
+import com.google.gson.Gson
+import com.google.gson.JsonArray
+import com.google.gson.JsonParser
+import com.google.gson.reflect.TypeToken
 import kotlinx.coroutines.sync.Mutex
 import java.io.File
 
 /**
  * # 数据对象类
  *
- * 根对象为一个[JSONObject]
+ * 根对象为一个[JsonArray]
  *
  * 继承[Mutex]
  *
@@ -40,19 +39,19 @@ abstract class DataArray(
     /**
      * 使用[getObj]和[setObj]进行访问
      */
-    val jsonArray: JSONArray
+    final override val element: JsonArray
 
     init {
         val file = File(filePath)
-        jsonArray = if (file.exists())
-            JSON.parseArray (file.readText().ifBlank { "[]" })
+        element = if (file.exists())
+            JsonParser().parse(file.readText().ifBlank { "[]" }) as JsonArray
         else
-            JSONArray()
+            JsonArray()
         if (saveAndUnload) launchSaveJob()
     }
 
     /**
-     * 保存前需将有变动的数据使用[setObj]存入[jsonArray]
+     * 保存前需将有变动的数据使用[setObj]存入[element]
      *
      * 会自动创建不存在的文件和文件夹
      */
@@ -62,7 +61,7 @@ abstract class DataArray(
             if (!file.parentFile.exists()) file.parentFile.mkdirs()
             file.createNewFile()
         }
-        file.writeText(JSON.toJSONString(jsonArray, true))
+        file.writeText(Gson().toJson(element))
         changed = false
     }
 
@@ -72,22 +71,14 @@ abstract class DataArray(
      * 自动更新访问时间
      */
     inline fun <reified T> getObj(index: Int): T? {
-        return if (index in jsonArray.indices) {
-            val tr = object : TypeReference<T>() {}
-            jsonArray.getObject(index, tr)
+        return if (index >= element.size()) {
+            val type = object : TypeToken<T>() {}.type
+            Gson().fromJson(element[index], type)
         } else null
     }
     inline fun <reified T: List<*>> getArray(): T {
-        val tr = object : TypeReference<T>() {}
-        return jsonArray.toJavaObject(tr)
-    }
-
-    fun <T> JSONArray.getObject(index: Int, typeReference: TypeReference<*>?): T {
-        val obj: Any = this[index]
-        @Suppress("UNCHECKED_CAST")
-        return if (typeReference == null) {
-            obj as T
-        } else TypeUtils.cast(obj, typeReference.type, ParserConfig.getGlobalInstance())
+        val type = object : TypeToken<T>() {}.type
+        return Gson().fromJson(element, type)
     }
 
     /**
@@ -101,11 +92,12 @@ abstract class DataArray(
      * @see withLockedAccessing
      */
     fun <T> setObj(index: Int, value: T?) {
-        jsonArray[index] = value
+        element[index] = Gson().toJsonTree(value)
         changed = true
     }
     fun <T: List<*>> setArray(value: T) {
-        jsonArray.fluentClear().fluentAddAll(value)
+        element.removeAll()
+        value.forEach { v -> element.add(Gson().toJsonTree(value)) }
         changed = true
     }
 }

+ 24 - 19
src/main/kotlin/bot_luo_core/data/DataObject.kt

@@ -1,16 +1,20 @@
 package bot_luo_core.data
 
-import com.alibaba.fastjson.JSON
-import com.alibaba.fastjson.JSONObject
-import com.alibaba.fastjson.JSONPObject
-import com.alibaba.fastjson.TypeReference
+import bot_luo_core.util.set
+import com.github.salomonbrys.kotson.put
+import com.github.salomonbrys.kotson.putAll
+import com.github.salomonbrys.kotson.removeAll
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import com.google.gson.reflect.TypeToken
 import kotlinx.coroutines.sync.Mutex
 import java.io.File
 
 /**
  * # 数据对象类
  *
- * 根对象为一个[JSONObject]
+ * 根对象为一个[JsonObject]
  *
  * 继承[Mutex]
  *
@@ -38,19 +42,19 @@ abstract class DataObject(
     /**
      * 使用[getObj]和[setObj]进行访问
      */
-    val jsonObj: JSONObject
+    final override val element: JsonObject
 
     init {
         val file = File(filePath)
-        jsonObj = if (file.exists())
-            JSON.parseObject(file.readText().ifBlank { "{}" })
+        element = if (file.exists())
+            JsonParser().parse(file.readText().ifBlank { "{}" }) as JsonObject
         else
-            JSONObject()
+            JsonObject()
         if (saveAndUnload) launchSaveJob()
     }
 
     /**
-     * 保存前需将有变动的数据使用[setObj]存入[jsonObj]
+     * 保存前需将有变动的数据使用[setObj]存入[element]
      *
      * 会自动创建不存在的文件和文件夹
      */
@@ -60,7 +64,7 @@ abstract class DataObject(
             if (!file.parentFile.exists()) file.parentFile.mkdirs()
             file.createNewFile()
         }
-        file.writeText(JSON.toJSONString(jsonObj, true))
+        file.writeText(Gson().toJson(element))
         changed = false
     }
 
@@ -70,14 +74,14 @@ abstract class DataObject(
      * 自动更新访问时间
      */
     inline fun <reified T> getObj(key: String): T? {
-        return if (jsonObj.containsKey(key)) {
-            val tr = object : TypeReference<T>() {}
-            jsonObj.getObject(key, tr)
+        return if (element.has(key)) {
+            val type = object : TypeToken<T>() {}.type
+            Gson().fromJson(element[key], type)
         } else null
     }
     inline fun <reified T: Map<*, *>> getObj(): T {
-        val tr = object : TypeReference<T>() {}
-        return jsonObj.toJavaObject(tr)
+        val type = object : TypeToken<T>() {}.type
+        return Gson().fromJson(element, type)
     }
 
     /**
@@ -91,11 +95,12 @@ abstract class DataObject(
      * @see withLockedAccessing
      */
     fun <T> setObj(key: String, value: T?) {
-        jsonObj[key] = value
+        element[key] = value
         changed = true
     }
-    fun <T: Map<out String, *>> setObj(value: T?) {
-        jsonObj.fluentClear().fluentPutAll(value)
+    fun <T: Map<String, *>> setObj(value: T?) {
+        element.removeAll()
+        value?.forEach{ (k,v) -> element.put(k to Gson().toJsonTree(v)) }
         changed = true
     }
 }

+ 16 - 1
src/main/kotlin/bot_luo_core/data/Group.kt

@@ -37,9 +37,24 @@ class Group internal constructor(
         get() = (contact as Group?)?.name ?: getObj("name") ?: ""
 
     var pmsGroup: PmsGroup
-        get() = PmsGroups.getPmsGroup(getObj("pmsGroup")?: "NONE")
+        get() = PmsGroups[(getObj("pmsGroup")?: "NONE")]
         set(value) = setObj("pmsGroup", value.name)
 
+    var pmsModify: HashMap<String, Int>
+        get() = getObj("pmsModify")?: HashMap()
+        set(value) = setObj("pmsModify", value)
+
+    val realPmsGroup: PmsGroup
+        get() {
+            return if (pmsModify.isEmpty()) pmsGroup
+            else PmsGroup(
+                name = pmsGroup.name+"*",
+                description = pmsGroup.description+"(modified)",
+                inherit = pmsGroup.name,
+                modify = pmsModify
+            )
+        }
+
     var mutedExceptions: ArrayList<String>
         get() = getObj("mutedExceptions")?: Config.MUTED_EXCEPTIONS
         set(value) = setObj("mutedExceptions", value)

+ 24 - 3
src/main/kotlin/bot_luo_core/data/PmsGroup.kt

@@ -2,12 +2,33 @@ package bot_luo_core.data
 
 import bot_luo_core.cli.CmdPermissionLevel
 
+/**
+ * # 权限组
+ *
+ * 一个权限组包含对所有命令方法的启用状态的描述
+ */
 data class PmsGroup (
+    /**
+     * 名称
+     */
     val name: String,
+
+    /**
+     * 描述
+     */
     val description: String,
+
+    /**
+     * 继承权限组名称,可空
+     *
+     * 若权限组未最终继承到[bot_luo_core.data.PmsGroups.BUILTIN_PMS_GROUPS]之一,则默认继承`BLOCKED`
+     */
+    var inherit: String?,
+
     /**
-     * 基础值,填写[CmdPermissionLevel]的名称或结构为'@'+其他权限组名称的链接
+     * 修改值,可空
+     *
+     * 修改值将覆盖继承值
      */
-    val base: String,
-    val modify: HashMap<String, CmdPermissionLevel>?
+    var modify: HashMap<String, Int>?
 )

+ 36 - 35
src/main/kotlin/bot_luo_core/data/PmsGroups.kt

@@ -1,36 +1,42 @@
 package bot_luo_core.data
 
+import bot_luo_core.cli.CmdCatalog.COMMANDS
 import bot_luo_core.cli.CmdExecutable
 import bot_luo_core.cli.CmdPermissionLevel
-import bot_luo_core.util.JsonWorker
+import bot_luo_core.util.Logger
+import kotlinx.coroutines.runBlocking
+import org.apache.logging.log4j.Level
 
-object PmsGroups {
+/**
+ * # 权限组数据
+ */
+object PmsGroups: DataObject("data/pms_groups.json", 10000L, false) {
+    override fun unload() {}
 
-    private val pmsGroups = HashMap<String, PmsGroup>()
-    private const val filePath = "data/pms_groups.json"
+    private const val DESC_BLOCK = "屏蔽组,所有命令均为关闭状态"
+    private const val DESC_NONE = "虚空组,所有命令均为叠加态(0)"
+    private const val DESC_NORMAL = "普通组,低级命令对此组开启"
+    private const val DESC_HIGH = "高级组,高级及以下的命令均开启"
+    private const val DESC_OP = "管理组,管理级及以下的命令均开启"
+    private const val DESC_DEBUG = "调试组,所有命令均为开启状态"
 
-    private const val DESC_NONE = "虚空组,无任何命令权限"
-    private const val DESC_NORMAL = "普通组,拥有执行最低级命令的权限"
-    private const val DESC_HIGH = "高级组,拥有执行较高级命令的权限"
-    private const val DESC_OP = "管理组,拥有执行管理命令的权限"
-    private const val DESC_DEBUG = "调试组,拥有执行调试中命令的权限"
+    val BUILTIN_PMS_GROUPS = arrayOf("BLOCKED","NONE","NORMAL","HIGH","OP","DEBUG")
 
     init {
-        pmsGroups["NONE"] = PmsGroup("NONE", DESC_NONE, CmdPermissionLevel.NONE.name, null)
-        pmsGroups["NORMAL"] = PmsGroup("NORMAL", DESC_NORMAL, CmdPermissionLevel.NORMAL.name, null)
-        pmsGroups["HIGH"] = PmsGroup("HIGH", DESC_HIGH, CmdPermissionLevel.HIGH.name, null)
-        pmsGroups["OP"] = PmsGroup("OP", DESC_OP, CmdPermissionLevel.OP.name, null)
-        pmsGroups["DEBUG"] = PmsGroup("DEBUG", DESC_DEBUG, CmdPermissionLevel.DEBUG.name, null)
-        JsonWorker.readJson<ArrayList<PmsGroup>?>(filePath)?.forEach { pmsGroups[it.name] = it }
+        Logger.sysLog(Level.DEBUG, "开始构建内建权限组……")
+        runBlocking { withLockedAccessing(this@PmsGroups) {
+            setObj("BLOCK", PmsGroup("BLOCK", DESC_BLOCK, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to -1 })}))
+            setObj("NONE", PmsGroup("NONE", DESC_NONE, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to 0 })}))
+            setObj("NORMAL", PmsGroup("NORMAL", DESC_NORMAL, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to if (it.pmsLevel <= CmdPermissionLevel.NORMAL) 1 else 0 })}))
+            setObj("HIGH", PmsGroup("HIGH", DESC_HIGH, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to if (it.pmsLevel <= CmdPermissionLevel.HIGH) 1 else 0 })}))
+            setObj("OP", PmsGroup("OP", DESC_OP, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to if (it.pmsLevel <= CmdPermissionLevel.OP) 1 else 0 })}))
+            setObj("DEBUG", PmsGroup("DEBUG", DESC_DEBUG, null, HashMap<String, Int>().apply { putAll(COMMANDS.associate { it.id to 1 })}))
+        } }
+        Logger.sysLog(Level.DEBUG, "内建权限组构建完毕")
     }
 
-    fun getPmsGroupOrNull(name: String) = pmsGroups[name]
-    fun getPmsGroup(name: String) = pmsGroups[name]?: pmsGroups["NONE"]!!
-
-    fun PmsGroup.readPmsOn(id: String): CmdPermissionLevel {
-        return this.modify?.get(id)?: if (this.base.startsWith("@")) {
-            getPmsGroup(base.substring(1)).readPmsOn(id)
-        } else CmdPermissionLevel(this.base)
+    fun PmsGroup.readPmsOn(id: String): Int {
+        return this.modify?.get(id)?: get(this.inherit?: "BLOCK").readPmsOn(id)
     }
     fun PmsGroup.readPmsOn(cmd: CmdExecutable) = readPmsOn(cmd.id)
 
@@ -39,10 +45,11 @@ object PmsGroups {
      *
      * 使用前应先使用[isCyclingRef]检查是否出现循环引用
      */
-    fun setPmsGroup(pmsGroup: PmsGroup) {
-        pmsGroups[pmsGroup.name] = pmsGroup
-        save()
-    }
+    operator fun set(name: String, value: PmsGroup?) = setObj(name, value)
+
+    operator fun get(name: String): PmsGroup = getObj(name.uppercase())?: getObj("BLOCK")!!
+
+    fun getOrNull(name: String): PmsGroup? = getObj(name.uppercase())
 
     /**
      * 检查是否出现循环引用
@@ -51,16 +58,10 @@ object PmsGroups {
      */
     fun isCyclingRef(pmsGroup: PmsGroup): Boolean {
         var p = pmsGroup
-        while (p.base.startsWith('@')) {
-            val b = p.base.substring(1)
-            if (b == pmsGroup.name) return true
-            else p = getPmsGroup(b)
+        while (p.inherit != null) {
+            if (p.inherit == pmsGroup.name) return true
+            else p = get(p.inherit!!)
         }
         return false
     }
-
-    private fun save() {
-        JsonWorker.writeJson(filePath, pmsGroups.values)
-    }
-
 }

+ 16 - 1
src/main/kotlin/bot_luo_core/data/User.kt

@@ -47,9 +47,24 @@ class User internal constructor (
     fun getAt() = At(id)
 
     var pmsGroup: PmsGroup
-        get() = PmsGroups.getPmsGroup(getObj("pmsGroup")?: "NONE")
+        get() = PmsGroups[(getObj("pmsGroup")?: "NONE")]
         set(value) = setObj("pmsGroup", value.name)
 
+    var pmsModify: HashMap<String, Int>
+        get() = getObj("pmsModify")?: HashMap()
+        set(value) = setObj("pmsModify", value)
+
+    val realPmsGroup: PmsGroup
+        get() {
+            return if (pmsModify.isEmpty()) pmsGroup
+            else PmsGroup(
+                name = pmsGroup.name+"*",
+                description = pmsGroup.description+"(modified)",
+                inherit = pmsGroup.name,
+                modify = pmsModify
+            )
+        }
+
     inline fun withServeBot(action: (contact: User)->Unit): Boolean {
         if (virtual) return false
         val contact = if (bot == null) BotLuo.getContactableBots(this).map { it.getFriend(id)?:it.getStranger(id) }.randomOrNull()

+ 13 - 0
src/main/kotlin/bot_luo_core/util/GsonHelper.kt

@@ -0,0 +1,13 @@
+package bot_luo_core.util
+
+import com.google.gson.Gson
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
+
+operator fun JsonObject.set(key: String, value: Any?) {
+    this.add(key, Gson().toJsonTree(value))
+}
+
+operator fun JsonArray.set(key: Int, value: Any?) {
+    this.set(key, Gson().toJsonTree(value))
+}

+ 5 - 5
src/main/kotlin/bot_luo_core/util/JsonWorker.kt

@@ -1,7 +1,7 @@
 package bot_luo_core.util
 
-import com.alibaba.fastjson.JSON
-import com.alibaba.fastjson.TypeReference
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
 import java.io.File
 import java.lang.ClassCastException
 import kotlin.jvm.Throws
@@ -21,10 +21,10 @@ object JsonWorker {
             file.createNewFile()
             return null
         }
-        val typeRef = object : TypeReference<T>() {}
-        return JSON.parseObject(file.readText(),typeRef)
+        val type = object : TypeToken<T>() {}.type
+        return Gson().fromJson(file.readText(), type)
     }
 
-    fun writeJson (filePath: String, obj: Any) = File(filePath).writeText(JSON.toJSONString(obj, true))
+    fun writeJson (filePath: String, obj: Any) = File(filePath).writeText(Gson().toJson(obj))
 
 }

+ 3 - 3
src/main/kotlin/bot_luo_core/util/TableBuilder.kt

@@ -43,7 +43,7 @@ class TableBuilder (
         x = 0
         table.add(ArrayList())
         y++
-        if (str != null) tb(str)
+        if (str != null) td(str)
         return this
     }
     fun tr(any: Any?): TableBuilder = tr(any?.toString())
@@ -55,14 +55,14 @@ class TableBuilder (
      *
      * @param str 文本,默认为""
      */
-    fun tb(str: String = ""): TableBuilder {
+    fun td(str: String = ""): TableBuilder {
         table[y].add(str)
         while (len.size <= x) len.add(0)
         if (len[x] < str.length) len[x] = str.length
         x++
         return this
     }
-    fun tb(any: Any?) = tb(any?.toString()?:"")
+    fun td(any: Any?) = td(any?.toString()?:"")
 
     /**
      * 附加到独立的一行,不影响当前表格布局

+ 5 - 5
src/main/kotlin/bot_luo_core/util/Tag.kt

@@ -74,14 +74,14 @@ object Tag {
         table.tr()
         line.forEachIndexed { index, l ->
             if (index < line.lastIndex) {
-                if (l > 0) table.tb("│")
-                else table.tb("  ")
+                if (l > 0) table.td("│")
+                else table.td("  ")
             } else {
-                if (l > 0) table.tb("├")
-                else table.tb("└")
+                if (l > 0) table.td("├")
+                else table.td("└")
             }
         }
-        table.tb(tag)
+        table.td(tag)
         val items = tagGetter(tag)
         items.sortedWith { o1, o2 ->
             var r = 0

+ 2 - 3
src/main/kotlin/bot_luo_core/util/Text.kt

@@ -1,7 +1,6 @@
 package bot_luo_core.util
 
-import com.alibaba.fastjson.JSON
-import com.sun.deploy.util.StringUtils
+import com.google.gson.Gson
 import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageChainBuilder
@@ -22,7 +21,7 @@ object Text {
         return sb.toString()
     }
 
-    fun String.escapeJson(): String = JSON.toJSONString(this@escapeJson)
+    fun String.escapeJson(): String = Gson().toJson(this@escapeJson)
 
     infix operator fun String.times(n: Int): String {
         if (n<=0) return ""