1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-03-25 06:50:09 +08:00

Command Arg

This commit is contained in:
jiahua.liu 2020-05-12 15:00:28 +08:00
parent ad68868cb6
commit 643b0cc131
5 changed files with 406 additions and 168 deletions
mirai-console/src
main
java/net/mamoe/mirai/console/utils
kotlin/net/mamoe/mirai/console
test/kotlin

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View 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")
}