From f137e09af3d49ac4b66cda75e27c5060a33658ed Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Wed, 13 May 2020 09:35:24 +0800
Subject: [PATCH] Adjust object name, unified errors

---
 mirai-console/build.gradle.kts                |   3 +
 .../mirai/console/utils/CommandArgParser.kt   | 100 ------
 .../mamoe/mirai/console/command/CommandArg.kt | 272 -----------------
 .../mirai/console/command/CommandArgParser.kt | 285 ++++++++++++++++++
 .../net/mamoe/mirai/console/utils/Utils.kt    |  30 +-
 5 files changed, 303 insertions(+), 387 deletions(-)
 delete mode 100644 mirai-console/src/main/java/net/mamoe/mirai/console/utils/CommandArgParser.kt
 delete mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArg.kt
 create mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArgParser.kt

diff --git a/mirai-console/build.gradle.kts b/mirai-console/build.gradle.kts
index 8a1eb1389..7ad3bc229 100644
--- a/mirai-console/build.gradle.kts
+++ b/mirai-console/build.gradle.kts
@@ -21,6 +21,9 @@ kotlin {
             languageSettings.useExperimentalAnnotation("kotlin.OptIn")
             languageSettings.progressiveMode = true
             languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
+            languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
+            languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
+            languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
         }
     }
 }
