import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "./fetchTachi"; import { CreateLayeredLogger } from "./logger"; import { Sleep } from "./misc"; import { LoggerLayers } from "../data/data"; import { ScoreDocument } from "tachi-common"; import type { ImportDeferred, ImportPollStatus, SessionInfo, UGPTStats } from "./returnTypes"; import type { CommandInteraction } from "discord.js"; import type { ChartDocument, FolderDocument, Game, GoalDocument, ImportDocument, integer, PBScoreDocument, Playtype, PublicUserDocument, SessionDocument, SongDocument, } from "tachi-common"; const logger = CreateLayeredLogger(LoggerLayers.apiRequests); export async function GetUserInfo(userID: integer | string) { const res = await TachiServerV1Get(`/users/${userID}`, null); if (!res.success) { throw new Error(`Failed to fetch user with userID ${userID}.`); } return res.body; } export async function GetUGPTStats(userID: integer | string, game: Game, playtype: Playtype) { const res = await TachiServerV1Get( `/users/${userID}/games/${game}/${playtype}`, null ); if (!res.success) { throw new Error(`Failed to fetch UGPT stats for userID ${userID}, ${game}, ${playtype}.`); } return res.body; } export async function GetGoalWithID(goalID: string, game: Game, playtype: Playtype) { const res = await TachiServerV1Get( `/games/${game}/${playtype}/targets/goals/${goalID}`, null ); if (!res.success) { throw new Error(`Failed to fetch goal with ID ${goalID}. '${res.description}'.`); } return res.body; } export async function GetChartInfoForUser( userID: integer | string, chartID: string, game: Game, playtype: Playtype ) { const res = await TachiServerV1Get<{ song: SongDocument; chart: ChartDocument }>( `/games/${game}/${playtype}/charts/${chartID}`, null ); if (!res.success) { throw new Error(`Failed to fetch song/chart with chartID ${chartID}.`); } const pbRes = await TachiServerV1Get<{ chart: ChartDocument; pb: PBScoreDocument }>( `/users/${userID}/games/${game}/${playtype}/pbs/${chartID}`, null ); const pb = pbRes.success ? pbRes.body.pb : null; if (pb === null && pbRes.statusCode !== 404) { throw new Error(`Failed to fetch score info for userID ${userID} on chart ${chartID}.`); } return { song: res.body.song, chart: res.body.chart, pb }; } export async function PerformScoreImport( url: string, authToken: string, body: Record, interaction?: CommandInteraction ) { const initRes = await TachiServerV1Request( RequestTypes.POST, url, authToken, body ); if (!initRes.success) { logger.error(`Failed to perform score import on ${url}.`, { body }); throw new Error(`Failed to perform import on ${url}.`); } // this server does not defer imports to a scorequeue if (initRes.statusCode === 200) { const result = initRes.body as ImportDocument; return result; } else if (initRes.statusCode === 202) { // this server defers imports. // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop const pollRes = await TachiServerV1Get( `/imports/${initRes.body.importID}/poll-status`, authToken ); if (pollRes.success) { if (pollRes.body.importStatus === "completed") { // is there even a nice way around this -- // why *are* we nested so deeply? // eslint-disable-next-line max-depth if (interaction) { void interaction.editReply(`Import finished!`); } return pollRes.body.import; } if (interaction) { void interaction.editReply( `Importing Scores: ${pollRes.body.progress.description}..` ); } // eslint-disable-next-line no-await-in-loop await Sleep(1000); } else { throw new Error(`Failed to import scores. ${pollRes.description}.`); } } } logger.error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`, { body }); throw new Error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`); } export async function FindFolders(game: Game, playtype: Playtype, folderName: string) { const res = await TachiServerV1Get>( `/games/${game}/${playtype}/folders`, null, { search: folderName, } ); if (!res.success) { throw new Error(res.description); } return res.body; } export async function GetChartInfo(game: Game, playtype: Playtype, chartID: string) { const res = await TachiServerV1Get<{ chart: ChartDocument; song: SongDocument }>( `/games/${game}/${playtype}/charts/${chartID}`, null ); if (!res.success) { throw new Error(res.description); } return res.body; } export async function GetMostRecentSession(userID: integer, game: Game, playtype: Playtype) { const res = await TachiServerV1Get( `/users/${userID}/games/${game}/${playtype}/sessions/last`, null ); if (!res.success) { throw new Error(res.description); } return res.body; } export async function GetSessionInfo(sessionID: string) { const res = await TachiServerV1Get(`/sessions/${sessionID}`, null); if (!res.success) { throw new Error(res.description); } return res.body; }