diff --git a/projects/backend/src/service/score.service.ts b/projects/backend/src/service/score.service.ts index 1efae0f..2f3b63d 100644 --- a/projects/backend/src/service/score.service.ts +++ b/projects/backend/src/service/score.service.ts @@ -1,7 +1,7 @@ import ScoreSaberPlayerScoreToken from "@ssr/common/types/token/scoresaber/score-saber-player-score-token"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { formatPp } from "@ssr/common/utils/number-utils"; +import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils"; import { isProduction } from "@ssr/common/utils/utils"; import { Metadata } from "@ssr/common/types/metadata"; import { NotFoundError } from "elysia"; @@ -33,8 +33,14 @@ export class ScoreService { return; } - const { score, leaderboard } = playerScore; - const player = score.leaderboardPlayerInfo; + const { score: scoreToken, leaderboard: leaderboardToken } = playerScore; + const score = getScoreSaberScoreFromToken(scoreToken, leaderboardToken); + const leaderboard = getScoreSaberLeaderboardFromToken(leaderboardToken); + const playerInfo = score.playerInfo; + const player = await scoresaberService.lookupPlayer(playerInfo.id); + if (!player) { + return; + } // Not ranked if (leaderboard.stars <= 0) { @@ -48,15 +54,44 @@ export class ScoreService { await logToChannel( DiscordChannels.numberOneFeed, new EmbedBuilder() - .setTitle(`${player.name} set a #1 on ${leaderboard.songName} ${leaderboard.songSubName}`) + .setTitle(`${player.name} just set a #1!`) .setDescription( - ` - **Player:** https://ssr.fascinated.cc/player/${player.id} - **Leaderboard:** https://ssr.fascinated.cc/leaderboard/${leaderboard.id} - **PP:** ${formatPp(score.pp)} - ` + `${leaderboard.songName} ${leaderboard.songSubName} (${leaderboard.difficulty.difficulty} ${leaderboard.stars.toFixed(2)}★)` ) - .setThumbnail(leaderboard.coverImage) + .addFields([ + { + name: "Accuracy", + value: `${score.accuracy.toFixed(2)}%`, + inline: true, + }, + { + name: "PP", + value: formatPp(score.pp), + inline: true, + }, + { + name: "Player Rank", + value: formatNumberWithCommas(player.rank), + inline: true, + }, + { + name: "Misses", + value: formatNumberWithCommas(score.missedNotes), + inline: true, + }, + { + name: "Bad Cuts", + value: formatNumberWithCommas(score.badCuts), + inline: true, + }, + { + name: "Max Combo", + value: formatNumberWithCommas(score.maxCombo), + inline: true, + }, + ]) + .setThumbnail(leaderboard.songArt) + .setTimestamp(score.timestamp) .setColor("#00ff00") ); } diff --git a/projects/common/src/score/impl/scoresaber-score.ts b/projects/common/src/score/impl/scoresaber-score.ts index 419ba21..ac7ed5e 100644 --- a/projects/common/src/score/impl/scoresaber-score.ts +++ b/projects/common/src/score/impl/scoresaber-score.ts @@ -2,6 +2,7 @@ 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 "@ssr/types/token/scoresaber/score-saber-leaderboard-token"; export default interface ScoreSaberScore extends Score { /** @@ -21,6 +22,11 @@ export default interface ScoreSaberScore extends Score { */ readonly weight?: number; + /** + * The max combo of the score. + */ + readonly maxCombo: number; + /** * The player who set the score */ @@ -31,8 +37,12 @@ export default interface ScoreSaberScore extends Score { * 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): ScoreSaberScore { +export function getScoreSaberScoreFromToken( + token: ScoreSaberScoreToken, + leaderboard?: ScoreSaberLeaderboardToken +): ScoreSaberScore { const modifiers: Modifier[] = token.modifiers == undefined || token.modifiers === "" ? [] @@ -48,15 +58,18 @@ export function getScoreSaberScoreFromToken(token: ScoreSaberScoreToken): ScoreS return { leaderboard: "scoresaber", score: token.baseScore, + accuracy: leaderboard ? token.baseScore / leaderboard.maxScore : Infinity, rank: token.rank, modifiers: modifiers, - misses: token.missedNotes, + 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, }; } diff --git a/projects/common/src/score/score.ts b/projects/common/src/score/score.ts index b52cb7b..f56c09a 100644 --- a/projects/common/src/score/score.ts +++ b/projects/common/src/score/score.ts @@ -13,6 +13,11 @@ export default interface Score { */ readonly score: number; + /** + * The accuracy of the score. + */ + readonly accuracy: number; + /** * The rank for the score. * @private @@ -26,11 +31,16 @@ export default interface Score { readonly modifiers: Modifier[]; /** - * The amount missed notes. + * The amount total amount of misses. * @private */ readonly misses: number; + /** + * The amount of missed notes. + */ + readonly missedNotes: number; + /** * The amount of bad cuts. * @private diff --git a/projects/website/src/components/leaderboard/leaderboard-score-stats.tsx b/projects/website/src/components/leaderboard/leaderboard-score-stats.tsx index 7b86de1..6e6c0e3 100644 --- a/projects/website/src/components/leaderboard/leaderboard-score-stats.tsx +++ b/projects/website/src/components/leaderboard/leaderboard-score-stats.tsx @@ -1,11 +1,9 @@ -import { formatNumberWithCommas } from "@ssr/common/utils/number-utils"; -import { XMarkIcon } from "@heroicons/react/24/solid"; -import clsx from "clsx"; import { getScoreBadgeFromAccuracy } from "@/common/song-utils"; import Tooltip from "@/components/tooltip"; import { ScoreBadge, ScoreBadges } from "@/components/score/score-badge"; import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score"; import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard"; +import FullComboBadge from "@/components/score/badges/full-combo"; const badges: ScoreBadge[] = [ { @@ -57,14 +55,7 @@ const badges: ScoreBadge[] = [ { name: "Full Combo", create: (score: ScoreSaberScore) => { - const fullCombo = score.misses === 0; - - return ( - <> -

