2024-10-09 01:17:00 +01:00
|
|
|
import { PlayerDocument, PlayerModel } from "../model/player";
|
|
|
|
import { NotFoundError } from "../error/not-found-error";
|
|
|
|
import { getDaysAgoDate, getMidnightAlignedDate } from "@ssr/common/utils/time-utils";
|
|
|
|
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
|
|
|
import ScoreSaberPlayerToken from "@ssr/common/types/token/scoresaber/score-saber-player-token";
|
2024-10-09 15:25:21 +01:00
|
|
|
import { InternalServerError } from "../error/internal-server-error";
|
2024-10-09 01:17:00 +01:00
|
|
|
|
|
|
|
export class PlayerService {
|
|
|
|
/**
|
|
|
|
* Get a player from the database.
|
|
|
|
*
|
|
|
|
* @param id the player to fetch
|
|
|
|
* @param create if true, create the player if it doesn't exist
|
2024-10-12 02:36:44 +01:00
|
|
|
* @param playerToken an optional player token for the player
|
2024-10-09 01:17:00 +01:00
|
|
|
* @returns the player
|
|
|
|
* @throws NotFoundError if the player is not found
|
|
|
|
*/
|
2024-10-12 02:36:44 +01:00
|
|
|
public static async getPlayer(
|
|
|
|
id: string,
|
|
|
|
create: boolean = false,
|
|
|
|
playerToken?: ScoreSaberPlayerToken
|
|
|
|
): Promise<PlayerDocument> {
|
2024-10-09 01:17:00 +01:00
|
|
|
let player: PlayerDocument | null = await PlayerModel.findById(id);
|
|
|
|
if (player === null) {
|
2024-10-11 01:12:27 +01:00
|
|
|
// If create is on, create the player, otherwise return unknown player
|
2024-10-12 02:36:44 +01:00
|
|
|
playerToken = create ? (playerToken ? playerToken : await scoresaberService.lookupPlayer(id)) : undefined;
|
2024-10-09 01:17:00 +01:00
|
|
|
if (playerToken === undefined) {
|
|
|
|
throw new NotFoundError(`Player "${id}" not found`);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(`Creating player "${id}"...`);
|
2024-10-11 02:43:28 +01:00
|
|
|
try {
|
|
|
|
player = (await PlayerModel.create({ _id: id })) as PlayerDocument;
|
|
|
|
player.trackedSince = new Date();
|
|
|
|
await this.seedPlayerHistory(player, playerToken);
|
|
|
|
} catch (err) {
|
|
|
|
const message = `Failed to create player document for "${id}"`;
|
|
|
|
console.log(message, err);
|
|
|
|
throw new InternalServerError(message);
|
2024-10-09 01:17:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return player;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Seeds the player's history using data from
|
|
|
|
* the ScoreSaber API.
|
|
|
|
*
|
|
|
|
* @param player the player to seed
|
|
|
|
* @param playerToken the SoreSaber player token
|
|
|
|
*/
|
|
|
|
public static async seedPlayerHistory(player: PlayerDocument, playerToken: ScoreSaberPlayerToken): Promise<void> {
|
|
|
|
// Loop through rankHistory in reverse, from current day backwards
|
|
|
|
const playerRankHistory = playerToken.histories.split(",").map((value: string) => {
|
|
|
|
return parseInt(value);
|
|
|
|
});
|
|
|
|
playerRankHistory.push(playerToken.rank);
|
|
|
|
|
|
|
|
let daysAgo = 1; // Start from yesterday
|
|
|
|
for (let i = playerRankHistory.length - daysAgo - 1; i >= 0; i--) {
|
|
|
|
const rank = playerRankHistory[i];
|
|
|
|
const date = getMidnightAlignedDate(getDaysAgoDate(daysAgo));
|
|
|
|
player.setStatisticHistory(date, {
|
|
|
|
rank: rank,
|
|
|
|
});
|
|
|
|
daysAgo += 1; // Increment daysAgo for each earlier rank
|
|
|
|
}
|
|
|
|
await player.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tracks a players statistics
|
|
|
|
*
|
|
|
|
* @param foundPlayer the player to track
|
2024-10-11 18:42:46 +01:00
|
|
|
* @param playerToken an optional player token
|
2024-10-09 01:17:00 +01:00
|
|
|
*/
|
2024-10-11 18:42:46 +01:00
|
|
|
public static async trackScoreSaberPlayer(
|
|
|
|
foundPlayer: PlayerDocument,
|
|
|
|
playerToken?: ScoreSaberPlayerToken
|
|
|
|
): Promise<void> {
|
2024-10-10 01:40:21 +01:00
|
|
|
const dateToday = getMidnightAlignedDate(new Date());
|
2024-10-11 18:42:46 +01:00
|
|
|
const player = playerToken ? playerToken : await scoresaberService.lookupPlayer(foundPlayer.id);
|
2024-10-09 01:17:00 +01:00
|
|
|
if (player == undefined) {
|
|
|
|
console.log(`Player "${foundPlayer.id}" not found on ScoreSaber`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (player.inactive) {
|
|
|
|
console.log(`Player "${foundPlayer.id}" is inactive on ScoreSaber`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seed the history with ScoreSaber data if no history exists
|
|
|
|
if (foundPlayer.getDaysTracked() === 0) {
|
|
|
|
await this.seedPlayerHistory(foundPlayer, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update current day's statistics
|
|
|
|
let history = foundPlayer.getHistoryByDate(dateToday);
|
|
|
|
if (history == undefined) {
|
|
|
|
history = {}; // Initialize if history is not found
|
|
|
|
}
|
|
|
|
// Set the history data
|
|
|
|
history.pp = player.pp;
|
|
|
|
history.countryRank = player.countryRank;
|
|
|
|
history.rank = player.rank;
|
2024-10-11 01:06:02 +01:00
|
|
|
history.accuracy = {
|
|
|
|
averageRankedAccuracy: player.scoreStats.averageRankedAccuracy,
|
|
|
|
};
|
2024-10-09 01:17:00 +01:00
|
|
|
foundPlayer.setStatisticHistory(dateToday, history);
|
|
|
|
foundPlayer.sortStatisticHistory();
|
|
|
|
foundPlayer.lastTracked = new Date();
|
|
|
|
await foundPlayer.save();
|
|
|
|
|
|
|
|
console.log(`Tracked player "${foundPlayer.id}"!`);
|
|
|
|
}
|
|
|
|
}
|