diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt index 6456f4671..90822791c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt @@ -195,12 +195,15 @@ public object ExistMemberArgumentParser : InternalCommandArgumentParserExtension - `botId.group.memberId` - `botId.group.memberCard` (模糊搜索, 寻找最优匹配) - `~` (指代指令调用人自己. 仅聊天环境下) + - `$` (随机成员) 当只登录了一个 [Bot] 时, 无需上述 `botId` 参数即可 """.trimIndent() public override fun parse(raw: String, sender: CommandSender): Member { - if (raw == "~") return (sender as? MemberCommandSender)?.user ?: illegalArgument("当前语境下无法推断目标群员") + if (raw == "~") return (sender as? MemberCommandSender)?.user ?: illegalArgument("当前语境下无法推断自身作为群员") + if (raw == "\$") return (sender as? MemberCommandSender)?.group?.members?.randomOrNull() + ?: illegalArgument("当前语境下无法推断随机群员") val components = raw.split(".") @@ -221,7 +224,12 @@ public object ExistMemberArgumentParser : InternalCommandArgumentParserExtension public override fun parse(raw: SingleMessage, sender: CommandSender): Member { return if (raw is At) { checkArgument(sender is MemberCommandSender) - sender.group.members[raw.target] + val bot = sender.inferBotOrFail() + val group = sender.inferGroupOrFail() + if (raw.target == bot.id) { + return group.botAsMember + } + group[raw.target] } else { parse(raw.content, sender) } @@ -247,10 +255,12 @@ internal interface InternalCommandArgumentParserExtensions : CommandArg fun Bot.findMemberOrFail(id: String): Friend = getFriendOrNull(id.parseToLongOrFail()) ?: illegalArgument("无法找到群员: $this") - fun Group.findMemberOrFail(idOrCard: String): Member = - idOrCard.toLongOrNull()?.let { getOrNull(it) } + fun Group.findMemberOrFail(idOrCard: String): Member { + if (idOrCard == "\$") return members.randomOrNull() ?: illegalArgument("当前语境下无法推断随机群员") + return idOrCard.toLongOrNull()?.let { getOrNull(it) } ?: fuzzySearchMember(idOrCard) ?: illegalArgument("无法找到目标群员 $idOrCard") + } fun CommandSender.inferBotOrFail(): Bot = (this as? BotAwareCommandSender)?.bot ?: illegalArgument("当前语境下无法推断目标群员") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt index d90a3a281..d4688315c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/internal.kt @@ -14,6 +14,7 @@ import net.mamoe.mirai.console.command.description.CommandArgumentParserExceptio import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.nameCardOrNick +import kotlin.math.max internal infix fun Array.matchesBeginning(list: List): Boolean { @@ -31,53 +32,39 @@ internal infix fun Array.intersectsIgnoringCase(other: Array this.length) { - return 0.0 - } - for (i in this.indices) { - if (target.length == i) { - step-- - } else { - if (this[i] != target[i]) { - break - } - step++ + var match = 0 + for (i in 0..(max(this.lastIndex, target.lastIndex))) { + val t = target.getOrNull(match) + if (t == this.getOrNull(i) && t != null) { + match++ } } - - if (step == this.length - 1) { - return 1.0 - } - return step.toDouble() / this.length + return match.toDouble() / (max(this.lastIndex, target.lastIndex) + 1) } -/** - * 模糊搜索一个List中index最接近target的东西 - */ internal inline fun Collection.fuzzySearch( target: String, - index: (T) -> String + crossinline index: (T) -> String ): T? { - var potential: T? = null - var rate = 0.0 - this.forEach { - val thisIndex = index(it) - if (thisIndex == target) { - return it - } - with(thisIndex.fuzzyCompare(target)) { - if (this > rate) { - rate = this - potential = it - } + var maxElement: T? = null + var max = 0.0 + + for (t in this) { + val r = index(t).fuzzyMatchWith(target) + if (r > max) { + maxElement = t + max = r } } - return potential + + if (max >= 0.7) { + return maxElement + } + return null } /** @@ -93,7 +80,7 @@ internal inline fun Collection.fuzzySearchOnly( var rate = 0.0 var collide = 0 this.forEach { - with(index(it).fuzzyCompare(target)) { + with(index(it).fuzzyMatchWith(target)) { if (this > rate) { rate = this potential = it