experimental: initial autofix blast

This commit is contained in:
zkldi
2022-05-06 03:39:03 +01:00
parent 61ea7a4bf7
commit 0adc39b5cb
275 changed files with 2207 additions and 1877 deletions
+8 -13
View File
@@ -1,15 +1,16 @@
/* eslint-disable no-await-in-loop */
import deepmerge from "deepmerge";
import db from "external/mongo/db";
import { Game, integer, Playtype, Playtypes, PublicUserDocument } from "tachi-common";
import CreateLogCtx from "lib/logger/logger";
import { CreateCalculatedData } from "lib/score-import/framework/calculated-data/calculated-data";
import { UpdateUsersGamePlaytypeStats } from "lib/score-import/framework/user-game-stats/update-ugs";
import { GetAndUpdateUsersGoals } from "lib/score-import/framework/goals/goals";
import { UpdateUsersMilestones } from "lib/score-import/framework/milestones/milestones";
import { EfficientDBIterate } from "utils/efficient-db-iterate";
import { ProcessPBs } from "lib/score-import/framework/pb/process-pbs";
import { UpdateUsersGamePlaytypeStats } from "lib/score-import/framework/user-game-stats/update-ugs";
import { Playtypes } from "tachi-common";
import { EfficientDBIterate } from "utils/efficient-db-iterate";
import { FormatUserDoc } from "utils/user";
import deepmerge from "deepmerge";
import type { Game, integer, Playtype, PublicUserDocument } from "tachi-common";
const logger = CreateLogCtx(__filename);
@@ -71,13 +72,7 @@ export async function RecalcAllScores(filter = {}) {
const userID = Number(strUserID);
await UpdateUsersGamePlaytypeStats(
game as Game,
playtype as Playtype,
userID,
null,
logger
);
await UpdateUsersGamePlaytypeStats(game, playtype, userID, null, logger);
const goalInfo = await GetAndUpdateUsersGoals(game, userID, chartIDs, logger);
@@ -87,8 +82,8 @@ export async function RecalcAllScores(filter = {}) {
logger.info(`Done!`);
}
export async function UpdateAllPBs(userIDs?: integer[], filter = {}) {
let allUsers: PublicUserDocument[];
export async function UpdateAllPBs(userIDs?: Array<integer>, filter = {}) {
let allUsers: Array<PublicUserDocument>;
if (!userIDs) {
allUsers = await db.users.find({});
@@ -8,6 +8,7 @@ const logger = CreateLogCtx(__filename);
export async function RecalcSessions(filter = {}) {
const allSessions = await db.sessions.find(filter);
logger.info(`Recalcing ${allSessions.length} sessions.`);
for (const session of allSessions) {
@@ -19,6 +20,7 @@ export async function RecalcSessions(filter = {}) {
);
let c;
try {
c = CreateSessionCalcData(session.game, session.playtype, scores);
} catch (err) {
+16 -16
View File
@@ -2,8 +2,8 @@ import db from "external/mongo/db";
import { CreateGameSettings } from "lib/game-settings/create-game-settings";
import CreateLogCtx from "lib/logger/logger";
import { EmitWebhookEvent } from "lib/webhooks/webhooks";
import { Game, IDStrings, integer, Playtype, UserGameStats } from "tachi-common";
import { GameClassSets } from "tachi-common/js/game-classes";
import type { Game, IDStrings, integer, Playtype, UserGameStats } from "tachi-common";
import type { GameClassSets } from "tachi-common/js/game-classes";
const logger = CreateLogCtx(__filename);
@@ -86,19 +86,19 @@ export async function UpdateClassIfGreater(
});
return null;
} else {
EmitWebhookEvent({
type: "class-update/v1",
content: {
userID,
new: classVal,
old: userGameStats!.classes[classSet]!,
set: classSet,
game,
playtype,
},
});
return true;
}
EmitWebhookEvent({
type: "class-update/v1",
content: {
userID,
new: classVal,
old: userGameStats!.classes[classSet]!,
set: classSet,
game,
playtype,
},
});
return true;
}
+33 -40
View File
@@ -1,8 +1,8 @@
import db from "external/mongo/db";
import CreateLogCtx from "lib/logger/logger";
import { FilterQuery } from "mongodb";
import {
FormatChart,
import { FormatChart } from "tachi-common";
import type { FilterQuery } from "mongodb";
import type {
Game,
GoalDocument,
GoalSubscriptionDocument,
@@ -13,6 +13,7 @@ import {
PBScoreDocument,
ScoreDocument,
} from "tachi-common";
const logger = CreateLogCtx(__filename);
export async function GetNextCounterValue(counterName: string): Promise<integer> {
@@ -66,7 +67,7 @@ export async function DecrementCounterValue(counterName: string): Promise<intege
}
export async function GetRelevantSongsAndCharts(
scores: (ScoreDocument | PBScoreDocument)[],
scores: Array<PBScoreDocument | ScoreDocument>,
game: Game
) {
const [songs, charts] = await Promise.all([
@@ -177,13 +178,11 @@ export async function GetRecentlyAchievedGoals(
baseQuery: FilterQuery<GoalSubscriptionDocument>,
limit = 100
) {
const query = Object.assign(
{
wasInstantlyAchieved: false,
achieved: true,
},
baseQuery
);
const query = {
wasInstantlyAchieved: false,
achieved: true,
...baseQuery,
};
const goalSubs = await db["goal-subs"].find(query, {
sort: {
@@ -218,14 +217,12 @@ export async function GetRecentlyInteractedGoals(
baseQuery: FilterQuery<GoalSubscriptionDocument>,
limit = 100
) {
const query = Object.assign(
{
wasInstantlyAchieved: false,
achieved: false,
lastInteraction: { $ne: null },
},
baseQuery
);
const query = {
wasInstantlyAchieved: false,
achieved: false,
lastInteraction: { $ne: null },
...baseQuery,
};
const goalSubs = await db["goal-subs"].find(query, {
sort: {
@@ -260,13 +257,11 @@ export async function GetRecentlyAchievedMilestones(
baseQuery: FilterQuery<MilestoneSubscriptionDocument>,
limit = 100
) {
const query = Object.assign(
{
wasInstantlyAchieved: false,
achieved: true,
},
baseQuery
);
const query = {
wasInstantlyAchieved: false,
achieved: true,
...baseQuery,
};
const milestoneSubs = await db["milestone-subs"].find(query, {
sort: {
@@ -301,14 +296,12 @@ export async function GetRecentlyInteractedMilestones(
baseQuery: FilterQuery<MilestoneSubscriptionDocument>,
limit = 100
) {
const query = Object.assign(
{
lastInteraction: { $ne: null },
achieved: false,
wasInstantlyAchieved: false,
},
baseQuery
);
const query = {
lastInteraction: { $ne: null },
achieved: false,
wasInstantlyAchieved: false,
...baseQuery,
};
const milestoneSubs = await db["milestone-subs"].find(query, {
sort: {
@@ -335,8 +328,8 @@ export async function GetRecentlyInteractedMilestones(
export async function GetMostSubscribedGoals(
query: FilterQuery<GoalSubscriptionDocument>,
limit = 100
): Promise<(GoalDocument & { __subscriptions: integer })[]> {
const mostSubscribedGoals = (await db["goal-subs"].aggregate([
): Promise<Array<GoalDocument & { __subscriptions: integer }>> {
const mostSubscribedGoals = await db["goal-subs"].aggregate([
{
$match: query,
},
@@ -370,7 +363,7 @@ export async function GetMostSubscribedGoals(
{
$unset: "goal._id",
},
])) as { goal: GoalDocument; subscriptions: integer }[];
]);
return mostSubscribedGoals.map((e) => ({
__subscriptions: e.subscriptions,
@@ -381,8 +374,8 @@ export async function GetMostSubscribedGoals(
export async function GetMostSubscribedMilestones(
query: FilterQuery<MilestoneSubscriptionDocument>,
limit = 100
): Promise<(MilestoneDocument & { __subscriptions: integer })[]> {
const mostSubscribedMilesones = (await db["milestone-subs"].aggregate([
): Promise<Array<MilestoneDocument & { __subscriptions: integer }>> {
const mostSubscribedMilesones = await db["milestone-subs"].aggregate([
{
$match: query,
},
@@ -416,7 +409,7 @@ export async function GetMostSubscribedMilestones(
{
$unset: "milestone._id",
},
])) as { milestone: MilestoneDocument; subscriptions: integer }[];
]);
return mostSubscribedMilesones.map((e) => ({
__subscriptions: e.subscriptions,
+1 -1
View File
@@ -5,7 +5,7 @@ export function ParseEA3SoftID(ver: string) {
throw new Error(`Invalid Version Code. Had ${a.length} components.`);
}
if (!ver.match(/^[A-Z0-9]{3}:[A-Z]:[A-Z]:[A-Z]:[0-9]{10}$/u)) {
if (!/^[A-Z0-9]{3}:[A-Z]:[A-Z]:[A-Z]:[0-9]{10}$/u.exec(ver)) {
throw new Error(`Invalid Version Code.`);
}
+3 -3
View File
@@ -1,13 +1,13 @@
/* eslint-disable no-await-in-loop */
import CreateLogCtx from "lib/logger/logger";
import { ICollection } from "monk";
import type { ICollection } from "monk";
const logger = CreateLogCtx(__filename);
export async function EfficientDBIterate<T, R>(
collection: ICollection<T>,
callbackFn: (c: T) => Promise<R>,
saveOp: (c: R[]) => Promise<void>,
saveOp: (c: Array<R>) => Promise<void>,
// eslint-disable-next-line @typescript-eslint/ban-types
filter: object = {},
bucketSize = 10_000
@@ -26,7 +26,7 @@ export async function EfficientDBIterate<T, R>(
const rDocs = await Promise.all(docs.map(callbackFn));
i += bucketSize;
i = i + bucketSize;
await saveOp(rDocs);
}
+2 -1
View File
@@ -1,5 +1,6 @@
import { Environment } from "lib/setup/config";
import nodeFetch, { RequestInfo, RequestInit, Response } from "node-fetch";
import nodeFetch from "node-fetch";
import type { RequestInfo, RequestInit, Response } from "node-fetch";
const fetch =
Environment.nodeEnv === "test"
+20 -14
View File
@@ -3,8 +3,8 @@ import db from "external/mongo/db";
import fjsh from "fast-json-stable-hash";
import CreateLogCtx from "lib/logger/logger";
import { TachiConfig } from "lib/setup/config";
import { FilterQuery } from "mongodb";
import {
import type { FilterQuery } from "mongodb";
import type {
ChartDocument,
FolderDocument,
Game,
@@ -26,19 +26,19 @@ export async function ResolveFolderToCharts(
folder: FolderDocument,
filter: FilterQuery<ChartDocument>,
getSongs: true
): Promise<{ songs: SongDocument[]; charts: ChartDocument[] }>;
): Promise<{ songs: Array<SongDocument>; charts: Array<ChartDocument> }>;
export async function ResolveFolderToCharts(
folder: FolderDocument,
filter?: FilterQuery<ChartDocument>,
getSongs?: false
): Promise<{ charts: ChartDocument[] }>;
): Promise<{ charts: Array<ChartDocument> }>;
export async function ResolveFolderToCharts(
folder: FolderDocument,
filter: FilterQuery<ChartDocument> = {},
getSongs = false
): Promise<{ songs?: SongDocument[]; charts: ChartDocument[] }> {
let songs: SongDocument[] | null = null;
let charts: ChartDocument[];
): Promise<{ songs?: Array<SongDocument>; charts: Array<ChartDocument> }> {
let songs: Array<SongDocument> | null = null;
let charts: Array<ChartDocument>;
if (folder.type === "static") {
charts = await db.charts[folder.game].find(
@@ -115,17 +115,17 @@ export async function GetFolderCharts(
folder: FolderDocument,
filter: FilterQuery<ChartDocument>,
getSongs: true
): Promise<{ songs: SongDocument[]; charts: ChartDocument[] }>;
): Promise<{ songs: Array<SongDocument>; charts: Array<ChartDocument> }>;
export async function GetFolderCharts(
folder: FolderDocument,
filter: FilterQuery<ChartDocument>,
getSongs: false
): Promise<{ charts: ChartDocument[] }>;
): Promise<{ charts: Array<ChartDocument> }>;
export async function GetFolderCharts(
folder: FolderDocument,
filter: FilterQuery<ChartDocument> = {},
getSongs = false
): Promise<{ songs?: SongDocument[]; charts: ChartDocument[] }> {
): Promise<{ songs?: Array<SongDocument>; charts: Array<ChartDocument> }> {
const chartIDs = await GetFolderChartIDs(folder.folderID);
const charts = await db.charts[folder.game].find(
@@ -157,6 +157,7 @@ export async function GetFolderChartIDs(folderID: string) {
return chartIDs.map((e) => e.chartID);
}
export async function CreateFolderChartLookup(folder: FolderDocument, flush = false) {
try {
const { charts } = await ResolveFolderToCharts(folder, {}, false);
@@ -194,6 +195,7 @@ export async function InitaliseFolderChartLookup() {
const folders = await db.folders.find({
game: { $in: TachiConfig.GAMES },
});
logger.info(`Reloading ${folders.length} folders.`);
await Promise.all(folders.map((folder) => CreateFolderChartLookup(folder)));
@@ -230,7 +232,7 @@ export async function GetPBsOnFolder(userID: integer, folder: FolderDocument) {
return { pbs, charts, songs };
}
export function CalculateLampDistribution(pbs: PBScoreDocument[]) {
export function CalculateLampDistribution(pbs: Array<PBScoreDocument>) {
const lampDist: Partial<Record<Lamps[IDStrings], integer>> = {};
for (const pb of pbs) {
@@ -245,7 +247,7 @@ export function CalculateLampDistribution(pbs: PBScoreDocument[]) {
return lampDist;
}
export function CalculateGradeDistribution(pbs: PBScoreDocument[]) {
export function CalculateGradeDistribution(pbs: Array<PBScoreDocument>) {
const gradeDist: Partial<Record<Grades[IDStrings], integer>> = {};
for (const pb of pbs) {
@@ -259,6 +261,7 @@ export function CalculateGradeDistribution(pbs: PBScoreDocument[]) {
return gradeDist;
}
export async function GetGradeLampDistributionForFolder(userID: integer, folder: FolderDocument) {
const pbData = await GetPBsOnFolder(userID, folder);
@@ -270,12 +273,15 @@ export async function GetGradeLampDistributionForFolder(userID: integer, folder:
};
}
export function GetGradeLampDistributionForFolders(userID: integer, folders: FolderDocument[]) {
export function GetGradeLampDistributionForFolders(
userID: integer,
folders: Array<FolderDocument>
) {
return Promise.all(folders.map((f) => GetGradeLampDistributionForFolder(userID, f)));
}
export function CreateFolderID(query: Record<string, unknown>, game: Game, playtype: Playtype) {
return `F${fjsh.hash(Object.assign({ game, playtype }, query), "SHA256")}`;
return `F${fjsh.hash({ game, playtype, ...query }, "SHA256")}`;
}
export async function GetRecentlyViewedFolders(userID: integer, game: Game, playtype: Playtype) {
+24 -16
View File
@@ -1,9 +1,10 @@
import { exec } from "child_process";
import crypto from "crypto";
import { ONE_HOUR } from "lib/constants/time";
import { TachiConfig } from "lib/setup/config";
import { Game, GamePTConfig, GetGameConfig, integer, Playtype } from "tachi-common";
import { GetGameConfig } from "tachi-common";
import { exec } from "child_process";
import crypto from "crypto";
import { URL } from "url";
import type { Game, GamePTConfig, integer, Playtype } from "tachi-common";
// https://github.com/sindresorhus/escape-string-regexp/blob/main/index.js
// the developer of this has migrated everything to Force ES6 style modules,
@@ -39,7 +40,7 @@ export function MStoS(ms: number) {
/**
* Random From Array - Selects a random value from an array.
*/
export function RFA<T>(arr: T[]): T {
export function RFA<T>(arr: Array<T>): T {
return arr[Math.floor(Math.random() * arr.length)];
}
@@ -60,12 +61,12 @@ export function SplitAuthorizationHeader(authHeader: string) {
* key to keyof T.
* False if the object doesn't.
*/
export function HasOwnProperty<T>(obj: T, key: string | number | symbol): key is keyof T {
export function HasOwnProperty<T>(obj: T, key: number | string | symbol): key is keyof T {
return Object.prototype.hasOwnProperty.call(obj, key);
}
export function IsValidGame(str: string): str is Game {
return !!TachiConfig.GAMES.includes(str as Game);
return Boolean(TachiConfig.GAMES.includes(str as Game));
}
export function IsValidPlaytype(game: Game, str: string): str is Playtype {
@@ -83,12 +84,12 @@ export function IsString(val: unknown): val is string {
return typeof val === "string";
}
export function DedupeArr<T>(arr: T[]): T[] {
export function DedupeArr<T>(arr: Array<T>): Array<T> {
return [...new Set(arr)];
}
export function StripUrl(url: string, userInput: string | null) {
if (!userInput) {
if (userInput === null || userInput === "") {
return userInput;
}
@@ -122,22 +123,28 @@ export function DeleteUndefinedProps(record: any) {
export function IsValidURL(string: string) {
try {
const url = new URL(string);
return url.protocol === "http:" || url.protocol === "https:";
} catch (err) {
} catch (_err) {
return false;
}
}
export function Sleep(ms: number) {
return new Promise<void>((resolve) => setTimeout(() => resolve(), ms));
return new Promise<void>((resolve) =>
setTimeout(() => {
resolve();
}, ms)
);
}
export function GetTimeXHoursAgo(hours: integer) {
return Date.now() - ONE_HOUR * hours;
}
export function ApplyNTimes<T>(n: integer, fn: (i: integer) => T): T[] {
export function ApplyNTimes<T>(n: integer, fn: (i: integer) => T): Array<T> {
const arr = [];
for (let i = 0; i < n; i++) {
arr.push(fn(i));
}
@@ -176,15 +183,16 @@ export function asyncExec(command: string) {
exec(command, (err, stdout, stderr) => {
if (err) {
// eslint-disable-next-line prefer-promise-reject-errors
return reject({ err, stdout, stderr });
reject({ err, stdout, stderr });
return;
}
return resolve({ stdout, stderr });
resolve({ stdout, stderr });
});
});
}
export function FormatBMSTables(bmsTables: { table: string; level: string }[]) {
export function FormatBMSTables(bmsTables: Array<{ table: string; level: string }>) {
if (bmsTables.length === 0) {
return null;
}
@@ -192,7 +200,7 @@ export function FormatBMSTables(bmsTables: { table: string; level: string }[]) {
return bmsTables.map((e) => `${e.table}${e.level}`).join(", ");
}
export function HumanisedJoinArray(arr: string[], lastJoiner = "or") {
export function HumanisedJoinArray(arr: Array<string>, lastJoiner = "or") {
return `${arr.slice(0, arr.length - 1).join(", ")} ${lastJoiner} ${arr[arr.length - 1]}`;
}
@@ -213,6 +221,6 @@ export function IsSupported(game: Game) {
* @param right - The right side of elements; everything in here not in left will be
* returned.
*/
export function ArrayDiff<T>(left: T[], right: T[]) {
export function ArrayDiff<T>(left: Array<T>, right: Array<T>) {
return right.filter((e) => !left.includes(e));
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { KtLogger } from "lib/logger/logger";
import type { KtLogger } from "lib/logger/logger";
export class CSVParseError extends Error {
constructor(description: string) {
@@ -42,7 +42,7 @@ export function NaiveCSVParse(csvBuffer: Buffer, logger: KtLogger) {
rawHeaders.push(curStr);
curStr = "";
} else {
curStr += char;
curStr = curStr + char;
}
}
+6 -3
View File
@@ -1,12 +1,15 @@
import p, { PrudenceError, ValidSchemaValue } from "prudence";
import p from "prudence";
import type { PrudenceError, ValidSchemaValue } from "prudence";
export function FormatPrError(err: PrudenceError, foreword = "Error") {
const receivedText =
typeof err.userVal === "object" && err.userVal !== null
? ""
: ` | Received ${err.userVal} [${err.userVal === null ? "null" : typeof err.userVal}]`;
: ` | Received ${err.userVal.toString()} [${
err.userVal === null ? "null" : typeof err.userVal
}]`;
return `${foreword}: ${err.keychain} | ${err.message}${receivedText}.`;
return `${foreword}: ${err.keychain ?? "null"} | ${err.message}${receivedText}.`;
}
export const optNull = (v: ValidSchemaValue) => p.optional(p.nullable(v));
+4 -4
View File
@@ -1,9 +1,9 @@
import db from "external/mongo/db";
import { KtLogger } from "lib/logger/logger";
import ScoreImportFatalError from "lib/score-import/framework/score-importing/score-import-error";
import { integer } from "tachi-common";
import type { KtLogger } from "lib/logger/logger";
import type { integer } from "tachi-common";
export function GetKaiAuth(userID: integer, service: "FLO" | "EAG" | "MIN") {
export function GetKaiAuth(userID: integer, service: "EAG" | "FLO" | "MIN") {
return db["kai-auth-tokens"].findOne({
userID,
service,
@@ -12,7 +12,7 @@ export function GetKaiAuth(userID: integer, service: "FLO" | "EAG" | "MIN") {
export async function GetKaiAuthGuaranteed(
userID: integer,
service: "FLO" | "EAG" | "MIN",
service: "EAG" | "FLO" | "MIN",
logger: KtLogger
) {
const authDoc = await GetKaiAuth(userID, service);
+24 -21
View File
@@ -1,6 +1,6 @@
import db from "external/mongo/db";
import { FilterQuery } from "mongodb";
import {
import type { FilterQuery } from "mongodb";
import type {
ChartDocument,
Difficulties,
Game,
@@ -26,9 +26,9 @@ export function FindChartWithPTDF<
I extends IDStrings = IDStrings
>(game: G, songID: integer, playtype: P, difficulty: Difficulties[I]) {
return db.charts[game].findOne({
songID: songID,
playtype: playtype,
difficulty: difficulty,
songID,
playtype,
difficulty,
isPrimary: true,
});
}
@@ -49,9 +49,9 @@ export function FindChartWithPTDFVersion<
version: GPTSupportedVersions[I]
) {
return db.charts[game].findOne({
songID: songID,
playtype: playtype,
difficulty: difficulty,
songID,
playtype,
difficulty,
versions: version,
});
}
@@ -69,6 +69,7 @@ export function FindChartWithPTDFVersion<
*/
export function FindDDRChartOnSongHash(
songHash: string,
// Technically both of these should be "ddr" instead of Game, but it proves hard to work with.
playtype: Playtype,
difficulty: Difficulties[IDStrings]
@@ -190,7 +191,7 @@ export function FindSDVXChartOnInGameID(
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Difficulties["sdvx:Single"][] }
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({
@@ -202,12 +203,12 @@ export function FindSDVXChartOnInGameID(
export function FindSDVXChartOnInGameIDVersion(
inGameID: number,
difficulty: "NOV" | "ADV" | "EXH" | "MXM" | "ANY_INF",
difficulty: "ADV" | "ANY_INF" | "EXH" | "MXM" | "NOV",
version: GPTSupportedVersions["sdvx:Single"]
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Difficulties["sdvx:Single"][] }
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({
@@ -219,12 +220,12 @@ export function FindSDVXChartOnInGameIDVersion(
export function FindSDVXChartOnDFVersion(
songID: integer,
difficulty: "NOV" | "ADV" | "EXH" | "MXM" | "ANY_INF",
difficulty: "ADV" | "ANY_INF" | "EXH" | "MXM" | "NOV",
version: GPTSupportedVersions["sdvx:Single"]
) {
const diffQuery =
difficulty === "ANY_INF"
? { $in: ["INF", "GRV", "HVN", "VVD"] as Difficulties["sdvx:Single"][] }
? { $in: ["INF", "GRV", "HVN", "VVD"] as Array<Difficulties["sdvx:Single"]> }
: difficulty;
return db.charts.sdvx.findOne({
@@ -255,7 +256,7 @@ export function FindChartOnSHA256Playtype(game: Game, hash: string, playtype: Pl
});
}
export function FindChartOnARCID(game: "iidx" | "ddr" | "jubeat" | "sdvx", arcID: string) {
export function FindChartOnARCID(game: "ddr" | "iidx" | "jubeat" | "sdvx", arcID: string) {
return db.charts[game].findOne({
"data.arcChartID": arcID,
});
@@ -269,11 +270,11 @@ export function FindChartOnARCID(game: "iidx" | "ddr" | "jubeat" | "sdvx", arcID
export async function FindChartsOnPopularity(
game: Game,
playtype: Playtype,
songIDs?: integer[],
songIDs?: Array<integer>,
skip = 0,
limit = 100,
scoreCollection: "personal-bests" | "scores" = "personal-bests"
): Promise<(ChartDocument & { __playcount: integer })[]> {
): Promise<Array<ChartDocument & { __playcount: integer }>> {
const matchQuery: FilterQuery<ChartDocument> = {
playtype,
};
@@ -290,11 +291,13 @@ export async function FindChartsOnPopularity(
// magnitude faster.
// Not entirely sure why, but $lookup is incredibly inefficient,
// and you should just avoid it.
const charts = (await db.charts[game].find(matchQuery)) as unknown as (ChartDocument & {
__playcount: integer;
})[];
const charts = (await db.charts[game].find(matchQuery)) as unknown as Array<
ChartDocument & {
__playcount: integer;
}
>;
const scoreCounts = (await db[scoreCollection].aggregate([
const scoreCounts = await db[scoreCollection].aggregate([
{
$match: { chartID: { $in: charts.map((e) => e.chartID) } },
},
@@ -315,7 +318,7 @@ export async function FindChartsOnPopularity(
{
$limit: limit,
},
])) as { _id: string; count: integer }[];
]);
const scoreCountMap = new Map();
+3 -3
View File
@@ -1,5 +1,5 @@
import db from "external/mongo/db";
import { PBScoreDocument } from "tachi-common";
import type { PBScoreDocument } from "tachi-common";
export async function GetAdjacentAbove(userPB: PBScoreDocument, size = 5) {
const adjAbove = (await db["personal-bests"].find(
@@ -11,7 +11,7 @@ export async function GetAdjacentAbove(userPB: PBScoreDocument, size = 5) {
limit: size,
sort: { "rankingData.rank": -1 },
}
)) as PBScoreDocument<"usc:Controller" | "usc:Keyboard">[];
)) as Array<PBScoreDocument<"usc:Controller" | "usc:Keyboard">>;
return adjAbove;
}
@@ -26,7 +26,7 @@ export async function GetAdjacentBelow(userPB: PBScoreDocument, size = 5) {
limit: size,
sort: { "rankingData.rank": 1 },
}
)) as PBScoreDocument<"usc:Controller" | "usc:Keyboard">[];
)) as Array<PBScoreDocument<"usc:Controller" | "usc:Keyboard">>;
return adjAbove;
}
+7 -6
View File
@@ -1,9 +1,9 @@
import db from "external/mongo/db";
import { KtLogger } from "lib/logger/logger";
import { InternalFailure } from "lib/score-import/framework/common/converter-failures";
import { FindOneResult } from "monk";
import { Game, integer, SongDocument } from "tachi-common";
import { EscapeStringRegexp } from "../misc";
import db from "external/mongo/db";
import { InternalFailure } from "lib/score-import/framework/common/converter-failures";
import type { KtLogger } from "lib/logger/logger";
import type { FindOneResult } from "monk";
import type { Game, integer, SongDocument } from "tachi-common";
/**
* Finds a song document for the given game with the given title (or alt-title).
@@ -18,7 +18,7 @@ export function FindSongOnTitle(game: Game, title: string): Promise<FindOneResul
return db.songs[game].findOne({
$or: [
{
title: title,
title,
},
{
altTitles: title,
@@ -38,6 +38,7 @@ export function FindSongOnTitleInsensitive(
// @optimisable: Performance should be tested here by having a utility field for all-titles.
const regex = new RegExp(`^${EscapeStringRegexp(title)}$`, "iu");
return db.songs[game].findOne({
$or: [
{
+5 -5
View File
@@ -1,7 +1,7 @@
import db from "external/mongo/db";
import { ONE_HOUR } from "lib/constants/time";
import NodeCache from "node-cache";
import { Game, gameClasses, IDStrings, integer, Playtype, Playtypes } from "tachi-common";
import type { Game, gameClasses, IDStrings, Playtype } from "tachi-common";
const classDistCache = new NodeCache();
@@ -14,7 +14,7 @@ export async function GetClassDistribution(
const cache = classDistCache.get(cacheKey);
if (!cache) {
const distribution = (await db["game-stats"].aggregate([
const distribution = await db["game-stats"].aggregate([
{
$match: {
game,
@@ -27,7 +27,7 @@ export async function GetClassDistribution(
count: { $sum: 1 },
},
},
])) as { _id: string; count: integer }[];
]);
// Converts {_id: "kaiden", count: 3} to {"kaiden": 3}, more or less.
const convert = Object.fromEntries(distribution.map((e) => [e._id, e.count]));
@@ -35,7 +35,7 @@ export async function GetClassDistribution(
classDistCache.set(cacheKey, convert, ONE_HOUR);
return convert;
} else {
return cache;
}
return cache;
}
+1 -1
View File
@@ -1,7 +1,7 @@
import db from "external/mongo/db";
import { integer } from "tachi-common";
import { GetGradeLampDistributionForFolders } from "utils/folder";
import { GetTimeXHoursAgo } from "utils/misc";
import type { integer } from "tachi-common";
// Various utils related to the player summary endpoint.
const REASONABLE_HOURS_AGO = 16;
+13 -13
View File
@@ -1,28 +1,28 @@
import { SYMBOL_TachiData } from "lib/constants/tachi";
import { Request } from "express";
import { TachiRequestData } from "./types";
import deepmerge from "deepmerge";
import { SYMBOL_TACHI_DATA } from "lib/constants/tachi";
import CreateLogCtx from "lib/logger/logger";
import type { TachiRequestData } from "./types";
import type { Request } from "express";
export function AssignToReqTachiData(req: Request, data: Partial<TachiRequestData>) {
if (!req[SYMBOL_TachiData]) {
req[SYMBOL_TachiData] = data;
if (!req[SYMBOL_TACHI_DATA]) {
req[SYMBOL_TACHI_DATA] = data;
} else {
req[SYMBOL_TachiData] = deepmerge(req[SYMBOL_TachiData]!, data);
req[SYMBOL_TACHI_DATA] = deepmerge(req[SYMBOL_TACHI_DATA]!, data);
}
}
const logger = CreateLogCtx(__filename);
export function GetUGPT(req: Request) {
if (!req[SYMBOL_TachiData]) {
if (!req[SYMBOL_TACHI_DATA]) {
logger.error(`Tried to get UGPT from a req that doesn't have SYMBOL_TachiData.`);
throw new Error("Failed to get UGPT.");
}
const user = req[SYMBOL_TachiData]!.requestedUser;
const game = req[SYMBOL_TachiData]!.game;
const playtype = req[SYMBOL_TachiData]!.playtype;
const user = req[SYMBOL_TACHI_DATA]!.requestedUser;
const game = req[SYMBOL_TACHI_DATA]!.game;
const playtype = req[SYMBOL_TACHI_DATA]!.playtype;
if (!user || !game || !playtype) {
logger.error(`Tried to get UGPT from a req that doesnt have U, G and PT.`);
@@ -33,13 +33,13 @@ export function GetUGPT(req: Request) {
}
export function GetGPT(req: Request) {
if (!req[SYMBOL_TachiData]) {
if (!req[SYMBOL_TACHI_DATA]) {
logger.error(`Tried to get GPT from a req that doesn't have SYMBOL_TachiData.`);
throw new Error("Failed to get GPT.");
}
const game = req[SYMBOL_TachiData]!.game;
const playtype = req[SYMBOL_TachiData]!.playtype;
const game = req[SYMBOL_TACHI_DATA]!.game;
const playtype = req[SYMBOL_TACHI_DATA]!.playtype;
if (!game || !playtype) {
logger.error(`Tried to get GPT from a req that doesnt have a Game and Playtype.`);
+2 -1
View File
@@ -1,6 +1,7 @@
import db from "external/mongo/db";
import { integer, Game, Playtypes, Playtype } from "tachi-common";
import { Playtypes } from "tachi-common";
import { UpdateAllPBs } from "utils/calculations/recalc-scores";
import type { integer, Game, Playtype } from "tachi-common";
/**
* Completely resets a UGPT profile.
+11 -5
View File
@@ -1,6 +1,12 @@
import db from "external/mongo/db";
import { ChartDocument, integer, PBScoreDocument, ScoreDocument, SongDocument } from "tachi-common";
import { DedupeArr } from "./misc";
import db from "external/mongo/db";
import type {
ChartDocument,
integer,
PBScoreDocument,
ScoreDocument,
SongDocument,
} from "tachi-common";
export function GetPBOnChart(userID: integer, chartID: string) {
return db["personal-bests"].findOne({
@@ -17,9 +23,9 @@ export function GetServerRecordOnChart(chartID: string) {
}
export function FilterChartsAndSongs(
scores: (PBScoreDocument | ScoreDocument)[],
charts: ChartDocument[],
songs: SongDocument[]
scores: Array<PBScoreDocument | ScoreDocument>,
charts: Array<ChartDocument>,
songs: Array<SongDocument>
) {
const chartIDs = new Set();
const songIDs = new Set();
+2 -2
View File
@@ -1,6 +1,6 @@
import db from "external/mongo/db";
import { Condition } from "mongodb";
import { ScoreDocument, SessionDocument, SessionScoreInfo } from "tachi-common";
import type { Condition } from "mongodb";
import type { ScoreDocument, SessionDocument, SessionScoreInfo } from "tachi-common";
/**
* Returns all the score documents inside a session.
+2 -1
View File
@@ -1,5 +1,6 @@
import { Game, GamePTConfig, GetGamePTConfig, Playtype, Playtypes } from "tachi-common";
import { IsString } from "./misc";
import { GetGamePTConfig, Playtypes } from "tachi-common";
import type { Game, GamePTConfig, Playtype } from "tachi-common";
const isIntegerRegex = /^-?\d+$/u;
+2 -2
View File
@@ -1,4 +1,4 @@
import {
import type {
FolderDocument,
TableDocument,
SessionDocument,
@@ -20,8 +20,8 @@ import {
ImportDocument,
} from "tachi-common";
// Inject additional properties on express-session
declare module "express-session" {
// Inject additional properties on express-session
interface SessionData {
tachi: TachiSessionData;
}
+9 -10
View File
@@ -1,16 +1,15 @@
import db from "external/mongo/db";
import CreateLogCtx from "lib/logger/logger";
import { FindOneResult } from "monk";
import {
import { GetGamePTConfig, UserAuthLevels } from "tachi-common";
import type { FindOneResult } from "monk";
import type {
APITokenDocument,
Game,
GetGamePTConfig,
IDStrings,
integer,
Playtype,
PublicUserDocument,
UGSRatingsLookup,
UserAuthLevels,
UserGameStats,
} from "tachi-common";
@@ -46,13 +45,13 @@ export function GetUserCaseInsensitive(
): Promise<FindOneResult<PublicUserDocument>> {
return db.users.findOne({
usernameLowercase: username.toLowerCase(),
}) as Promise<FindOneResult<PublicUserDocument>>;
});
}
export async function CheckIfEmailInUse(email: string) {
const doc = await db["user-private-information"].findOne({ email });
return !!doc;
return Boolean(doc);
}
export function GetUserPrivateInfo(userID: integer) {
@@ -65,19 +64,19 @@ export function GetUserPrivateInfo(userID: integer) {
export function GetUserWithID(userID: integer): Promise<FindOneResult<PublicUserDocument>> {
return db.users.findOne({
id: userID,
}) as Promise<FindOneResult<PublicUserDocument>>;
});
}
export function GetSettingsForUser(userID: integer) {
return db["user-settings"].findOne({
userID: userID,
userID,
});
}
/**
* Gets the users for these user IDs.
*/
export async function GetUsersWithIDs(userIDs: integer[]) {
export async function GetUsersWithIDs(userIDs: Array<integer>) {
const users = await db.users.find({
id: { $in: userIDs },
});
@@ -123,7 +122,7 @@ export async function GetUserWithIDGuaranteed(userID: integer): Promise<PublicUs
*/
export function ResolveUser(usernameOrID: string) {
// user ID passed
if (usernameOrID.match(/^[0-9]+$/u)) {
if (/^[0-9]+$/u.exec(usernameOrID)) {
const intID = Number(usernameOrID);
return GetUserWithID(intID);