feat: add osu command
This commit is contained in:
10 files changed
+152
-38
No files matched your search
@@ -68,3 +68,7 @@ application {
|
|||||||
mainClass = "cn.rtast.fancybot.FancyBotKt"
|
mainClass = "cn.rtast.fancybot.FancyBotKt"
|
||||||
applicationName = "FancyBot"
|
applicationName = "FancyBot"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(17)
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import cn.rtast.fancybot.util.file.*
|
|||||||
import cn.rtast.fancybot.util.item.ItemManager
|
import cn.rtast.fancybot.util.item.ItemManager
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
val gson: Gson = GsonBuilder()
|
val gson: Gson = GsonBuilder()
|
||||||
@@ -31,6 +32,7 @@ val gson: Gson = GsonBuilder()
|
|||||||
.create()
|
.create()
|
||||||
|
|
||||||
const val ROOT_PATH = "./data"
|
const val ROOT_PATH = "./data"
|
||||||
|
val ROOT_PATH_ = File(ROOT_PATH).apply { mkdirs() }
|
||||||
const val ASSETS_BASE_URL = "https://static.rtast.cn"
|
const val ASSETS_BASE_URL = "https://static.rtast.cn"
|
||||||
const val API_RTAST_URL = "https://api.rtast.cn"
|
const val API_RTAST_URL = "https://api.rtast.cn"
|
||||||
const val PBI_API_URL = "https://pbi.us.kg"
|
const val PBI_API_URL = "https://pbi.us.kg"
|
||||||
@@ -73,7 +75,9 @@ val DEFAULT_CONFIG = Config(
|
|||||||
apiRtastKey = "114514",
|
apiRtastKey = "114514",
|
||||||
azureAppClientId = "114514",
|
azureAppClientId = "114514",
|
||||||
youtubeDataApiKey = "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()
|
val configManager = ConfigManager()
|
||||||
@@ -112,5 +116,6 @@ val commands = listOf(
|
|||||||
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand(), RCONCommand(),
|
TodayEatCommand(), TodayDrinkCommand(), MCLoginCommand(), RCONCommand(),
|
||||||
GravatarCommand(), PastebinCommand(), ShuffleEchoCommand(), TodayDontEatCommand(),
|
GravatarCommand(), PastebinCommand(), ShuffleEchoCommand(), TodayDontEatCommand(),
|
||||||
TodayDontDrinkCommand(), MCBotCommand(), HTTPCatCommand(), HTTPDogCommand(),
|
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.RandomRGBCommand
|
||||||
import cn.rtast.fancybot.commands.reply.SpeedUpGIFCommand
|
import cn.rtast.fancybot.commands.reply.SpeedUpGIFCommand
|
||||||
import cn.rtast.fancybot.enums.WSType
|
import cn.rtast.fancybot.enums.WSType
|
||||||
import cn.rtast.fancybot.util.CommandInterceptor
|
|
||||||
import cn.rtast.fancybot.util.Logger
|
import cn.rtast.fancybot.util.Logger
|
||||||
import cn.rtast.fancybot.util.initDatabase
|
import cn.rtast.fancybot.util.initDatabase
|
||||||
import cn.rtast.fancybot.util.misc.*
|
import cn.rtast.fancybot.util.misc.*
|
||||||
@@ -285,7 +284,6 @@ suspend fun main() {
|
|||||||
WSType.Client -> ROneBotFactory.createClient(configManager.wsAddress, accessToken, fancyBot)
|
WSType.Client -> ROneBotFactory.createClient(configManager.wsAddress, accessToken, fancyBot)
|
||||||
WSType.Server -> ROneBotFactory.createServer(configManager.wsPort, accessToken, fancyBot)
|
WSType.Server -> ROneBotFactory.createServer(configManager.wsPort, accessToken, fancyBot)
|
||||||
}
|
}
|
||||||
ROneBotFactory.interceptor = CommandInterceptor()
|
|
||||||
instance.addListeningGroups(*configManager.listeningGroups.toLongArray())
|
instance.addListeningGroups(*configManager.listeningGroups.toLongArray())
|
||||||
initDatabase()
|
initDatabase()
|
||||||
initFilesDir()
|
initFilesDir()
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ package cn.rtast.fancybot.commands
|
|||||||
import cn.rtast.fancybot.START_UP_TIME
|
import cn.rtast.fancybot.START_UP_TIME
|
||||||
import cn.rtast.fancybot.annotations.CommandDescription
|
import cn.rtast.fancybot.annotations.CommandDescription
|
||||||
import cn.rtast.fancybot.enums.CommandAction
|
import cn.rtast.fancybot.enums.CommandAction
|
||||||
import cn.rtast.fancybot.util.CommandInterceptor
|
|
||||||
import cn.rtast.fancybot.util.file.insertActionRecord
|
import cn.rtast.fancybot.util.file.insertActionRecord
|
||||||
import cn.rtast.rob.entity.GroupMessage
|
import cn.rtast.rob.entity.GroupMessage
|
||||||
import cn.rtast.rob.onebot.MessageChain
|
import cn.rtast.rob.onebot.MessageChain
|
||||||
@@ -44,8 +43,6 @@ class StatusCommand : BaseCommand() {
|
|||||||
.addText("操作系统: $osName / 系统架构: $osArch")
|
.addText("操作系统: $osName / 系统架构: $osArch")
|
||||||
.addNewLine()
|
.addNewLine()
|
||||||
.addText("Java版本: $javaVersion / Kotlin版本: $kotlinVersion")
|
.addText("Java版本: $javaVersion / Kotlin版本: $kotlinVersion")
|
||||||
.addNewLine()
|
|
||||||
.addText("指令共执行: ${CommandInterceptor.file.readText()} 次")
|
|
||||||
.build()
|
.build()
|
||||||
message.reply(msg)
|
message.reply(msg)
|
||||||
insertActionRecord(CommandAction.Status, message.sender.userId)
|
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 azureAppClientId: String,
|
||||||
val youtubeDataApiKey: String,
|
val youtubeDataApiKey: String,
|
||||||
val naiLongApiUrl: 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 azureAppClientId = config.azureAppClientId
|
||||||
val youtubeDataApiKey = config.youtubeDataApiKey
|
val youtubeDataApiKey = config.youtubeDataApiKey
|
||||||
val naiLongApiUrl = config.naiLongApiUrl
|
val naiLongApiUrl = config.naiLongApiUrl
|
||||||
|
val osuClientId = config.osuClientId
|
||||||
|
val osuClientSecret = config.osuClientSecret
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user