implement scoresaber score tracking (for previous scores)
Some checks failed
Deploy Backend / docker (ubuntu-latest) (push) Successful in 1m6s
Deploy Website / docker (ubuntu-latest) (push) Failing after 1m40s

This commit is contained in:
Lee 2024-10-23 20:20:57 +01:00
parent 3c4406c4b7
commit d42c888e82
31 changed files with 315 additions and 224 deletions

BIN
bun.lockb

Binary file not shown.

@ -14,6 +14,7 @@
"@elysiajs/swagger": "^1.1.3",
"@ssr/common": "workspace:common",
"@tqman/nice-logger": "^1.0.1",
"@typegoose/auto-increment": "^4.7.0",
"@typegoose/typegoose": "^12.8.0",
"@typescript-eslint/eslint-plugin": "^8.9.0",
"@typescript-eslint/parser": "^8.9.0",

@ -8,7 +8,6 @@ import { etag } from "@bogeychan/elysia-etag";
import AppController from "./controller/app.controller";
import * as dotenv from "@dotenvx/dotenvx";
import mongoose from "mongoose";
import { setLogLevel } from "@typegoose/typegoose";
import PlayerController from "./controller/player.controller";
import { PlayerService } from "./service/player.service";
import { cron } from "@elysiajs/cron";
@ -33,9 +32,10 @@ dotenv.config({
override: true,
});
// Connect to Mongo
await mongoose.connect(Config.mongoUri!); // Connect to MongoDB
setLogLevel("DEBUG");
// Connect to websockets
connectScoresaberWebsocket({
onScore: async score => {
await ScoreService.trackScoreSaberScore(score);

@ -7,7 +7,6 @@ import BeatSaverService from "./beatsaver.service";
import ScoreSaberLeaderboard, {
getScoreSaberLeaderboardFromToken,
} from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { getScoreSaberScoreFromToken } from "@ssr/common/score/impl/scoresaber-score";
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
import { ScoreSort } from "@ssr/common/score/score-sort";
import { Leaderboards } from "@ssr/common/leaderboard";
@ -16,7 +15,6 @@ import LeaderboardService from "./leaderboard.service";
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
import { PlayerScore } from "@ssr/common/score/player-score";
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
import Score from "@ssr/common/score/score";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
import { DiscordChannels, logToChannel } from "../bot/bot";
import { EmbedBuilder } from "discord.js";
@ -30,6 +28,8 @@ import {
AdditionalScoreDataModel,
} from "@ssr/common/model/additional-score-data/additional-score-data";
import { BeatLeaderScoreImprovementToken } from "@ssr/common/types/token/beatleader/score/score-improvement";
import { getScoreSaberScoreFromToken, ScoreSaberScoreModel } from "@ssr/common/model/score/impl/scoresaber-score";
import { ScoreType } from "@ssr/common/model/score/score";
const playerScoresCache = new SSRCache({
ttl: 1000 * 60, // 1 minute
@ -52,7 +52,7 @@ export class ScoreService {
}
const { score: scoreToken, leaderboard: leaderboardToken } = playerScore;
const score = getScoreSaberScoreFromToken(scoreToken, leaderboardToken);
const score = getScoreSaberScoreFromToken(scoreToken, leaderboardToken, scoreToken.leaderboardPlayerInfo.id);
const leaderboard = getScoreSaberLeaderboardFromToken(leaderboardToken);
const playerInfo = score.playerInfo;
@ -153,8 +153,13 @@ export class ScoreService {
player.markModified("statisticHistory");
await player.save();
const scoreToken = getScoreSaberScoreFromToken(score, leaderboard, playerId);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
delete scoreToken.playerInfo;
await ScoreSaberScoreModel.create(scoreToken);
console.log(
`Updated scores set statistic for "${playerName}"(${playerId}), scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked`
`Tracked score and updated scores set statistic for "${playerName}"(${playerId}), scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked`
);
}
@ -268,7 +273,7 @@ export class ScoreService {
* Gets scores for a player.
*
* @param leaderboardName the leaderboard to get the scores from
* @param id the players id
* @param playerId the players id
* @param page the page to get
* @param sort the sort to use
* @param search the search to use
@ -276,14 +281,14 @@ export class ScoreService {
*/
public static async getPlayerScores(
leaderboardName: Leaderboards,
id: string,
playerId: string,
page: number,
sort: string,
search?: string
): Promise<PlayerScoresResponse<unknown, unknown> | undefined> {
return fetchWithCache(
playerScoresCache,
`player-scores-${leaderboardName}-${id}-${page}-${sort}-${search}`,
`player-scores-${leaderboardName}-${playerId}-${page}-${sort}-${search}`,
async () => {
const scores: PlayerScore<unknown, unknown>[] | undefined = [];
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
@ -291,7 +296,7 @@ export class ScoreService {
switch (leaderboardName) {
case "scoresaber": {
const leaderboardScores = await scoresaberService.lookupPlayerScores({
playerId: id,
playerId: playerId,
page: page,
sort: sort as ScoreSort,
search: search,
@ -308,17 +313,18 @@ export class ScoreService {
);
for (const token of leaderboardScores.playerScores) {
const score = getScoreSaberScoreFromToken(token.score, token.leaderboard);
const score = getScoreSaberScoreFromToken(token.score, token.leaderboard, playerId);
if (score == undefined) {
continue;
}
const leaderboard = getScoreSaberLeaderboardFromToken(token.leaderboard);
if (leaderboard == undefined) {
continue;
}
const additionalData = await this.getAdditionalScoreData(
id,
playerId,
leaderboard.songHash,
`${leaderboard.difficulty.difficulty}-${leaderboard.difficulty.characteristic}`,
score.score
@ -352,65 +358,73 @@ export class ScoreService {
* Gets scores for a leaderboard.
*
* @param leaderboardName the leaderboard to get the scores from
* @param id the leaderboard id
* @param leaderboardId the leaderboard id
* @param page the page to get
* @returns the scores
*/
public static async getLeaderboardScores(
leaderboardName: Leaderboards,
id: string,
leaderboardId: string,
page: number
): Promise<LeaderboardScoresResponse<unknown, unknown> | undefined> {
return fetchWithCache(leaderboardScoresCache, `leaderboard-scores-${leaderboardName}-${id}-${page}`, async () => {
const scores: Score[] = [];
let leaderboard: Leaderboard | undefined;
let beatSaverMap: BeatSaverMap | undefined;
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
return fetchWithCache(
leaderboardScoresCache,
`leaderboard-scores-${leaderboardName}-${leaderboardId}-${page}`,
async () => {
const scores: ScoreType[] = [];
let leaderboard: Leaderboard | undefined;
let beatSaverMap: BeatSaverMap | undefined;
let metadata: Metadata = new Metadata(0, 0, 0, 0); // Default values
switch (leaderboardName) {
case "scoresaber": {
const leaderboardResponse = await LeaderboardService.getLeaderboard<ScoreSaberLeaderboard>(
leaderboardName,
id
);
if (leaderboardResponse == undefined) {
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
}
leaderboard = leaderboardResponse.leaderboard;
beatSaverMap = leaderboardResponse.beatsaver;
switch (leaderboardName) {
case "scoresaber": {
const leaderboardResponse = await LeaderboardService.getLeaderboard<ScoreSaberLeaderboard>(
leaderboardName,
leaderboardId
);
if (leaderboardResponse == undefined) {
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
}
leaderboard = leaderboardResponse.leaderboard;
beatSaverMap = leaderboardResponse.beatsaver;
const leaderboardScores = await scoresaberService.lookupLeaderboardScores(id, page);
if (leaderboardScores == undefined) {
const leaderboardScores = await scoresaberService.lookupLeaderboardScores(leaderboardId, page);
if (leaderboardScores == undefined) {
break;
}
for (const token of leaderboardScores.scores) {
const score = getScoreSaberScoreFromToken(
token,
leaderboardResponse.leaderboard,
token.leaderboardPlayerInfo.id
);
if (score == undefined) {
continue;
}
scores.push(score);
}
metadata = new Metadata(
Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage),
leaderboardScores.metadata.total,
leaderboardScores.metadata.page,
leaderboardScores.metadata.itemsPerPage
);
break;
}
for (const token of leaderboardScores.scores) {
const score = getScoreSaberScoreFromToken(token, leaderboardResponse.leaderboard);
if (score == undefined) {
continue;
}
scores.push(score);
default: {
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
}
}
metadata = new Metadata(
Math.ceil(leaderboardScores.metadata.total / leaderboardScores.metadata.itemsPerPage),
leaderboardScores.metadata.total,
leaderboardScores.metadata.page,
leaderboardScores.metadata.itemsPerPage
);
break;
}
default: {
throw new NotFoundError(`Leaderboard "${leaderboardName}" not found`);
}
return {
scores: scores,
leaderboard: leaderboard,
beatSaver: beatSaverMap,
metadata: metadata,
};
}
return {
scores: scores,
leaderboard: leaderboard,
beatSaver: beatSaverMap,
metadata: metadata,
};
});
);
}
}

@ -19,6 +19,7 @@
"typescript": "^5"
},
"dependencies": {
"@typegoose/auto-increment": "^4.7.0",
"@typegoose/typegoose": "^12.8.0",
"ky": "^1.7.2",
"ws": "^8.18.0"

@ -4,7 +4,7 @@ import { HandAccuracy } from "./hand-accuracy";
import { Misses } from "./misses";
/**
* The model for a BeatSaver map.
* The model for additional score data.
*/
@modelOptions({
options: { allowMixed: Severity.ALLOW },

@ -0,0 +1,123 @@
import { getModelForClass, modelOptions, plugin, Prop, ReturnModelType, Severity } from "@typegoose/typegoose";
import Score from "../score";
import { Modifier } from "../../../score/modifier";
import ScoreSaberScoreToken from "../../../types/token/scoresaber/score-saber-score-token";
import ScoreSaberLeaderboardToken from "../../../types/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberLeaderboard from "../../../leaderboard/impl/scoresaber-leaderboard";
import { type ScoreSaberLeaderboardPlayerInfoToken } from "../../../types/token/scoresaber/score-saber-leaderboard-player-info-token";
import { Document } from "mongoose";
import { AutoIncrementID } from "@typegoose/auto-increment";
@modelOptions({
options: { allowMixed: Severity.ALLOW },
schemaOptions: {
collection: "scoresaber-scores",
toObject: {
virtuals: true,
transform: function (_, ret) {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
return ret;
},
},
},
})
@plugin(AutoIncrementID, {
field: "_id",
startAt: 1,
trackerModelName: "scores",
trackerCollection: "increments",
overwriteModelName: "scoresaber-scores",
})
export class ScoreSaberScoreInternal extends Score {
/**
* The score's id.
*/
@Prop({ required: true, index: true })
public readonly scoreId!: string;
/**
* The leaderboard the score was set on.
*/
@Prop({ required: true, index: true })
public readonly leaderboardId!: number;
/**
* The amount of pp for the score.
* @private
*/
@Prop({ required: true })
public readonly pp!: number;
/**
* The weight of the score, or undefined if not ranked.
* @private
*/
@Prop()
public readonly weight?: number;
/**
* The max combo of the score.
*/
@Prop({ required: true })
public readonly maxCombo!: number;
}
class ScoreSaberScorePublic extends ScoreSaberScoreInternal {
/**
* The player who set the score.
*/
public playerInfo!: ScoreSaberLeaderboardPlayerInfoToken;
}
export type ScoreSaberScore = InstanceType<typeof ScoreSaberScorePublic>;
/**
* Gets a {@link ScoreSaberScore} from a {@link ScoreSaberScoreToken}.
*
* @param token the token to convert
* @param playerId the id of the player who set the score
* @param leaderboard the leaderboard the score was set on
*/
export function getScoreSaberScoreFromToken(
token: ScoreSaberScoreToken,
leaderboard: ScoreSaberLeaderboardToken | ScoreSaberLeaderboard,
playerId?: string
): ScoreSaberScore {
const modifiers: Modifier[] =
token.modifiers == undefined || token.modifiers === ""
? []
: token.modifiers.split(",").map(mod => {
mod = mod.toUpperCase();
const modifier = Modifier[mod as keyof typeof Modifier];
if (modifier === undefined) {
throw new Error(`Unknown modifier: ${mod}`);
}
return modifier;
});
return {
leaderboard: "scoresaber",
playerId: playerId || token.leaderboardPlayerInfo.id,
leaderboardId: leaderboard.id,
score: token.baseScore,
accuracy: (token.baseScore / leaderboard.maxScore) * 100,
rank: token.rank,
modifiers: modifiers,
misses: token.missedNotes + token.badCuts,
missedNotes: token.missedNotes,
badCuts: token.badCuts,
fullCombo: token.fullCombo,
timestamp: new Date(token.timeSet),
scoreId: token.id,
pp: token.pp,
weight: token.weight,
maxCombo: token.maxCombo,
playerInfo: token.leaderboardPlayerInfo,
};
}
export type ScoreSaberScoreDocument = ScoreSaberScore & Document;
export const ScoreSaberScoreModel: ReturnModelType<typeof ScoreSaberScoreInternal> =
getModelForClass(ScoreSaberScoreInternal);

@ -0,0 +1,96 @@
import { Modifier } from "../../score/modifier";
import { AdditionalScoreData } from "../additional-score-data/additional-score-data";
import { type Leaderboards } from "../../leaderboard";
import { prop } from "@typegoose/typegoose";
/**
* The model for a score.
*/
export default class Score {
/**
* The internal score id.
*/
@prop()
private _id?: number;
/**
* The leaderboard the score is from.
*/
@prop({ required: true })
public readonly leaderboard!: Leaderboards;
/**
* The id of the player who set the score.
* @private
*/
@prop({ required: true, index: true })
public readonly playerId!: string;
/**
* The base score for the score.
* @private
*/
@prop({ required: true })
public readonly score!: number;
/**
* The accuracy of the score.
*/
@prop({ required: true })
public readonly accuracy!: number;
/**
* The rank for the score.
* @private
*/
@prop({ required: true })
public readonly rank!: number;
/**
* The modifiers used on the score.
* @private
*/
@prop({ enum: () => Modifier, type: String, required: true })
public readonly modifiers!: Modifier[];
/**
* The total amount of misses.
* @private
*/
@prop({ required: true })
public readonly misses!: number;
/**
* The amount of missed notes.
*/
@prop({ required: true })
public readonly missedNotes!: number;
/**
* The amount of bad cuts.
* @private
*/
@prop({ required: true })
public readonly badCuts!: number;
/**
* Whether every note was hit.
* @private
*/
@prop({ required: true })
public readonly fullCombo!: boolean;
/**
* The additional data for the score.
*/
public additionalData?: AdditionalScoreData;
/**
* The time the score was set.
* @private
*/
@prop({ required: true })
public readonly timestamp!: Date;
}
export type ScoreType = InstanceType<typeof Score>;

@ -1,76 +0,0 @@
import Score from "../score";
import { Modifier } from "../modifier";
import ScoreSaberScoreToken from "../../types/token/scoresaber/score-saber-score-token";
import ScoreSaberLeaderboardPlayerInfoToken from "../../types/token/scoresaber/score-saber-leaderboard-player-info-token";
import ScoreSaberLeaderboardToken from "../../types/token/scoresaber/score-saber-leaderboard-token";
import ScoreSaberLeaderboard from "../../leaderboard/impl/scoresaber-leaderboard";
export default interface ScoreSaberScore extends Score {
/**
* The score's id.
*/
readonly id: string;
/**
* The amount of pp for the score.
* @private
*/
readonly pp: number;
/**
* The weight of the score, or undefined if not ranked.s
* @private
*/
readonly weight?: number;
/**
* The max combo of the score.
*/
readonly maxCombo: number;
/**
* The player who set the score
*/
readonly playerInfo: ScoreSaberLeaderboardPlayerInfoToken;
}
/**
* Gets a {@link ScoreSaberScore} from a {@link ScoreSaberScoreToken}.
*
* @param token the token to convert
* @param leaderboard the leaderboard the score was set on
*/
export function getScoreSaberScoreFromToken(
token: ScoreSaberScoreToken,
leaderboard?: ScoreSaberLeaderboardToken | ScoreSaberLeaderboard
): ScoreSaberScore {
const modifiers: Modifier[] =
token.modifiers == undefined || token.modifiers === ""
? []
: token.modifiers.split(",").map(mod => {
mod = mod.toUpperCase();
const modifier = Modifier[mod as keyof typeof Modifier];
if (modifier === undefined) {
throw new Error(`Unknown modifier: ${mod}`);
}
return modifier;
});
return {
leaderboard: "scoresaber",
score: token.baseScore,
accuracy: leaderboard ? (token.baseScore / leaderboard.maxScore) * 100 : Infinity,
rank: token.rank,
modifiers: modifiers,
misses: token.missedNotes + token.badCuts,
missedNotes: token.missedNotes,
badCuts: token.badCuts,
fullCombo: token.fullCombo,
timestamp: new Date(token.timeSet),
id: token.id,
pp: token.pp,
weight: token.weight,
maxCombo: token.maxCombo,
playerInfo: token.leaderboardPlayerInfo,
};
}

@ -1,67 +0,0 @@
import { Modifier } from "./modifier";
import { Leaderboards } from "../leaderboard";
import { AdditionalScoreData } from "../model/additional-score-data/additional-score-data";
export default interface Score {
/**
* The leaderboard the score is from.
*/
readonly leaderboard: Leaderboards;
/**
* The base score for the score.
* @private
*/
readonly score: number;
/**
* The accuracy of the score.
*/
readonly accuracy: number;
/**
* The rank for the score.
* @private
*/
readonly rank: number;
/**
* The modifiers used on the score.
* @private
*/
readonly modifiers: Modifier[];
/**
* The amount total amount of misses.
* @private
*/
readonly misses: number;
/**
* The amount of missed notes.
*/
readonly missedNotes: number;
/**
* The amount of bad cuts.
* @private
*/
readonly badCuts: number;
/**
* Whether every note was hit.
* @private
*/
readonly fullCombo: boolean;
/**
* The additional data for the score.
*/
additionalData?: AdditionalScoreData;
/**
* The time the score was set.
* @private
*/
readonly timestamp: Date;
}

@ -1,8 +1,8 @@
export default interface ScoreSaberLeaderboardPlayerInfoToken {
export type ScoreSaberLeaderboardPlayerInfoToken = {
id: string;
name: string;
profilePicture: string;
country: string;
permissions: number;
role: string;
}
};

@ -1,5 +1,5 @@
import ScoreSaberLeaderboardToken from "./score-saber-leaderboard-token";
import ScoreSaberLeaderboardPlayerInfoToken from "./score-saber-leaderboard-player-info-token";
import { ScoreSaberLeaderboardPlayerInfoToken } from "./score-saber-leaderboard-player-info-token";
export default interface ScoreSaberScoreToken {
id: string;

@ -1,6 +1,6 @@
import ScoreSaberPlayerToken from "../types/token/scoresaber/score-saber-player-token";
import ScoreSaberLeaderboardPlayerInfoToken from "../types/token/scoresaber/score-saber-leaderboard-player-info-token";
import ScoreSaberPlayer from "../player/impl/scoresaber-player";
import { ScoreSaberLeaderboardPlayerInfoToken } from "../types/token/scoresaber/score-saber-leaderboard-player-info-token";
export type ScoreSaberRole = {
/**

@ -4,7 +4,7 @@ import { Colors } from "@/common/colors";
import { getAverageColor } from "@/common/image-utils";
import { LeaderboardData } from "@/components/leaderboard/leaderboard-data";
import { Config } from "@ssr/common/config";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { fetchLeaderboard } from "@ssr/common/utils/leaderboard.util";

@ -8,7 +8,7 @@ import { getCookieValue } from "@ssr/common/utils/cookie-utils";
import { Config } from "@ssr/common/config";
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
import { ScoreSort } from "@ssr/common/score/score-sort";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";

@ -1,6 +1,5 @@
"use client";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreStatsToken } from "@ssr/common/types/token/beatleader/score-stats/score-stats";
import { formatTime } from "@ssr/common/utils/time-utils";
import GenericChart, { DatasetConfig } from "@/components/chart/generic-chart";

@ -2,7 +2,7 @@
import LeaderboardScores from "@/components/leaderboard/leaderboard-scores";
import { LeaderboardInfo } from "@/components/leaderboard/leaderboard-info";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { LeaderboardResponse } from "@ssr/common/response/leaderboard-response";
import { useQuery } from "@tanstack/react-query";

@ -1,4 +1,4 @@
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils";
import ScoreSaberPlayerToken from "@ssr/common/types/token/scoresaber/score-saber-player-token";
import { PlayerInfo } from "@/components/player/player-info";

@ -10,7 +10,7 @@ import { scoreAnimation } from "@/components/score/score-animation";
import { Button } from "@/components/ui/button";
import { getDifficulty } from "@/common/song-utils";
import { fetchLeaderboardScores } from "@ssr/common/utils/score-utils";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
import useDatabase from "@/hooks/use-database";

@ -15,7 +15,7 @@ import useDatabase from "@/hooks/use-database";
import { useLiveQuery } from "dexie-react-hooks";
import ScoreSaberPlayer, { getScoreSaberPlayerFromToken } from "@ssr/common/player/impl/scoresaber-player";
import { ScoreSort } from "@ssr/common/score/score-sort";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";

@ -15,7 +15,7 @@ import { scoreAnimation } from "@/components/score/score-animation";
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
import { ScoreSort } from "@ssr/common/score/score-sort";
import { setCookieValue } from "@ssr/common/utils/cookie-utils";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { fetchPlayerScores } from "@ssr/common/utils/score-utils";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";

@ -1,4 +1,4 @@
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
export type ScoreBadgeProps = {
/**

@ -1,5 +1,5 @@
import StatValue from "@/components/stat-value";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
/**

@ -10,7 +10,7 @@ import { copyToClipboard } from "@/common/browser-utils";
import { ArrowDownIcon } from "@heroicons/react/24/solid";
import clsx from "clsx";
import ScoreEditorButton from "@/components/score/score-editor-button";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
import BeatSaberPepeLogo from "@/components/logos/beatsaber-pepe-logo";

@ -6,7 +6,7 @@ import { Slider } from "@/components/ui/slider";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { ResetIcon } from "@radix-ui/react-icons";
import Tooltip from "@/components/tooltip";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
type ScoreEditorButtonProps = {

@ -7,7 +7,7 @@ import { parseDate } from "@ssr/common/utils/time-utils";
import Link from "next/link";
import useWebSocket, { ReadyState } from "react-use-websocket";
import { ScoreSaberWebsocketMessageToken } from "@ssr/common/types/token/scoresaber/websocket/scoresaber-websocket-message";
import { getScoreSaberScoreFromToken } from "@ssr/common/score/impl/scoresaber-score";
import { getScoreSaberScoreFromToken } from "@ssr/common/model/score/impl/scoresaber-score";
import { getScoreSaberLeaderboardFromToken } from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
export default function ScoreFeed() {

@ -1,4 +1,4 @@
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import { Modifier } from "@ssr/common/score/modifier";
type ScoreModifiersProps = {

@ -2,7 +2,7 @@ import { formatNumberWithCommas } from "@ssr/common/utils/number-utils";
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
import Link from "next/link";
import { getPageFromRank } from "@ssr/common/utils/utils";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { ScoreTimeSet } from "@/components/score/score-time-set";

@ -1,6 +1,6 @@
import { getScoreBadgeFromAccuracy } from "@/common/song-utils";
import { ScoreBadge, ScoreBadges } from "@/components/score/score-badge";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import ScoreMissesBadge from "@/components/score/badges/score-misses";
import { HandAccuracyBadge } from "@/components/score/badges/hand-accuracy";

@ -1,7 +1,7 @@
import { format } from "@formkit/tempo";
import { timeAgo } from "@ssr/common/utils/time-utils";
import Tooltip from "@/components/tooltip";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
type ScoreTimeSetProps = {
/**

@ -9,7 +9,7 @@ import ScoreStats from "./score-stats";
import { motion } from "framer-motion";
import { getPageFromRank } from "@ssr/common/utils/utils";
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score";
import { ScoreSaberScore } from "@ssr/common/model/score/impl/scoresaber-score";
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
import { BeatSaverMap } from "@ssr/common/model/beatsaver/map";
import { useIsMobile } from "@/hooks/use-is-mobile";