{fullCombo ? FC : formatNumberWithCommas(score.misses)}

- - - ); + return ; }, }, ]; diff --git a/projects/website/src/components/score/badges/badge-props.ts b/projects/website/src/components/score/badges/badge-props.ts new file mode 100644 index 0000000..1860ce0 --- /dev/null +++ b/projects/website/src/components/score/badges/badge-props.ts @@ -0,0 +1,8 @@ +import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score"; + +export type ScoreBadgeProps = { + /** + * The score for this badge + */ + score: ScoreSaberScore; +}; diff --git a/projects/website/src/components/score/badges/full-combo.tsx b/projects/website/src/components/score/badges/full-combo.tsx new file mode 100644 index 0000000..48cb21a --- /dev/null +++ b/projects/website/src/components/score/badges/full-combo.tsx @@ -0,0 +1,29 @@ +import { formatNumberWithCommas } from "@ssr/common/utils/number-utils"; +import { XMarkIcon } from "@heroicons/react/24/solid"; +import clsx from "clsx"; +import Tooltip from "@/components/tooltip"; +import { ScoreBadgeProps } from "@/components/score/badges/badge-props"; + +export default function FullComboBadge({ score }: ScoreBadgeProps) { + return ( + + {!score.fullCombo ? ( + <> +

Missed Notes: {formatNumberWithCommas(score.missedNotes)}

+

Bad Cuts: {formatNumberWithCommas(score.badCuts)}

+ + ) : ( +

Full Combo

+ )} + + } + > +
+

{score.fullCombo ? FC : formatNumberWithCommas(score.misses)}

+ +
+
+ ); +} diff --git a/projects/website/src/components/score/score-stats.tsx b/projects/website/src/components/score/score-stats.tsx index 52c7c8d..bd4a28b 100644 --- a/projects/website/src/components/score/score-stats.tsx +++ b/projects/website/src/components/score/score-stats.tsx @@ -1,11 +1,10 @@ import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils"; import { getScoreBadgeFromAccuracy } from "@/common/song-utils"; -import { XMarkIcon } from "@heroicons/react/24/solid"; -import clsx from "clsx"; import Tooltip from "@/components/tooltip"; import { ScoreBadge, ScoreBadges } from "@/components/score/score-badge"; import ScoreSaberScore from "@ssr/common/score/impl/scoresaber-score"; import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard"; +import FullComboBadge from "@/components/score/badges/full-combo"; const badges: ScoreBadge[] = [ { @@ -88,33 +87,7 @@ const badges: ScoreBadge[] = [ { name: "Full Combo", create: (score: ScoreSaberScore) => { - return ( - - {!score.fullCombo ? ( - <> -

Missed Notes: {formatNumberWithCommas(score.misses)}

-

Bad Cuts: {formatNumberWithCommas(score.badCuts)}

- - ) : ( -

Full Combo

- )} - - } - > -
-

- {score.fullCombo ? ( - FC - ) : ( - formatNumberWithCommas(score.misses + score.badCuts) - )} -

- -
-
- ); + return ; }, }, ];