feat: add osu command

This commit is contained in:
2025-02-05 13:24:18 +08:00
parent 24c8ef0cb7
commit 6bce847a0f
10 files changed
+152 -38

No files matched your search

+4
View File
@@ -67,4 +67,8 @@ tasks.register("deployBot") {
application {
mainClass = "cn.rtast.fancybot.FancyBotKt"
applicationName = "FancyBot"
}
kotlin {
jvmToolchain(17)
}
+7 -2
View File
@@ -23,6 +23,7 @@ import cn.rtast.fancybot.util.file.*
import cn.rtast.fancybot.util.item.ItemManager
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.io.File
import java.time.Instant
val gson: Gson = GsonBuilder()
@@ -31,6 +32,7 @@ val gson: Gson = GsonBuilder()
.create()
const val ROOT_PATH = "./data"
val ROOT_PATH_ = File(ROOT_PATH).apply { mkdirs() }
const val ASSETS_BASE_URL = "https://static.rtast.cn"
const val API_RTAST_URL = "https://api.rtast.cn"
const val PBI_API_URL = "https://pbi.us.kg"
@@ -73,7 +75,9 @@ val DEFAULT_CONFIG = Config(
apiRtastKey = "114514",
azureAppClientId = "114514",
youtubeDataApiKey = "114514",
naiLongApiUrl = "http://127.0.0.1:5000/nailong"
naiLongApiUrl = "http://127.0.0.1:5000/nailong",
osuClientId = 111,
osuClientSecret = "222"
)
val configManager = ConfigManager()
@@ -112,5 +116,6 @@ val commands = listOf(
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand(), RCONCommand(),
GravatarCommand(), PastebinCommand(), ShuffleEchoCommand(), TodayDontEatCommand(),
TodayDontDrinkCommand(), MCBotCommand(), HTTPCatCommand(), HTTPDogCommand(),
AITTSCommand(), NWWNCommand(), TJBGXLCommand(), XNMCommand(), SJCommand()
AITTSCommand(), NWWNCommand(), TJBGXLCommand(), XNMCommand(), SJCommand(),
OSUCommand()
)
@@ -18,7 +18,6 @@ import cn.rtast.fancybot.commands.reply.InvertImageCommand
import cn.rtast.fancybot.commands.reply.RandomRGBCommand
import cn.rtast.fancybot.commands.reply.SpeedUpGIFCommand
import cn.rtast.fancybot.enums.WSType
import cn.rtast.fancybot.util.CommandInterceptor
import cn.rtast.fancybot.util.Logger
import cn.rtast.fancybot.util.initDatabase
import cn.rtast.fancybot.util.misc.*
@@ -285,7 +284,6 @@ suspend fun main() {
WSType.Client -> ROneBotFactory.createClient(configManager.wsAddress, accessToken, fancyBot)
WSType.Server -> ROneBotFactory.createServer(configManager.wsPort, accessToken, fancyBot)
}
ROneBotFactory.interceptor = CommandInterceptor()
instance.addListeningGroups(*configManager.listeningGroups.toLongArray())
initDatabase()
initFilesDir()
@@ -10,7 +10,6 @@ package cn.rtast.fancybot.commands
import cn.rtast.fancybot.START_UP_TIME
import cn.rtast.fancybot.annotations.CommandDescription
import cn.rtast.fancybot.enums.CommandAction
import cn.rtast.fancybot.util.CommandInterceptor
import cn.rtast.fancybot.util.file.insertActionRecord
import cn.rtast.rob.entity.GroupMessage
import cn.rtast.rob.onebot.MessageChain
@@ -44,8 +43,6 @@ class StatusCommand : BaseCommand() {
.addText("操作系统: $osName / 系统架构: $osArch")
.addNewLine()
.addText("Java版本: $javaVersion / Kotlin版本: $kotlinVersion")
.addNewLine()
.addText("指令共执行: ${CommandInterceptor.file.readText()}")
.build()
message.reply(msg)
insertActionRecord(CommandAction.Status, message.sender.userId)
@@ -0,0 +1,85 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/2/4
*/
package cn.rtast.fancybot.commands.lookup
import cn.rtast.fancybot.annotations.CommandDescription
import cn.rtast.fancybot.configManager
import cn.rtast.fancybot.entity.osu.OSUAccessToken
import cn.rtast.fancybot.entity.osu.OSUUserData
import cn.rtast.fancybot.util.Http
import cn.rtast.rob.entity.GroupMessage
import cn.rtast.rob.onebot.MessageChain
import cn.rtast.rob.util.BaseCommand
import java.awt.Color
import java.awt.RenderingHints
import java.awt.image.BufferedImage
@CommandDescription("osu!")
class OSUCommand : BaseCommand() {
override val commandNames = listOf("/osu")
private val clientSecret = configManager.osuClientSecret
private val clientId = configManager.osuClientId
private fun getAccessToken(): String {
return Http.post<OSUAccessToken>(
"https://osu.ppy.sh/oauth/token",
formBody = mapOf(
"client_id" to clientId, "client_secret" to clientSecret,
"grant_type" to "client_credentials", "scope" to "public"
),
headers = mapOf("Content-Type" to "application/x-www-form-urlencoded", "Accept" to "application/json")
).accessToken
}
private fun getUser(username: String): OSUUserData {
val token = this.getAccessToken()
val resp = Http.get<OSUUserData>(
"https://osu.ppy.sh/api/v2/users/$username/osu",
headers = mapOf("Authorization" to "Bearer $token"),
)
return resp
}
private fun generateProgressBar(percent: Int, length: Int = 10): String {
val completedLength = (percent * length) / 100
val remainingLength = length - completedLength
val progressBar = "".repeat(completedLength) + " ".repeat(remainingLength)
return "[$progressBar] $percent%"
}
private fun formatDuration(seconds: Long): String {
val days = seconds / (24 * 3600)
val hours = (seconds % (24 * 3600)) / 3600
val minutes = (seconds % 3600) / 60
val parts = mutableListOf<String>()
if (days > 0) parts.add("${days}d")
if (hours > 0) parts.add("${hours}h")
if (minutes > 0) parts.add("${minutes}m")
return parts.joinToString(" ")
}
override suspend fun executeGroup(message: GroupMessage, args: List<String>) {
val username = args.first()
val userData = getUser(username)
val msg = MessageChain.Builder()
.addText(userData.username)
.addNewLine()
.addText("等级 | Lv.${userData.statistics.level.current} ${generateProgressBar(userData.statistics.level.progress)}")
.addNewLine()
.addText("排名 | #${userData.statistics.globalRank} / ${userData.countryCode} #${userData.statistics.countryRank}")
.addNewLine()
.addText("准确率 | ${userData.statistics.hitAccuracy}%")
.addNewLine()
.addText("游玩时间 | ${formatDuration(userData.statistics.playTime)}")
.addNewLine()
.addText("游玩次数 | ${userData.statistics.playCount}")
.build()
message.reply(msg)
}
}
@@ -50,4 +50,6 @@ data class Config(
val azureAppClientId: String,
val youtubeDataApiKey: String,
val naiLongApiUrl: String,
val osuClientSecret: String,
val osuClientId: Int
)
@@ -0,0 +1,15 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/2/5
*/
package cn.rtast.fancybot.entity.osu
import com.google.gson.annotations.SerializedName
data class OSUAccessToken(
@SerializedName("access_token")
val accessToken: String,
)
@@ -0,0 +1,37 @@
/*
* Copyright © 2025 RTAkland
* Author: RTAkland
* Date: 2025/2/5
*/
package cn.rtast.fancybot.entity.osu
import com.google.gson.annotations.SerializedName
data class OSUUserData(
val statistics: Statistics,
@SerializedName("country_code")
val countryCode: String,
val username: String,
) {
data class Statistics(
@SerializedName("global_rank")
val globalRank: Long,
@SerializedName("play_time")
val playTime: Long,
@SerializedName("hit_accuracy")
val hitAccuracy: Double,
@SerializedName("country_rank")
val countryRank: Int,
val pp: Float,
val level: Level,
@SerializedName("play_count")
val playCount: Int,
)
data class Level(
val current: Int,
val progress: Int
)
}
@@ -1,31 +0,0 @@
/*
* Copyright © 2024 RTAkland
* Author: RTAkland
* Date: 2024/11/7
*/
package cn.rtast.fancybot.util
import cn.rtast.fancybot.ROOT_PATH
import cn.rtast.rob.ROneBotFactory
import cn.rtast.rob.entity.GroupMessage
import cn.rtast.rob.entity.PrivateMessage
import cn.rtast.rob.interceptor.ExecutionInterceptor
import cn.rtast.rob.util.BaseCommand
import java.io.File
class CommandInterceptor : ExecutionInterceptor() {
companion object {
val file = File(ROOT_PATH, "/caches/command_execution_count").apply { createNewFile() }
}
override suspend fun afterGroupExecute(message: GroupMessage, command: BaseCommand) {
file.writeText(ROneBotFactory.totalCommandExecutionTimes.toString())
}
override suspend fun afterPrivateExecute(message: PrivateMessage, command: BaseCommand) {
file.writeText(ROneBotFactory.totalCommandExecutionTimes.toString())
}
}
@@ -50,4 +50,6 @@ class ConfigManager : JsonFileHandler<Config>("config.json") {
val azureAppClientId = config.azureAppClientId
val youtubeDataApiKey = config.youtubeDataApiKey
val naiLongApiUrl = config.naiLongApiUrl
val osuClientId = config.osuClientId
val osuClientSecret = config.osuClientSecret
}