From 9f8ec5f991dcb70c299e7f58fe4b3ad22ce079cd Mon Sep 17 00:00:00 2001 From: LaoLittle Date: Sat, 28 May 2022 14:45:14 +0800 Subject: [PATCH] update --- src/main/kotlin/CodecExtension.kt | 5 +- src/main/kotlin/DrawMeme.kt | 7 ++ src/main/kotlin/General.kt | 108 +---------------------------- src/main/kotlin/Match.kt | 110 ++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 110 deletions(-) create mode 100644 src/main/kotlin/Match.kt diff --git a/src/main/kotlin/CodecExtension.kt b/src/main/kotlin/CodecExtension.kt index 004ec32..604f69d 100644 --- a/src/main/kotlin/CodecExtension.kt +++ b/src/main/kotlin/CodecExtension.kt @@ -2,12 +2,9 @@ package org.laolittle.plugin.draw import org.jetbrains.skia.Bitmap import org.jetbrains.skia.Codec -import org.jetbrains.skia.Image operator fun Codec.get(frame: Int) { val bitmap = Bitmap() bitmap.allocPixels(imageInfo) readPixels(bitmap, frame) -} - -fun Bitmap.asImage() = Image.makeFromBitmap(this) \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/DrawMeme.kt b/src/main/kotlin/DrawMeme.kt index 56e6dfb..c7dc95a 100644 --- a/src/main/kotlin/DrawMeme.kt +++ b/src/main/kotlin/DrawMeme.kt @@ -104,6 +104,13 @@ object DrawMeme : KotlinPlugin( // 零溢事件 finding(zeroReg) { r -> + if (message.firstIsInstance() + .content + .trim() + .replace("#", "") + .contains(Regex("""\D""")) + ) return@finding // avoid #\dxxx + val real = r.groupValues[1].toInt() if (real > 100) return@finding diff --git a/src/main/kotlin/General.kt b/src/main/kotlin/General.kt index 33ebc85..8abb4ff 100644 --- a/src/main/kotlin/General.kt +++ b/src/main/kotlin/General.kt @@ -3,7 +3,6 @@ package org.laolittle.plugin.draw import io.ktor.client.* import io.ktor.client.engine.okhttp.* import kotlinx.coroutines.TimeoutCancellationException -import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.events.MessageEvent import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.MessageSource.Key.quote @@ -11,11 +10,10 @@ import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.firstIsInstanceOrNull import net.mamoe.mirai.message.nextMessage import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource +import org.jetbrains.skia.Bitmap import org.jetbrains.skia.Rect import java.nio.file.Path import kotlin.io.path.readBytes -import kotlin.math.max -import kotlin.math.min import org.jetbrains.skia.Image as SkImage internal val httpClient = HttpClient(OkHttp) @@ -68,106 +66,4 @@ internal suspend fun MessageEvent.getOrWaitImage(): Image? { }).firstIsInstanceOrNull<Image>() } -internal fun String.fuzzyMatchWith(target: String): Double { - if (this == target) { - return 1.0 - } - var match = 0 - for (i in 0..(max(this.lastIndex, target.lastIndex))) { - val t = target.getOrNull(match) ?: break - if (t == this.getOrNull(i)) { - match++ - } - } - - val longerLength = max(this.length, target.length) - val shorterLength = min(this.length, target.length) - - return match.toDouble() / (longerLength + (shorterLength - match)) -} - -/** - * @return candidates - */ -internal fun Group.fuzzySearchMember( - nameCardTarget: String, - minRate: Double = 0.2, // 参与判断, 用于提示可能的解 - matchRate: Double = 0.6,// 最终选择的最少需要的匹配率, 减少歧义 - /** - * 如果有多个值超过 [matchRate], 并相互差距小于等于 [disambiguationRate], 则认为有较大歧义风险, 返回可能的解的列表. - */ - disambiguationRate: Double = 0.1, -): List<Pair<Member, Double>> { - val candidates = (this.members + botAsMember) - .asSequence() - .associateWith { it.nameCardOrNick.fuzzyMatchWith(nameCardTarget) } - .filter { it.value >= minRate } - .toList() - .sortedByDescending { it.second } - - val bestMatches = candidates.filter { it.second >= matchRate } - - return when { - bestMatches.isEmpty() -> candidates - bestMatches.size == 1 -> listOf(bestMatches.single().first to 1.0) - else -> { - if (bestMatches.first().second - bestMatches.last().second <= disambiguationRate) { - // resolution ambiguity - candidates - } else { - listOf(bestMatches.first().first to 1.0) - } - } - } -} - -/** - * 通过消息获取联系人 - * 若[Contact]为[Group],则可通过群员昵称获取联系人 - * 否则通过QQ号查找,查找失败返回``null`` - * @param msg 传入的消息[String] - * @return User if only one is found null otherwise - * */ -fun Contact.findUserOrNull(msg: String): User? { - val noneAt = msg.replace("@", "").replace(" ", "") - if (noneAt.isBlank()) { - return null - } - return if (noneAt.contains(Regex("""\D"""))) { - when (this) { - is Group -> this.findMemberOrNull(noneAt) - else -> null - } - } else { - val number = noneAt.toLong() - when (this) { - is Group -> this[number] - else -> bot.getFriend(number) ?: bot.getStranger(number) - } - } -} - -/** - * 从一个群中模糊搜索昵称是[nameCard]的群员 - * @param nameCard 群员昵称 - * @return Member if only one exist or null otherwise - * @author mamoe - * */ -private fun Group.findMemberOrNull(nameCard: String): Member? { - this.members.singleOrNull { it.nameCardOrNick.contains(nameCard) }?.let { return it } - this.members.singleOrNull { it.nameCardOrNick.contains(nameCard, ignoreCase = true) }?.let { return it } - - val candidates = this.fuzzySearchMember(nameCard) - candidates.singleOrNull()?.let { - if (it.second == 1.0) return it.first // single match - } - var maxPerMember: Member? = null - if (candidates.isNotEmpty()) { - var maxPer = 0.0 - candidates.forEach { - if (it.second > maxPer) maxPerMember = it.first - maxPer = it.second - } - } - return maxPerMember -} \ No newline at end of file +fun Bitmap.asImage() = org.jetbrains.skia.Image.makeFromBitmap(this) \ No newline at end of file diff --git a/src/main/kotlin/Match.kt b/src/main/kotlin/Match.kt new file mode 100644 index 0000000..bc65747 --- /dev/null +++ b/src/main/kotlin/Match.kt @@ -0,0 +1,110 @@ +package org.laolittle.plugin.draw + +import net.mamoe.mirai.contact.* +import kotlin.math.max +import kotlin.math.min + + +internal fun String.fuzzyMatchWith(target: String): Double { + if (this == target) { + return 1.0 + } + var match = 0 + for (i in 0..(max(this.lastIndex, target.lastIndex))) { + val t = target.getOrNull(match) ?: break + if (t == this.getOrNull(i)) { + match++ + } + } + + val longerLength = max(this.length, target.length) + val shorterLength = min(this.length, target.length) + + return match.toDouble() / (longerLength + (shorterLength - match)) +} + +/** + * @return candidates + */ +internal fun Group.fuzzySearchMember( + nameCardTarget: String, + minRate: Double = 0.2, // 参与判断, 用于提示可能的解 + matchRate: Double = 0.6,// 最终选择的最少需要的匹配率, 减少歧义 + /** + * 如果有多个值超过 [matchRate], 并相互差距小于等于 [disambiguationRate], 则认为有较大歧义风险, 返回可能的解的列表. + */ + disambiguationRate: Double = 0.1, +): List<Pair<Member, Double>> { + val candidates = (this.members + botAsMember) + .asSequence() + .associateWith { it.nameCardOrNick.fuzzyMatchWith(nameCardTarget) } + .filter { it.value >= minRate } + .toList() + .sortedByDescending { it.second } + + val bestMatches = candidates.filter { it.second >= matchRate } + + return when { + bestMatches.isEmpty() -> candidates + bestMatches.size == 1 -> listOf(bestMatches.single().first to 1.0) + else -> { + if (bestMatches.first().second - bestMatches.last().second <= disambiguationRate) { + // resolution ambiguity + candidates + } else { + listOf(bestMatches.first().first to 1.0) + } + } + } +} + +/** + * 通过消息获取联系人 + * 若[Contact]为[Group],则可通过群员昵称获取联系人 + * 否则通过QQ号查找,查找失败返回``null`` + * @param msg 传入的消息[String] + * @return User if only one is found null otherwise + * */ +fun Contact.findUserOrNull(msg: String): User? { + val noneAt = msg.replace("@", "").replace(" ", "") + if (noneAt.isBlank()) { + return null + } + return if (noneAt.contains(Regex("""\D"""))) { + when (this) { + is Group -> this.findMemberOrNull(noneAt) + else -> null + } + } else { + val number = noneAt.toLong() + when (this) { + is Group -> this[number] + else -> bot.getFriend(number) ?: bot.getStranger(number) + } + } +} + +/** + * 从一个群中模糊搜索昵称是[nameCard]的群员 + * @param nameCard 群员昵称 + * @return Member if only one exist or null otherwise + * @author mamoe + * */ +private fun Group.findMemberOrNull(nameCard: String): Member? { + this.members.singleOrNull { it.nameCardOrNick.contains(nameCard) }?.let { return it } + this.members.singleOrNull { it.nameCardOrNick.contains(nameCard, ignoreCase = true) }?.let { return it } + + val candidates = this.fuzzySearchMember(nameCard) + candidates.singleOrNull()?.let { + if (it.second == 1.0) return it.first // single match + } + var maxPerMember: Member? = null + if (candidates.isNotEmpty()) { + var maxPer = 0.0 + candidates.forEach { + if (it.second > maxPer) maxPerMember = it.first + maxPer = it.second + } + } + return maxPerMember +} \ No newline at end of file