Compare commits

...

4 Commits

Author SHA1 Message Date
ed5ee33126 Update version to v1.0.0
Some checks failed
Gradle CI / Gradle-Build (push) Has been cancelled
2025-03-04 00:45:37 +08:00
67f9d1ca75 Add rate limit 2025-03-04 00:45:22 +08:00
05c115e3e8 Fix rank bug 2022-07-26 20:33:17 +08:00
b4dd18cc19 Update version to v0.1.3
增加答题统计
增加排行榜
2022-07-25 21:47:20 +08:00
4 changed files with 180 additions and 34 deletions

View File

@ -1,13 +1,13 @@
plugins { plugins {
val kotlinVersion = "1.7.0" val kotlinVersion = "1.8.10"
kotlin("jvm") version kotlinVersion kotlin("jvm") version kotlinVersion
kotlin("plugin.serialization") version kotlinVersion kotlin("plugin.serialization") version kotlinVersion
id("net.mamoe.mirai-console") version "2.12.0" id("net.mamoe.mirai-console") version "2.16.0"
} }
group = "top.jie65535" group = "top.jie65535"
version = "0.1.2" version = "1.0.0"
repositories { repositories {
maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库 maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库

View File

@ -0,0 +1,38 @@
package top.jie65535.j24
import net.mamoe.mirai.console.data.AutoSavePluginData
import net.mamoe.mirai.console.data.value
object PluginData : AutoSavePluginData("data") {
/**
* 统计数据
* -
* - 群员
* - 数据
*/
val stats: MutableMap<Long, MutableMap<Long, PlayerStat>> by value()
}
@kotlinx.serialization.Serializable
data class PlayerStat(
/**
* 总答题数
*/
var totalCount: Int = 0,
/**
* 统计答题数
*/
var count: Int = 0,
/**
* 平均答题时间(S)
*/
var avgTime: Double = 0.0,
/**
* 最快答题时间(S)
*/
var minTime: Double = Double.MAX_VALUE,
)

View File

@ -3,18 +3,20 @@ package top.jie65535.j24
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
import net.mamoe.mirai.contact.nameCardOrNick
import net.mamoe.mirai.event.GlobalEventChannel import net.mamoe.mirai.event.GlobalEventChannel
import net.mamoe.mirai.event.events.GroupMessageEvent import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.subscribeGroupMessages import net.mamoe.mirai.event.subscribeGroupMessages
import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.info
import java.time.Duration import java.text.DecimalFormat
import java.time.LocalDateTime import java.time.*
import kotlin.math.max
object PluginMain : KotlinPlugin( object PluginMain : KotlinPlugin(
JvmPluginDescription( JvmPluginDescription(
id = "top.jie65535.j24", id = "top.jie65535.j24",
name = "J 24点游戏", name = "J 24点游戏",
version = "0.1.2" version = "1.0.0"
) { ) {
author("jie65535") author("jie65535")
info("24点游戏") info("24点游戏")
@ -22,43 +24,148 @@ object PluginMain : KotlinPlugin(
) { ) {
private val games: MutableMap<Long, Point24> = mutableMapOf() private val games: MutableMap<Long, Point24> = mutableMapOf()
private val df = DecimalFormat("0.00")
private const val prefix = "=" private const val prefix = "="
private const val maxRound = 10
class GroupState(
var lastGameTimes: Int = 1,
/**
* 游戏计时器
* 为了防止频繁刷屏记录开始时间一定间隔后才可再次触发
*/
var lastStartedAt: LocalDateTime = LocalDateTime.MIN,
/**
* 排行榜计时器
* 为了防止频繁刷屏记录出榜时间一定间隔后才可再次触发
*/
var lastRankedAt: LocalDateTime = LocalDateTime.MIN
)
private val gameStats = mutableMapOf<Long, GroupState>()
override fun onEnable() { override fun onEnable() {
logger.info { "Plugin loaded" } logger.info { "Plugin loaded" }
PluginCommand.register() PluginCommand.register()
PluginConfig.reload() PluginConfig.reload()
PluginData.reload()
val eventChannel = GlobalEventChannel.parentScope(this) GlobalEventChannel.parentScope(this)
eventChannel.filter { it is GroupMessageEvent && PluginConfig.enabledGroups.contains(it.group.id) }.subscribeGroupMessages { .filter { it is GroupMessageEvent && PluginConfig.enabledGroups.contains(it.group.id) }
startsWith("24点") reply { .subscribeGroupMessages {
var game = games[group.id] "24点" reply {
if (game == null || game.time.plusMinutes(1) < LocalDateTime.now()) { var game = games[group.id]
game = Point24() val state = gameStats.getOrPut(group.id) { GroupState() }
games[group.id] = game
"请用 $game 组成结果为24的算式以'$prefix'开头验证"
} else Unit
}
startsWith(prefix) quoteReply { if (state.lastStartedAt.plusMinutes(10).isBefore(LocalDateTime.now())) {
val game = games[group.id] state.lastStartedAt = LocalDateTime.now()
if (game != null) { state.lastGameTimes = 0
try {
val result = game.evaluate(message.contentToString().removePrefix(prefix).trim())
if (result == 24.0) {
val newGame = Point24()
games[group.id] = newGame
val duration = Duration.between(game.time, LocalDateTime.now())
"答对了! ${duration.toMinutes()}:${duration.toSecondsPart()}\n下一题:$newGame"
} else {
"答错了,计算结果为 $result"
}
} catch (e: Throwable) {
// logger.error(e)
"错误:${e.message}"
} }
} else Unit
if (state.lastGameTimes >= maxRound) {
"您参与的太过频繁了,为避免影响他人聊天体验,请稍后再试~"
} else if (game == null || game.time.plusMinutes(1) < LocalDateTime.now()) {
game = Point24()
games[group.id] = game
state.lastGameTimes++
"{${state.lastGameTimes}/$maxRound} 请用 $game 组成结果为24的算式以'$prefix'开头验证"
} else Unit
}
"24点榜" reply aaa@{
val g = PluginData.stats[group.id] ?: return@aaa Unit
val state = gameStats.getOrPut(group.id) { GroupState() }
// 一小时内仅可查询一次
if (state.lastRankedAt.plusHours(1).isBefore(LocalDateTime.now())) {
// 记录查询时间
state.lastRankedAt = LocalDateTime.now()
// 拼接排行榜
val sb = StringBuilder()
sb.appendLine("[均时榜]")
sb.append(g.entries.filter { it.value.avgTime != 0.0 }.sortedBy { it.value.avgTime }.take(3).joinToString("\n") {
"${df.format(it.value.avgTime)}s | ${group[it.key]?.nameCardOrNick ?: "侠名"}"
}).appendLine().appendLine()
sb.appendLine("[速度榜]")
sb.append(g.entries.sortedBy { it.value.minTime }.take(3).joinToString("\n") {
"${df.format(it.value.minTime)}s | ${group[it.key]?.nameCardOrNick ?: "侠名"}"
}).appendLine().appendLine()
sb.appendLine("[答题榜]")
sb.append(g.entries.sortedByDescending { it.value.totalCount }.take(3).joinToString("\n") {
"${it.value.totalCount} 道 | ${group[it.key]?.nameCardOrNick ?: "侠名"}"
})
sb.toString()
} else Unit
}
startsWith(prefix) quoteReply {
val game = games[group.id]
if (game != null) {
try {
val now = LocalDateTime.now()
val result = game.evaluate(message.contentToString().removePrefix(prefix).trim())
if (result == 24.0) {
val duration = Duration.between(game.time, now)
// Instant.ofEpochSecond(
// this.time.toLong())
// .atZone(ZoneId.systemDefault())
// .toLocalDateTime())
// 群
var g = PluginData.stats[group.id]
if (g == null) {
g = mutableMapOf()
PluginData.stats[group.id] = g
}
// 玩家
var stat = g[sender.id]
if (stat == null) {
stat = PlayerStat()
g[sender.id] = stat
}
// 答题数增加
stat.totalCount += 1
// 用时
val t = duration.seconds + duration.nano / 1000000000.0
if (stat.minTime > t) {
stat.minTime = t
}
// 仅统计一定时间内的均值
val resultText = if (t < 60) {
// 计数增加
stat.count += 1
// 更新均值
stat.avgTime += (t - stat.avgTime) / max(20, stat.count)
"答对了!用时:${df.format(t)}s 平均:${df.format(stat.avgTime)}s 最快:${df.format(stat.minTime)}s\n"
} else {
"回答正确!"
}
val state = gameStats[group.id]!!
val nextTip = if (state.lastGameTimes >= maxRound) {
games.remove(group.id)
"本轮游戏已经结束,感谢参与,请稍作休息~"
} else {
state.lastGameTimes++
val newGame = Point24()
games[group.id] = newGame
"{${state.lastGameTimes}/$maxRound}下一题:$newGame"
}
resultText + nextTip
} else {
"答错了,计算结果为 $result"
}
} catch (e: Throwable) {
// logger.error(e)
"错误:${e.message}"
}
} else Unit
}
} }
}
} }
} }

View File

@ -71,6 +71,7 @@ class Point24 {
val expr = expression val expr = expression
.replace('', '(') .replace('', '(')
.replace('', ')') .replace('', ')')
.replace('X', '*')
.replace('x', '*') .replace('x', '*')
.replace('×', '*') .replace('×', '*')
.replace('÷', '/') .replace('÷', '/')