diff --git a/mirai-console/src/main/java/net/mamoe/mirai/console/utils/CommandArgParser.kt b/mirai-console/src/main/java/net/mamoe/mirai/console/utils/CommandArgParser.kt
deleted file mode 100644
index 6365f68b0..000000000
--- a/mirai-console/src/main/java/net/mamoe/mirai/console/utils/CommandArgParser.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-@file:Suppress("NOTHING_TO_INLINE")
-
-package net.mamoe.mirai.console.utils
-
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.command.CommandSender
-import net.mamoe.mirai.console.command.GroupContactCommandSender
-import net.mamoe.mirai.contact.Group
-
-/**
- * this output type of that arg
- * input is always String
- */
-abstract class CommandArgParser<T : Any> {
-    abstract fun parse(s: String, sender: CommandSender): T
-    protected inline fun parseError(message: String, cause: Throwable? = null): Nothing {
-        throw ParserException(message, cause)
-    }
-}
-
-@Suppress("FunctionName")
-inline fun <T : Any> CommandArgParser(
-    crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
-): CommandArgParser<T> {
-    return object : CommandArgParser<T>() {
-        override fun parse(s: String, sender: CommandSender): T = parser(s, sender)
-    }
-}
-
-/**
- * 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范.
- */
-class ParserException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
-
-inline fun Int.Companion.parser(): CommandArgParser<Int> = IntArgParser
-inline fun Long.Companion.parser(): CommandArgParser<Long> = LongArgParser
-inline fun Byte.Companion.parser(): CommandArgParser<Byte> = ByteArgParser
-inline fun Short.Companion.parser(): CommandArgParser<Short> = ShortArgParser
-inline fun Float.Companion.parser(): CommandArgParser<Float> = FloatArgParser
-inline fun Double.Companion.parser(): CommandArgParser<Double> = DoubleArgParser
-
-
-object IntArgParser : CommandArgParser<Int>() {
-    override fun parse(s: String, sender: CommandSender): Int = s.toIntOrNull() ?: parseError("无法解析 $s 为整数")
-}
-
-object LongArgParser : CommandArgParser<Long>() {
-    override fun parse(s: String, sender: CommandSender): Long = s.toLongOrNull() ?: parseError("无法解析 $s 为长整数")
-}
-
-object ShortArgParser : CommandArgParser<Short>() {
-    override fun parse(s: String, sender: CommandSender): Short = s.toShortOrNull() ?: parseError("无法解析 $s 为短整数")
-}
-
-object ByteArgParser : CommandArgParser<Byte>() {
-    override fun parse(s: String, sender: CommandSender): Byte = s.toByteOrNull() ?: parseError("无法解析 $s 为字节")
-}
-
-object DoubleArgParser : CommandArgParser<Double>() {
-    override fun parse(s: String, sender: CommandSender): Double =
-        s.toDoubleOrNull() ?: parseError("无法解析 $s 为小数")
-}
-
-object FloatArgParser : CommandArgParser<Float>() {
-    override fun parse(s: String, sender: CommandSender): Float =
-        s.toFloatOrNull() ?: parseError("无法解析 $s 为小数")
-}
-
-object StringArgParser : CommandArgParser<String>() {
-    override fun parse(s: String, sender: CommandSender): String = s
-}
-
-/**
- * require a bot that already login in console
- * input: Bot UIN
- * output: Bot
- * errors: String->Int convert, Bot Not Exist
- */
-object ExistBotArgParser : CommandArgParser<Bot>() {
-    override fun parse(s: String, sender: CommandSender): Bot {
-        val uin = s.toLongOrNull() ?: parseError("无法识别机器人账号 $s")
-        return try {
-            Bot.getInstance(uin)
-        } catch (e: NoSuchElementException) {
-            error("无法找到 Bot $uin")
-        }
-    }
-}
-
-
-object ExistGroupArgParser : CommandArgParser<Group>() {
-    override fun parse(s: String, sender: CommandSender): Group {
-        if ((s == "" || s == "~") && sender is GroupContactCommandSender) {
-            return sender.contact as Group
-        }
-
-        val code = s.toLongOrNull() ?: parseError("无法识别群号码 $s")
-        TODO()
-    }
-}
diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArg.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArg.kt
deleted file mode 100644
index 6c1d4163a..000000000
--- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArg.kt
+++ /dev/null
@@ -1,272 +0,0 @@
-package net.mamoe.mirai.console.command
-
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.console.utils.fuzzySearchMember
-import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.message.data.At
-import net.mamoe.mirai.message.data.SingleMessage
-import net.mamoe.mirai.message.data.content
-import java.lang.NumberFormatException
-
-/**
- * this output type of that arg
- * input is always String
- */
-interface CommandArg<T:Any>{
-    operator fun invoke(s:String, commandSender: CommandSender):T = parse(s,commandSender)
-
-    operator fun invoke(s:SingleMessage, commandSender: CommandSender):T = parse(s,commandSender)
-
-    fun parse(s:String, commandSender: CommandSender):T
-
-    fun parse(s:SingleMessage, commandSender: CommandSender):T
-}
-
-
-abstract class CommandArgImpl<T:Any>(): CommandArg<T> {
-    override fun parse(s: SingleMessage, commandSender: CommandSender): T  = parse(s.content,commandSender)
-}
-
-class IntArg: CommandArgImpl<Int>(){
-    override fun parse(s: String, commandSender: CommandSender): Int {
-        return try{
-            s.toInt()
-        }catch (e:Exception){
-            error("无法识别整数$s")
-        }
-    }
-}
-
-class LongArg: CommandArgImpl<Long>(){
-    override fun parse(s: String, commandSender: CommandSender): Long {
-        return try{
-            s.toLong()
-        }catch (e:Exception){
-            error("无法识别长整数$s")
-        }
-    }
-}
-
-class DoubleArg: CommandArgImpl<Double>(){
-    override fun parse(s: String, commandSender: CommandSender): Double {
-        return try{
-            s.toDouble()
-        }catch (e:Exception){
-            error("无法识别小数$s")
-        }
-    }
-}
-
-class FloatArg: CommandArgImpl<Float>(){
-    override fun parse(s: String, commandSender: CommandSender): Float{
-        return try{
-            s.toFloat()
-        }catch (e:Exception){
-            error("无法识别小数$s")
-        }
-    }
-}
-
-class BooleanArg: CommandArgImpl<Boolean>(){
-    override fun parse(s: String, commandSender: CommandSender): Boolean {
-        return s.equals("true",true) || s.equals("yes",true)
-    }
-}
-
-class StringArg: CommandArgImpl<String>(){
-    override fun parse(s: String, commandSender: CommandSender): String {
-        return s
-    }
-}
-
-/**
- * require a bot that already login in console
- * input: Bot UIN
- * output: Bot
- * errors: String->Int convert, Bot Not Exist
- */
-class ExistBotArg : CommandArgImpl<Bot>() {
-    override fun parse(s: String, commandSender: CommandSender): Bot {
-        val uin = try {
-            s.toLong()
-        } catch (e: Exception) {
-            error("无法识别QQ UIN$s")
-        }
-        return try {
-            Bot.getInstance(uin)
-        } catch (e: NoSuchElementException) {
-            error("无法找到Bot $uin")
-        }
-    }
-}
-
-
-class ExistFriendArg: CommandArgImpl<Friend>(){
-    //Bot.friend
-    //friend
-    //~ = self
-    override fun parse(s: String, commandSender: CommandSender): Friend {
-        if(s == "~"){
-            if(commandSender !is BotAware){
-                error("无法解析~作为默认")
-            }
-            val targetID = when (commandSender) {
-                is GroupContactCommandSender -> commandSender.realSender.id
-                is ContactCommandSender -> commandSender.contact.id
-                else -> error("无法解析~作为默认")
-            }
-            return try{
-                commandSender.bot.friends[targetID]
-            }catch (e:NoSuchElementException){
-                error("无法解析~作为默认")
-            }
-        }
-        if(commandSender is BotAware){
-            return try{
-                commandSender.bot.friends[s.toLong()]
-            }catch (e:NoSuchElementException){
-                error("无法找到" + s + "这个好友")
-            }catch (e:NumberFormatException){
-                error("无法解析$s")
-            }
-        }else{
-            with(s.split(".")){
-                if(this.size != 2){
-                    error("无法解析$s, 格式应为Bot.Friend")
-                }
-                return try{
-                    Bot.getInstance(this[0].toLong()).friends[this[1].toLong()]
-                }catch (e:NoSuchElementException){
-                    error("无法找到好友或Bot")
-                }catch (e:NumberFormatException){
-                    error("无法解析$s")
-                }
-            }
-        }
-    }
-
-    override fun parse(s: SingleMessage, commandSender: CommandSender): Friend {
-        return if(s is At){
-            assert(commandSender is GroupContactCommandSender)
-            return try {
-                (commandSender as BotAware).bot.friends[s.target]
-            }catch (e:NoSuchElementException){
-                error("At的对象非Bot好友")
-            }
-        }else{
-            error("无法识别Member" + s.content)
-        }
-    }
-}
-
-class ExistGroupArg: CommandArgImpl<Group>(){
-    override fun parse(s: String, commandSender: CommandSender): Group {
-        //by default
-        if ((s == "" || s == "~") && commandSender is GroupContactCommandSender) {
-            return commandSender.contact as Group
-        }
-        //from bot to group
-        if (commandSender is BotAware) {
-            val code = try {
-                s.toLong()
-            } catch (e: NoSuchElementException) {
-                error("无法识别Group Code$s")
-            }
-            return try {
-                commandSender.bot.getGroup(code)
-            } catch (e: NoSuchElementException) {
-                error("无法找到Group " + code + " from Bot " + commandSender.bot.id)
-            }
-        }
-        //from console/other
-        return with(s.split(".")) {
-            if (this.size != 2) {
-                error("请使用BotQQ号.群号 来表示Bot的一个群")
-            }
-            try {
-                Bot.getInstance(this[0].toLong()).getGroup(this[1].toLong())
-            }catch (e:NoSuchElementException){
-                error("无法找到" + this[0] + "的" + this[1] + "群")
-            }catch (e:NumberFormatException){
-                error("无法识别群号或机器人UIN")
-            }
-        }
-    }
-}
-
-class ExistMemberArg: CommandArgImpl<Member>(){
-    //后台: Bot.Group.Member[QQ/名片]
-    //私聊: Group.Member[QQ/名片]
-    //群内: Q号
-    //群内: 名片
-    override fun parse(s: String, commandSender: CommandSender): Member {
-        if(commandSender !is BotAware){
-            with(s.split(".")){
-                if(this.size < 3){
-                    error("无法识别Member, 请使用Bot.Group.Member[QQ/名片]的格式")
-                }
-                val bot = try {
-                     Bot.getInstance(this[0].toLong())
-                }catch (e:NoSuchElementException){
-                    error("无法找到Bot")
-                }catch (e:NumberFormatException){
-                    error("无法识别Bot")
-                }
-                val group = try{
-                    bot.getGroup(this[1].toLong())
-                }catch (e:NoSuchElementException){
-                    error("无法找到Group")
-                }catch (e:NumberFormatException){
-                    error("无法识别Group")
-                }
-
-                val memberIndex = this.subList(2,this.size).joinToString(".")
-                return try{
-                    group.members[memberIndex.toLong()]
-                }catch (ignored:Exception){
-                    group.fuzzySearchMember(memberIndex)?: error("无法找到成员$memberIndex")
-                }
-            }
-        }else {
-            val bot = commandSender.bot
-            if(commandSender is GroupContactCommandSender){
-                val group = commandSender.contact as Group
-                return try {
-                    group.members[s.toLong()]
-                } catch (ignored: Exception) {
-                    group.fuzzySearchMember(s) ?: error("无法找到成员$s")
-                }
-            }else {
-                with(s.split(".")) {
-                    if (this.size < 2) {
-                        error("无法识别Member, 请使用Group.Member[QQ/名片]的格式")
-                    }
-                    val group = try {
-                        bot.getGroup(this[0].toLong())
-                    } catch (e: NoSuchElementException) {
-                        error("无法找到Group")
-                    } catch (e: NumberFormatException) {
-                        error("无法识别Group")
-                    }
-
-                    val memberIndex = this.subList(1, this.size).joinToString(".")
-                    return try {
-                        group.members[memberIndex.toLong()]
-                    } catch (ignored: Exception) {
-                        group.fuzzySearchMember(memberIndex) ?: error("无法找到成员$memberIndex")
-                    }
-                }
-            }
-        }
-    }
-
-    override fun parse(s: SingleMessage, commandSender: CommandSender): Member {
-        return if(s is At){
-            assert(commandSender is GroupContactCommandSender)
-            ((commandSender as GroupContactCommandSender).contact as Group).members[s.target]
-        }else{
-            error("无法识别Member" + s.content)
-        }
-    }
-}
-
diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArgParser.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArgParser.kt
new file mode 100644
index 000000000..e839cec99
--- /dev/null
+++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandArgParser.kt
@@ -0,0 +1,285 @@
+@file:Suppress("NOTHING_TO_INLINE")
+
+package net.mamoe.mirai.console.command
+
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.console.utils.fuzzySearchMember
+import net.mamoe.mirai.contact.Friend
+import net.mamoe.mirai.contact.Group
+import net.mamoe.mirai.contact.Member
+import net.mamoe.mirai.message.data.At
+import net.mamoe.mirai.message.data.SingleMessage
+import net.mamoe.mirai.message.data.content
+import kotlin.contracts.contract
+
+/**
+ * this output type of that arg
+ * input is always String
+ */
+abstract class CommandArgParser<T : Any> {
+    abstract fun parse(s: String, sender: CommandSender): T
+    open fun parse(s: SingleMessage, sender: CommandSender): T = parse(s.content, sender)
+}
+
+@JvmSynthetic
+inline fun CommandArgParser<*>.illegalArgument(message: String, cause: Throwable? = null): Nothing {
+    throw ParserException(message, cause)
+}
+
+@JvmSynthetic
+inline fun CommandArgParser<*>.checkArgument(
+    condition: Boolean,
+    crossinline message: () -> String = { "Check failed." }
+) {
+    contract {
+        returns() implies condition
+    }
+    if (!condition) illegalArgument(message())
+}
+
+/**
+ * 创建匿名 [CommandArgParser]
+ */
+@Suppress("FunctionName")
+@JvmSynthetic
+inline fun <T : Any> CommandArgParser(
+    crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
+): CommandArgParser<T> = object : CommandArgParser<T>() {
+    override fun parse(s: String, sender: CommandSender): T = parser(s, sender)
+}
+
+
+/**
+ * 在解析参数时遇到的 _正常_ 错误. 如参数不符合规范.
+ */
+class ParserException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
+
+
+object IntArgParser : CommandArgParser<Int>() {
+    override fun parse(s: String, sender: CommandSender): Int =
+        s.toIntOrNull() ?: illegalArgument("无法解析 $s 为整数")
+}
+
+object LongArgParser : CommandArgParser<Long>() {
+    override fun parse(s: String, sender: CommandSender): Long =
+        s.toLongOrNull() ?: illegalArgument("无法解析 $s 为长整数")
+}
+
+object ShortArgParser : CommandArgParser<Short>() {
+    override fun parse(s: String, sender: CommandSender): Short =
+        s.toShortOrNull() ?: illegalArgument("无法解析 $s 为短整数")
+}
+
+object ByteArgParser : CommandArgParser<Byte>() {
+    override fun parse(s: String, sender: CommandSender): Byte =
+        s.toByteOrNull() ?: illegalArgument("无法解析 $s 为字节")
+}
+
+object DoubleArgParser : CommandArgParser<Double>() {
+    override fun parse(s: String, sender: CommandSender): Double =
+        s.toDoubleOrNull() ?: illegalArgument("无法解析 $s 为小数")
+}
+
+object FloatArgParser : CommandArgParser<Float>() {
+    override fun parse(s: String, sender: CommandSender): Float =
+        s.toFloatOrNull() ?: illegalArgument("无法解析 $s 为小数")
+}
+
+object StringArgParser : CommandArgParser<String>() {
+    override fun parse(s: String, sender: CommandSender): String = s
+}
+
+object BooleanArgParser : CommandArgParser<Boolean>() {
+    override fun parse(s: String, sender: CommandSender): Boolean = s.trim().let { str ->
+        str.equals("true", ignoreCase = true)
+                || str.equals("yes", ignoreCase = true)
+                || str.equals("enabled", ignoreCase = true)
+    }
+}
+
+/**
+ * require a bot that already login in console
+ * input: Bot UIN
+ * output: Bot
+ * errors: String->Int convert, Bot Not Exist
+ */
+object ExistBotArgParser : CommandArgParser<Bot>() {
+    override fun parse(s: String, sender: CommandSender): Bot {
+        val uin = try {
+            s.toLong()
+        } catch (e: Exception) {
+            error("无法识别QQ UIN$s")
+        }
+        return try {
+            Bot.getInstance(uin)
+        } catch (e: NoSuchElementException) {
+            error("无法找到Bot $uin")
+        }
+    }
+}
+
+object ExistFriendArgParser : CommandArgParser<Friend>() {
+    //Bot.friend
+    //friend
+    //~ = self
+    override fun parse(s: String, sender: CommandSender): Friend {
+        if (s == "~") {
+            if (sender !is BotAware) {
+                illegalArgument("无法解析~作为默认")
+            }
+            val targetID = when (sender) {
+                is GroupContactCommandSender -> sender.realSender.id
+                is ContactCommandSender -> sender.contact.id
+                else -> illegalArgument("无法解析~作为默认")
+            }
+            return try {
+                sender.bot.friends[targetID]
+            } catch (e: NoSuchElementException) {
+                illegalArgument("无法解析~作为默认")
+            }
+        }
+        if (sender is BotAware) {
+            return try {
+                sender.bot.friends[s.toLong()]
+            } catch (e: NoSuchElementException) {
+                error("无法找到" + s + "这个好友")
+            } catch (e: NumberFormatException) {
+                error("无法解析$s")
+            }
+        } else {
+            s.split(".").let { args ->
+                if (args.size != 2) {
+                    illegalArgument("无法解析 $s, 格式应为 机器人账号.好友账号")
+                }
+                return try {
+                    Bot.getInstance(args[0].toLong()).friends.getOrNull(
+                        args[1].toLongOrNull() ?: illegalArgument("无法解析 $s 为好友")
+                    ) ?: illegalArgument("无法找到好友 ${args[1]}")
+                } catch (e: NoSuchElementException) {
+                    illegalArgument("无法找到机器人账号 ${args[0]}")
+                }
+            }
+        }
+    }
+
+    override fun parse(s: SingleMessage, sender: CommandSender): Friend {
+        if (s is At) {
+            assert(sender is GroupContactCommandSender)
+            return (sender as BotAware).bot.friends.getOrNull(s.target) ?: illegalArgument("At的对象非Bot好友")
+        } else {
+            error("无法解析 $s 为好友")
+        }
+    }
+}
+
+object ExistGroupArgParser : CommandArgParser<Group>() {
+    override fun parse(s: String, sender: CommandSender): Group {
+        //by default
+        if ((s == "" || s == "~") && sender is GroupContactCommandSender) {
+            return sender.contact as Group
+        }
+        //from bot to group
+        if (sender is BotAware) {
+            val code = try {
+                s.toLong()
+            } catch (e: NoSuchElementException) {
+                error("无法识别Group Code$s")
+            }
+            return try {
+                sender.bot.getGroup(code)
+            } catch (e: NoSuchElementException) {
+                error("无法找到Group " + code + " from Bot " + sender.bot.id)
+            }
+        }
+        //from console/other
+        return with(s.split(".")) {
+            if (this.size != 2) {
+                error("请使用BotQQ号.群号 来表示Bot的一个群")
+            }
+            try {
+                Bot.getInstance(this[0].toLong()).getGroup(this[1].toLong())
+            } catch (e: NoSuchElementException) {
+                error("无法找到" + this[0] + "的" + this[1] + "群")
+            } catch (e: NumberFormatException) {
+                error("无法识别群号或机器人UIN")
+            }
+        }
+    }
+}
+
+object ExistMemberArgParser : CommandArgParser<Member>() {
+    //后台: Bot.Group.Member[QQ/名片]
+    //私聊: Group.Member[QQ/名片]
+    //群内: Q号
+    //群内: 名片
+    override fun parse(s: String, sender: CommandSender): Member {
+        if (sender !is BotAware) {
+            with(s.split(".")) {
+                checkArgument(this.size >= 3) {
+                    "无法识别Member, 请使用Bot.Group.Member[QQ/名片]的格式"
+                }
+
+                val bot = try {
+                    Bot.getInstance(this[0].toLong())
+                } catch (e: NoSuchElementException) {
+                    illegalArgument("无法找到Bot")
+                } catch (e: NumberFormatException) {
+                    illegalArgument("无法识别Bot")
+                }
+
+                val group = try {
+                    bot.getGroup(this[1].toLong())
+                } catch (e: NoSuchElementException) {
+                    illegalArgument("无法找到Group")
+                } catch (e: NumberFormatException) {
+                    illegalArgument("无法识别Group")
+                }
+
+                val memberIndex = this.subList(2, this.size).joinToString(".")
+                return group.members.getOrNull(memberIndex.toLong())
+                    ?: group.fuzzySearchMember(memberIndex)
+                    ?: error("无法找到成员$memberIndex")
+            }
+        } else {
+            val bot = sender.bot
+            if (sender is GroupContactCommandSender) {
+                val group = sender.contact as Group
+                return try {
+                    group.members[s.toLong()]
+                } catch (ignored: Exception) {
+                    group.fuzzySearchMember(s) ?: illegalArgument("无法找到成员$s")
+                }
+            } else {
+                with(s.split(".")) {
+                    if (this.size < 2) {
+                        illegalArgument("无法识别Member, 请使用Group.Member[QQ/名片]的格式")
+                    }
+                    val group = try {
+                        bot.getGroup(this[0].toLong())
+                    } catch (e: NoSuchElementException) {
+                        illegalArgument("无法找到Group")
+                    } catch (e: NumberFormatException) {
+                        illegalArgument("无法识别Group")
+                    }
+
+                    val memberIndex = this.subList(1, this.size).joinToString(".")
+                    return try {
+                        group.members[memberIndex.toLong()]
+                    } catch (ignored: Exception) {
+                        group.fuzzySearchMember(memberIndex) ?: illegalArgument("无法找到成员$memberIndex")
+                    }
+                }
+            }
+        }
+    }
+
+    override fun parse(s: SingleMessage, sender: CommandSender): Member {
+        return if (s is At) {
+            checkArgument(sender is GroupContactCommandSender)
+            (sender.contact as Group).members[s.target]
+        } else {
+            illegalArgument("无法识别Member" + s.content)
+        }
+    }
+}
+
diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt
index 847bd6f64..9201cc2c7 100644
--- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt
+++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt
@@ -69,16 +69,16 @@ internal fun Throwable.addSuppressedMirai(e: Throwable) {
  *  XXXXXYYYYY.fuzzyCompare(XXXXXYYYZZ) = 0.8
  */
 
-fun String.fuzzyCompare(target:String):Double{
+internal fun String.fuzzyCompare(target: String): Double {
     var step = 0
-    if(this == target){
+    if (this == target) {
         return 1.0
     }
-    if(target.length > this.length){
+    if (target.length > this.length) {
         return 0.0
     }
-    for(i in this.indices){
-        if(target.length == i){
+    for (i in this.indices) {
+        if (target.length == i) {
             step--
         }else {
             if (this[i] != target[i]) {
@@ -97,14 +97,14 @@ fun String.fuzzyCompare(target:String):Double{
 /**
  * 模糊搜索一个List中index最接近target的东西
  */
-inline fun <T:Any> Collection<T>.fuzzySearch(
+internal inline fun <T : Any> Collection<T>.fuzzySearch(
     target: String,
     index: (T) -> String
-):T?{
-    if(this.isEmpty()){
+): T? {
+    if (this.isEmpty()) {
         return null
     }
-    var potential:T? = null
+    var potential: T? = null
     var rate = 0.0
     this.forEach {
         val thisIndex = index(it)
@@ -126,14 +126,14 @@ inline fun <T:Any> Collection<T>.fuzzySearch(
  * 并且确保target是唯一的
  * 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
  */
-inline fun <T:Any> Collection<T>.fuzzySearchOnly(
+internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
     target: String,
     index: (T) -> String
-):T?{
-    if(this.isEmpty()){
+): T? {
+    if (this.isEmpty()) {
         return null
     }
-    var potential:T? = null
+    var potential: T? = null
     var rate = 0.0
     var collide = 0
     this.forEach {
@@ -155,8 +155,8 @@ inline fun <T:Any> Collection<T>.fuzzySearchOnly(
 }
 
 
-fun Group.fuzzySearchMember(nameCardTarget:String):Member?{
-    return this.members.fuzzySearchOnly(nameCardTarget){
+internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
+    return this.members.fuzzySearchOnly(nameCardTarget) {
         it.nameCard
     }
 }
\ No newline at end of file