ソースを参照

Public API stabilization

Him188 5 年 前
コミット
0f5d46d017
76 ファイル変更965 行追加1022 行削除
  1. 1 1
      PluginDocs/java/source.java
  2. 2 4
      backend/codegen/build.gradle.kts
  3. 6 4
      backend/mirai-console/build.gradle.kts
  4. 0 189
      backend/mirai-console/src/main/java/net/mamoe/mirai/console/command/JCommandManager.java
  5. 0 39
      backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java
  6. 6 96
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
  7. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt
  8. 9 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt
  9. 4 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt
  10. 35 16
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt
  11. 2 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecutionException.kt
  12. 154 240
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt
  13. 156 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManagerImpl.kt
  14. 1 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt
  15. 2 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermissionDeniedException.kt
  16. 5 14
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt
  17. 7 7
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt
  18. 3 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt
  19. 12 12
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt
  20. 13 13
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt
  21. 228 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentContext.kt
  22. 14 13
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParser.kt
  23. 0 193
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandParserContext.kt
  24. 6 6
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt
  25. 9 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt
  26. 88 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt
  27. 9 8
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt
  28. 37 57
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt
  29. 4 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt
  30. 3 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt
  31. 2 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt
  32. 3 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt
  33. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginsLoader.kt
  34. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/CompositeValueImpl.kt
  35. 1 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SemverAsStringSerializer.kt
  36. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/Setting.value composite impl.kt
  37. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SettingImpl.kt
  38. 5 5
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SettingStorage internal.kt
  39. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/_PrimitiveValueDeclarations.kt
  40. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/_Setting.value.kt
  41. 2 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/asKClass.kt
  42. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/collectionUtil.kt
  43. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/serializerHelper.kt
  44. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/serializerUtil.kt
  45. 17 16
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/utils/BotManagerImpl.kt
  46. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt
  47. 4 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt
  48. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/center/PluginCenter.kt
  49. 2 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt
  50. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin.kt
  51. 7 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
  52. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPlugin.kt
  53. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt
  54. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt
  55. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/KotlinPlugin.kt
  56. 3 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt
  57. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt
  58. 3 3
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt
  59. 25 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt
  60. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt
  61. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaFriendlyAPI.kt
  62. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaPluginScheduler.kt
  63. 3 4
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ResourceContainer.kt
  64. 1 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/retryCatching.kt
  65. 1 1
      backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt
  66. 13 8
      backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt
  67. 3 0
      backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/commanTestingUtil.kt
  68. 1 1
      backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt
  69. 3 0
      build.gradle.kts
  70. 1 3
      buildSrc/build.gradle.kts
  71. 2 2
      buildSrc/src/main/kotlin/MiraiConsoleBuildPlugin.kt
  72. 2 0
      buildSrc/src/main/kotlin/Versions.kt
  73. 10 3
      frontend/mirai-console-pure/build.gradle.kts
  74. 3 4
      frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt
  75. 1 1
      frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt
  76. 10 5
      frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt

+ 1 - 1
PluginDocs/java/source.java

@@ -7,7 +7,7 @@ import net.mamoe.mirai.console.plugin.Config;
 import net.mamoe.mirai.console.plugin.ConfigSection;
 import net.mamoe.mirai.console.plugin.ConfigSectionFactory;
 import net.mamoe.mirai.console.plugin.PluginBase;
-import net.mamoe.mirai.console.utils.Utils;
+import net.mamoe.mirai.console.util.Utils;
 import net.mamoe.mirai.message.GroupMessage;
 import org.jetbrains.annotations.NotNull;
 import org.jsoup.Jsoup;

+ 2 - 4
backend/codegen/build.gradle.kts

@@ -1,6 +1,6 @@
 plugins {
-    kotlin("jvm") version "1.4-M2"
-    kotlin("plugin.serialization") version "1.4-M2"
+    kotlin("jvm")
+    kotlin("plugin.serialization")
     id("java")
 }
 
