Compare commits
3 Commits
e0cabfefcf
...
dc846854e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc846854e4 | ||
|
|
31f301eab2 | ||
|
|
7d44e4014b |
@ -137,6 +137,19 @@ class OsuApi(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBeatmapFromId(beatmapId: Int): OsuApiModels.Beatmap? {
|
||||||
|
val response = doRequest("https://osu.ppy.sh/api/v2/beatmaps/$beatmapId", emptyMap())
|
||||||
|
if (response == null) {
|
||||||
|
this.logger.info("Error loading beatmap $beatmapId")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (response.statusCode()) {
|
||||||
|
200 -> serializer.decodeFromString<OsuApiModels.Beatmap>(response.body())
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the replay data for a given score ID from the osu!api.
|
* Retrieves the replay data for a given score ID from the osu!api.
|
||||||
* Efficiently cycles through the API keys to avoid rate limiting.
|
* Efficiently cycles through the API keys to avoid rate limiting.
|
||||||
@ -215,7 +228,7 @@ class OsuApi(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getUserBeatmapScores(userId: Long, beatmapId: Int): OsuApiModels.BeatmapScores? {
|
fun getUserBeatmapScores(userId: Long, beatmapId: Int): OsuApiModels.BeatmapScores? {
|
||||||
val response = doRequest("https://osu.ppy.sh/api/v2/beatmaps/$beatmapId/scores/users/$userId/all", mapOf())
|
val response = doRequest("https://osu.ppy.sh/api/v2/beatmaps/$beatmapId/scores/users/$userId/all", emptyMap())
|
||||||
|
|
||||||
if(response == null) {
|
if(response == null) {
|
||||||
this.logger.info("Error getting scores on beatmap $beatmapId for user $userId")
|
this.logger.info("Error getting scores on beatmap $beatmapId for user $userId")
|
||||||
@ -249,7 +262,7 @@ class OsuApi(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfUserBanned(userId: Long): Boolean? {
|
fun checkIfUserBanned(userId: Long): Boolean? {
|
||||||
val response = this.doRequest("https://osu.ppy.sh/api/v2/users/$userId/osu?key=id", mapOf())
|
val response = this.doRequest("https://osu.ppy.sh/api/v2/users/$userId/osu?key=id", emptyMap())
|
||||||
if(response == null) {
|
if(response == null) {
|
||||||
this.logger.info("Error loading user with userId = $userId")
|
this.logger.info("Error loading user with userId = $userId")
|
||||||
return null
|
return null
|
||||||
|
|||||||
@ -205,6 +205,7 @@ class OsuApiModels {
|
|||||||
data class Beatmap(
|
data class Beatmap(
|
||||||
val beatmapset_id: Int,
|
val beatmapset_id: Int,
|
||||||
val difficulty_rating: Double?,
|
val difficulty_rating: Double?,
|
||||||
|
val checksum: String?,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val version: String?,
|
val version: String?,
|
||||||
val beatmapset: BeatmapSet,
|
val beatmapset: BeatmapSet,
|
||||||
@ -222,6 +223,7 @@ class OsuApiModels {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class BeatmapSet(
|
data class BeatmapSet(
|
||||||
|
val id: Int,
|
||||||
val artist: String?,
|
val artist: String?,
|
||||||
val creator: String?,
|
val creator: String?,
|
||||||
val source: String?,
|
val source: String?,
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.nisemoe.nise.osu
|
||||||
|
|
||||||
|
fun OsuApiModels.Beatmap.toScoreBeatmap(): OsuApiModels.ScoreBeatmap =
|
||||||
|
OsuApiModels.ScoreBeatmap(
|
||||||
|
id = this.id,
|
||||||
|
checksum = this.checksum,
|
||||||
|
difficulty_rating = this.difficulty_rating,
|
||||||
|
version = this.version,
|
||||||
|
max_combo = this.max_combo,
|
||||||
|
total_length = this.total_length,
|
||||||
|
bpm = this.bpm,
|
||||||
|
accuracy = this.accuracy,
|
||||||
|
ar = this.ar,
|
||||||
|
cs = this.cs,
|
||||||
|
drain = this.drain,
|
||||||
|
count_circles = this.count_circles,
|
||||||
|
count_sliders = this.count_sliders,
|
||||||
|
count_spinners = this.count_spinners,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun OsuApiModels.BeatmapSet.toScoreBeatmapSet(): OsuApiModels.ScoreBeatmapset =
|
||||||
|
OsuApiModels.ScoreBeatmapset(
|
||||||
|
id = this.id,
|
||||||
|
title = this.title,
|
||||||
|
artist = this.artist,
|
||||||
|
creator = this.creator,
|
||||||
|
source = this.source,
|
||||||
|
)
|
||||||
@ -13,9 +13,7 @@ import com.nisemoe.nise.integrations.DiscordService
|
|||||||
import com.nisemoe.nise.konata.Replay
|
import com.nisemoe.nise.konata.Replay
|
||||||
import com.nisemoe.nise.konata.ReplaySetComparison
|
import com.nisemoe.nise.konata.ReplaySetComparison
|
||||||
import com.nisemoe.nise.konata.compareReplaySet
|
import com.nisemoe.nise.konata.compareReplaySet
|
||||||
import com.nisemoe.nise.osu.Mod
|
import com.nisemoe.nise.osu.*
|
||||||
import com.nisemoe.nise.osu.OsuApi
|
|
||||||
import com.nisemoe.nise.osu.OsuApiModels
|
|
||||||
import com.nisemoe.nise.service.CacheService
|
import com.nisemoe.nise.service.CacheService
|
||||||
import com.nisemoe.nise.service.CompressReplay
|
import com.nisemoe.nise.service.CompressReplay
|
||||||
import com.nisemoe.nise.service.UpdateUserQueueService
|
import com.nisemoe.nise.service.UpdateUserQueueService
|
||||||
@ -165,38 +163,86 @@ class ImportScores(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(userId in queue) {
|
for(userId in queue) {
|
||||||
val topUserScores = this.osuApi.getTopUserScores(userId = userId)
|
val user = this.osuApi.getUserProfile(userId.toString())
|
||||||
val recentUserScores = this.osuApi.getTopUserScores(userId = userId, type = "recent")
|
|
||||||
val firstPlaceUserScores = this.osuApi.getTopUserScores(userId = userId, type = "firsts")
|
if (user == null) {
|
||||||
|
this.logger.error("Failed to fetch user from queue $userId")
|
||||||
|
this.updateUserQueueService.setUserAsProcessed(userId, failed = true)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userScores = mutableListOf<OsuApiModels.Score>()
|
||||||
|
|
||||||
|
if (user.beatmap_playcounts_count != null) {
|
||||||
|
val mapsPlayed: MutableSet<Int> = mutableSetOf()
|
||||||
|
|
||||||
|
this.logger.info("User has ${user.beatmap_playcounts_count} unique beatmap plays")
|
||||||
|
|
||||||
|
for (page in 1..(user.beatmap_playcounts_count / 50) + 1) {
|
||||||
|
val maps = this.osuApi.getUserMostPlayed(userId, 50, 50 * page)
|
||||||
|
?: break
|
||||||
|
|
||||||
|
mapsPlayed.addAll(maps.map { it.beatmap_id })
|
||||||
|
|
||||||
|
this.logger.info("Page: $page/${(user.beatmap_playcounts_count / 50) + 1}")
|
||||||
|
|
||||||
|
Thread.sleep(SLEEP_AFTER_API_CALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
var scoreProcessCount = 0
|
||||||
|
for (mapId in mapsPlayed) {
|
||||||
|
val scores = this.osuApi.getUserBeatmapScores(userId, mapId)
|
||||||
|
?: continue
|
||||||
|
|
||||||
|
for (mapScore in scores.scores) {
|
||||||
|
if (mapScore.replay && mapScore.id != null) {
|
||||||
|
val beatmap = this.osuApi.getBeatmapFromId(mapId)
|
||||||
|
?: continue
|
||||||
|
|
||||||
|
userScores.add(mapScore.copy(
|
||||||
|
beatmap = beatmap.toScoreBeatmap(),
|
||||||
|
beatmapset = beatmap.beatmapset.toScoreBeatmapSet(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info(
|
||||||
|
"Getting all user scores for $userId: Processed map scores ${++scoreProcessCount}/${mapsPlayed.size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
Thread.sleep(SLEEP_AFTER_API_CALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val topUserScores = this.osuApi.getTopUserScores(userId = userId)
|
||||||
|
val recentUserScores = this.osuApi.getTopUserScores(userId = userId, type = "recent")
|
||||||
|
val firstPlaceUserScores = this.osuApi.getTopUserScores(userId = userId, type = "firsts")
|
||||||
|
|
||||||
|
if (topUserScores == null || recentUserScores == null || firstPlaceUserScores == null) {
|
||||||
|
this.logger.error("Failed to fetch top scores for user with id = $userId")
|
||||||
|
this.updateUserQueueService.setUserAsProcessed(userId, failed = true)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
userScores += (topUserScores + recentUserScores + firstPlaceUserScores)
|
||||||
|
|
||||||
|
userScores = userScores
|
||||||
|
.filter { it.beatmap != null && it.beatmapset != null }
|
||||||
|
.distinctBy { it.best_id }
|
||||||
|
.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
this.logger.info("Processing user with id = $userId")
|
this.logger.info("Processing user with id = $userId")
|
||||||
this.logger.info("Top scores: ${topUserScores?.size}")
|
this.logger.info("User has ${userScores.size} total scores")
|
||||||
this.logger.info("Recent scores: ${recentUserScores?.size}")
|
|
||||||
this.logger.info("First place scores: ${firstPlaceUserScores?.size}")
|
|
||||||
|
|
||||||
Thread.sleep(SLEEP_AFTER_API_CALL)
|
Thread.sleep(SLEEP_AFTER_API_CALL)
|
||||||
|
|
||||||
if(topUserScores == null || recentUserScores == null || firstPlaceUserScores == null) {
|
this.logger.info("Unique scores: ${userScores.size}")
|
||||||
this.logger.error("Failed to fetch top scores for user with id = $userId")
|
|
||||||
this.updateUserQueueService.setUserAsProcessed(userId, failed = true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val allUserScores = (topUserScores + recentUserScores + firstPlaceUserScores)
|
|
||||||
.filter { it.beatmap != null && it.beatmapset != null }
|
|
||||||
.distinctBy { it.best_id }
|
|
||||||
|
|
||||||
this.logger.info("Unique scores: ${allUserScores.size}")
|
|
||||||
|
|
||||||
val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(userId), USERS.SYS_LAST_UPDATE.greaterOrEqual(OffsetDateTime.now(ZoneOffset.UTC).minusDays(UPDATE_USER_EVERY_DAYS)))
|
val userExists = dslContext.fetchExists(USERS, USERS.USER_ID.eq(userId), USERS.SYS_LAST_UPDATE.greaterOrEqual(OffsetDateTime.now(ZoneOffset.UTC).minusDays(UPDATE_USER_EVERY_DAYS)))
|
||||||
if(!userExists) {
|
if (!userExists) {
|
||||||
val apiUser = this.osuApi.getUserProfile(userId = userId.toString(), mode = "osu", key = "id")
|
this.userService.insertApiUser(user)
|
||||||
if(apiUser != null) {
|
this.statistics.usersAddedToDatabase++
|
||||||
this.userService.insertApiUser(apiUser)
|
|
||||||
this.statistics.usersAddedToDatabase++
|
|
||||||
} else {
|
|
||||||
this.logger.error("Failed to fetch user with id = $userId")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = 0
|
var current = 0
|
||||||
@ -209,7 +255,7 @@ class ImportScores(
|
|||||||
.limit(1)
|
.limit(1)
|
||||||
.fetchOneInto(OffsetDateTime::class.java)
|
.fetchOneInto(OffsetDateTime::class.java)
|
||||||
|
|
||||||
for(topScore in allUserScores) {
|
for(topScore in userScores) {
|
||||||
val beatmapExists = dslContext.fetchExists(BEATMAPS, BEATMAPS.BEATMAP_ID.eq(topScore.beatmap!!.id))
|
val beatmapExists = dslContext.fetchExists(BEATMAPS, BEATMAPS.BEATMAP_ID.eq(topScore.beatmap!!.id))
|
||||||
if (!beatmapExists) {
|
if (!beatmapExists) {
|
||||||
val beatmapFile = this.osuApi.getBeatmapFile(beatmapId = topScore.beatmap.id)
|
val beatmapFile = this.osuApi.getBeatmapFile(beatmapId = topScore.beatmap.id)
|
||||||
@ -264,7 +310,7 @@ class ImportScores(
|
|||||||
// Update the database
|
// Update the database
|
||||||
dslContext.update(UPDATE_USER_QUEUE)
|
dslContext.update(UPDATE_USER_QUEUE)
|
||||||
.set(UPDATE_USER_QUEUE.PROGRESS_CURRENT, current)
|
.set(UPDATE_USER_QUEUE.PROGRESS_CURRENT, current)
|
||||||
.set(UPDATE_USER_QUEUE.PROGRESS_TOTAL, allUserScores.size)
|
.set(UPDATE_USER_QUEUE.PROGRESS_TOTAL, userScores.size)
|
||||||
.where(UPDATE_USER_QUEUE.USER_ID.eq(userId))
|
.where(UPDATE_USER_QUEUE.USER_ID.eq(userId))
|
||||||
.and(UPDATE_USER_QUEUE.PROCESSED.isFalse)
|
.and(UPDATE_USER_QUEUE.PROCESSED.isFalse)
|
||||||
.execute()
|
.execute()
|
||||||
@ -274,7 +320,7 @@ class ImportScores(
|
|||||||
lastCompletedUpdate = lastCompletedUpdate,
|
lastCompletedUpdate = lastCompletedUpdate,
|
||||||
canUpdate = false,
|
canUpdate = false,
|
||||||
progressCurrent = current,
|
progressCurrent = current,
|
||||||
progressTotal = allUserScores.size
|
progressTotal = userScores.size
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update the frontend
|
// Update the frontend
|
||||||
@ -289,7 +335,7 @@ class ImportScores(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for stolen replays.
|
// Check for stolen replays.
|
||||||
val uniqueBeatmapIds = allUserScores
|
val uniqueBeatmapIds = userScores
|
||||||
.groupBy { it.beatmap!!.id }
|
.groupBy { it.beatmap!!.id }
|
||||||
|
|
||||||
this.logger.info("Checking similarity for ${uniqueBeatmapIds.size} beatmaps.")
|
this.logger.info("Checking similarity for ${uniqueBeatmapIds.size} beatmaps.")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user