mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-25 06:50:09 +08:00
Command Arg
This commit is contained in:
parent
ad68868cb6
commit
643b0cc131
mirai-console/src
main
java/net/mamoe/mirai/console/utils
kotlin/net/mamoe/mirai/console
test/kotlin
@ -1,168 +0,0 @@
|
||||
package net.mamoe.mirai.console.utils
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.BotAware
|
||||
import net.mamoe.mirai.console.command.ContactCommandSender
|
||||
import net.mamoe.mirai.console.command.GroupContactCommandSender
|
||||
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>(){
|
||||
override fun parse(s: String, commandSender: CommandSender): Friend {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
//TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
package net.mamoe.mirai.console.utils
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
@ -57,3 +59,104 @@ internal fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||
this.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 两个字符串的近似值
|
||||
* 要求前面完全一致
|
||||
* 如
|
||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYY) = 0.8
|
||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYYZZ) = 0.8
|
||||
*/
|
||||
|
||||
fun String.fuzzyCompare(target:String):Double{
|
||||
var step = 0
|
||||
if(this == target){
|
||||
return 1.0
|
||||
}
|
||||
if(target.length > this.length){
|
||||
return 0.0
|
||||
}
|
||||
for(i in this.indices){
|
||||
if(target.length == i){
|
||||
step--
|
||||
}else {
|
||||
if (this[i] != target[i]) {
|
||||
break
|
||||
}
|
||||
step++
|
||||
}
|
||||
}
|
||||
|
||||
if(step == this.length-1){
|
||||
return 1.0
|
||||
}
|
||||
return step.toDouble()/this.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
*/
|
||||
inline fun <T:Any> Collection<T>.fuzzySearch(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
):T?{
|
||||
if(this.isEmpty()){
|
||||
return null
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索一个List中index最接近target的东西
|
||||
* 并且确保target是唯一的
|
||||
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
|
||||
*/
|
||||
inline fun <T:Any> Collection<T>.fuzzySearchOnly(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
):T?{
|
||||
if(this.isEmpty()){
|
||||
return null
|
||||
}
|
||||
var potential:T? = null
|
||||
var rate = 0.0
|
||||
var collide = 0
|
||||
this.forEach {
|
||||
with(index(it).fuzzyCompare(target)) {
|
||||
println(index(it) + "->" + this)
|
||||
if (this > rate) {
|
||||
rate = this
|
||||
potential = it
|
||||
}
|
||||
if(this == 1.0){
|
||||
collide++
|
||||
}
|
||||
if(collide > 1){
|
||||
return null//collide
|
||||
}
|
||||
}
|
||||
}
|
||||
return potential
|
||||
}
|
||||
|
||||
|
||||
fun Group.fuzzySearchMember(nameCardTarget:String):Member?{
|
||||
return this.members.fuzzySearchOnly(nameCardTarget){
|
||||
it.nameCard
|
||||
}
|
||||
}
|
31
mirai-console/src/test/kotlin/StringFuzzyTest.kt
Normal file
31
mirai-console/src/test/kotlin/StringFuzzyTest.kt
Normal file
@ -0,0 +1,31 @@
|
||||
import net.mamoe.mirai.console.utils.fuzzySearch
|
||||
import net.mamoe.mirai.console.utils.fuzzySearchOnly
|
||||
|
||||
class Him188(val name:String){
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
fun main(){
|
||||
val list = listOf(
|
||||
Him188("111122"),
|
||||
Him188("H1hsncm"),
|
||||
Him188("Ahsndb1"),
|
||||
Him188("Him188"),
|
||||
Him188("aisb11j2"),
|
||||
Him188("aisndnme"),
|
||||
Him188("a9su102"),
|
||||
Him188("nmsl"),
|
||||
Him188("Him1888"),
|
||||
Him188("Him18887")
|
||||
)
|
||||
val s1 = list.fuzzySearch("Him1888"){
|
||||
it.name
|
||||
}
|
||||
val s2 = list.fuzzySearchOnly("Him1888"){
|
||||
it.name
|
||||
}
|
||||
println("S1: $s1")
|
||||
println("S2: $s2")
|
||||
}
|
Loading…
Reference in New Issue
Block a user