All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
xyz.cssxsh.mirai.plugin.ArknightsTools.kt Maven / Gradle / Ivy
package xyz.cssxsh.mirai.plugin
import io.ktor.client.features.*
import net.mamoe.mirai.*
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSource.Key.quote
import net.mamoe.mirai.message.*
import net.mamoe.mirai.utils.*
import xyz.cssxsh.arknights.excel.*
import xyz.cssxsh.arknights.*
import xyz.cssxsh.arknights.market.*
import xyz.cssxsh.arknights.mine.*
import xyz.cssxsh.arknights.penguin.*
import java.io.*
import java.net.*
import java.time.*
import javax.net.ssl.*
import kotlin.properties.*
import kotlin.reflect.*
internal val logger by lazy {
val open = System.getProperty("xyz.cssxsh.mirai.plugin.logger", "${true}").toBoolean()
if (open) ArknightsHelperPlugin.logger else SilentLogger
}
internal suspend fun > T.nextContent(): String {
return fromEvent.nextMessage { it.message.content.isNotBlank() }.content
}
const val SendDelay = 60 * 1000L
suspend fun > T.sendMessage(block: suspend T.(Contact) -> Message): Boolean {
return runCatching {
block(fromEvent.subject)
}.onSuccess { message ->
quoteReply(message)
}.onFailure { throwable ->
logger.warning { "发送消息失败, $throwable" }
when {
"本群每分钟只能发" in throwable.message.orEmpty() -> {
kotlinx.coroutines.delay(SendDelay)
sendMessage { throwable.message.orEmpty().toPlainText() }
}
else -> {
quoteReply("发送消息失败, ${throwable.message}")
}
}
}.isSuccess
}
suspend fun CommandSenderOnMessage<*>.quoteReply(message: Message) = sendMessage(fromEvent.message.quote() + message)
suspend fun CommandSenderOnMessage<*>.quoteReply(message: String) = quoteReply(message.toPlainText())
class SubjectDelegate(private val default: (Contact) -> T) : ReadWriteProperty, T> {
private val map: MutableMap = mutableMapOf()
override fun setValue(thisRef: CommandSenderOnMessage<*>, property: KProperty<*>, value: T) {
map[thisRef.fromEvent.subject] = value
}
override fun getValue(thisRef: CommandSenderOnMessage<*>, property: KProperty<*>): T {
return map.getOrPut(thisRef.fromEvent.subject) { default(thisRef.fromEvent.subject) }
}
}
/**
* 通过正负号区分群和用户
*/
val Contact.delegate get() = if (this is Group) id * -1 else id
/**
* 查找Contact
*/
fun findContact(delegate: Long): Contact? {
for (bot in Bot.instances) {
if (delegate < 0) {
for (group in bot.groups) {
if (group.id == delegate * -1) return group
}
} else {
for (friend in bot.friends) {
if (friend.id == delegate) return friend
}
for (stranger in bot.strangers) {
if (stranger.id == delegate) return stranger
}
for (group in bot.groups) {
for (member in group.members) {
if (member.id == delegate) return member
}
}
}
}
return null
}
internal val DownloaderIgnore: suspend (Throwable) -> Boolean = {
when (it) {
is UnknownHostException, is SSLHandshakeException -> {
false
}
is IOException,
is HttpRequestTimeoutException -> {
logger.warning { "Downloader Ignore $it" }
true
}
else -> {
false
}
}
}
/**
* 检查并转换TAG
*/
internal fun tag(words: List, tags: Set = ExcelData.gacha.tags()): Set {
val temp = tags.map { it.substring(0..1) }
return words.map { it.trim() }.filter { it.isNotBlank() }.map { word ->
when (word) {
in tags -> word
"高资" -> "高级资深干员"
"支援机械", "机械" -> "支援机械"
"控制" -> "控场"
"回费" -> "费用回复"
in temp -> tags.elementAt(temp.indexOf(word))
else -> throw IllegalArgumentException("未知TAG: $word")
}
}.toSet()
}
internal fun recruit(words: List) = ExcelData.characters.recruit(tag(words), ExcelData.gacha.recruit())
internal fun role(name: String, roles: Set = ExcelData.gacha.recruit()) = name.trim().let {
when (it) {
in roles -> it
in RoleAlias -> RoleAlias.getValue(it)
else -> throw IllegalArgumentException("未知干员: $it")
}
}
@JvmName("buildRecruitMessage")
internal fun RecruitResult.toMessage() = buildMessageChain {
for ((rarity, character) in this@toMessage) {
val sum = character.groupBy { it.name }.mapValues { it.value.size }.entries.sortedBy { it.value }.map {
"${it.key}${if (it.value == 1) "" else "*${it.value}"}"
}
appendLine("${rarity + 1}星干员(${character.size}): $sum")
}
}
@JvmName("buildRecruitMapMessage")
internal fun RecruitMap.toMessage() = buildMessageChain {
for ((tags, result) in this@toMessage) {
append("====> $tags ")
if ((result.keys - 0).all { it >= 3 }) {
appendLine("${(result.keys - 0).minOrNull()!! + 1}星保底")
} else {
appendLine("")
}
append(result.toMessage())
}
}
@JvmName("buildMarketFaceMapMessage")
internal fun ArknightsFaceMap.toMessage() = buildMessageChain {
appendLine("共${[email protected] }个表情")
for ((name, list) in this@toMessage) {
appendLine("$name ${list.first().detail}")
}
}
@JvmName("buildArknightsFaceMapMessage")
internal fun ArknightsFace.toMessage() = buildMessageChain {
appendLine("表情名: $title $content")
appendLine("详情: $detail")
appendLine("原图: $image")
appendLine("Hash: $key")
}
private fun duration(millis: Long) = Duration.ofMillis(millis).run { "${toMinutesPart()}m${toSecondsPart()}s" }
private fun Pair.toMessage() = buildMessageChain {
appendLine("概率: ${frequency.quantity}/${frequency.times}=${frequency.probability.percentage()}")
appendLine("单件期望理智: ${single.intercept()}")
appendLine("最短通关用时: ${duration(stage.minClearTime)}")
appendLine("单件期望用时: ${duration(short)}")
}
private val Stage.zone get() = ExcelData.zone.zones[zoneId]
internal fun item(name: String, limit: Int, now: Boolean) = buildMessageChain {
val (item, list) = (PenguinData.items to PenguinData.matrices.let { if (now) it.now() else it }).item(name)
appendLine("${item.alias.get()} 统计结果 By 企鹅物流数据统计")
if (list.isEmpty()) {
appendLine("列表为空,请尝试更新数据")
return@buildMessageChain
}
var count = 0
for (pair in (list with PenguinData.stages).sortedBy { it.single }) {
if (count >= limit) break
if (pair.stage.isGacha) continue
appendLine("====> 作战: [${pair.stage.code}] <${pair.stage.zone?.title}> (cost=${pair.stage.cost})")
append(pair.toMessage())
count++
}
}
internal fun alias() = buildMessageChain {
appendLine("企鹅物流材料别名")
for (item in PenguinData.items) {
appendLine("名称: ${item.i18n.get()},别名: ${item.alias.get()}")
}
}
internal fun stage(code: String, limit: Int, now: Boolean) = buildMessageChain {
val (stage, list) = (PenguinData.stages to PenguinData.matrices.let { if (now) it.now() else it }).stage(code)
appendLine("[${stage.code}] <${stage.zone?.title}> (cost=${stage.cost}) 统计结果 By 企鹅物流数据统计")
if (list.isEmpty()) {
appendLine("列表为空,请尝试更新数据")
return@buildMessageChain
}
var count = 0
for ((matrix, item) in (list with PenguinData.items).sortedByDescending { it.rarity }) {
if (count >= limit) break
if (item.type != ItemType.MATERIAL) continue
appendLine("=======> 掉落: ${item.name} (rarity=${item.rarity}) ")
append((matrix to stage).toMessage())
count++
}
}
internal fun zone(name: String, limit: Int, now: Boolean) = buildMessageChain {
val (_, list) = (PenguinData.zones to PenguinData.stages).name(name)
for (stage in list) {
append(stage(stage.code, limit, now))
}
}
internal fun QuestionType.random() = random(QuestionDataLoader)
internal fun countQuestionType(type: QuestionType, mode: Int) {
MineCount.compute(type) { _, s ->
(s ?: mutableListOf(0, 0, 0)).apply { this[mode] += 1 }
}
}
internal fun tableMineCount() = buildString {
appendLine("# 答题统计")
appendLine("| 类型 | 正确 | 错误 | 超时 | 总计 |")
appendLine("|:----:|:----:|:----:|:----:|:----:|")
MineCount.forEach { type, (f, s, t) ->
appendLine("| $type | $f | $s | $t | ${f + s + t} |")
}
}
internal fun Question.toMessage() = buildMessageChain {
appendLine("[${type}]<${coin}>:${problem} (${timeout / 1000}s内作答)")
for ((index, text) in options) {
appendLine("${index}.${text}")
}
}