@@ -10,8 +10,6 @@ kotlin {
             languageSettings.useExperimentalAnnotation("kotlin.Experimental")
             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")

+ 6 - 4
backend/mirai-console/build.gradle.kts

@@ -6,11 +6,12 @@ import java.util.Date
 import java.util.TimeZone
 
 plugins {
-    kotlin("jvm") version Versions.kotlinCompiler
-    kotlin("plugin.serialization") version Versions.kotlinCompiler
+    kotlin("jvm")
+    kotlin("plugin.serialization")
     id("java")
     `maven-publish`
     id("com.jfrog.bintray")
+    id("net.mamoe.kotlin-jvm-blocking-bridge")
 }
 
 version = Versions.console
@@ -31,8 +32,9 @@ kotlin {
     sourceSets.all {
         target.compilations.all {
             kotlinOptions {
-                freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=enable"
                 jvmTarget = "1.8"
+                freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all"
+                //useIR = true
             }
         }
         languageSettings.apply {
@@ -45,7 +47,7 @@ kotlin {
             useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
             useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
             useExperimentalAnnotation("net.mamoe.mirai.console.ConsoleFrontEndImplementation")
-            useExperimentalAnnotation("net.mamoe.mirai.console.utils.ConsoleExperimentalAPI")
+            useExperimentalAnnotation("net.mamoe.mirai.console.util.ConsoleExperimentalAPI")
             useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
             useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
             useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")

+ 0 - 189
backend/mirai-console/src/main/java/net/mamoe/mirai/console/command/JCommandManager.java

@@ -1,189 +0,0 @@
-package net.mamoe.mirai.console.command;
-
-import kotlin.NotImplementedError;
-import kotlin.coroutines.Continuation;
-import kotlin.coroutines.EmptyCoroutineContext;
-import kotlinx.coroutines.BuildersKt;
-import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.CoroutineStart;
-import kotlinx.coroutines.future.FutureKt;
-import net.mamoe.mirai.console.plugin.jvm.JavaPlugin;
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI;
-import net.mamoe.mirai.message.data.Message;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Java 适配的 {@link CommandManagerKt}
- */
-@SuppressWarnings({"unused", "RedundantSuppression"})
-public final class JCommandManager {
-    private JCommandManager() {
-        throw new NotImplementedError();
-    }
-
-    /**
-     * 获取指令前缀
-     *
-     * @return 指令前缀
-     */
-    @NotNull
-    public static String getCommandPrefix() {
-        return CommandManagerKt.getCommandPrefix();
-    }
-
-    /**
-     * 获取一个指令所有者已经注册了的指令列表.
-     *
-     * @param owner 指令所有者
-     * @return 指令列表
-     */
-    @NotNull
-    public static List<@NotNull Command> getRegisteredCommands(final @NotNull CommandOwner owner) {
-        return CommandManagerKt.getRegisteredCommands(Objects.requireNonNull(owner, "owner"));
-    }
-
-    /**
-     * 注册一个指令.
-     *
-     * @param command  指令实例
-     * @param override 是否覆盖重名指令.
-     *                 <p>
-     *                 若原有指令 P, 其 {@link Command#getNames()} 为 'a', 'b', 'c'. <br>
-     *                 新指令 Q, 其 {@link Command#getNames()}  为 'b', 将会覆盖原指令 A 注册的 'b'.
-     *                 <p>
-     *                 即注册完成后, 'a' 和 'c' 将会解析到指令 P, 而 'b' 会解析到指令 Q.
-     * @return 若已有重名指令, 且 <code>override</code> 为 <code>false</code>, 返回 <code>false</code>; <br>
-     * 若已有重名指令, 但 <code>override</code> 为 <code>true</code>, 覆盖原有指令并返回 <code>true</code>.
-     */
-    public static boolean register(final @NotNull Command command, final boolean override) {
-        Objects.requireNonNull(command, "command");
-        return CommandManagerKt.register(command, override);
-    }
-
-    /**
-     * 注册一个指令, 已有重复名称的指令时返回 <code>false</code>
-     *
-     * @param command 指令实例
-     * @return 若已有重名指令, 返回 <code>false</code>, 否则返回 <code>true</code>.
-     */
-    public static boolean register(final @NotNull Command command) {
-        Objects.requireNonNull(command, "command");
-        return register(command, false);
-    }
-
-    /**
-     * 查找并返回重名的指令. 返回重名指令.
-     */
-    @Nullable
-    public static Command findDuplicate(final @NotNull Command command) {
-        Objects.requireNonNull(command, "command");
-        return CommandManagerKt.findDuplicate(command);
-    }
-
-    /**
-     * 取消注册这个指令. 若指令未注册, 返回 <code>false</code>.
-     */
-    public static boolean unregister(final @NotNull Command command) {
-        Objects.requireNonNull(command, "command");
-        return CommandManagerKt.unregister(command);
-    }
-
-    /**
-     * 取消注册所有属于 <code>owner</code> 的指令
-     *
-     * @param owner 指令所有者
-     */
-    public static void unregisterAllCommands(final @NotNull CommandOwner owner) {
-        Objects.requireNonNull(owner, "owner");
-        CommandManagerKt.unregisterAllCommands(owner);
-    }
-
-
-    /**
-     * 解析并执行一个指令
-     *
-     * @param args 接受 {@link String} 或 {@link Message} , 其他对象将会被 {@link Object#toString()}
-     * @return 成功执行的指令, 在无匹配指令时返回 <code>null</code>
-     * @throws CommandExecutionException 当 {@link Command#onCommand(CommandSender, Object[], Continuation)} 抛出异常时包装并附带相关指令信息抛出
-     * @see #executeCommandAsync(CoroutineScope, CommandSender, Object...)
-     */
-    @Nullable
-    public static Command executeCommand(final @NotNull CommandSender sender, final @NotNull Object... args) throws CommandExecutionException, InterruptedException {
-        Objects.requireNonNull(sender, "sender");
-        Objects.requireNonNull(args, "args");
-        for (Object arg : args) {
-            Objects.requireNonNull(arg, "element of args");
-        }
-
-        return BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE, (scope, completion) -> CommandManagerKt.executeCommand(sender, args, completion));
-    }
-
-    /**
-     * 异步 (在 Kotlin 协程线程池) 解析并执行一个指令
-     *
-     * @param scope 协程作用域 (用于管理协程生命周期). 一般填入 {@link JavaPlugin} 实例.
-     * @param args  接受 {@link String} 或 {@link Message} , 其他对象将会被 {@link Object#toString()}
-     * @return 成功执行的指令, 在无匹配指令时返回 <code>null</code>
-     * @see #executeCommand(CommandSender, Object...)
-     */
-    @NotNull
-    public static CompletableFuture<@Nullable Command> 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");
-        for (Object arg : args) {
-            Objects.requireNonNull(arg, "element of args");
-        }
-
-        return FutureKt.future(scope, EmptyCoroutineContext.INSTANCE, CoroutineStart.DEFAULT, (sc, completion) -> CommandManagerKt.executeCommand(sender, args, completion));
-    }
-
-
-    /**
-     * 解析并执行一个指令, 获取详细的指令参数等信息.
-     * <br />
-     * 执行过程中产生的异常将不会直接抛出, 而会包装为 {@link CommandExecuteResult.ExecutionException}
-     *
-     * @param args 接受 {@link String} 或 {@link Message} , 其他对象将会被 {@link Object#toString()}
-     * @return 执行结果
-     * @see #executeCommandDetailedAsync(CoroutineScope, CommandSender, Object...)
-     */
-    @ConsoleExperimentalAPI
-    @NotNull
-    public static CommandExecuteResult executeCommandDetailed(final @NotNull CommandSender sender, final @NotNull Object... args) throws InterruptedException {
-        Objects.requireNonNull(sender, "sender");
-        Objects.requireNonNull(args, "args");
-        for (Object arg : args) {
-            Objects.requireNonNull(arg, "element of args");
-        }
-
-        return BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE, (scope, completion) -> CommandManagerKt.executeCommandDetailed(sender, args, completion));
-    }
-
-    /**
-     * 异步 (在 Kotlin 协程线程池) 解析并执行一个指令, 获取详细的指令参数等信息
-     *
-     * @param scope 协程作用域 (用于管理协程生命周期). 一般填入 {@link JavaPlugin} 实例.
-     * @param args  接受 {@link String} 或 {@link Message} , 其他对象将会被 {@link Object#toString()}
-     * @return 执行结果
-     * @see #executeCommandDetailed(CommandSender, Object...)
-     */
-    @ConsoleExperimentalAPI
-    @NotNull
-    public static CompletableFuture<@NotNull CommandExecuteResult>
-    executeCommandDetailedAsync(final @NotNull CoroutineScope scope, final @NotNull CommandSender sender, final @NotNull Object... args) {
-        Objects.requireNonNull(sender, "sender");
-        Objects.requireNonNull(args, "args");
-        Objects.requireNonNull(scope, "scope");
-        for (Object arg : args) {
-            Objects.requireNonNull(arg, "element of args");
-        }
-
-        return FutureKt.future(scope, EmptyCoroutineContext.INSTANCE, CoroutineStart.DEFAULT, (sc, completion) -> CommandManagerKt.executeCommandDetailed(sender, args, completion));
-    }
-}

+ 0 - 39
backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java

@@ -1,39 +0,0 @@
-/*
- * 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.utils;
-
-import net.mamoe.mirai.Bot;
-
-import java.util.List;
-
-/**
- * 获取 Bot Manager
- * Java 友好 API
- */
-public class BotManager {
-
-    public static List<Long> getManagers(long botAccount) {
-        Bot bot = Bot.getInstance(botAccount);
-        return getManagers(bot);
-    }
-
-    public static List<Long> getManagers(Bot bot) {
-        return BotManagers.getManagers(bot);
-    }
-
-    public static boolean isManager(Bot bot, long target) {
-        return getManagers(bot).contains(target);
-    }
-
-    public static boolean isManager(long botAccount, long target) {
-        return getManagers(botAccount).contains(target);
-    }
-}
-

+ 6 - 96
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt

@@ -14,38 +14,23 @@ package net.mamoe.mirai.console
 
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.io.charsets.Charset
-import net.mamoe.mirai.Bot
 import net.mamoe.mirai.console.MiraiConsole.INSTANCE
-import net.mamoe.mirai.console.command.BuiltInCommands
-import net.mamoe.mirai.console.command.ConsoleCommandSender
-import net.mamoe.mirai.console.command.internal.InternalCommandManager
-import net.mamoe.mirai.console.command.primaryName
+import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
 import net.mamoe.mirai.console.plugin.PluginLoader
-import net.mamoe.mirai.console.plugin.PluginManager
-import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
 import net.mamoe.mirai.console.plugin.center.PluginCenter
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
-import net.mamoe.mirai.console.plugin.jvm.PluginManagerImpl
-import net.mamoe.mirai.console.setting.SettingStorage
-import net.mamoe.mirai.console.utils.ConsoleBuiltInSettingStorage
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
-import net.mamoe.mirai.utils.DefaultLogger
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.info
-import java.io.ByteArrayOutputStream
 import java.io.File
-import java.io.PrintStream
-import java.text.SimpleDateFormat
 import java.util.*
-import kotlin.coroutines.CoroutineContext
 
 
 /**
  * mirai-console 实例
  *
  * @see INSTANCE
+ * @see MiraiConsoleImplementation
  */
 public interface MiraiConsole : CoroutineScope {
     /**
@@ -56,6 +41,7 @@ public interface MiraiConsole : CoroutineScope {
     /**
      * Console 前端接口
      */
+    @ConsoleExperimentalAPI
     public val frontEnd: MiraiConsoleFrontEnd
 
     /**
@@ -74,6 +60,7 @@ public interface MiraiConsole : CoroutineScope {
 
     public val version: String
 
+    @ConsoleExperimentalAPI
     public val pluginCenter: PluginCenter
 
     @ConsoleExperimentalAPI
@@ -94,80 +81,3 @@ public class IllegalMiraiConsoleImplementationError @JvmOverloads constructor(
     public override val cause: Throwable? = null
 ) : Error()
 
-
-internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
-    @JvmStatic
-    val buildDate: Date = Date(1595136353901L) // 2020-07-19 13:25:53
-    const val version: String = "1.0-dev-4"
-}
-
-/**
- * [MiraiConsole] 公开 API 与前端实现的连接桥.
- */
-internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleImplementation,
-    MiraiConsole {
-    override val pluginCenter: PluginCenter get() = CuiPluginCenter
-
-    private val instance: MiraiConsoleImplementation get() = MiraiConsoleImplementation.instance
-    override val buildDate: Date get() = MiraiConsoleBuildConstants.buildDate
-    override val version: String get() = MiraiConsoleBuildConstants.version
-    override val rootDir: File get() = instance.rootDir
-    override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
-
-    @ConsoleExperimentalAPI
-    override val mainLogger: MiraiLogger
-        get() = instance.mainLogger
-    override val coroutineContext: CoroutineContext get() = instance.coroutineContext
-    override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
-    override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender
-
-    override val settingStorageForJarPluginLoader: SettingStorage get() = instance.settingStorageForJarPluginLoader
-    override val settingStorageForBuiltIns: SettingStorage get() = instance.settingStorageForBuiltIns
-
-    init {
-        DefaultLogger = { identity -> this.newLogger(identity) }
-    }
-
-    @ConsoleExperimentalAPI
-    override fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
-
-    internal fun doStart() {
-        val buildDateFormatted = SimpleDateFormat("yyyy-MM-dd").format(buildDate)
-        mainLogger.info { "Starting mirai-console..." }
-        mainLogger.info { "Backend: version $version, built on $buildDateFormatted." }
-        mainLogger.info { "Frontend ${frontEnd.name}: version $version." }
-
-        if (coroutineContext[Job] == null) {
-            throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
-        }
-        MiraiConsole.job.invokeOnCompletion {
-            Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
-        }
-
-        BuiltInCommands.registerAll()
-        mainLogger.info { "Preparing built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
-        InternalCommandManager.commandListener // start
-
-        mainLogger.info { "Loading plugins..." }
-        PluginManagerImpl.loadEnablePlugins()
-        mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
-        mainLogger.info { "mirai-console started successfully." }
-
-        ConsoleBuiltInSettingStorage // init
-        // Only for initialize
-    }
-}
-
-/**
- * Included in kotlin stdlib 1.4
- */
-internal val Throwable.stacktraceString: String
-    get() =
-        ByteArrayOutputStream().apply {
-            printStackTrace(PrintStream(this))
-        }.use { it.toByteArray().encodeToString() }
-
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun ByteArray.encodeToString(charset: Charset = Charsets.UTF_8): String =
-    kotlinx.io.core.String(this, charset = charset)

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt

@@ -10,7 +10,7 @@
 package net.mamoe.mirai.console
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import net.mamoe.mirai.utils.LoginSolver
 import net.mamoe.mirai.utils.MiraiLogger
 

+ 9 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt

@@ -13,10 +13,13 @@ package net.mamoe.mirai.console
 
 import kotlinx.atomicfu.locks.withLock
 import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
 import net.mamoe.mirai.console.command.ConsoleCommandSender
+import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
 import net.mamoe.mirai.console.plugin.PluginLoader
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
 import net.mamoe.mirai.console.setting.SettingStorage
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import net.mamoe.mirai.utils.MiraiLogger
 import java.io.File
 import java.util.concurrent.locks.ReentrantLock
@@ -35,7 +38,9 @@ import kotlin.annotation.AnnotationTarget.*
 public annotation class ConsoleFrontEndImplementation
 
 /**
- * [MiraiConsole] 前端实现, 需低啊用
+ * 由前端实现这个接口
+ *
+ * @see  MiraiConsoleImplementation.start
  */
 @ConsoleFrontEndImplementation
 public interface MiraiConsoleImplementation : CoroutineScope {
@@ -47,6 +52,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
     /**
      * Console 前端接口
      */
+    @ConsoleExperimentalAPI
     public val frontEnd: MiraiConsoleFrontEnd
 
     /**
@@ -70,8 +76,9 @@ public interface MiraiConsoleImplementation : CoroutineScope {
         internal lateinit var instance: MiraiConsoleImplementation
         private val initLock = ReentrantLock()
 
-        /** 由前端调用, 初始化 [MiraiConsole] 实例, 并 */
+        /** 由前端调用, 初始化 [MiraiConsole] 实例, 并启动 */
         @JvmStatic
+        @ConsoleFrontEndImplementation
         public fun MiraiConsoleImplementation.start(): Unit = initLock.withLock {
             [email protected] = this
             MiraiConsoleImplementationBridge.doStart()

+ 4 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/BuiltInCommands.kt

@@ -16,7 +16,9 @@ import kotlinx.coroutines.sync.withLock
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.alsoLogin
 import net.mamoe.mirai.console.MiraiConsole
-import net.mamoe.mirai.console.stacktraceString
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+import net.mamoe.mirai.console.command.CommandManagerImpl.allRegisteredCommands
+import net.mamoe.mirai.console.command.CommandManagerImpl.register
 import net.mamoe.mirai.event.selectMessagesUnit
 import net.mamoe.mirai.utils.DirectoryLogger
 import net.mamoe.mirai.utils.weeksToMillis
@@ -138,7 +140,7 @@ public object BuiltInCommands {
                                     if (this is MessageEventContextAware<*>) {
                                         this.fromEvent.selectMessagesUnit {
                                             "stacktrace" reply {
-                                                throwable.stacktraceString
+                                                throwable.stackTraceToString()
                                             }
                                         }
                                         "test"

+ 35 - 16
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt

@@ -11,16 +11,20 @@
 
 package net.mamoe.mirai.console.command
 
-import net.mamoe.mirai.console.command.internal.isValidSubName
+import net.mamoe.kjbb.JvmBlockingBridge
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
+import net.mamoe.mirai.console.internal.command.isValidSubName
 import net.mamoe.mirai.message.data.SingleMessage
 
 /**
  * 指令
- * 通常情况下, 你的指令应继承 @see CompositeCommand/SimpleCommand
- * @see register 注册这个指令
+ *
+ * @see CommandManager.register 注册这个指令
  *
  * @see RawCommand
  * @see CompositeCommand
+ * @see SimpleCommand
  */
 public interface Command {
     /**
@@ -28,8 +32,14 @@ public interface Command {
      */
     public val names: Array<out String>
 
+    /**
+     * 用法说明, 用于发送给用户
+     */
     public val usage: String
 
+    /**
+     * 指令描述, 用于显示在 [BuiltInCommands.Help]
+     */
     public val description: String
 
     /**
@@ -38,20 +48,37 @@ public interface Command {
     public val permission: CommandPermission
 
     /**
-     * 为 `true` 时表示 [指令前缀][CommandPrefix] 可选
+     * 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选
      */
     public val prefixOptional: Boolean
 
+    /**
+     * 指令拥有者, 对于插件的指令通常是 [PluginCommandOwner]
+     */
     public val owner: CommandOwner
 
     /**
-     * @param args 指令参数. 可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
+     * @param args 指令参数. 数组元素类型可能是 [SingleMessage] 或 [String]. 且已经以 ' ' 分割.
      *
-     * @see Command.execute
-     */ // TODO: 2020/6/28 Java-friendly bridges
+     * @see CommandManager.execute
+     */
+    @JvmBlockingBridge
     public suspend fun CommandSender.onCommand(args: Array<out Any>)
+
+    public companion object {
+        /**
+         * 主要指令名. 为 [Command.names] 的第一个元素.
+         */
+        @JvmStatic
+        public val Command.primaryName: String
+            get() = names[0]
+    }
 }
 
+@JvmSynthetic
+public suspend inline fun Command.onCommand(sender: CommandSender, args: Array<out Any>): Unit =
+    sender.run { onCommand(args) }
+
 /**
  * [Command] 的基础实现
  */
@@ -68,12 +95,4 @@ public abstract class AbstractCommand @JvmOverloads constructor(
             list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") }
         }.toTypedArray()
 
-}
-
-public suspend inline fun Command.onCommand(sender: CommandSender, args: Array<out Any>): Unit =
-    sender.run { onCommand(args) }
-
-/**
- * 主要指令名. 为 [Command.names] 的第一个元素.
- */
-public val Command.primaryName: String get() = names[0]
+}

+ 2 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecutionException.kt

@@ -11,6 +11,8 @@
 
 package net.mamoe.mirai.console.command
 
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+
 /**
  * 在 [executeCommand] 中, [Command.onCommand] 抛出异常时包装的异常.
  */

+ 154 - 240
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt

@@ -15,15 +15,163 @@
 
 package net.mamoe.mirai.console.command
 
-import kotlinx.atomicfu.locks.withLock
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import net.mamoe.mirai.console.command.internal.*
+import net.mamoe.kjbb.JvmBlockingBridge
+import net.mamoe.mirai.console.command.CommandManagerImpl.unregisterAllCommands
 import net.mamoe.mirai.console.plugin.Plugin
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.MessageChain
 
+public interface CommandManager {
+    /**
+     * 获取已经注册了的属于这个 [CommandOwner] 的指令列表.
+     */
+    public val CommandOwner.registeredCommands: List<Command>
+
+    /**
+     * 获取所有已经注册了指令列表.
+     */
+    public val allRegisteredCommands: List<Command>
+
+    /**
+     * 指令前缀, 如 '/'
+     */
+    public val commandPrefix: String
+
+    /**
+     * 取消注册所有属于 [this] 的指令
+     */
+    public fun CommandOwner.unregisterAllCommands()
+
+    /**
+     * 注册一个指令.
+     *
+     * @param override 是否覆盖重名指令.
+     *
+     * 若原有指令 P, 其 [Command.names] 为 'a', 'b', 'c'.
+     * 新指令 Q, 其 [Command.names] 为 'b', 将会覆盖原指令 A 注册的 'b'.
+     *
+     * 即注册完成后, 'a' 和 'c' 将会解析到指令 P, 而 'b' 会解析到指令 Q.
+     *
+     * @return
+     * 若已有重名指令, 且 [override] 为 `false`, 返回 `false`;
+     * 若已有重名指令, 但 [override] 为 `true`, 覆盖原有指令并返回 `true`.
+     */
+    public fun Command.register(override: Boolean = false): Boolean
+
+    /**
+     * 查找并返回重名的指令. 返回重名指令.
+     */
+    public fun Command.findDuplicate(): Command?
+
+    /**
+     * 取消注册这个指令. 若指令未注册, 返回 `false`.
+     */
+    public fun Command.unregister(): Boolean
+
+    /**
+     * 当 [this] 已经 [注册][register] 后返回 `true`
+     */
+    public fun Command.isRegistered(): Boolean
+
+    /**
+     * 解析并执行一个指令. 将会检查指令权限, 在无权限时抛出
+     *
+     * @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
+     *
+     * @return 成功执行的指令, 在无匹配指令时返回 `null`
+     * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
+     */
+    @JvmBlockingBridge
+    public suspend fun CommandSender.executeCommand(vararg messages: Any): Command?
+
+    /**
+     * 解析并执行一个指令
+     *
+     * @return 成功执行的指令, 在无匹配指令时返回 `null`
+     * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
+     */
+    @Throws(CommandExecutionException::class)
+    @JvmBlockingBridge
+    public suspend fun CommandSender.executeCommand(message: MessageChain): Command?
+
+    /**
+     * 执行一个指令
+     *
+     * @return 成功执行的指令, 在无匹配指令时返回 `null`
+     * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
+     */
+    @JvmBlockingBridge
+    @Throws(CommandExecutionException::class)
+    public suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPermission: Boolean = true)
+
+    /**
+     * 执行一个指令
+     *
+     * @return 成功执行的指令, 在无匹配指令时返回 `null`
+     * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
+     */
+    @JvmBlockingBridge
+    @Throws(CommandExecutionException::class)
+    public suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean = true)
+
+    /**
+     * 解析并执行一个指令, 获取详细的指令参数等信息
+     *
+     * @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
+     *
+     * @return 执行结果
+     */
+    @ConsoleExperimentalAPI
+    @JvmBlockingBridge
+    public suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult
+
+    /**
+     * 解析并执行一个指令, 获取详细的指令参数等信息
+     *
+     * 执行过程中产生的异常将不会直接抛出, 而会包装为 [CommandExecuteResult.ExecutionException]
+     *
+     * @return 执行结果
+     */
+    @ConsoleExperimentalAPI
+    @JvmBlockingBridge
+    public suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult
+
+    public companion object INSTANCE : CommandManager by CommandManagerImpl {
+        override val CommandOwner.registeredCommands: List<Command> get() = CommandManagerImpl.run { registeredCommands }
+        override fun CommandOwner.unregisterAllCommands(): Unit = CommandManagerImpl.run { unregisterAllCommands() }
+        override fun Command.register(override: Boolean): Boolean = CommandManagerImpl.run { register(override) }
+        override fun Command.findDuplicate(): Command? = CommandManagerImpl.run { findDuplicate() }
+        override fun Command.unregister(): Boolean = CommandManagerImpl.run { unregister() }
+        override fun Command.isRegistered(): Boolean = CommandManagerImpl.run { isRegistered() }
+        override suspend fun CommandSender.executeCommand(vararg messages: Any): Command? =
+            CommandManagerImpl.run { executeCommand(*messages) }
+
+        override suspend fun CommandSender.executeCommand(message: MessageChain): Command? =
+            CommandManagerImpl.run { executeCommand(message) }
+
+        override suspend fun Command.execute(
+            sender: CommandSender,
+            args: MessageChain,
+            checkPermission: Boolean
+        ): Unit =
+            CommandManagerImpl.run { execute(sender, args = args, checkPermission = checkPermission) }
+
+        override suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean): Unit =
+            CommandManagerImpl.run { execute(sender, args = args, checkPermission = checkPermission) }
+
+        @ConsoleExperimentalAPI
+        override suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult =
+            CommandManagerImpl.run { executeCommandDetailed(*messages) }
+
+        @ConsoleExperimentalAPI
+        override suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult =
+            CommandManagerImpl.run { executeCommandDetailed(messages) }
+    }
+}
+
 /**
  * 指令的所有者.
  * @see PluginCommandOwner
@@ -33,7 +181,9 @@ public sealed class CommandOwner
 /**
  * 插件指令所有者. 插件只能通过 [PluginCommandOwner] 管理指令.
  */
-public abstract class PluginCommandOwner(public val plugin: Plugin) : CommandOwner() {
+public abstract class PluginCommandOwner(
+    public val plugin: Plugin
+) : CommandOwner() {
     init {
         if (plugin is CoroutineScope) { // JVM Plugin
             plugin.coroutineContext[Job]?.invokeOnCompletion {
@@ -47,239 +197,3 @@ public abstract class PluginCommandOwner(public val plugin: Plugin) : CommandOwn
  * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner].
  */
 public object ConsoleCommandOwner : CommandOwner()
-
-/**
- * 获取已经注册了的属于这个 [CommandOwner] 的指令列表.
- * @see JCommandManager.getRegisteredCommands Java 方法
- */
-public val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
-
-/**
- * 获取所有已经注册了指令列表.
- * @see JCommandManager.getRegisteredCommands Java 方法
- */
-public val allRegisteredCommands: List<Command> get() = InternalCommandManager.registeredCommands.toList() // copy
-
-/**
- * 指令前缀, 如 '/'
- * @see JCommandManager.getCommandPrefix Java 方法
- */
-@get:JvmName("getCommandPrefix")
-public val CommandPrefix: String
-    get() = InternalCommandManager.COMMAND_PREFIX
-
-/**
- * 取消注册所有属于 [this] 的指令
- * @see JCommandManager.unregisterAllCommands Java 方法
- */
-public fun CommandOwner.unregisterAllCommands() {
-    for (registeredCommand in registeredCommands) {
-        registeredCommand.unregister()
-    }
-}
-
-/**
- * 注册一个指令.
- *
- * @param override 是否覆盖重名指令.
- *
- * 若原有指令 P, 其 [Command.names] 为 'a', 'b', 'c'.
- * 新指令 Q, 其 [Command.names] 为 'b', 将会覆盖原指令 A 注册的 'b'.
- *
- * 即注册完成后, 'a' 和 'c' 将会解析到指令 P, 而 'b' 会解析到指令 Q.
- *
- * @return
- * 若已有重名指令, 且 [override] 为 `false`, 返回 `false`;
- * 若已有重名指令, 但 [override] 为 `true`, 覆盖原有指令并返回 `true`.
- *
- * @see JCommandManager.register Java 方法
- */
-@JvmOverloads
-public fun Command.register(override: Boolean = false): Boolean {
-    if (this is CompositeCommand) this.subCommands // init
-
-    InternalCommandManager.modifyLock.withLock {
-        if (!override) {
-            if (findDuplicate() != null) return false
-        }
-        InternalCommandManager.registeredCommands.add(this@register)
-        if (this.prefixOptional) {
-            for (name in this.names) {
-                val lowerCaseName = name.toLowerCase()
-                InternalCommandManager.optionalPrefixCommandMap[lowerCaseName] = this
-                InternalCommandManager.requiredPrefixCommandMap[lowerCaseName] = this
-            }
-        } else {
-            for (name in this.names) {
-                val lowerCaseName = name.toLowerCase()
-                InternalCommandManager.optionalPrefixCommandMap.remove(lowerCaseName) // ensure resolution consistency
-                InternalCommandManager.requiredPrefixCommandMap[lowerCaseName] = this
-            }
-        }
-        return true
-    }
-}
-
-/**
- * 查找并返回重名的指令. 返回重名指令.
- *
- * @see JCommandManager.findDuplicate Java 方法
- */
-public fun Command.findDuplicate(): Command? =
-    InternalCommandManager.registeredCommands.firstOrNull { it.names intersectsIgnoringCase this.names }
-
-/**
- * 取消注册这个指令. 若指令未注册, 返回 `false`.
- *
- * @see JCommandManager.unregister Java 方法
- */
-public fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
-    if (this.prefixOptional) {
-        this.names.forEach {
-            InternalCommandManager.optionalPrefixCommandMap.remove(it)
-        }
-    }
-    this.names.forEach {
-        InternalCommandManager.requiredPrefixCommandMap.remove(it)
-    }
-    InternalCommandManager.registeredCommands.remove(this)
-}
-
-/**
- * 当 [this] 已经 [注册][register] 后返回 `true`
- */
-public fun Command.isRegistered(): Boolean = this in InternalCommandManager.registeredCommands
-
-//// executing without detailed result (faster)
-
-/**
- * 解析并执行一个指令. 将会检查指令权限, 在无权限时抛出
- *
- * @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
- *
- * @return 成功执行的指令, 在无匹配指令时返回 `null`
- * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
- *
- * @see JCommandManager.executeCommand Java 方法
- */
-public suspend fun CommandSender.executeCommand(vararg messages: Any): Command? {
-    if (messages.isEmpty()) return null
-    return matchAndExecuteCommandInternal(messages, messages[0].toString().substringBefore(' '))
-}
-
-/**
- * 解析并执行一个指令
- *
- * @return 成功执行的指令, 在无匹配指令时返回 `null`
- * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
- *
- * @see JCommandManager.executeCommand Java 方法
- */
-@Throws(CommandExecutionException::class)
-public suspend fun CommandSender.executeCommand(message: MessageChain): Command? {
-    if (message.isEmpty()) return null
-    return matchAndExecuteCommandInternal(message, message[0].toString().substringBefore(' '))
-}
-
-/**
- * 执行一个指令
- *
- * @return 成功执行的指令, 在无匹配指令时返回 `null`
- * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
- *
- * @see JCommandManager.executeCommand Java 方法
- */
-@JvmOverloads
-@Throws(CommandExecutionException::class)
-public suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPermission: Boolean = true) {
-    sender.executeCommandInternal(
-        this,
-        args.flattenCommandComponents().toTypedArray(),
-        this.primaryName,
-        checkPermission
-    )
-}
-
-/**
- * 执行一个指令
- *
- * @return 成功执行的指令, 在无匹配指令时返回 `null`
- * @throws CommandExecutionException 当 [Command.onCommand] 抛出异常时包装并附带相关指令信息抛出
- *
- * @see JCommandManager.executeCommand Java 方法
- */
-@JvmOverloads
-@Throws(CommandExecutionException::class)
-public suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean = true) {
-    sender.executeCommandInternal(
-        this,
-        args.flattenCommandComponents().toTypedArray(),
-        this.primaryName,
-        checkPermission
-    )
-}
-
-//// execution with detailed result
-
-/**
- * 解析并执行一个指令, 获取详细的指令参数等信息
- *
- * @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
- *
- * @return 执行结果
- *
- * @see JCommandManager.executeCommandDetailed Java 方法
- */
-@ConsoleExperimentalAPI
-public suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult {
-    if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
-    return executeCommandDetailedInternal(messages, messages[0].toString().substringBefore(' '))
-}
-
-/**
- * 解析并执行一个指令, 获取详细的指令参数等信息
- *
- * 执行过程中产生的异常将不会直接抛出, 而会包装为 [CommandExecuteResult.ExecutionException]
- *
- * @return 执行结果
- *
- * @see JCommandManager.executeCommandDetailed Java 方法
- */
-@ConsoleExperimentalAPI
-public suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult {
-    if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
-    return executeCommandDetailedInternal(messages, messages[0].toString())
-}
-
-@JvmSynthetic
-internal suspend inline fun CommandSender.executeCommandDetailedInternal(
-    messages: Any,
-    commandName: String
-): CommandExecuteResult {
-    val command =
-        InternalCommandManager.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName)
-    val args = messages.flattenCommandComponents().dropToTypedArray(1)
-
-    if (!command.testPermission(this)) {
-        return CommandExecuteResult.PermissionDenied(command, commandName)
-    }
-    kotlin.runCatching {
-        command.onCommand(this, args)
-    }.fold(
-        onSuccess = {
-            return CommandExecuteResult.Success(
-                commandName = commandName,
-                command = command,
-                args = args
-            )
-        },
-        onFailure = {
-            return CommandExecuteResult.ExecutionException(
-                commandName = commandName,
-                command = command,
-                exception = it,
-                args = args
-            )
-        }
-    )
-}

+ 156 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManagerImpl.kt

@@ -0,0 +1,156 @@
+package net.mamoe.mirai.console.command
+
+import kotlinx.atomicfu.locks.withLock
+import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.console.MiraiConsole
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+import net.mamoe.mirai.console.internal.command.*
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.event.Listener
+import net.mamoe.mirai.event.subscribeAlways
+import net.mamoe.mirai.message.MessageEvent
+import net.mamoe.mirai.message.data.MessageChain
+import java.util.concurrent.locks.ReentrantLock
+
+internal object CommandManagerImpl : CommandManager, CoroutineScope by CoroutineScope(MiraiConsole.job) {
+    @JvmField
+    internal val registeredCommands: MutableList<Command> = mutableListOf()
+
+    /**
+     * 全部注册的指令
+     * /mute -> MuteCommand
+     * /jinyan -> MuteCommand
+     */
+    @JvmField
+    internal val requiredPrefixCommandMap: MutableMap<String, Command> = mutableMapOf()
+
+    /**
+     * Command name of commands that are prefix optional
+     * mute -> MuteCommand
+     */
+    @JvmField
+    internal val optionalPrefixCommandMap: MutableMap<String, Command> = mutableMapOf()
+
+    @JvmField
+    internal val modifyLock = ReentrantLock()
+
+
+    /**
+     * 从原始的 command 中解析出 Command 对象
+     */
+    internal fun matchCommand(rawCommand: String): Command? {
+        if (rawCommand.startsWith(commandPrefix)) {
+            return requiredPrefixCommandMap[rawCommand.substringAfter(commandPrefix).toLowerCase()]
+        }
+        return optionalPrefixCommandMap[rawCommand.toLowerCase()]
+    }
+
+    internal val commandListener: Listener<MessageEvent> by lazy {
+        @Suppress("RemoveExplicitTypeArguments")
+        subscribeAlways<MessageEvent>(
+            concurrency = Listener.ConcurrencyKind.CONCURRENT,
+            priority = Listener.EventPriority.HIGH
+        ) {
+            if (this.sender.asCommandSender().executeCommand(message) != null) {
+                intercept()
+            }
+        }
+    }
+
+
+    ///// IMPL
+
+
+    override val CommandOwner.registeredCommands: List<Command> get() = registeredCommands.filter { it.owner == this }
+    override val allRegisteredCommands: List<Command> get() = registeredCommands.toList() // copy
+    override val commandPrefix: String get() = "/"
+    override fun CommandOwner.unregisterAllCommands() {
+        for (registeredCommand in registeredCommands) {
+            registeredCommand.unregister()
+        }
+    }
+
+    override fun Command.register(override: Boolean): Boolean {
+        if (this is CompositeCommand) this.subCommands // init
+
+        modifyLock.withLock {
+            if (!override) {
+                if (findDuplicate() != null) return false
+            }
+            registeredCommands.add(this@register)
+            if (this.prefixOptional) {
+                for (name in this.names) {
+                    val lowerCaseName = name.toLowerCase()
+                    optionalPrefixCommandMap[lowerCaseName] = this
+                    requiredPrefixCommandMap[lowerCaseName] = this
+                }
+            } else {
+                for (name in this.names) {
+                    val lowerCaseName = name.toLowerCase()
+                    optionalPrefixCommandMap.remove(lowerCaseName) // ensure resolution consistency
+                    requiredPrefixCommandMap[lowerCaseName] = this
+                }
+            }
+            return true
+        }
+    }
+
+    override fun Command.findDuplicate(): Command? =
+        registeredCommands.firstOrNull { it.names intersectsIgnoringCase this.names }
+
+    override fun Command.unregister(): Boolean = modifyLock.withLock {
+        if (this.prefixOptional) {
+            this.names.forEach {
+                optionalPrefixCommandMap.remove(it)
+            }
+        }
+        this.names.forEach {
+            requiredPrefixCommandMap.remove(it)
+        }
+        registeredCommands.remove(this)
+    }
+
+    override fun Command.isRegistered(): Boolean = this in registeredCommands
+
+    //// executing without detailed result (faster)
+    override suspend fun CommandSender.executeCommand(vararg messages: Any): Command? {
+        if (messages.isEmpty()) return null
+        return matchAndExecuteCommandInternal(messages, messages[0].toString().substringBefore(' '))
+    }
+
+    override suspend fun CommandSender.executeCommand(message: MessageChain): Command? {
+        if (message.isEmpty()) return null
+        return matchAndExecuteCommandInternal(message, message[0].toString().substringBefore(' '))
+    }
+
+    override suspend fun Command.execute(sender: CommandSender, args: MessageChain, checkPermission: Boolean) {
+        sender.executeCommandInternal(
+            this,
+            args.flattenCommandComponents().toTypedArray(),
+            this.primaryName,
+            checkPermission
+        )
+    }
+
+    override suspend fun Command.execute(sender: CommandSender, vararg args: Any, checkPermission: Boolean) {
+        sender.executeCommandInternal(
+            this,
+            args.flattenCommandComponents().toTypedArray(),
+            this.primaryName,
+            checkPermission
+        )
+    }
+
+    //// execution with detailed result
+    @ConsoleExperimentalAPI
+    override suspend fun CommandSender.executeCommandDetailed(vararg messages: Any): CommandExecuteResult {
+        if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
+        return executeCommandDetailedInternal(messages, messages[0].toString().substringBefore(' '))
+    }
+
+    @ConsoleExperimentalAPI
+    override suspend fun CommandSender.executeCommandDetailed(messages: MessageChain): CommandExecuteResult {
+        if (messages.isEmpty()) return CommandExecuteResult.CommandNotFound("")
+        return executeCommandDetailedInternal(messages, messages[0].toString())
+    }
+}

+ 1 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt

@@ -12,7 +12,7 @@
 package net.mamoe.mirai.console.command
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.utils.isManager
+import net.mamoe.mirai.console.util.BotManager.INSTANCE.isManager
 import net.mamoe.mirai.contact.isAdministrator
 import net.mamoe.mirai.contact.isOperator
 import net.mamoe.mirai.contact.isOwner
@@ -35,13 +35,11 @@ public interface CommandPermission {
     /**
      * 满足两个权限其中一个即可使用指令
      */ // no extension for Java
-    @JvmDefault
     public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
 
     /**
      * 同时拥有两个权限才能使用指令
      */ // no extension for Java
-    @JvmDefault
     public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
 
 

+ 2 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermissionDeniedException.kt

@@ -9,6 +9,8 @@
 
 package net.mamoe.mirai.console.command
 
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+
 /**
  * 在 [executeCommand] 中, [CommandSender] 未拥有 [Command.permission] 所要求的权限时抛出的异常.
  *

+ 5 - 14
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandSender.kt

@@ -11,11 +11,10 @@
 
 package net.mamoe.mirai.console.command
 
-import kotlinx.coroutines.runBlocking
+import net.mamoe.kjbb.JvmBlockingBridge
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.MiraiConsoleImplementationBridge
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.console.utils.JavaFriendlyAPI
+import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.message.*
 import net.mamoe.mirai.message.data.Message
@@ -38,17 +37,8 @@ public interface CommandSender {
      * 立刻发送一条消息
      */
     @JvmSynthetic
+    @JvmBlockingBridge
     public suspend fun sendMessage(message: Message)
-
-    @JvmDefault
-    @JavaFriendlyAPI
-    @JvmName("sendMessage")
-    public fun __sendMessageBlocking(messageChain: Message): Unit = runBlocking { sendMessage(messageChain) }
-
-    @JvmDefault
-    @JavaFriendlyAPI
-    @JvmName("sendMessage")
-    public fun __sendMessageBlocking(message: String): Unit = runBlocking { sendMessage(message) }
 }
 
 /**
@@ -58,6 +48,7 @@ public interface BotAwareCommandSender : CommandSender {
     public override val bot: Bot
 }
 
+@JvmSynthetic
 public suspend inline fun CommandSender.sendMessage(message: String): Unit = sendMessage(PlainText(message))
 
 /**

+ 7 - 7
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt

@@ -18,9 +18,9 @@
 package net.mamoe.mirai.console.command
 
 import net.mamoe.mirai.console.command.description.*
-import net.mamoe.mirai.console.command.internal.AbstractReflectionCommand
-import net.mamoe.mirai.console.command.internal.CompositeCommandSubCommandAnnotationResolver
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
+import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import kotlin.annotation.AnnotationRetention.RUNTIME
 import kotlin.annotation.AnnotationTarget.FUNCTION
 import kotlin.reflect.KClass
@@ -35,13 +35,13 @@ public abstract class CompositeCommand @JvmOverloads constructor(
     description: String = "no description available",
     permission: CommandPermission = CommandPermission.Default,
     prefixOptional: Boolean = false,
-    overrideContext: CommandParserContext = EmptyCommandParserContext
+    overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
 ) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
-    CommandParserContextAware {
+    CommandArgumentContextAware {
     /**
-     * [CommandArgParser] 的环境
+     * [CommandArgumentParser] 的环境
      */
-    public final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
+    public final override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext
 
     /**
      * 标记一个函数为子指令, 当 [value] 为空时使用函数名.

+ 3 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt

@@ -1,5 +1,8 @@
 package net.mamoe.mirai.console.command
 
+/**
+ * 无参数解析, 接收原生参数的指令.
+ */
 public abstract class RawCommand(
     public override val owner: CommandOwner,
     public override vararg val names: String,

+ 12 - 12
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt

@@ -17,12 +17,12 @@
 
 package net.mamoe.mirai.console.command
 
-import net.mamoe.mirai.console.command.description.CommandParserContext
-import net.mamoe.mirai.console.command.description.CommandParserContextAware
-import net.mamoe.mirai.console.command.description.EmptyCommandParserContext
+import net.mamoe.mirai.console.command.description.CommandArgumentContext
+import net.mamoe.mirai.console.command.description.CommandArgumentContextAware
+import net.mamoe.mirai.console.command.description.EmptyCommandArgumentContext
 import net.mamoe.mirai.console.command.description.plus
-import net.mamoe.mirai.console.command.internal.AbstractReflectionCommand
-import net.mamoe.mirai.console.command.internal.SimpleCommandSubCommandAnnotationResolver
+import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
+import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
 
 public abstract class SimpleCommand @JvmOverloads constructor(
     owner: CommandOwner,
@@ -30,9 +30,9 @@ public abstract class SimpleCommand @JvmOverloads constructor(
     description: String = "no description available",
     permission: CommandPermission = CommandPermission.Default,
     prefixOptional: Boolean = false,
-    overrideContext: CommandParserContext = EmptyCommandParserContext
+    overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
 ) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional),
-    CommandParserContextAware {
+    CommandArgumentContextAware {
 
     public override val usage: String
         get() = super.usage
@@ -42,7 +42,11 @@ public abstract class SimpleCommand @JvmOverloads constructor(
      */
     protected annotation class Handler
 
-    public final override val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
+    public final override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext
+
+    public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
+        subCommands.single().parseAndExecute(this, args, false)
+    }
 
     internal override fun checkSubCommand(subCommands: Array<SubCommandDescriptor>) {
         super.checkSubCommand(subCommands)
@@ -52,10 +56,6 @@ public abstract class SimpleCommand @JvmOverloads constructor(
     @Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
     internal override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = sendMessage(usage)
 
-    public final override suspend fun CommandSender.onCommand(args: Array<out Any>) {
-        subCommands.single().parseAndExecute(this, args, false)
-    }
-
     internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
         get() = SimpleCommandSubCommandAnnotationResolver
 }

+ 13 - 13
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt

@@ -14,7 +14,7 @@ import net.mamoe.mirai.console.command.BotAwareCommandSender
 import net.mamoe.mirai.console.command.CommandSender
 import net.mamoe.mirai.console.command.MemberCommandSender
 import net.mamoe.mirai.console.command.UserCommandSender
-import net.mamoe.mirai.console.command.internal.fuzzySearchMember
+import net.mamoe.mirai.console.internal.command.fuzzySearchMember
 import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
@@ -23,43 +23,43 @@ import net.mamoe.mirai.message.data.SingleMessage
 import net.mamoe.mirai.message.data.content
 
 
-public object IntArgParser : CommandArgParser<Int> {
+public object IntArgumentParser : CommandArgumentParser<Int> {
     public override fun parse(raw: String, sender: CommandSender): Int =
         raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数")
 }
 
-public object LongArgParser : CommandArgParser<Long> {
+public object LongArgumentParser : CommandArgumentParser<Long> {
     public override fun parse(raw: String, sender: CommandSender): Long =
         raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数")
 }
 
-public object ShortArgParser : CommandArgParser<Short> {
+public object ShortArgumentParser : CommandArgumentParser<Short> {
     public override fun parse(raw: String, sender: CommandSender): Short =
         raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数")
 }
 
-public object ByteArgParser : CommandArgParser<Byte> {
+public object ByteArgumentParser : CommandArgumentParser<Byte> {
     public override fun parse(raw: String, sender: CommandSender): Byte =
         raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节")
 }
 
-public object DoubleArgParser : CommandArgParser<Double> {
+public object DoubleArgumentParser : CommandArgumentParser<Double> {
     public override fun parse(raw: String, sender: CommandSender): Double =
         raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数")
 }
 
-public object FloatArgParser : CommandArgParser<Float> {
+public object FloatArgumentParser : CommandArgumentParser<Float> {
     public override fun parse(raw: String, sender: CommandSender): Float =
         raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数")
 }
 
-public object StringArgParser : CommandArgParser<String> {
+public object StringArgumentParser : CommandArgumentParser<String> {
     public override fun parse(raw: String, sender: CommandSender): String {
         return raw
     }
 }
 
-public object BooleanArgParser : CommandArgParser<Boolean> {
+public object BooleanArgumentParser : CommandArgumentParser<Boolean> {
     public override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str ->
         str.equals("true", ignoreCase = true)
                 || str.equals("yes", ignoreCase = true)
@@ -73,14 +73,14 @@ public object BooleanArgParser : CommandArgParser<Boolean> {
  * output: Bot
  * errors: String->Int convert, Bot Not Exist
  */
-public object ExistBotArgParser : CommandArgParser<Bot> {
+public object ExistBotArgumentParser : CommandArgumentParser<Bot> {
     public override fun parse(raw: String, sender: CommandSender): Bot {
         val uin = raw.toLongOrNull() ?: illegalArgument("无法识别 QQ ID: $raw")
         return Bot.getInstanceOrNull(uin) ?: illegalArgument("无法找到 Bot $uin")
     }
 }
 
-public object ExistFriendArgParser : CommandArgParser<Friend> {
+public object ExistFriendArgumentParser : CommandArgumentParser<Friend> {
     //Bot.friend
     //friend
     //~ = self
@@ -125,7 +125,7 @@ public object ExistFriendArgParser : CommandArgParser<Friend> {
     }
 }
 
-public object ExistGroupArgParser : CommandArgParser<Group> {
+public object ExistGroupArgumentParser : CommandArgumentParser<Group> {
     public override fun parse(raw: String, sender: CommandSender): Group {
         //by default
         if ((raw == "" || raw == "~") && sender is MemberCommandSender) {
@@ -160,7 +160,7 @@ public object ExistGroupArgParser : CommandArgParser<Group> {
     }
 }
 
-public object ExistMemberArgParser : CommandArgParser<Member> {
+public object ExistMemberArgumentParser : CommandArgumentParser<Member> {
     //后台: Bot.Group.Member[QQ/名片]
     //私聊: Group.Member[QQ/名片]
     //群内: Q号

+ 228 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentContext.kt

@@ -0,0 +1,228 @@
+/*
+ * 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", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "unused", "MemberVisibilityCanBePrivate")
+
+package net.mamoe.mirai.console.command.description
+
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.console.command.CommandSender
+import net.mamoe.mirai.console.command.CompositeCommand
+import net.mamoe.mirai.console.command.SimpleCommand
+import net.mamoe.mirai.console.command.description.CommandArgumentContext.ParserPair
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.contact.Friend
+import net.mamoe.mirai.contact.Group
+import net.mamoe.mirai.contact.Member
+import kotlin.internal.LowPriorityInOverloadResolution
+import kotlin.reflect.KClass
+import kotlin.reflect.full.isSubclassOf
+
+
+/**
+ * [CommandArgumentParser] 的集合, 用于 [CompositeCommand] 和 [SimpleCommand].
+ *
+ * @see SimpleCommandArgumentContext 简单实现
+ * @see EmptyCommandArgumentContext 空实现, 类似 [emptyList]
+ * @see CommandArgumentContext.EMPTY 空实现的另一种获取方式.
+ *
+ * @see CommandArgumentContext.Builtins 内建 [CommandArgumentParser]
+ *
+ * @see CommandArgumentContext DSL
+ */
+public interface CommandArgumentContext {
+    /**
+     * [KClass] 到 [CommandArgumentParser] 的匹配
+     */
+    public data class ParserPair<T : Any>(
+        val klass: KClass<T>,
+        val parser: CommandArgumentParser<T>
+    )
+
+    public operator fun <T : Any> get(klass: KClass<out T>): CommandArgumentParser<T>?
+
+    public fun toList(): List<ParserPair<*>>
+
+    public companion object {
+        /**
+         * For Java callers.
+         *
+         * @see [EmptyCommandArgumentContext]
+         */
+        @JvmStatic
+        public val EMPTY: CommandArgumentContext = EmptyCommandArgumentContext
+    }
+
+    /**
+     * 内建的默认 [CommandArgumentParser]
+     */
+    public object Builtins : CommandArgumentContext by (CommandArgumentContext {
+        Int::class with IntArgumentParser
+        Byte::class with ByteArgumentParser
+        Short::class with ShortArgumentParser
+        Boolean::class with BooleanArgumentParser
+        String::class with StringArgumentParser
+        Long::class with LongArgumentParser
+        Double::class with DoubleArgumentParser
+        Float::class with FloatArgumentParser
+
+        Member::class with ExistMemberArgumentParser
+        Group::class with ExistGroupArgumentParser
+        Bot::class with ExistBotArgumentParser
+        Friend::class with ExistFriendArgumentParser
+    })
+}
+
+/**
+ * 拥有 [CommandArgumentContext] 的类
+ *
+ * @see SimpleCommand
+ * @see CompositeCommand
+ */
+public interface CommandArgumentContextAware {
+    /**
+     * [CommandArgumentParser] 的集合
+     */
+    public val context: CommandArgumentContext
+}
+
+public object EmptyCommandArgumentContext : CommandArgumentContext by SimpleCommandArgumentContext(listOf())
+
+/**
+ * 合并两个 [CommandArgumentContext], [replacer] 将会替换 [this] 中重复的 parser.
+ */
+public operator fun CommandArgumentContext.plus(replacer: CommandArgumentContext): CommandArgumentContext {
+    if (replacer == EmptyCommandArgumentContext) return this
+    if (this == EmptyCommandArgumentContext) return replacer
+    return object : CommandArgumentContext {
+        override fun <T : Any> get(klass: KClass<out T>): CommandArgumentParser<T>? =
+            replacer[klass] ?: this@plus[klass]
+
+        override fun toList(): List<ParserPair<*>> = replacer.toList() + [email protected]()
+    }
+}
+
+/**
+ * 合并 [this] 与 [replacer], [replacer] 将会替换 [this] 中重复的 parser.
+ */
+public operator fun CommandArgumentContext.plus(replacer: List<ParserPair<*>>): CommandArgumentContext {
+    if (replacer.isEmpty()) return this
+    if (this == EmptyCommandArgumentContext) return SimpleCommandArgumentContext(replacer)
+    return object : CommandArgumentContext {
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : Any> get(klass: KClass<out T>): CommandArgumentParser<T>? =
+            replacer.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgumentParser<T>?
+                ?: this@plus[klass]
+
+        override fun toList(): List<ParserPair<*>> = replacer.toList() + [email protected]()
+    }
+}
+
+/**
+ * 自定义 [CommandArgumentContext]
+ *
+ * @see CommandArgumentContext
+ */
+@Suppress("UNCHECKED_CAST")
+public class SimpleCommandArgumentContext(
+    public val list: List<ParserPair<*>>
+) : CommandArgumentContext {
+    override fun <T : Any> get(klass: KClass<out T>): CommandArgumentParser<T>? =
+        this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgumentParser<T>?
+
+    override fun toList(): List<ParserPair<*>> = list
+}
+
+/**
+ * 构建一个 [CommandArgumentContext].
+ *
+ * ```
+ * CommandArgumentContext {
+ *     Int::class with IntArgParser
+ *     Member::class with ExistMemberArgParser
+ *     Group::class with { s: String, sender: CommandSender ->
+ *          Bot.getInstance(s.toLong()).getGroup(s.toLong())
+ *     }
+ *     Bot::class with { s: String ->
+ *          Bot.getInstance(s.toLong())
+ *     }
+ * }
+ * ```
+ *
+ * @see CommandArgumentContextBuilder
+ * @see CommandArgumentContext
+ */
+@Suppress("FunctionName")
+@JvmSynthetic
+public inline fun CommandArgumentContext(block: CommandArgumentContextBuilder.() -> Unit): CommandArgumentContext {
+    return SimpleCommandArgumentContext(CommandArgumentContextBuilder().apply(block).distinctByReversed { it.klass })
+}
+
+/**
+ * @see CommandArgumentContext
+ */
+public class CommandArgumentContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
+    @JvmName("add")
+    public inline infix fun <T : Any> KClass<T>.with(parser: CommandArgumentParser<T>): ParserPair<*> =
+        ParserPair(this, parser).also { add(it) }
+
+    /**
+     * 添加一个指令解析器
+     */
+    @JvmSynthetic
+    @LowPriorityInOverloadResolution
+    public inline infix fun <T : Any> KClass<T>.with(
+        crossinline parser: CommandArgumentParser<T>.(s: String, sender: CommandSender) -> T
+    ): ParserPair<*> = ParserPair(this, CommandArgParser(parser)).also { add(it) }
+
+    /**
+     * 添加一个指令解析器
+     */
+    @JvmSynthetic
+    public inline infix fun <T : Any> KClass<T>.with(
+        crossinline parser: CommandArgumentParser<T>.(s: String) -> T
+    ): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) }).also { add(it) }
+
+    @JvmSynthetic
+    public inline fun <reified T : Any> add(parser: CommandArgumentParser<T>): ParserPair<*> =
+        ParserPair(T::class, parser).also { add(it) }
+
+    /**
+     * 添加一个指令解析器
+     */
+    @ConsoleExperimentalAPI
+    @JvmSynthetic
+    public inline infix fun <reified T : Any> add(
+        crossinline parser: CommandArgumentParser<*>.(s: String) -> T
+    ): ParserPair<*> = T::class with CommandArgParser { s: String, _: CommandSender -> parser(s) }
+
+    /**
+     * 添加一个指令解析器
+     */
+    @ConsoleExperimentalAPI
+    @JvmSynthetic
+    @LowPriorityInOverloadResolution
+    public inline infix fun <reified T : Any> add(
+        crossinline parser: CommandArgumentParser<*>.(s: String, sender: CommandSender) -> T
+    ): ParserPair<*> = T::class with CommandArgParser(parser)
+}
+
+
+@PublishedApi
+internal inline fun <T, K> List<T>.distinctByReversed(selector: (T) -> K): List<T> {
+    val set = HashSet<K>()
+    val list = ArrayList<T>()
+    for (i in this.indices.reversed()) {
+        val element = this[i]
+        if (set.add(element.let(selector))) {
+            list.add(element)
+        }
+    }
+    return list
+}

+ 14 - 13
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParser.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentParser.kt

@@ -17,17 +17,18 @@ import net.mamoe.mirai.message.data.content
 import kotlin.contracts.contract
 
 /**
- * this output type of that arg
- * input is always String
+ * 指令参数解析器.
+ *
+ * @see CommandArgumentContext
  */
-public interface CommandArgParser<out T : Any> {
+public interface CommandArgumentParser<out T : Any> {
     public fun parse(raw: String, sender: CommandSender): T
 
     @JvmDefault
     public fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
 }
 
-public fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T {
+public fun <T : Any> CommandArgumentParser<T>.parse(raw: Any, sender: CommandSender): T {
     contract {
         returns() implies (raw is String || raw is SingleMessage)
     }
@@ -41,12 +42,12 @@ public fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender):
 
 @Suppress("unused")
 @JvmSynthetic
-public inline fun CommandArgParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing {
+public inline fun CommandArgumentParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing {
     throw ParserException(message, cause)
 }
 
 @JvmSynthetic
-public inline fun CommandArgParser<*>.checkArgument(
+public inline fun CommandArgumentParser<*>.checkArgument(
     condition: Boolean,
     crossinline message: () -> String = { "Check failed." }
 ) {
@@ -57,25 +58,25 @@ public inline fun CommandArgParser<*>.checkArgument(
 }
 
 /**
- * 创建匿名 [CommandArgParser]
+ * 创建匿名 [CommandArgumentParser]
  */
 @Suppress("FunctionName")
 @JvmSynthetic
 public inline fun <T : Any> CommandArgParser(
-    crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
-): CommandArgParser<T> = object : CommandArgParser<T> {
+    crossinline stringParser: CommandArgumentParser<T>.(s: String, sender: CommandSender) -> T
+): CommandArgumentParser<T> = object : CommandArgumentParser<T> {
     override fun parse(raw: String, sender: CommandSender): T = stringParser(raw, sender)
 }
 
 /**
- * 创建匿名 [CommandArgParser]
+ * 创建匿名 [CommandArgumentParser]
  */
 @Suppress("FunctionName")
 @JvmSynthetic
 public inline fun <T : Any> CommandArgParser(
-    crossinline stringParser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T,
-    crossinline messageParser: CommandArgParser<T>.(m: SingleMessage, sender: CommandSender) -> T
-): CommandArgParser<T> = object : CommandArgParser<T> {
+    crossinline stringParser: CommandArgumentParser<T>.(s: String, sender: CommandSender) -> T,
+    crossinline messageParser: CommandArgumentParser<T>.(m: SingleMessage, sender: CommandSender) -> T
+): CommandArgumentParser<T> = object : CommandArgumentParser<T> {
     override fun parse(raw: String, sender: CommandSender): T = stringParser(raw, sender)
     override fun parse(raw: SingleMessage, sender: CommandSender): T = messageParser(raw, sender)
 }

+ 0 - 193
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandParserContext.kt

@@ -1,193 +0,0 @@
-/*
- * 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", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "unused", "MemberVisibilityCanBePrivate")
-
-package net.mamoe.mirai.console.command.description
-
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.command.CommandSender
-import net.mamoe.mirai.console.command.description.CommandParserContext.ParserPair
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.contact.Friend
-import net.mamoe.mirai.contact.Group
-import net.mamoe.mirai.contact.Member
-import kotlin.internal.LowPriorityInOverloadResolution
-import kotlin.reflect.KClass
-import kotlin.reflect.full.isSubclassOf
-
-
-/**
- * [KClass] 到 [CommandArgParser] 的匹配
- * @see CustomCommandParserContext 自定义
- */
-public interface CommandParserContext {
-    public data class ParserPair<T : Any>(
-        val klass: KClass<T>,
-        val parser: CommandArgParser<T>
-    )
-
-    public operator fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>?
-
-    public fun toList(): List<ParserPair<*>>
-
-    /**
-     * 内建的默认 [CommandArgParser]
-     */
-    public object Builtins : CommandParserContext by (CommandParserContext {
-        Int::class with IntArgParser
-        Byte::class with ByteArgParser
-        Short::class with ShortArgParser
-        Boolean::class with BooleanArgParser
-        String::class with StringArgParser
-        Long::class with LongArgParser
-        Double::class with DoubleArgParser
-        Float::class with FloatArgParser
-
-        Member::class with ExistMemberArgParser
-        Group::class with ExistGroupArgParser
-        Bot::class with ExistBotArgParser
-        Friend::class with ExistFriendArgParser
-    })
-}
-
-/**
- * 拥有 [CommandParserContext] 的类
- */
-public interface CommandParserContextAware {
-    /**
-     * [CommandArgParser] 的环境
-     */
-    public val context: CommandParserContext
-}
-
-public object EmptyCommandParserContext : CommandParserContext by CustomCommandParserContext(listOf())
-
-/**
- * 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
- */
-public operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext {
-    if (replacer == EmptyCommandParserContext) return this
-    if (this == EmptyCommandParserContext) return replacer
-    return object : CommandParserContext {
-        override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? = replacer[klass] ?: this@plus[klass]
-        override fun toList(): List<ParserPair<*>> = replacer.toList() + [email protected]()
-    }
-}
-
-/**
- * 合并 [this] 与 [replacer], [replacer] 将会替换 [this] 中重复的 parser.
- */
-public operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext {
-    if (replacer.isEmpty()) return this
-    if (this == EmptyCommandParserContext) return CustomCommandParserContext(replacer)
-    return object : CommandParserContext {
-        @Suppress("UNCHECKED_CAST")
-        override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
-            replacer.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>? ?: this@plus[klass]
-
-        override fun toList(): List<ParserPair<*>> = replacer.toList() + [email protected]()
-    }
-}
-
-@Suppress("UNCHECKED_CAST")
-public open class CustomCommandParserContext(public val list: List<ParserPair<*>>) : CommandParserContext {
-
-    override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
-        this.list.firstOrNull { klass.isSubclassOf(it.klass) }?.parser as CommandArgParser<T>?
-
-    override fun toList(): List<ParserPair<*>> {
-        return list
-    }
-}
-
-/**
- * 构建一个 [CommandParserContext].
- *
- * ```
- * CommandParserContext {
- *     Int::class with IntArgParser
- *     Member::class with ExistMemberArgParser
- *     Group::class with { s: String, sender: CommandSender ->
- *          Bot.getInstance(s.toLong()).getGroup(s.toLong())
- *     }
- *     Bot::class with { s: String ->
- *          Bot.getInstance(s.toLong())
- *     }
- * }
- * ```
- */
-@Suppress("FunctionName")
-@JvmSynthetic
-public inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit): CommandParserContext {
-    return CustomCommandParserContext(CommandParserContextBuilder().apply(block).distinctByReversed { it.klass })
-}
-
-/**
- * @see CommandParserContext
- */
-public class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
-    @JvmName("add")
-    public inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): ParserPair<*> =
-        ParserPair(this, parser).also { add(it) }
-
-    /**
-     * 添加一个指令解析器
-     */
-    @JvmSynthetic
-    @LowPriorityInOverloadResolution
-    public inline infix fun <T : Any> KClass<T>.with(
-        crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
-    ): ParserPair<*> = ParserPair(this, CommandArgParser(parser)).also { add(it) }
-
-    /**
-     * 添加一个指令解析器
-     */
-    @JvmSynthetic
-    public inline infix fun <T : Any> KClass<T>.with(
-        crossinline parser: CommandArgParser<T>.(s: String) -> T
-    ): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) }).also { add(it) }
-
-    @JvmSynthetic
-    public inline fun <reified T : Any> add(parser: CommandArgParser<T>): ParserPair<*> =
-        ParserPair(T::class, parser).also { add(it) }
-
-    /**
-     * 添加一个指令解析器
-     */
-    @ConsoleExperimentalAPI
-    @JvmSynthetic
-    public inline infix fun <reified T : Any> add(
-        crossinline parser: CommandArgParser<*>.(s: String) -> T
-    ): ParserPair<*> = T::class with CommandArgParser { s: String, _: CommandSender -> parser(s) }
-
-    /**
-     * 添加一个指令解析器
-     */
-    @ConsoleExperimentalAPI
-    @JvmSynthetic
-    @LowPriorityInOverloadResolution
-    public inline infix fun <reified T : Any> add(
-        crossinline parser: CommandArgParser<*>.(s: String, sender: CommandSender) -> T
-    ): ParserPair<*> = T::class with CommandArgParser(parser)
-}
-
-
-@PublishedApi
-internal inline fun <T, K> List<T>.distinctByReversed(selector: (T) -> K): List<T> {
-    val set = HashSet<K>()
-    val list = ArrayList<T>()
-    for (i in this.indices.reversed()) {
-        val element = this[i]
-        if (set.add(element.let(selector))) {
-            list.add(element)
-        }
-    }
-    return list
-}

+ 6 - 6
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt

@@ -34,24 +34,24 @@ internal data class CommandParam<T : Any>(
      */
     val name: String,
     /**
-     * 参数类型. 将从 [CompositeCommand.context] 中寻找 [CommandArgParser] 解析.
+     * 参数类型. 将从 [CompositeCommand.context] 中寻找 [CommandArgumentParser] 解析.
      */
     val type: KClass<T> // exact type
 ) {
-    constructor(name: String, type: KClass<T>, parser: CommandArgParser<T>) : this(name, type) {
+    constructor(name: String, type: KClass<T>, parser: CommandArgumentParser<T>) : this(name, type) {
         this._overrideParser = parser
     }
 
     @Suppress("PropertyName")
     @JvmField
-    internal var _overrideParser: CommandArgParser<T>? = null
+    internal var _overrideParser: CommandArgumentParser<T>? = null
 
 
     /**
-     * 覆盖的 [CommandArgParser].
+     * 覆盖的 [CommandArgumentParser].
      *
-     * 如果非 `null`, 将不会从 [CommandParserContext] 寻找 [CommandArgParser]
+     * 如果非 `null`, 将不会从 [CommandArgumentContext] 寻找 [CommandArgumentParser]
      */
-    val overrideParser: CommandArgParser<T>? get() = _overrideParser
+    val overrideParser: CommandArgumentParser<T>? get() = _overrideParser
 }
 

+ 9 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleBuildConstants.kt

@@ -0,0 +1,9 @@
+package net.mamoe.mirai.console.internal
+
+import java.util.*
+
+internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
+    @JvmStatic
+    val buildDate: Date = Date(1596350800177L) // 2020-08-02 14:46:40
+    const val version: String = "1.0-M1"
+}

+ 88 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt

@@ -0,0 +1,88 @@
+@file:OptIn(ConsoleExperimentalAPI::class)
+
+package net.mamoe.mirai.console.internal
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.console.IllegalMiraiConsoleImplementationError
+import net.mamoe.mirai.console.MiraiConsole
+import net.mamoe.mirai.console.MiraiConsoleFrontEnd
+import net.mamoe.mirai.console.MiraiConsoleImplementation
+import net.mamoe.mirai.console.command.BuiltInCommands
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+import net.mamoe.mirai.console.command.CommandManagerImpl
+import net.mamoe.mirai.console.command.ConsoleCommandSender
+import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
+import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
+import net.mamoe.mirai.console.internal.utils.ConsoleBuiltInSettingStorage
+import net.mamoe.mirai.console.plugin.PluginLoader
+import net.mamoe.mirai.console.plugin.PluginManager
+import net.mamoe.mirai.console.plugin.center.PluginCenter
+import net.mamoe.mirai.console.setting.SettingStorage
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.utils.DefaultLogger
+import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.info
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * [MiraiConsole] 公开 API 与前端实现的连接桥.
+ */
+internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleImplementation,
+    MiraiConsole {
+    override val pluginCenter: PluginCenter get() = CuiPluginCenter
+
+    private val instance: MiraiConsoleImplementation get() = MiraiConsoleImplementation.instance
+    override val buildDate: Date get() = MiraiConsoleBuildConstants.buildDate
+    override val version: String get() = MiraiConsoleBuildConstants.version
+    override val rootDir: File get() = instance.rootDir
+    override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
+
+    @ConsoleExperimentalAPI
+    override val mainLogger: MiraiLogger
+        get() = instance.mainLogger
+    override val coroutineContext: CoroutineContext get() = instance.coroutineContext
+    override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
+    override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender
+
+    override val settingStorageForJarPluginLoader: SettingStorage get() = instance.settingStorageForJarPluginLoader
+    override val settingStorageForBuiltIns: SettingStorage get() = instance.settingStorageForBuiltIns
+
+    init {
+        DefaultLogger = { identity -> this.newLogger(identity) }
+    }
+
+    @ConsoleExperimentalAPI
+    override fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
+
+    @OptIn(ConsoleExperimentalAPI::class)
+    internal fun doStart() {
+        val buildDateFormatted = SimpleDateFormat("yyyy-MM-dd").format(buildDate)
+        mainLogger.info { "Starting mirai-console..." }
+        mainLogger.info { "Backend: version $version, built on $buildDateFormatted." }
+        mainLogger.info { "Frontend ${frontEnd.name}: version $version." }
+
+        if (coroutineContext[Job] == null) {
+            throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
+        }
+        MiraiConsole.job.invokeOnCompletion {
+            Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
+        }
+
+        BuiltInCommands.registerAll()
+        mainLogger.info { "Preparing built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
+        CommandManagerImpl.commandListener // start
+
+        mainLogger.info { "Loading plugins..." }
+        PluginManagerImpl.loadEnablePlugins()
+        mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
+        mainLogger.info { "mirai-console started successfully." }
+
+        ConsoleBuiltInSettingStorage // init
+        // Only for initialize
+    }
+}

+ 9 - 8
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal/CompositeCommandInternal.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt

@@ -9,12 +9,13 @@
 
 @file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
 
-package net.mamoe.mirai.console.command.internal
+package net.mamoe.mirai.console.internal.command
 
 import net.mamoe.mirai.console.command.*
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+import net.mamoe.mirai.console.command.description.CommandArgumentContext
+import net.mamoe.mirai.console.command.description.CommandArgumentContextAware
 import net.mamoe.mirai.console.command.description.CommandParam
-import net.mamoe.mirai.console.command.description.CommandParserContext
-import net.mamoe.mirai.console.command.description.CommandParserContextAware
 import net.mamoe.mirai.message.data.PlainText
 import net.mamoe.mirai.message.data.SingleMessage
 import kotlin.reflect.KAnnotatedElement
@@ -43,11 +44,11 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
     prefixOptional: Boolean = false
 ) : Command, AbstractCommand(
     owner,
-    names = *names,
+    names = names,
     description = description,
     permission = permission,
     prefixOptional = prefixOptional
-), CommandParserContextAware {
+), CommandArgumentContextAware {
     internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver
 
     @JvmField
@@ -101,7 +102,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
                     map[name] = descriptor
                 }
             }
-            map.toSortedMap(Comparator { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() })
+            map.toSortedMap { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() }
         }
     }
 
@@ -117,7 +118,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
         val description: String,
         val permission: CommandPermission,
         val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean,
-        val context: CommandParserContext,
+        val context: CommandArgumentContext,
         val usage: String
     ) {
         internal suspend inline fun parseAndExecute(
@@ -210,7 +211,7 @@ internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?:
 
 internal fun AbstractReflectionCommand.createSubCommand(
     function: KFunction<*>,
-    context: CommandParserContext
+    context: CommandArgumentContext
 ): AbstractReflectionCommand.SubCommandDescriptor {
     val notStatic = !function.hasAnnotation<JvmStatic>()
     val overridePermission = function.findAnnotation<CompositeCommand.Permission>()//optional

+ 37 - 57
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal/internal.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt

@@ -7,17 +7,11 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.command.internal
+package net.mamoe.mirai.console.internal.command
 
-import kotlinx.coroutines.CoroutineScope
-import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.command.*
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.event.Listener
-import net.mamoe.mirai.event.subscribeAlways
-import net.mamoe.mirai.message.MessageEvent
-import java.util.concurrent.locks.ReentrantLock
 
 
 internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
@@ -27,54 +21,6 @@ internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
     return true
 }
 
-internal object InternalCommandManager : CoroutineScope by CoroutineScope(MiraiConsole.job) {
-    const val COMMAND_PREFIX = "/"
-
-    @JvmField
-    internal val registeredCommands: MutableList<Command> = mutableListOf()
-
-    /**
-     * 全部注册的指令
-     * /mute -> MuteCommand
-     * /jinyan -> MuteCommand
-     */
-    @JvmField
-    internal val requiredPrefixCommandMap: MutableMap<String, Command> = mutableMapOf()
-
-    /**
-     * Command name of commands that are prefix optional
-     * mute -> MuteCommand
-     */
-    @JvmField
-    internal val optionalPrefixCommandMap: MutableMap<String, Command> = mutableMapOf()
-
-    @JvmField
-    internal val modifyLock = ReentrantLock()
-
-
-    /**
-     * 从原始的 command 中解析出 Command 对象
-     */
-    internal fun matchCommand(rawCommand: String): Command? {
-        if (rawCommand.startsWith(COMMAND_PREFIX)) {
-            return requiredPrefixCommandMap[rawCommand.substringAfter(COMMAND_PREFIX).toLowerCase()]
-        }
-        return optionalPrefixCommandMap[rawCommand.toLowerCase()]
-    }
-
-    internal val commandListener: Listener<MessageEvent> by lazy {
-        @Suppress("RemoveExplicitTypeArguments")
-        subscribeAlways<MessageEvent>(
-            concurrency = Listener.ConcurrencyKind.CONCURRENT,
-            priority = Listener.EventPriority.HIGH
-        ) {
-            if (this.sender.asCommandSender().executeCommand(message) != null) {
-                intercept()
-            }
-        }
-    }
-}
-
 internal infix fun Array<out String>.intersectsIgnoringCase(other: Array<out String>): Boolean {
     val max = this.size.coerceAtMost(other.size)
     for (i in 0 until max) {
@@ -186,7 +132,7 @@ internal suspend inline fun CommandSender.matchAndExecuteCommandInternal(
     messages: Any,
     commandName: String
 ): Command? {
-    val command = InternalCommandManager.matchCommand(
+    val command = CommandManagerImpl.matchCommand(
         commandName
     ) ?: return null
 
@@ -215,4 +161,38 @@ internal suspend inline fun CommandSender.executeCommandInternal(
     }.onFailure {
         throw CommandExecutionException(command, commandName, it)
     }
-}
+}
+
+
+@JvmSynthetic
+internal suspend inline fun CommandSender.executeCommandDetailedInternal(
+    messages: Any,
+    commandName: String
+): CommandExecuteResult {
+    val command =
+        CommandManagerImpl.matchCommand(commandName) ?: return CommandExecuteResult.CommandNotFound(commandName)
+    val args = messages.flattenCommandComponents().dropToTypedArray(1)
+
+    if (!command.testPermission(this)) {
+        return CommandExecuteResult.PermissionDenied(command, commandName)
+    }
+    kotlin.runCatching {
+        command.onCommand(this, args)
+    }.fold(
+        onSuccess = {
+            return CommandExecuteResult.Success(
+                commandName = commandName,
+                command = command,
+                args = args
+            )
+        },
+        onFailure = {
+            return CommandExecuteResult.ExecutionException(
+                commandName = commandName,
+                command = command,
+                exception = it,
+                args = args
+            )
+        }
+    )
+}

+ 4 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/center/CuiPluginCenter.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/CuiPluginCenter.kt

@@ -9,7 +9,7 @@
 
 @file:OptIn(ConsoleExperimentalAPI::class)
 
-package net.mamoe.mirai.console.plugin.center
+package net.mamoe.mirai.console.internal.plugin
 
 import io.ktor.client.*
 import io.ktor.client.engine.cio.*
@@ -17,8 +17,9 @@ import io.ktor.client.request.*
 import io.ktor.util.*
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.console.utils.retryCatching
+import net.mamoe.mirai.console.plugin.center.PluginCenter
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.retryCatching
 import java.io.File
 
 internal val json = Json {

+ 3 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/internal/JarPluginLoaderImpl.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt

@@ -7,18 +7,18 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.plugin.internal
+package net.mamoe.mirai.console.internal.plugin
 
 import kotlinx.coroutines.*
 import net.mamoe.mirai.console.MiraiConsole
-import net.mamoe.mirai.console.MiraiConsoleImplementationBridge
+import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
 import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
 import net.mamoe.mirai.console.plugin.PluginLoadException
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
 import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
 import net.mamoe.mirai.console.setting.SettingStorage
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.yamlkt.Yaml
 import java.io.File

+ 2 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/internal/JvmPluginInternal.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.plugin.internal
+package net.mamoe.mirai.console.internal.plugin
 
 import kotlinx.atomicfu.AtomicLong
 import kotlinx.atomicfu.locks.withLock
@@ -20,7 +20,7 @@ import net.mamoe.mirai.console.plugin.Plugin
 import net.mamoe.mirai.console.plugin.PluginManager
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
 import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
-import net.mamoe.mirai.console.utils.ResourceContainer.Companion.asResourceContainer
+import net.mamoe.mirai.console.util.ResourceContainer.Companion.asResourceContainer
 import net.mamoe.mirai.utils.MiraiLogger
 import java.io.File
 import java.io.InputStream

+ 3 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/PluginManagerImpl.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt

@@ -9,12 +9,12 @@
 
 @file:Suppress("NOTHING_TO_INLINE", "unused")
 
-package net.mamoe.mirai.console.plugin.jvm
+package net.mamoe.mirai.console.internal.plugin
 
 import kotlinx.atomicfu.locks.withLock
 import net.mamoe.mirai.console.MiraiConsole
+import net.mamoe.mirai.console.internal.setting.cast
 import net.mamoe.mirai.console.plugin.*
-import net.mamoe.mirai.console.setting.internal.cast
 import net.mamoe.mirai.utils.info
 import java.io.File
 import java.util.concurrent.locks.ReentrantLock
@@ -120,7 +120,7 @@ internal object PluginManagerImpl : PluginManager {
     @Throws(PluginMissingDependencyException::class)
     private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
         val allDescriptions =
-            this.builtInLoaders.listAllPlugins()
+            builtInLoaders.listAllPlugins()
                 .asSequence()
                 .onEach { (loader, descriptions) ->
                     loader as PluginLoader<Plugin, PluginDescription>

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/internal/PluginsLoader.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginsLoader.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.plugin.internal
+package net.mamoe.mirai.console.internal.plugin
 
 import net.mamoe.mirai.console.MiraiConsole
 import java.io.File

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/CompositeValueImpl.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/CompositeValueImpl.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("unused")
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import net.mamoe.mirai.console.setting.*
 

+ 1 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/SemverAsStringSerializer.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SemverAsStringSerializer.kt

@@ -7,13 +7,12 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.internal.setting
 
 import com.vdurmont.semver4j.Semver
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.Serializer
 import kotlinx.serialization.builtins.serializer
-import net.mamoe.mirai.console.setting.internal.map
 
 @Serializer(forClass = Semver::class)
 internal object SemverAsStringSerializerLoose : KSerializer<Semver> by String.serializer().map(

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/Setting.value composite impl.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/Setting.value composite impl.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("NOTHING_TO_INLINE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.KSerializer
 import net.mamoe.mirai.console.setting.SerializableValue.Companion.serializableValueWith

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/SettingImpl.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SettingImpl.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS")
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.SerialName

+ 5 - 5
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/SettingStorage internal.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/SettingStorage internal.kt

@@ -7,19 +7,19 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
-import net.mamoe.mirai.console.command.internal.qualifiedNameOrTip
-import net.mamoe.mirai.console.plugin.internal.updateWhen
+import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
+import net.mamoe.mirai.console.internal.plugin.updateWhen
 import net.mamoe.mirai.console.plugin.jvm.loadSetting
 import net.mamoe.mirai.console.setting.*
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.utils.currentTimeMillis
 import net.mamoe.yamlkt.Yaml
 import java.io.File

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_PrimitiveValueDeclarations.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/_PrimitiveValueDeclarations.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.builtins.serializer

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_Setting.value.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/_Setting.value.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.builtins.serializer
 import net.mamoe.mirai.console.setting.SerializerAwareValue

+ 2 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/asKClass.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/asKClass.kt

@@ -7,9 +7,9 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
-import net.mamoe.mirai.console.command.internal.qualifiedNameOrTip
+import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
 import net.mamoe.mirai.console.setting.Setting
 import kotlin.reflect.KClass
 import kotlin.reflect.KType

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/collectionUtil.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("DuplicatedCode")
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.UnsafeSerializationApi
 import kotlinx.serialization.serializer

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerHelper.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/serializerHelper.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.UnsafeSerializationApi

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/serializerUtil.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/setting/serializerUtil.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.setting.internal
+package net.mamoe.mirai.console.internal.setting
 
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.SerialName

+ 17 - 16
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotManagers.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/utils/BotManagerImpl.kt

@@ -6,41 +6,42 @@
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
-@file:JvmName("BotManagers")
+@file:Suppress("MemberVisibilityCanBePrivate")
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.internal.utils
 
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.SupervisorJob
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.console.MiraiConsole
-import net.mamoe.mirai.console.MiraiConsoleImplementationBridge
+import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
 import net.mamoe.mirai.console.setting.*
 import net.mamoe.mirai.console.setting.SettingStorage.Companion.load
+import net.mamoe.mirai.console.util.BotManager
 import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.utils.minutesToMillis
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
+internal object BotManagerImpl : BotManager {
+    /**
+     * 判断此用户是否为 console 管理员
+     */
+    override val User.isManager: Boolean get() = this.id in this.bot.managers
 
-/**
- * 判断此用户是否为 console 管理员
- */
-public val User.isManager: Boolean get() = this.id in this.bot.managers
-
-public fun Bot.removeManager(id: Long): Boolean {
-    return ManagersConfig[this].remove(id)
-}
+    override fun Bot.removeManager(id: Long): Boolean {
+        return ManagersConfig[this].remove(id)
+    }
 
-public val Bot.managers: List<Long>
-    get() = ManagersConfig[this].toList()
+    override val Bot.managers: List<Long>
+        get() = ManagersConfig[this].toList()
 
-internal fun Bot.addManager(id: Long): Boolean {
-    return ManagersConfig[this].add(id)
+    override fun Bot.addManager(id: Long): Boolean {
+        return ManagersConfig[this].add(id)
+    }
 }
 
-
 internal object ManagersConfig : Setting by ConsoleBuiltInSettingStorage.load() {
     private val managers: MutableMap<Long, MutableSet<Long>> by value()
 

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt

@@ -12,7 +12,7 @@
 package net.mamoe.mirai.console.plugin
 
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import java.io.File
 
 /**

+ 4 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt

@@ -12,7 +12,7 @@
 package net.mamoe.mirai.console.plugin
 
 import net.mamoe.mirai.console.MiraiConsole
-import net.mamoe.mirai.console.plugin.jvm.PluginManagerImpl
+import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
 import java.io.File
 
 /**
@@ -55,7 +55,9 @@ public interface PluginManager {
      */
     public val Plugin.description: PluginDescription
 
-    public companion object INSTANCE : PluginManager by PluginManagerImpl
+    public companion object INSTANCE : PluginManager by PluginManagerImpl {
+        override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description }
+    }
 }
 
 @JvmSynthetic

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/center/PluginCenter.kt

@@ -11,7 +11,7 @@ package net.mamoe.mirai.console.plugin.center
 
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import java.io.File
 
 @ConsoleExperimentalAPI

+ 2 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt

@@ -13,8 +13,8 @@ import com.vdurmont.semver4j.Semver
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.builtins.serializer
-import net.mamoe.mirai.console.setting.internal.map
-import net.mamoe.mirai.console.utils.SemverAsStringSerializerIvy
+import net.mamoe.mirai.console.internal.setting.SemverAsStringSerializerIvy
+import net.mamoe.mirai.console.internal.setting.map
 import net.mamoe.yamlkt.Yaml
 import net.mamoe.yamlkt.YamlDynamicSerializer
 import java.io.File

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin.kt

@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.console.plugin.jvm
 
-import net.mamoe.mirai.console.plugin.internal.JvmPluginInternal
+import net.mamoe.mirai.console.internal.plugin.JvmPluginInternal
 import net.mamoe.mirai.utils.minutesToSeconds
 import net.mamoe.mirai.utils.secondsToMillis
 import kotlin.coroutines.CoroutineContext

+ 7 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt

@@ -10,10 +10,10 @@
 package net.mamoe.mirai.console.plugin.jvm
 
 import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.console.internal.plugin.JarPluginLoaderImpl
 import net.mamoe.mirai.console.plugin.FilePluginLoader
-import net.mamoe.mirai.console.plugin.internal.JarPluginLoaderImpl
 import net.mamoe.mirai.console.setting.SettingStorage
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 
 /**
  * 内建的 Jar (JVM) 插件加载器
@@ -25,5 +25,9 @@ public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, J
     @ConsoleExperimentalAPI
     public val settingStorage: SettingStorage
 
-    public companion object INSTANCE : JarPluginLoader by JarPluginLoaderImpl
+    public companion object INSTANCE : JarPluginLoader by JarPluginLoaderImpl {
+        @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
+        override val JvmPlugin.description: JvmPluginDescription
+            get() = JarPluginLoaderImpl.run { description }
+    }
 }

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPlugin.kt

@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.console.plugin.jvm
 
-import net.mamoe.mirai.console.utils.JavaPluginScheduler
+import net.mamoe.mirai.console.util.JavaPluginScheduler
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt

@@ -16,7 +16,7 @@ import net.mamoe.mirai.console.plugin.Plugin
 import net.mamoe.mirai.console.plugin.PluginFileExtensions
 import net.mamoe.mirai.console.setting.AutoSaveSettingHolder
 import net.mamoe.mirai.console.setting.Setting
-import net.mamoe.mirai.console.utils.ResourceContainer
+import net.mamoe.mirai.console.util.ResourceContainer
 import net.mamoe.mirai.utils.MiraiLogger
 import kotlin.reflect.KClass
 

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt

@@ -13,11 +13,11 @@ import com.vdurmont.semver4j.Semver
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.Transient
+import net.mamoe.mirai.console.internal.setting.SemverAsStringSerializerLoose
 import net.mamoe.mirai.console.plugin.FilePluginDescription
 import net.mamoe.mirai.console.plugin.PluginDependency
 import net.mamoe.mirai.console.plugin.PluginDescription
 import net.mamoe.mirai.console.plugin.PluginKind
-import net.mamoe.mirai.console.utils.SemverAsStringSerializerLoose
 import java.io.File
 
 @Serializable

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/KotlinPlugin.kt

@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.console.plugin.jvm
 
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 

+ 3 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt

@@ -12,11 +12,11 @@
 package net.mamoe.mirai.console.setting
 
 import kotlinx.serialization.KSerializer
+import net.mamoe.mirai.console.internal.setting.*
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
 import net.mamoe.mirai.console.plugin.jvm.loadSetting
-import net.mamoe.mirai.console.setting.internal.*
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import kotlin.internal.LowPriorityInOverloadResolution
 import kotlin.reflect.KProperty
 import kotlin.reflect.KType

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt

@@ -13,10 +13,10 @@ package net.mamoe.mirai.console.setting
 
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
+import net.mamoe.mirai.console.internal.setting.*
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
 import net.mamoe.mirai.console.setting.SettingStorage.Companion.load
-import net.mamoe.mirai.console.setting.internal.*
 import java.io.File
 import kotlin.reflect.KClass
 import kotlin.reflect.KType

+ 3 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt

@@ -14,9 +14,9 @@ package net.mamoe.mirai.console.setting
 import kotlinx.serialization.BinaryFormat
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.StringFormat
-import net.mamoe.mirai.console.setting.internal.map
-import net.mamoe.mirai.console.setting.internal.setValueBySerializer
-import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
+import net.mamoe.mirai.console.internal.setting.map
+import net.mamoe.mirai.console.internal.setting.setValueBySerializer
+import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
 import kotlin.reflect.KProperty
 
 /**

+ 25 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt

@@ -0,0 +1,25 @@
+@file:Suppress("NOTHING_TO_INLINE")
+
+package net.mamoe.mirai.console.util
+
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.console.internal.utils.BotManagerImpl
+import net.mamoe.mirai.contact.User
+
+public interface BotManager {
+    /**
+     * 判断此用户是否为 console 管理员
+     */
+    public val User.isManager: Boolean
+    public val Bot.managers: List<Long>
+
+    public fun Bot.removeManager(id: Long): Boolean
+    public fun Bot.addManager(id: Long): Boolean
+
+    public companion object INSTANCE : BotManager { // kotlin import handler doesn't recognize delegation.
+        override fun Bot.addManager(id: Long): Boolean = BotManagerImpl.run { addManager(id) }
+        override fun Bot.removeManager(id: Long): Boolean = BotManagerImpl.run { removeManager(id) }
+        override val User.isManager: Boolean get() = BotManagerImpl.run { isManager }
+        override val Bot.managers: List<Long> get() = BotManagerImpl.run { managers }
+    }
+}

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ConsoleInput.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("INAPPLICABLE_JVM_NAME", "unused")
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.util
 
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.sync.Mutex

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaFriendlyAPI.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaFriendlyAPI.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.util
 
 import kotlin.annotation.AnnotationTarget.*
 

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaPluginScheduler.kt

@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.util
 
 import kotlinx.coroutines.*
 import kotlinx.coroutines.future.future

+ 3 - 4
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ResourceContainer.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/ResourceContainer.kt

@@ -9,11 +9,10 @@
 
 @file:Suppress("unused")
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.util
 
-import net.mamoe.mirai.console.encodeToString
 import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
-import net.mamoe.mirai.console.utils.ResourceContainer.Companion.asResourceContainer
+import net.mamoe.mirai.console.util.ResourceContainer.Companion.asResourceContainer
 import java.io.InputStream
 import java.nio.charset.Charset
 import kotlin.reflect.KClass
@@ -42,7 +41,7 @@ public interface ResourceContainer {
      */
     @JvmDefault
     public fun getResource(name: String, charset: Charset): String =
-        this.getResourceAsStream(name).use { it.readBytes() }.encodeToString(charset)
+        String(this.getResourceAsStream(name).use { it.readBytes() })
 
     public companion object {
         /**

+ 1 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt → backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/retryCatching.kt

@@ -9,7 +9,7 @@
 
 @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
 
-package net.mamoe.mirai.console.utils
+package net.mamoe.mirai.console.util
 
 import org.jetbrains.annotations.Range
 import kotlin.contracts.InvocationKind

+ 1 - 1
backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/TestMiraiConosle.kt

@@ -21,7 +21,7 @@ import net.mamoe.mirai.console.plugin.PluginLoader
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
 import net.mamoe.mirai.console.setting.MemorySettingStorage
 import net.mamoe.mirai.console.setting.SettingStorage
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.LoginSolver

+ 13 - 8
backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt

@@ -16,11 +16,16 @@ import kotlinx.coroutines.runBlocking
 import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.Testing
 import net.mamoe.mirai.console.Testing.withTesting
-import net.mamoe.mirai.console.command.description.CommandArgParser
-import net.mamoe.mirai.console.command.description.CommandParserContext
-import net.mamoe.mirai.console.command.internal.InternalCommandManager
-import net.mamoe.mirai.console.command.internal.flattenCommandComponents
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.registeredCommands
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregister
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands
+import net.mamoe.mirai.console.command.description.CommandArgumentContext
+import net.mamoe.mirai.console.command.description.CommandArgumentParser
 import net.mamoe.mirai.console.initTestEnvironment
+import net.mamoe.mirai.console.internal.command.flattenCommandComponents
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.SingleMessage
 import net.mamoe.mirai.message.data.toMessage
@@ -75,8 +80,8 @@ internal class TestCommand {
 
             assertEquals(1, ConsoleCommandOwner.registeredCommands.size)
 
-            assertEquals(1, InternalCommandManager.registeredCommands.size)
-            assertEquals(2, InternalCommandManager.requiredPrefixCommandMap.size)
+            assertEquals(1, CommandManagerImpl.registeredCommands.size)
+            assertEquals(2, CommandManagerImpl.requiredPrefixCommandMap.size)
         } finally {
             TestCompositeCommand.unregister()
         }
@@ -180,8 +185,8 @@ internal class TestCommand {
             val composite = object : CompositeCommand(
                 ConsoleCommandOwner,
                 "test",
-                overrideContext = CommandParserContext {
-                    add(object : CommandArgParser<MyClass> {
+                overrideContext = CommandArgumentContext {
+                    add(object : CommandArgumentParser<MyClass> {
                         override fun parse(raw: String, sender: CommandSender): MyClass {
                             return MyClass(raw.toInt())
                         }

+ 3 - 0
backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/commanTestingUtil.kt

@@ -9,6 +9,9 @@
 
 package net.mamoe.mirai.console.command
 
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregister
+
 inline fun <T : Command, R> T.withRegistration(block: T.() -> R): R {
     this.register()
     try {

+ 1 - 1
backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/setting/SettingTest.kt

@@ -10,7 +10,7 @@
 package net.mamoe.mirai.console.setting
 
 import kotlinx.serialization.json.Json
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import org.junit.jupiter.api.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertSame

+ 3 - 0
build.gradle.kts

@@ -1,6 +1,9 @@
 @file:Suppress("UnstableApiUsage")
 plugins {
     id("com.jfrog.bintray") version Versions.bintray apply false
+    id("net.mamoe.kotlin-jvm-blocking-bridge") version Versions.blockingBridge apply false
+    kotlin("jvm") version Versions.kotlinCompiler
+    kotlin("plugin.serialization") version Versions.kotlinCompiler
 }
 
 tasks.withType(JavaCompile::class.java) {

+ 1 - 3
buildSrc/build.gradle.kts

@@ -29,9 +29,7 @@ dependencies {
     api(ktor("client-json", "1.3.2"))
 
     compileOnly(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")
+    compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0-rc")
     compileOnly("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5")
     api("com.github.jengelman.gradle.plugins:shadow:6.0.0")
 }

+ 2 - 2
buildSrc/src/main/kotlin/MiraiConsoleBuildPlugin.kt

@@ -30,7 +30,7 @@ class MiraiConsoleBuildPlugin : Plugin<Project> {
         tasks.getByName("shadowJar") {
             with(this as ShadowJar) {
                 archiveFileName.set(
-                    "${target.name}-${target.version}.jar"
+                    "${target.name}-${target.version}-all.jar"
                 )
                 manifest {
                     attributes(
@@ -101,7 +101,7 @@ 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""")) }
+        .filter { it.name.matches(Regex("""${project.name}-[0-9][0-9]*(\.[0-9]*)*.*\-all\.jar""")) }
         .onEach { println("matched file: ${it.name}") }
         .associateBy { it.nameWithoutExtension.substringAfterLast('-') }
         .onEach { println("versions: $it") }

+ 2 - 0
buildSrc/src/main/kotlin/Versions.kt

@@ -34,4 +34,6 @@ object Versions {
     const val androidGradle = "3.6.2"
 
     const val bintray = "1.8.5"
+
+    const val blockingBridge = "0.9.0"
 }

+ 10 - 3
frontend/mirai-console-pure/build.gradle.kts

@@ -1,12 +1,19 @@
 plugins {
-    kotlin("jvm") version Versions.kotlinCompiler
-    kotlin("plugin.serialization") version Versions.kotlinCompiler
+    kotlin("jvm")
+    kotlin("plugin.serialization")
     id("java")
     `maven-publish`
     id("com.jfrog.bintray")
 }
 
 kotlin {
+    target.compilations.all {
+        kotlinOptions {
+            jvmTarget = "1.8"
+            freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all"
+        }
+    }
+
     sourceSets {
         all {
             languageSettings.enableLanguageFeature("InlineClasses")
@@ -16,7 +23,7 @@ kotlin {
             languageSettings.progressiveMode = true
             languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
             languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
-            languageSettings.useExperimentalAnnotation("net.mamoe.mirai.console.utils.ConsoleExperimentalAPI")
+            languageSettings.useExperimentalAnnotation("net.mamoe.mirai.console.util.ConsoleExperimentalAPI")
             languageSettings.useExperimentalAnnotation("net.mamoe.mirai.console.ConsoleFrontEndImplementation")
             languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
             languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")

+ 3 - 4
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleFrontEndPure.kt

@@ -25,9 +25,8 @@ package net.mamoe.mirai.console.pure
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.MiraiConsoleBuildConstants
 import net.mamoe.mirai.console.MiraiConsoleFrontEnd
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.utils.DefaultLoginSolver
 import net.mamoe.mirai.utils.LoginSolver
 import net.mamoe.mirai.utils.MiraiLogger
@@ -72,13 +71,13 @@ object MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
     const val COLOR_RESET = "\u001b[39;49m"
     // }
 
-    val sdf by lazy {
+    private val sdf by lazy {
         SimpleDateFormat("HH:mm:ss")
     }
     override val name: String
         get() = "Pure"
     override val version: String
-        get() = MiraiConsoleBuildConstants.version
+        get() = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
 
     override fun loggerFor(identity: String?): MiraiLogger {
         identity?.apply {

+ 1 - 1
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt

@@ -34,7 +34,7 @@ import net.mamoe.mirai.console.plugin.PluginLoader
 import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
 import net.mamoe.mirai.console.setting.MultiFileSettingStorage
 import net.mamoe.mirai.console.setting.SettingStorage
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.utils.MiraiLogger
 import java.io.File
 import java.util.*

+ 10 - 5
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt

@@ -23,8 +23,13 @@ package net.mamoe.mirai.console.pure
 import kotlinx.coroutines.isActive
 import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
-import net.mamoe.mirai.console.command.*
-import net.mamoe.mirai.console.utils.ConsoleInternalAPI
+import net.mamoe.mirai.console.command.BuiltInCommands
+import net.mamoe.mirai.console.command.Command.Companion.primaryName
+import net.mamoe.mirai.console.command.CommandExecuteStatus
+import net.mamoe.mirai.console.command.CommandManager
+import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommandDetailed
+import net.mamoe.mirai.console.command.ConsoleCommandSender
+import net.mamoe.mirai.console.util.ConsoleInternalAPI
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.content
 import net.mamoe.mirai.utils.DefaultLogger
@@ -74,11 +79,11 @@ internal fun startConsoleThread() {
                 while (isActive) {
                     val next = MiraiConsoleFrontEndPure.requestInput("").let {
                         when {
-                            it.startsWith(CommandPrefix) -> {
+                            it.startsWith(CommandManager.commandPrefix) -> {
                                 it
                             }
-                            it == "?" -> CommandPrefix + BuiltInCommands.Help.primaryName
-                            else -> CommandPrefix + it
+                            it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName
+                            else -> CommandManager.commandPrefix + it
                         }
                     }
                     if (next.isBlank()) {