mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-07 14:28:09 +03:00
feat: start actionising on the west coast
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
NODE_ENV="dev"
|
||||
REDIS_URL="tachi-redis"
|
||||
MONGO_URL="tachi-mongo"
|
||||
PORT=8080
|
||||
LOG_LEVEL="debug"
|
||||
MSGPACKR_NATIVE_ACCELERATION_DISABLED=true
|
||||
|
||||
POSTGRES_URL="postgresql://tachi:tachi@tachi-postgres/tachi_dev"
|
||||
MIGRATIONS_DIR="/tachi/db/migrations"
|
||||
@@ -1,5 +1,4 @@
|
||||
# Test stuffs
|
||||
.env
|
||||
.setup_env
|
||||
failed-tests.txt
|
||||
|
||||
|
||||
@@ -8,9 +8,3 @@ Tachi-Server is the backend for Tachi.
|
||||
- Opens a public API on `/api`.
|
||||
- Supports various IR frameworks under `/ir`
|
||||
- Powers a ridiculously overengineered score import framework.
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure you've ran the bootstrap script in `../_scripts`. You can then start the server with
|
||||
`pnpm start` (in this directory) or `pnpm start-server` at the top level.
|
||||
@@ -0,0 +1,10 @@
|
||||
version: v2
|
||||
inputs:
|
||||
- directory: src/proto/myt
|
||||
plugins:
|
||||
- local: protoc-gen-es
|
||||
out: src/proto/generated
|
||||
opt: target=ts
|
||||
- local: protoc-gen-connect-es
|
||||
out: src/proto/generated
|
||||
opt: target=ts
|
||||
@@ -1,6 +0,0 @@
|
||||
NODE_ENV="dev"
|
||||
REDIS_URL="redis"
|
||||
MONGO_URL="mongo"
|
||||
PORT=8080
|
||||
LOG_LEVEL="debug"
|
||||
MSGPACKR_NATIVE_ACCELERATION_DISABLED=true
|
||||
@@ -1,98 +0,0 @@
|
||||
// Config file that the CI deployment uses. This is moved to $pwd/conf.json5 before CI tests are ran.
|
||||
|
||||
{
|
||||
MONGO_DATABASE_NAME: "localdb",
|
||||
CAPTCHA_SECRET_KEY: "unused",
|
||||
SESSION_SECRET: "unused",
|
||||
FLO_API_URL: "https://flo.example.com",
|
||||
EAG_API_URL: "https://eag.example.com",
|
||||
MIN_API_URL: "https://min.example.com",
|
||||
MYT_API_HOST: "myt.example.com",
|
||||
FLO_OAUTH2_INFO: {
|
||||
CLIENT_ID: "DUMMY_CLIENT_ID",
|
||||
CLIENT_SECRET: "DUMMY_CLIENT_SECRET",
|
||||
REDIRECT_URI: "https://example.com",
|
||||
},
|
||||
EAG_OAUTH2_INFO: {
|
||||
CLIENT_ID: "DUMMY_CLIENT_ID",
|
||||
CLIENT_SECRET: "DUMMY_CLIENT_SECRET",
|
||||
REDIRECT_URI: "https://example.com",
|
||||
},
|
||||
ARC_AUTH_TOKEN: "unused",
|
||||
MYT_AUTH_TOKEN: "unused",
|
||||
OUR_URL: "https://127.0.0.1:8080",
|
||||
CLIENT_DEV_SERVER: "http://127.0.0.1:3000",
|
||||
ENABLE_SERVER_HTTPS: true,
|
||||
OPTIONS_ALWAYS_SUCCEEDS: true,
|
||||
CDN_CONFIG: {
|
||||
WEB_LOCATION: "https://127.0.0.1:8080/cdn",
|
||||
SAVE_LOCATION: {
|
||||
TYPE: "LOCAL_FILESYSTEM",
|
||||
LOCATION: "./local-cdn",
|
||||
SERVE_OWN_CDN: true,
|
||||
},
|
||||
},
|
||||
TACHI_CONFIG: {
|
||||
TYPE: "omni",
|
||||
NAME: "Tachi Local Dev",
|
||||
GAMES: [
|
||||
"iidx",
|
||||
"museca",
|
||||
"sdvx",
|
||||
"bms",
|
||||
"chunithm",
|
||||
"usc",
|
||||
"wacca",
|
||||
"popn",
|
||||
"jubeat",
|
||||
"pms",
|
||||
"gitadora",
|
||||
"maimai",
|
||||
"maimaidx",
|
||||
"itg",
|
||||
"ongeki",
|
||||
"ddr"
|
||||
],
|
||||
IMPORT_TYPES: [
|
||||
"file/eamusement-iidx-csv",
|
||||
"file/batch-manual",
|
||||
"file/solid-state-squad",
|
||||
"file/pli-iidx-csv",
|
||||
"file/mypagescraper-records-csv",
|
||||
"file/mypagescraper-player-csv",
|
||||
"ir/direct-manual",
|
||||
"ir/barbatos",
|
||||
"ir/fervidex",
|
||||
"ir/fervidex-static",
|
||||
"ir/beatoraja",
|
||||
"ir/usc",
|
||||
"ir/kshook-sv6c",
|
||||
"ir/lr2hook",
|
||||
"api/eag-iidx",
|
||||
"api/eag-sdvx",
|
||||
"api/flo-iidx",
|
||||
"api/flo-sdvx",
|
||||
"api/min-sdvx",
|
||||
"api/cg-dev-sdvx",
|
||||
"api/cg-dev-popn",
|
||||
"api/cg-dev-museca",
|
||||
"api/cg-dev-jubeat",
|
||||
"api/cg-nag-sdvx",
|
||||
"api/cg-nag-popn",
|
||||
"api/cg-nag-jubeat",
|
||||
"api/cg-nag-museca",
|
||||
"api/cg-gan-sdvx",
|
||||
"api/cg-gan-popn",
|
||||
"api/cg-gan-museca",
|
||||
"api/cg-gan-jubeat",
|
||||
"api/myt-chunithm",
|
||||
"api/myt-maimaidx",
|
||||
"api/myt-ongeki",
|
||||
"api/myt-wacca",
|
||||
],
|
||||
},
|
||||
SEEDS_CONFIG: {
|
||||
TYPE: "LOCAL_FILES",
|
||||
PATH: "../seeds/collections",
|
||||
},
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"watch": ["src"],
|
||||
"ext": "ts,json",
|
||||
"ignore": ["src/**/*.spec.ts"],
|
||||
"exec": "ts-node ./src/main.ts"
|
||||
}
|
||||
@@ -6,9 +6,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/main.ts",
|
||||
"test": "tap",
|
||||
"snap": "TAP_SNAPSHOT=1 tap",
|
||||
"watchtest": "tap --watch",
|
||||
"test": "vitest",
|
||||
"build": "tsgo -b tsconfig.build.json -v",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"lint": "eslint ./src",
|
||||
@@ -21,8 +19,8 @@
|
||||
"@types/google-protobuf": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"@types/supertest": "catalog:",
|
||||
"@types/tap": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"glob": "catalog:",
|
||||
"google-protobuf": "catalog:",
|
||||
@@ -30,9 +28,9 @@
|
||||
"node-mocks-http": "catalog:",
|
||||
"nodemon": "catalog:",
|
||||
"supertest": "catalog:",
|
||||
"tap": "workspace:*",
|
||||
"ts-protoc-gen": "catalog:",
|
||||
"tsconfig-paths": "catalog:"
|
||||
"tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "catalog:",
|
||||
@@ -59,6 +57,7 @@
|
||||
"@types/semver": "catalog:",
|
||||
"@valuabletouch/winston-seq": "catalog:",
|
||||
"bcryptjs": "catalog:",
|
||||
"bliss": "workspace:*",
|
||||
"bms-table-loader": "catalog:",
|
||||
"bullmq": "catalog:",
|
||||
"bunyan": "catalog:",
|
||||
@@ -77,6 +76,7 @@
|
||||
"html-entities": "catalog:",
|
||||
"json5": "catalog:",
|
||||
"kysely": "catalog:",
|
||||
"lodash": "^4.17.23",
|
||||
"mkdirp": "catalog:",
|
||||
"mongodb": "catalog:",
|
||||
"monk": "catalog:",
|
||||
@@ -86,7 +86,6 @@
|
||||
"node-html-parser": "catalog:",
|
||||
"nodemailer": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"bliss": "workspace:*",
|
||||
"prudence": "catalog:",
|
||||
"rate-limit-redis": "catalog:",
|
||||
"redis": "catalog:",
|
||||
@@ -115,7 +114,7 @@
|
||||
"src/*/!(test-utils)/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.oldtest.ts",
|
||||
"src/external/mongo/schemas.ts"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { MakeAction } from "#lib/actions/actions.js";
|
||||
import DB from "#services/pg/db.js";
|
||||
import { Random20Hex } from "#utils/misc.js";
|
||||
import { IsUserAdmin } from "#utils/user.js";
|
||||
import { ExpectedErr } from "bliss";
|
||||
|
||||
export const ACTION_InstallBuiltinClient = MakeAction(
|
||||
"INSTALL_BUILTIN_CLIENT",
|
||||
async (
|
||||
taker,
|
||||
{ clientID, name, permissions, apiKeyFilename, apiKeyTemplate, redirectUri, webhookUri },
|
||||
) => {
|
||||
// verify taker is admin
|
||||
if (!(await IsUserAdmin(taker.acct.id))) {
|
||||
throw new ExpectedErr(403, "You are not authorized to perform this action.");
|
||||
}
|
||||
|
||||
// I honestly think this is unused for builtin clients
|
||||
// as they don't need to auth with anyone!
|
||||
const clientSecret = `CS${Random20Hex()}`;
|
||||
|
||||
// create client
|
||||
await DB.insertInto("priv_api_client")
|
||||
.values({
|
||||
client_id: clientID,
|
||||
client_secret: clientSecret,
|
||||
name: name,
|
||||
author: taker.acct.id,
|
||||
pm_customise_profile: permissions.customise_profile,
|
||||
pm_customise_score: permissions.customise_score,
|
||||
pm_customise_session: permissions.customise_session,
|
||||
pm_delete_score: permissions.delete_score,
|
||||
pm_manage_rivals: permissions.manage_rivals,
|
||||
pm_manage_targets: permissions.manage_targets,
|
||||
pm_submit_score: permissions.submit_score,
|
||||
pm_manage_challenges: permissions.manage_challenges,
|
||||
api_key_filename: apiKeyFilename,
|
||||
api_key_template: apiKeyTemplate,
|
||||
webhook_uri: webhookUri,
|
||||
redirect_uri: redirectUri,
|
||||
is_builtin: true,
|
||||
})
|
||||
.onConflict((oc) =>
|
||||
oc.doUpdateSet({
|
||||
client_secret: clientSecret,
|
||||
name: name,
|
||||
author: taker.acct.id,
|
||||
pm_customise_profile: permissions.customise_profile,
|
||||
pm_customise_score: permissions.customise_score,
|
||||
pm_customise_session: permissions.customise_session,
|
||||
pm_delete_score: permissions.delete_score,
|
||||
pm_manage_rivals: permissions.manage_rivals,
|
||||
pm_manage_targets: permissions.manage_targets,
|
||||
pm_submit_score: permissions.submit_score,
|
||||
pm_manage_challenges: permissions.manage_challenges,
|
||||
api_key_filename: apiKeyFilename,
|
||||
api_key_template: apiKeyTemplate,
|
||||
webhook_uri: webhookUri,
|
||||
redirect_uri: redirectUri,
|
||||
is_builtin: true,
|
||||
}),
|
||||
)
|
||||
.execute();
|
||||
|
||||
return {};
|
||||
},
|
||||
);
|
||||
+3
-3
@@ -2,7 +2,7 @@ import type { DeepPartial } from "#utils/types";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -146,8 +146,8 @@ t.test("Arcaea Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -297,10 +297,10 @@ for (const [game, playtype, impl] of [
|
||||
|
||||
t.test("PB Mergers", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(() => db.scores.insert(mockScore));
|
||||
t.beforeEach(() => MONGODB_KILL.scores.insert(mockScore));
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -326,7 +326,7 @@ for (const [game, playtype, impl] of [
|
||||
});
|
||||
|
||||
t.test("Should join best BP", async (t) => {
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.insert([
|
||||
dmf(mockScore, {
|
||||
scoreID: "whateverBP",
|
||||
scoreData: { optional: { bp: 100 } },
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { CHUNITHMBBKKChart, TestingChunithmScorePB } from "#test-utils/test-data";
|
||||
@@ -83,7 +83,7 @@ t.test("CHUNITHM Implementation", (t) => {
|
||||
const mockPBs = async (ratings: Array<number>) => {
|
||||
await Promise.all(
|
||||
ratings.map((rating, idx) =>
|
||||
db["personal-bests"].insert({
|
||||
MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingChunithmScorePB,
|
||||
chartID: `TEST${idx}`,
|
||||
calculatedData: {
|
||||
@@ -192,8 +192,8 @@ t.test("CHUNITHM Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestNoteLamp",
|
||||
scoreData: {
|
||||
@@ -203,7 +203,7 @@ t.test("CHUNITHM Implementation", (t) => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestClearLamp",
|
||||
scoreData: {
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
ScoreValidator,
|
||||
} from "#game-implementations/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { DDRFlare } from "rg-stats";
|
||||
import {
|
||||
type ChartDocument,
|
||||
@@ -157,7 +157,7 @@ const DDR_CALCULATE_FLARE_SKILL: GPTNewProfileCalcs<"ddr:DP" | "ddr:SP"> = async
|
||||
playtype,
|
||||
userID,
|
||||
) => {
|
||||
const sc: Array<PBScoreDocumentWithSong> = await db["personal-bests"].aggregate([
|
||||
const sc: Array<PBScoreDocumentWithSong> = await MONGODB_KILL["personal-bests"].aggregate([
|
||||
{
|
||||
$match: {
|
||||
userID,
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingGitadoraChart } from "#test-utils/test-data";
|
||||
@@ -170,8 +170,8 @@ for (const [playtype, impl] of [
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkFakePBIIDXSP, mkFakeScoreIIDXSP } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -399,7 +399,7 @@ t.test("IIDX Implementation", (t) => {
|
||||
|
||||
for (const gptStr of ["iidx:SP", "iidx:DP"] as const) {
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(
|
||||
mkFakeScoreIIDXSP({
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -428,7 +428,7 @@ t.test("IIDX Implementation", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should join best BP", async (t) => {
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.insert([
|
||||
mkFakeScoreIIDXSP({
|
||||
scoreID: "whateverBP",
|
||||
scoreData: { optional: { bp: 100 } },
|
||||
+5
-5
@@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -204,8 +204,8 @@ t.test("Jubeat Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -234,8 +234,8 @@ t.test("Jubeat Implementation", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should join best score", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestScore",
|
||||
scoreData: {
|
||||
@@ -3,7 +3,7 @@ import type { GPTServerImplementation } from "#game-implementations/types";
|
||||
import { CreatePBMergeFor } from "#game-implementations/utils/pb-merge";
|
||||
import { ProfileSumBestN } from "#game-implementations/utils/profile-calc";
|
||||
import { SessionAvgBest10For } from "#game-implementations/utils/session-calc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { IsNullish } from "#utils/misc";
|
||||
import { p } from "prudence";
|
||||
import { Jubility } from "rg-stats";
|
||||
@@ -27,7 +27,7 @@ async function GetBestJubilityOnSongs(
|
||||
playtype: Playtype,
|
||||
limit: integer,
|
||||
): Promise<Array<PBScoreDocument>> {
|
||||
const r: Array<{ doc: PBScoreDocument }> = await db["personal-bests"].aggregate([
|
||||
const r: Array<{ doc: PBScoreDocument }> = await MONGODB_KILL["personal-bests"].aggregate([
|
||||
{
|
||||
$match: {
|
||||
game,
|
||||
@@ -107,14 +107,14 @@ async function GetBestJubilityOnSongs(
|
||||
const CURRENT_JUBEAT_HOT_VERSION: Versions["jubeat:Single"] = "ave";
|
||||
|
||||
export async function GetPBsForJubility(userID: integer) {
|
||||
const hotSongs = await db.songs.jubeat.find(
|
||||
const hotSongs = await MONGODB_KILL.songs.jubeat.find(
|
||||
{ "data.displayVersion": CURRENT_JUBEAT_HOT_VERSION },
|
||||
{ projection: { id: 1 } },
|
||||
);
|
||||
|
||||
const hotSongIDs = hotSongs.map((e) => e.id);
|
||||
|
||||
const coldSongs = await db.songs.jubeat.find(
|
||||
const coldSongs = await MONGODB_KILL.songs.jubeat.find(
|
||||
{ "data.displayVersion": { $ne: CURRENT_JUBEAT_HOT_VERSION } },
|
||||
{ projection: { id: 1 } },
|
||||
);
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import type { integer, ProvidedMetrics, ScoreData } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -167,8 +167,8 @@ t.test("maimai Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+3
-3
@@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingMaimaiDXChart } from "#test-utils/test-data";
|
||||
@@ -134,8 +134,8 @@ t.test("Maimai DX Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingMusecaChart } from "#test-utils/test-data";
|
||||
@@ -100,8 +100,8 @@ t.test("MUSECA Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+13
-13
@@ -2,7 +2,7 @@
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc, UpdateChartRanking } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingOngekiChart, TestingOngekiScorePB } from "#test-utils/test-data";
|
||||
@@ -136,7 +136,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
? { rating: 0, scoreRating: rating, starRating: 0 }
|
||||
: { rating: 0, scoreRating: 0, starRating: rating };
|
||||
|
||||
return db["personal-bests"].insert({
|
||||
return MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
chartID: `TEST${field}${idx}`,
|
||||
calculatedData: {
|
||||
@@ -282,8 +282,8 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t: any) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -314,8 +314,8 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
});
|
||||
|
||||
t.test("Should join platinum score", async (t: any) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestPlatinum",
|
||||
scoreData: {
|
||||
@@ -348,7 +348,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should tiebreak according to platinum score", async (t: any) => {
|
||||
await db["personal-bests"].insert({
|
||||
await MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
scoreData: {
|
||||
...TestingOngekiScorePB.scoreData,
|
||||
@@ -358,7 +358,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
await db["personal-bests"].insert({
|
||||
await MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
scoreData: {
|
||||
...TestingOngekiScorePB.scoreData,
|
||||
@@ -368,7 +368,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
userID: 2,
|
||||
});
|
||||
|
||||
await db["personal-bests"].insert({
|
||||
await MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
scoreData: {
|
||||
...TestingOngekiScorePB.scoreData,
|
||||
@@ -380,7 +380,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
|
||||
await UpdateChartRanking("ongeki", "Single", TestingOngekiChart.chartID);
|
||||
|
||||
const pbs = (await db["personal-bests"].find({
|
||||
const pbs = (await MONGODB_KILL["personal-bests"].find({
|
||||
chartID: TestingOngekiChart.chartID,
|
||||
})) as Array<PBScoreDocument<"ongeki:Single">>;
|
||||
|
||||
@@ -401,7 +401,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
});
|
||||
|
||||
t.test("Should not tiebreak if tech scores differ", async (t: any) => {
|
||||
await db["personal-bests"].insert({
|
||||
await MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
scoreData: {
|
||||
...TestingOngekiScorePB.scoreData,
|
||||
@@ -411,7 +411,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
await db["personal-bests"].insert({
|
||||
await MONGODB_KILL["personal-bests"].insert({
|
||||
...TestingOngekiScorePB,
|
||||
scoreData: {
|
||||
...TestingOngekiScorePB.scoreData,
|
||||
@@ -423,7 +423,7 @@ t.test("ONGEKI Implementation", (t: any) => {
|
||||
|
||||
await UpdateChartRanking("ongeki", "Single", TestingOngekiChart.chartID);
|
||||
|
||||
const [pb1, pb2] = (await db["personal-bests"].find(
|
||||
const [pb1, pb2] = (await MONGODB_KILL["personal-bests"].find(
|
||||
{
|
||||
chartID: TestingOngekiChart.chartID,
|
||||
},
|
||||
+3
-3
@@ -2,7 +2,7 @@ import type { ProvidedMetrics, ScoreData } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingPopnChart } from "#test-utils/test-data";
|
||||
@@ -159,8 +159,8 @@ t.test("Pop'n Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best clear medal", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestClearMedal",
|
||||
scoreData: {
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -172,8 +172,8 @@ t.test("SDVX Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -197,8 +197,8 @@ t.test("SDVX Implementation", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should join best EX SCore", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingUSCChart } from "#test-utils/test-data";
|
||||
@@ -166,8 +166,8 @@ for (const [playtype, impl] of [
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
+3
-3
@@ -3,7 +3,7 @@ import type { ProvidedMetrics, ScoreData, ScoreDocument } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestSnapshot } from "#test-utils/single-process-snapshot";
|
||||
@@ -144,8 +144,8 @@ t.test("WACCA Implementation", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
await MONGODB_KILL.scores.insert(mockScore);
|
||||
await MONGODB_KILL.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
} from "tachi-common";
|
||||
import type { ExtractEnumMetricNames } from "tachi-common/types/metrics";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
// insane typemagic to get mongodb-safe names for this GPT's metrics.
|
||||
type MetricKeys<GPT extends GPTString> = Exclude<
|
||||
@@ -55,7 +55,7 @@ export function CreatePBMergeFor<GPT extends GPTString>(
|
||||
applicator: (base: PBScoreDocumentNoRank<GPT>, score: ScoreDocument<GPT>) => void,
|
||||
): PBMergeFunction<GPT> {
|
||||
return async (userID, chartID, asOfTimestamp, base) => {
|
||||
const bestScoreFor = (await db.scores.findOne(
|
||||
const bestScoreFor = (await MONGODB_KILL.scores.findOne(
|
||||
HandleAsOf(
|
||||
{
|
||||
userID,
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
ScoreRatingAlgorithms,
|
||||
} from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
/**
|
||||
* Curries a function that returns the sum of N best ratings on `key`.
|
||||
@@ -29,7 +29,7 @@ function CalcN<GPT extends GPTString>(
|
||||
multiplier = 1,
|
||||
) {
|
||||
return async (game: GameGroup, playtype: Playtype, userID: integer) => {
|
||||
const sc = await db["personal-bests"].find(
|
||||
const sc = await MONGODB_KILL["personal-bests"].find(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
@@ -100,7 +100,7 @@ export async function GetBestRatingOnSongs(
|
||||
ratingProp: "skill",
|
||||
limit: integer,
|
||||
): Promise<Array<PBScoreDocument>> {
|
||||
const r: Array<{ doc: PBScoreDocument }> = await db["personal-bests"].aggregate([
|
||||
const r: Array<{ doc: PBScoreDocument }> = await MONGODB_KILL["personal-bests"].aggregate([
|
||||
{
|
||||
$match: {
|
||||
game,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import db from "#services/pg/db";
|
||||
import DB from "#services/pg/db";
|
||||
import { type ActionTaker, type AnonActionTaker, MakeActionGuts } from "bliss";
|
||||
import { type ActionSignature } from "bliss/actions";
|
||||
import { zodPermission } from "tachi-common";
|
||||
import { z } from "zod";
|
||||
|
||||
const APP_NAME = "TACHI_SERVER";
|
||||
@@ -13,6 +14,29 @@ export const ActionSignatures = {
|
||||
input: z.object({}),
|
||||
output: z.object({}),
|
||||
},
|
||||
INSTALL_BUILTIN_CLIENT: {
|
||||
input: z.object({
|
||||
clientID: z.string(),
|
||||
name: z.string(),
|
||||
webhookUri: z.url().nullable(),
|
||||
redirectUri: z.url().nullable(),
|
||||
permissions: z
|
||||
.object({
|
||||
customise_profile: z.boolean(),
|
||||
customise_score: z.boolean(),
|
||||
customise_session: z.boolean(),
|
||||
delete_score: z.boolean(),
|
||||
manage_rivals: z.boolean(),
|
||||
manage_targets: z.boolean(),
|
||||
submit_score: z.boolean(),
|
||||
manage_challenges: z.boolean(),
|
||||
})
|
||||
.partial(),
|
||||
apiKeyTemplate: z.string().nullable(),
|
||||
apiKeyFilename: z.string().nullable(),
|
||||
}),
|
||||
output: z.object({}),
|
||||
},
|
||||
} satisfies Record<string, ActionSignature>;
|
||||
|
||||
export const AnonActionSignatures = {
|
||||
@@ -30,7 +54,7 @@ export function MakeAnonAction<A extends AnonActionName>(
|
||||
fn: AnonActionFn<A>,
|
||||
): AnonActionFn<A> {
|
||||
return MakeActionGuts({
|
||||
db,
|
||||
db: DB,
|
||||
appName: APP_NAME,
|
||||
kind,
|
||||
// @ts-expect-error we're being creative with the types here
|
||||
@@ -44,7 +68,7 @@ export function MakeAnonAction<A extends AnonActionName>(
|
||||
*/
|
||||
export function MakeAction<A extends ActionName>(kind: A, fn: ActionFn<A>): ActionFn<A> {
|
||||
return MakeActionGuts({
|
||||
db,
|
||||
db: DB,
|
||||
appName: APP_NAME,
|
||||
kind,
|
||||
// @ts-expect-error we're being creative with the types here
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
UserDocument,
|
||||
} from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import {
|
||||
GetRecentlyAchievedGoals,
|
||||
GetRecentlyAchievedQuests,
|
||||
@@ -79,7 +79,7 @@ export async function GetRecentActivity(
|
||||
timeStarted: { $lt: startFrom === null ? Infinity : startFrom },
|
||||
};
|
||||
|
||||
const recentSessions = await db.sessions.find(initialSessionQuery, {
|
||||
const recentSessions = await MONGODB_KILL.sessions.find(initialSessionQuery, {
|
||||
sort: {
|
||||
timeStarted: -1,
|
||||
},
|
||||
@@ -108,7 +108,7 @@ export async function GetRecentActivity(
|
||||
{ goals, goalSubs },
|
||||
{ quests, questSubs },
|
||||
] = await Promise.all([
|
||||
db["class-achievements"].find(
|
||||
MONGODB_KILL["class-achievements"].find(
|
||||
{
|
||||
...baseQuery,
|
||||
timeAchieved: timeConstraint,
|
||||
@@ -119,7 +119,7 @@ export async function GetRecentActivity(
|
||||
},
|
||||
},
|
||||
),
|
||||
db.scores.find(
|
||||
MONGODB_KILL.scores.find(
|
||||
{
|
||||
...baseQuery,
|
||||
highlight: true,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import type { PrudenceError } from "prudence";
|
||||
|
||||
import { ACTION_InstallBuiltinClient } from "#actions/install-builtin-client.js";
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { ServerConfig, TachiConfig } from "#lib/setup/config";
|
||||
import { type TachiAPIClientDocument, UserAuthLevels } from "tachi-common";
|
||||
import _ from "lodash";
|
||||
import { type TachiAPIClientDocument } from "tachi-common";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
import { DatabaseSchemas } from "#services/mongo/schemas";
|
||||
import { Random20Hex } from "#utils/misc";
|
||||
import { FormatPrError } from "#utils/prudence";
|
||||
import { GetClientByID } from "#utils/queries/api-clients.js";
|
||||
import { GetFirstAdmin } from "#utils/user.js";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
|
||||
type DefaultClients = Array<Omit<TachiAPIClientDocument, "author" | "clientSecret">>;
|
||||
@@ -307,56 +305,48 @@ export async function LoadDefaultClients() {
|
||||
}
|
||||
|
||||
async function LoadClients(clients: DefaultClients) {
|
||||
const firstAdmin = await db.users.findOne({
|
||||
authLevel: UserAuthLevels.ADMIN,
|
||||
});
|
||||
|
||||
if (!firstAdmin) {
|
||||
log.warn(`There are no admins on this instance of tachi-server. We cannot create default API Clients!
|
||||
Chances are, you're seeing this message because you just bootstrapped Tachi.
|
||||
You'll need to set a user's authLevel to ${UserAuthLevels.ADMIN}.
|
||||
If you have no users, go create an account using the frontend, then run pnpm make-user-admin 1.`);
|
||||
return;
|
||||
}
|
||||
const firstAdmin = await GetFirstAdmin();
|
||||
|
||||
for (const client of clients) {
|
||||
const exists = await db["api-clients"].findOne(
|
||||
const exists = await GetClientByID(client.clientID);
|
||||
|
||||
if (exists) {
|
||||
const existsOmitted = _.omit(exists, "author", "clientSecret");
|
||||
|
||||
// Skip if nothing has changed.
|
||||
if (fjsh.hash(existsOmitted, "sha256") === fjsh.hash(client, "sha256")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await ACTION_InstallBuiltinClient(
|
||||
{
|
||||
clientID: client.clientID,
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
clientSecret: 0,
|
||||
author: 0,
|
||||
ip: null,
|
||||
acct: {
|
||||
id: firstAdmin.id,
|
||||
username: firstAdmin.username,
|
||||
},
|
||||
},
|
||||
{
|
||||
clientID: client.clientID,
|
||||
name: client.name,
|
||||
permissions: {
|
||||
customise_profile: false,
|
||||
customise_score: false,
|
||||
customise_session: false,
|
||||
delete_score: false,
|
||||
manage_rivals: false,
|
||||
manage_targets: false,
|
||||
submit_score: false,
|
||||
manage_challenges: false,
|
||||
},
|
||||
apiKeyFilename: client.apiKeyFilename,
|
||||
apiKeyTemplate: client.apiKeyTemplate,
|
||||
redirectUri: client.redirectUri,
|
||||
webhookUri: client.webhookUri,
|
||||
},
|
||||
);
|
||||
|
||||
// Skip if nothing has changed.
|
||||
if (fjsh.hash(exists, "sha256") === fjsh.hash(client, "sha256")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const realClient: TachiAPIClientDocument = {
|
||||
...client,
|
||||
clientSecret: `CS${Random20Hex()}`,
|
||||
author: 1,
|
||||
};
|
||||
|
||||
try {
|
||||
DatabaseSchemas["api-clients"](realClient);
|
||||
} catch (err) {
|
||||
log.error(`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// No replaceOne support in monk -- have to do this.
|
||||
await db["api-clients"].remove({
|
||||
clientID: client.clientID,
|
||||
});
|
||||
|
||||
await db["api-clients"].insert(realClient);
|
||||
|
||||
log.info(`Loaded/Modified new built-in client ${client.name}.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { type Selection } from "kysely";
|
||||
import { type APIPermissions, type TachiAPIClientDocument } from "tachi-common";
|
||||
import { type Database } from "tachi-db";
|
||||
|
||||
export const SELECT_API_CLIENT = [
|
||||
"priv_api_client.client_id",
|
||||
"priv_api_client.client_secret",
|
||||
"priv_api_client.name",
|
||||
"priv_api_client.author",
|
||||
"priv_api_client.pm_customise_profile",
|
||||
"priv_api_client.pm_customise_score",
|
||||
"priv_api_client.pm_customise_session",
|
||||
"priv_api_client.pm_delete_score",
|
||||
"priv_api_client.pm_manage_rivals",
|
||||
"priv_api_client.pm_manage_targets",
|
||||
"priv_api_client.pm_submit_score",
|
||||
"priv_api_client.pm_manage_challenges",
|
||||
"priv_api_client.api_key_filename",
|
||||
"priv_api_client.api_key_template",
|
||||
"priv_api_client.webhook_uri",
|
||||
"priv_api_client.redirect_uri",
|
||||
] as const;
|
||||
|
||||
export function ToAPIClientDocument(
|
||||
row: Selection<Database, "priv_api_client", (typeof SELECT_API_CLIENT)[number]>,
|
||||
): TachiAPIClientDocument {
|
||||
const requestedPermissions: Array<APIPermissions> = [];
|
||||
if (row.pm_customise_profile) {
|
||||
requestedPermissions.push("customise_profile");
|
||||
}
|
||||
if (row.pm_customise_score) {
|
||||
requestedPermissions.push("customise_score");
|
||||
}
|
||||
if (row.pm_customise_session) {
|
||||
requestedPermissions.push("customise_session");
|
||||
}
|
||||
if (row.pm_delete_score) {
|
||||
requestedPermissions.push("delete_score");
|
||||
}
|
||||
if (row.pm_manage_rivals) {
|
||||
requestedPermissions.push("manage_rivals");
|
||||
}
|
||||
if (row.pm_manage_targets) {
|
||||
requestedPermissions.push("manage_targets");
|
||||
}
|
||||
if (row.pm_submit_score) {
|
||||
requestedPermissions.push("submit_score");
|
||||
}
|
||||
if (row.pm_manage_challenges) {
|
||||
requestedPermissions.push("manage_challenges");
|
||||
}
|
||||
|
||||
return {
|
||||
clientID: row.client_id,
|
||||
clientSecret: row.client_secret,
|
||||
name: row.name,
|
||||
author: row.author,
|
||||
apiKeyFilename: row.api_key_filename,
|
||||
apiKeyTemplate: row.api_key_template,
|
||||
redirectUri: row.redirect_uri,
|
||||
requestedPermissions,
|
||||
webhookUri: row.webhook_uri,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { type Selection } from "kysely";
|
||||
import { type integer, type UserSettingsDocument } from "tachi-common";
|
||||
import { type Database } from "tachi-db";
|
||||
|
||||
export const SELECT_USER_SETTINGS = [
|
||||
"account_settings.user_id",
|
||||
"account_settings.pf_invisible",
|
||||
"account_settings.pf_developer_mode",
|
||||
"account_settings.pf_advanced_mode",
|
||||
"account_settings.pf_contentious_content",
|
||||
"account_settings.pf_deletable_scores",
|
||||
] as const;
|
||||
|
||||
export function ToUserSettingsDocument(
|
||||
following: Array<integer>,
|
||||
row: Selection<Database, "account_settings", (typeof SELECT_USER_SETTINGS)[number]>,
|
||||
): UserSettingsDocument {
|
||||
return {
|
||||
userID: row.user_id,
|
||||
following,
|
||||
preferences: {
|
||||
invisible: row.pf_invisible,
|
||||
developerMode: row.pf_developer_mode,
|
||||
advancedMode: row.pf_advanced_mode,
|
||||
contentiousContent: row.pf_contentious_content,
|
||||
deletableScores: row.pf_deletable_scores,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { AuthLevelToInt } from "#utils/conversion.js";
|
||||
import { ISO8601ToUnixMilliseconds } from "#utils/time.js";
|
||||
import { type Selection } from "kysely";
|
||||
import { type UserBadges, type UserDocument } from "tachi-common";
|
||||
import { type Database } from "tachi-db";
|
||||
|
||||
export const SELECT_USER = [
|
||||
"account.id",
|
||||
"account.bd_alpha",
|
||||
"account.bd_beta",
|
||||
"account.bd_dev_team",
|
||||
"account.about",
|
||||
"account.auth_level",
|
||||
"account.custom_banner_location",
|
||||
"account.custom_pfp_location",
|
||||
"account.joined",
|
||||
"account.last_seen",
|
||||
"account.sm_discord",
|
||||
"account.sm_twitter",
|
||||
"account.sm_github",
|
||||
"account.sm_steam",
|
||||
"account.sm_youtube",
|
||||
"account.sm_twitch",
|
||||
"account.status",
|
||||
"account.username",
|
||||
"account.normalized_username",
|
||||
"account.is_supporter",
|
||||
] as const;
|
||||
|
||||
export function ToUserDocument(
|
||||
row: Selection<Database, "account", (typeof SELECT_USER)[number]>,
|
||||
): UserDocument {
|
||||
const badges: Array<UserBadges> = [];
|
||||
|
||||
if (row.bd_alpha) {
|
||||
badges.push("alpha");
|
||||
}
|
||||
if (row.bd_beta) {
|
||||
badges.push("beta");
|
||||
}
|
||||
if (row.bd_dev_team) {
|
||||
badges.push("dev-team");
|
||||
}
|
||||
|
||||
return {
|
||||
about: row.about,
|
||||
authLevel: AuthLevelToInt(row.auth_level),
|
||||
badges,
|
||||
customBannerLocation: row.custom_banner_location,
|
||||
customPfpLocation: row.custom_pfp_location,
|
||||
id: row.id,
|
||||
joinDate: ISO8601ToUnixMilliseconds(row.joined),
|
||||
lastSeen: ISO8601ToUnixMilliseconds(row.last_seen),
|
||||
socialMedia: {
|
||||
discord: row.sm_discord,
|
||||
twitter: row.sm_twitter,
|
||||
github: row.sm_github,
|
||||
steam: row.sm_steam,
|
||||
youtube: row.sm_youtube,
|
||||
twitch: row.sm_twitch,
|
||||
},
|
||||
status: row.status,
|
||||
username: row.username,
|
||||
usernameLowercase: row.normalized_username,
|
||||
isSupporter: row.is_supporter,
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import t from "tap";
|
||||
|
||||
@@ -10,7 +10,7 @@ t.test("#CreateGameSettings", (t) => {
|
||||
t.test("Should create a new user's settings.", async (t) => {
|
||||
await CreateGameSettings(1, "bms", "7K");
|
||||
|
||||
const data = await db["game-settings"].findOne({
|
||||
const data = await MONGODB_KILL["game-settings"].findOne({
|
||||
userID: 1,
|
||||
game: "bms",
|
||||
playtype: "7K",
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { GameGroup, integer, Playtype } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
/**
|
||||
* Create GameSettings for a UGPT (which contains their preferences).
|
||||
*/
|
||||
export async function CreateGameSettings(userID: integer, game: GameGroup, playtype: Playtype) {
|
||||
const exists = await db["game-settings"].findOne({
|
||||
const exists = await MONGODB_KILL["game-settings"].findOne({
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
@@ -32,7 +32,7 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt
|
||||
};
|
||||
}
|
||||
|
||||
await db["game-settings"].insert({
|
||||
await MONGODB_KILL["game-settings"].insert({
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ChartDocument, integer, Playtypes } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { PoyashiBPI } from "rg-stats";
|
||||
|
||||
interface PlaylistEntry {
|
||||
@@ -110,7 +110,7 @@ export const CUSTOM_TACHI_IIDX_PLAYLISTS: Array<TachiIIDXPlaylist> = [
|
||||
bounds.push([cutoffs[i], cutoffs[i + 1]] as [number, number]);
|
||||
}
|
||||
|
||||
const charts = (await db.charts.iidx.find({
|
||||
const charts = (await MONGODB_KILL.charts.iidx.find({
|
||||
"data.kaidenAverage": { $ne: null },
|
||||
playtype,
|
||||
})) as Array<ChartDocument<"iidx:DP" | "iidx:SP">>;
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf, mkFakeImport } from "#test-utils/misc";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingIIDXSPScore } from "#test-utils/test-data";
|
||||
@@ -9,7 +9,7 @@ import { RevertImport } from "./imports";
|
||||
t.test("#RevertImport", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(() =>
|
||||
db.scores.insert([
|
||||
MONGODB_KILL.scores.insert([
|
||||
dmf(TestingIIDXSPScore, { scoreID: "score_1" }),
|
||||
dmf(TestingIIDXSPScore, { scoreID: "score_2" }),
|
||||
dmf(TestingIIDXSPScore, { scoreID: "score_3" }),
|
||||
@@ -21,16 +21,16 @@ t.test("#RevertImport", (t) => {
|
||||
scoreIDs: ["score_1", "score_2"],
|
||||
});
|
||||
|
||||
await db.imports.insert(importDoc);
|
||||
await MONGODB_KILL.imports.insert(importDoc);
|
||||
|
||||
await RevertImport(importDoc);
|
||||
|
||||
const dbRes = await db.imports.findOne({ importID: "fake_import" });
|
||||
const dbRes = await MONGODB_KILL.imports.findOne({ importID: "fake_import" });
|
||||
|
||||
t.equal(dbRes, null, "Should have removed the import from the DB.");
|
||||
|
||||
t.resolveMatch(
|
||||
db.scores.findOne({ userID: 1, scoreID: "score_1" }),
|
||||
MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_1" }),
|
||||
|
||||
// @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60020
|
||||
null,
|
||||
@@ -38,7 +38,7 @@ t.test("#RevertImport", (t) => {
|
||||
);
|
||||
|
||||
t.resolveMatch(
|
||||
db.scores.findOne({ userID: 1, scoreID: "score_2" }),
|
||||
MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_2" }),
|
||||
|
||||
// @ts-expect-error see above
|
||||
null,
|
||||
@@ -46,7 +46,7 @@ t.test("#RevertImport", (t) => {
|
||||
);
|
||||
|
||||
t.resolveMatch(
|
||||
db.scores.findOne({ userID: 1, scoreID: "score_3" }),
|
||||
MONGODB_KILL.scores.findOne({ userID: 1, scoreID: "score_3" }),
|
||||
|
||||
// @ts-expect-error see above
|
||||
{ scoreID: "score_3" },
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
UnsetOngoingImportLock,
|
||||
} from "#lib/score-import/framework/import-locks/lock";
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
interface OngoingImportError {
|
||||
tag: "ONGOING_IMPORT";
|
||||
@@ -43,7 +43,7 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
|
||||
);
|
||||
|
||||
try {
|
||||
await db.imports.remove({ importID: importDoc.importID });
|
||||
await MONGODB_KILL.imports.remove({ importID: importDoc.importID });
|
||||
|
||||
log.info(`Reverted and deleted import '${importDoc.importID}'.`);
|
||||
} catch (err) {
|
||||
@@ -63,5 +63,5 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
|
||||
* Retrieve the scores inside this import.
|
||||
*/
|
||||
export function GetImportScores(importDoc: ImportDocument) {
|
||||
return db.scores.find({ scoreID: { $in: importDoc.scoreIDs } });
|
||||
return MONGODB_KILL.scores.find({ scoreID: { $in: importDoc.scoreIDs } });
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { PullDatabaseSeeds } from "#lib/seeds/repo";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@ export async function BacksyncBMSPMSSongsAndCharts() {
|
||||
log.info(`Fetching ${game} songs from DB.`);
|
||||
|
||||
// did you know, this code is liable to blow up in my face and OOM one day?
|
||||
let songs = await db.anySongs[game].find({});
|
||||
let songs = await MONGODB_KILL.anySongs[game].find({});
|
||||
|
||||
log.info(`Found ${songs.length} ${game} songs.`);
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function BacksyncBMSPMSSongsAndCharts() {
|
||||
songs = null;
|
||||
|
||||
log.info(`Fetching ${game} charts from DB.`);
|
||||
let charts = await db.anyCharts[game].find({});
|
||||
let charts = await MONGODB_KILL.anyCharts[game].find({});
|
||||
|
||||
log.info(`Found ${charts.length} ${game} charts.`);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FilterQuery } from "mongodb";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { DeorphanIfInQueue } from "#lib/orphan-queue/orphan-queue";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { InitaliseFolderChartLookup } from "#utils/folder";
|
||||
import { FormatBMSTables, WrapScriptPromise } from "#utils/misc";
|
||||
import { type BMSTableEntry, LoadBMSTable } from "bms-table-loader";
|
||||
@@ -34,7 +34,7 @@ async function HandleTableRemovals(
|
||||
|
||||
// *unless this script crashes, in which case it's
|
||||
// no longer temporary
|
||||
const existingCharts = (await db.charts.bms.find({
|
||||
const existingCharts = (await MONGODB_KILL.charts.bms.find({
|
||||
playtype,
|
||||
"data.tableFolders.table": prefix,
|
||||
})) as unknown as Array<ChartDocument<"bms:7K" | "bms:14K">>;
|
||||
@@ -85,7 +85,7 @@ async function HandleTableRemovals(
|
||||
|
||||
// remove this table info from all of the charts that no longer
|
||||
// exist in the table.
|
||||
await db.charts.bms.update(
|
||||
await MONGODB_KILL.charts.bms.update(
|
||||
{
|
||||
chartID: { $in: toRemove },
|
||||
},
|
||||
@@ -115,7 +115,7 @@ async function ImportTableLevels(
|
||||
.filter((e) => e.checksum.type === "md5")
|
||||
.map((e) => e.checksum.value);
|
||||
|
||||
await db.charts.bms.update(
|
||||
await MONGODB_KILL.charts.bms.update(
|
||||
{
|
||||
"data.hashMD5": { $in: md5s },
|
||||
},
|
||||
@@ -127,7 +127,7 @@ async function ImportTableLevels(
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db.charts.bms.update(
|
||||
await MONGODB_KILL.charts.bms.update(
|
||||
{
|
||||
"data.hashSHA256": { $in: sha256s },
|
||||
},
|
||||
@@ -154,7 +154,8 @@ async function ImportTableLevels(
|
||||
}
|
||||
}
|
||||
|
||||
let chart: ChartDocument<"bms:7K" | "bms:14K"> | null = await db.charts.bms.findOne(query);
|
||||
let chart: ChartDocument<"bms:7K" | "bms:14K"> | null =
|
||||
await MONGODB_KILL.charts.bms.findOne(query);
|
||||
|
||||
if (!chart) {
|
||||
// didn't find it in the DB?
|
||||
@@ -195,7 +196,7 @@ async function ImportTableLevels(
|
||||
return a.level.localeCompare(b.level);
|
||||
});
|
||||
|
||||
await db.charts.bms.update(
|
||||
await MONGODB_KILL.charts.bms.update(
|
||||
{
|
||||
chartID: chart.chartID,
|
||||
},
|
||||
@@ -206,7 +207,7 @@ async function ImportTableLevels(
|
||||
},
|
||||
);
|
||||
|
||||
await db.songs.bms.update(
|
||||
await MONGODB_KILL.songs.bms.update(
|
||||
{
|
||||
id: chart.songID,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UserGameStats, UserGameStatsSnapshotDocument } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetMillisecondsSince } from "#utils/misc";
|
||||
import { GetAllRankings } from "#utils/user";
|
||||
|
||||
@@ -16,7 +16,9 @@ let batchWrite: Array<UserGameStatsSnapshotDocument> = [];
|
||||
export async function UGSSnapshot() {
|
||||
const timeStart = process.hrtime.bigint();
|
||||
|
||||
const alreadyExists = await db["game-stats-snapshots"].findOne({ timestamp: currentTime });
|
||||
const alreadyExists = await MONGODB_KILL["game-stats-snapshots"].findOne({
|
||||
timestamp: currentTime,
|
||||
});
|
||||
|
||||
if (alreadyExists) {
|
||||
log.warn(
|
||||
@@ -31,7 +33,7 @@ export async function UGSSnapshot() {
|
||||
log.info(`Snapshotting UserGameStats.`);
|
||||
|
||||
try {
|
||||
await db["game-stats"]
|
||||
await MONGODB_KILL["game-stats"]
|
||||
.find({})
|
||||
|
||||
// @ts-expect-error faulty TS types
|
||||
@@ -41,7 +43,11 @@ export async function UGSSnapshot() {
|
||||
log.debug(`Snapshotting ${ugs.userID} ${ugs.playtype} ${ugs.game}.`);
|
||||
|
||||
const [playcount, rankings] = await Promise.all([
|
||||
db.scores.count({ userID: ugs.userID, playtype: ugs.playtype, game: ugs.game }),
|
||||
MONGODB_KILL.scores.count({
|
||||
userID: ugs.userID,
|
||||
playtype: ugs.playtype,
|
||||
game: ugs.game,
|
||||
}),
|
||||
GetAllRankings(ugs),
|
||||
]);
|
||||
|
||||
@@ -56,7 +62,7 @@ export async function UGSSnapshot() {
|
||||
|
||||
if (batchWrite.length >= 500) {
|
||||
log.debug(`Flushed batch.`);
|
||||
await db["game-stats-snapshots"].insert(batchWrite);
|
||||
await MONGODB_KILL["game-stats-snapshots"].insert(batchWrite);
|
||||
|
||||
batchWrite = [];
|
||||
}
|
||||
@@ -65,7 +71,7 @@ export async function UGSSnapshot() {
|
||||
});
|
||||
|
||||
if (batchWrite.length) {
|
||||
await db["game-stats-snapshots"].insert(batchWrite);
|
||||
await MONGODB_KILL["game-stats-snapshots"].insert(batchWrite);
|
||||
}
|
||||
|
||||
log.info(
|
||||
@@ -84,7 +90,7 @@ export async function UGSSnapshot() {
|
||||
|
||||
log.info(`Removing all snapshots at this timestamp (${currentTime}).`);
|
||||
|
||||
await db["game-stats-snapshots"].remove({ timestamp: currentTime });
|
||||
await MONGODB_KILL["game-stats-snapshots"].remove({ timestamp: currentTime });
|
||||
|
||||
log.info(`Removed.`);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { BacksyncCollection, PullDatabaseSeeds } from "#lib/seeds/repo";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
import fetch from "#utils/fetch";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
@@ -173,7 +173,7 @@ export async function UpdatePoyashiData() {
|
||||
|
||||
await repo.Destroy();
|
||||
|
||||
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update BPI Data");
|
||||
await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update BPI Data");
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { BacksyncCollection } from "#lib/seeds/repo";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
import fetch from "#utils/fetch";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
@@ -56,7 +56,7 @@ export async function UpdateDPTiers() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = await db.charts.iidx.update(
|
||||
const result = await MONGODB_KILL.charts.iidx.update(
|
||||
{
|
||||
versions: "29",
|
||||
playtype: "DP",
|
||||
@@ -92,7 +92,7 @@ export async function UpdateDPTiers() {
|
||||
|
||||
log.info(`Recalced those scores.`);
|
||||
|
||||
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update DP Tierlist");
|
||||
await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update DP Tierlist");
|
||||
}
|
||||
|
||||
log.info("Done.");
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ChartDocument, Difficulties, integer } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { BacksyncCollection } from "#lib/seeds/repo";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
import { FindChartWithPTDF } from "#utils/queries/charts";
|
||||
@@ -172,7 +172,7 @@ async function FetchSP12Data() {
|
||||
|
||||
updatedChartIDs.push(chart.chartID);
|
||||
|
||||
await db.charts.iidx.update(
|
||||
await MONGODB_KILL.charts.iidx.update(
|
||||
{
|
||||
chartID: chart.chartID,
|
||||
},
|
||||
@@ -203,7 +203,7 @@ async function FetchSP12Data() {
|
||||
|
||||
log.info(`Finished recalcing scores.`);
|
||||
|
||||
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update SP12 Tierlist");
|
||||
await BacksyncCollection("charts-iidx", MONGODB_KILL.charts.iidx, "Update SP12 Tierlist");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import {
|
||||
FormatGameGroup,
|
||||
type GameGroup,
|
||||
@@ -23,7 +23,7 @@ export async function SendSetRivalNotification(
|
||||
game: GameGroup,
|
||||
playtype: Playtype,
|
||||
) {
|
||||
const alreadyBeenPinged = await db.notifications.findOne({
|
||||
const alreadyBeenPinged = await MONGODB_KILL.notifications.findOne({
|
||||
sentTo: toUserID,
|
||||
"body.type": "RIVALED_BY",
|
||||
"body.content": {
|
||||
@@ -60,13 +60,13 @@ export async function SendSiteAnnouncementNotification(
|
||||
|
||||
if (maybeGame && maybePlaytype) {
|
||||
toUserIDs = (
|
||||
await db["game-stats"].find(
|
||||
await MONGODB_KILL["game-stats"].find(
|
||||
{ game: maybeGame, playtype: maybePlaytype },
|
||||
{ projection: { userID: 1 } },
|
||||
)
|
||||
).map((e) => e.userID);
|
||||
} else {
|
||||
toUserIDs = (await db.users.find({}, { projection: { id: 1 } })).map((e) => e.id);
|
||||
toUserIDs = (await MONGODB_KILL.users.find({}, { projection: { id: 1 } })).map((e) => e.id);
|
||||
}
|
||||
|
||||
return BulkSendNotification(title, toUserIDs, {
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import t from "tap";
|
||||
|
||||
@@ -13,7 +13,7 @@ t.test("#SendNotification", (t) => {
|
||||
content: { questID: "foo", game: "iidx", playtype: "SP" },
|
||||
});
|
||||
|
||||
const dbRes = await db.notifications.findOne({
|
||||
const dbRes = await MONGODB_KILL.notifications.findOne({
|
||||
sentTo: 1,
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ t.test("#BulkSendNotification", (t) => {
|
||||
content: { questID: "foo", game: "iidx", playtype: "SP" },
|
||||
});
|
||||
|
||||
const dbRes = await db.notifications.findOne({
|
||||
const dbRes = await MONGODB_KILL.notifications.findOne({
|
||||
sentTo: 1,
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ t.test("#BulkSendNotification", (t) => {
|
||||
},
|
||||
});
|
||||
|
||||
const dbRes2 = await db.notifications.findOne({
|
||||
const dbRes2 = await MONGODB_KILL.notifications.findOne({
|
||||
sentTo: 2,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { integer, NotificationBody, NotificationDocument } from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { Random20Hex } from "#utils/misc";
|
||||
|
||||
function ConstructNotificationDoc(
|
||||
@@ -28,7 +28,7 @@ function ConstructNotificationDoc(
|
||||
export function SendNotification(title: string, toUserID: integer, body: NotificationBody) {
|
||||
const notification = ConstructNotificationDoc(title, toUserID, body);
|
||||
|
||||
return db.notifications.insert(notification);
|
||||
return MONGODB_KILL.notifications.insert(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,5 +42,5 @@ export function BulkSendNotification(
|
||||
) {
|
||||
const notifications = toUserIDs.map((e) => ConstructNotificationDoc(title, e, body));
|
||||
|
||||
return db.notifications.insert(notifications);
|
||||
return MONGODB_KILL.notifications.insert(notifications);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
} from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetNextCounterValue } from "#utils/db";
|
||||
import { DedupeArr } from "#utils/misc";
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
) {
|
||||
log.debug(`Received orphanqueue request for ${chartName}.`);
|
||||
|
||||
const orphanChart = await db["orphan-chart-queue"].findOne(
|
||||
const orphanChart = await MONGODB_KILL["orphan-chart-queue"].findOne(
|
||||
{ gptString, ...orphanMatchCriteria },
|
||||
{
|
||||
projectID: true,
|
||||
@@ -47,7 +47,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
if (!orphanChart) {
|
||||
log.debug(`Received unknown chart ${chartName}, orphaning.`);
|
||||
|
||||
await db["orphan-chart-queue"].insert({
|
||||
await MONGODB_KILL["orphan-chart-queue"].insert({
|
||||
gptString,
|
||||
chartDoc,
|
||||
songDoc,
|
||||
@@ -77,10 +77,10 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
songDoc.id = songID;
|
||||
chartDoc.songID = songID;
|
||||
|
||||
await db.anySongs[game].insert(songDoc);
|
||||
await db.anyCharts[game].insert(chartDoc);
|
||||
await MONGODB_KILL.anySongs[game].insert(songDoc);
|
||||
await MONGODB_KILL.anyCharts[game].insert(chartDoc);
|
||||
|
||||
await db["orphan-chart-queue"].remove({
|
||||
await MONGODB_KILL["orphan-chart-queue"].remove({
|
||||
_id: orphanChart._id,
|
||||
});
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
// otherwise, update the state of this orphan.
|
||||
|
||||
log.debug(`UserID ${userID} played ${chartName}, which is now at ${playcount} plays.`);
|
||||
await db["orphan-chart-queue"].update(
|
||||
await MONGODB_KILL["orphan-chart-queue"].update(
|
||||
{
|
||||
_id: orphanChart._id,
|
||||
},
|
||||
@@ -115,7 +115,7 @@ export async function DeorphanIfInQueue<GPT extends GPTString>(
|
||||
game: GPTStringToGame[GPT],
|
||||
orphanMatchCriteria: FilterQuery<OrphanChartDocument<GPT>>,
|
||||
): Promise<ChartDocument<GPT> | null> {
|
||||
const orphanChart = await db["orphan-chart-queue"].findOne(
|
||||
const orphanChart = await MONGODB_KILL["orphan-chart-queue"].findOne(
|
||||
{ gptString, ...orphanMatchCriteria },
|
||||
{
|
||||
projectID: true,
|
||||
@@ -136,9 +136,9 @@ export async function DeorphanIfInQueue<GPT extends GPTString>(
|
||||
songDoc.id = songID;
|
||||
chartDoc.songID = songID;
|
||||
|
||||
await db.anySongs[game].insert(songDoc);
|
||||
await db.anyCharts[game].insert(chartDoc);
|
||||
await db["orphan-chart-queue"].remove({
|
||||
await MONGODB_KILL.anySongs[game].insert(songDoc);
|
||||
await MONGODB_KILL.anyCharts[game].insert(chartDoc);
|
||||
await MONGODB_KILL["orphan-chart-queue"].remove({
|
||||
_id: orphanChart._id,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { SetRivalsFailReasons } from "#lib/constants/err-codes";
|
||||
import { log } from "#lib/log/log.js";
|
||||
import { SendSetRivalNotification } from "#lib/notifications/notification-wrappers";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { ArrayDiff } from "#utils/misc";
|
||||
import { GetUsersWithIDs, GetUserWithIDGuaranteed } from "#utils/user";
|
||||
import {
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
* Throws if the user hasn't played the GPT in question.
|
||||
*/
|
||||
export async function GetRivalIDs(userID: integer, game: GameGroup, playtype: Playtype) {
|
||||
const gameSettings = await db["game-settings"].findOne(
|
||||
const gameSettings = await MONGODB_KILL["game-settings"].findOne(
|
||||
{
|
||||
userID,
|
||||
game,
|
||||
@@ -63,7 +63,7 @@ export async function GetEveryonesRivalIDs(
|
||||
game: GameGroup,
|
||||
playtype: Playtype,
|
||||
): Promise<Record<number, Array<number>>> {
|
||||
const allGameSettings = await db["game-settings"].find(
|
||||
const allGameSettings = await MONGODB_KILL["game-settings"].find(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
@@ -104,7 +104,7 @@ export async function SetRivals(
|
||||
return SetRivalsFailReasons.RIVALED_SELF;
|
||||
}
|
||||
|
||||
const playedGPTCount = await db["game-settings"].count({
|
||||
const playedGPTCount = await MONGODB_KILL["game-settings"].count({
|
||||
userID: { $in: newRivals },
|
||||
game,
|
||||
playtype,
|
||||
@@ -115,7 +115,7 @@ export async function SetRivals(
|
||||
return SetRivalsFailReasons.RIVALS_HAVENT_PLAYED_GPT;
|
||||
}
|
||||
|
||||
const currentGameSettings = await db["game-settings"].findOne({
|
||||
const currentGameSettings = await MONGODB_KILL["game-settings"].findOne({
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
@@ -145,7 +145,7 @@ export async function SetRivals(
|
||||
newSubs.map((toUserID) => SendSetRivalNotification(toUserID, user, game, playtype)),
|
||||
);
|
||||
|
||||
await db["game-settings"].update(
|
||||
await MONGODB_KILL["game-settings"].update(
|
||||
{
|
||||
userID,
|
||||
game,
|
||||
@@ -206,7 +206,7 @@ export async function RemoveRival(
|
||||
* Get all of the userIDs of people who rival the userID for this GPT.
|
||||
*/
|
||||
export async function GetChallengerIDs(userID: integer, game: GameGroup, playtype: Playtype) {
|
||||
const result = await db["game-settings"].find(
|
||||
const result = await MONGODB_KILL["game-settings"].find(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
@@ -252,7 +252,7 @@ export async function UpdatePlayersRivalRankings(
|
||||
const rivalIDs = await GetRivalIDs(userID, game, playtype);
|
||||
|
||||
// get all of this user's chartIDs so we know what to update
|
||||
const userPBs = (await db["personal-bests"].find(
|
||||
const userPBs = (await MONGODB_KILL["personal-bests"].find(
|
||||
{ userID, game, playtype },
|
||||
{ projection: { chartID: 1, [`scoreData.${gptConfig.defaultMetric}`]: 1 } },
|
||||
)) as Array<{ chartID: string; scoreData: { percent: number } }>;
|
||||
@@ -262,7 +262,7 @@ export async function UpdatePlayersRivalRankings(
|
||||
await Promise.all(
|
||||
userPBs.map(async (pb) => {
|
||||
const rivalRank =
|
||||
(await db["personal-bests"].count({
|
||||
(await MONGODB_KILL["personal-bests"].count({
|
||||
chartID: pb.chartID,
|
||||
userID: { $in: rivalIDs },
|
||||
[`scoreData.${gptConfig.defaultMetric}`]: { $gt: pb.scoreData.percent },
|
||||
@@ -285,5 +285,5 @@ export async function UpdatePlayersRivalRankings(
|
||||
return;
|
||||
}
|
||||
|
||||
await db["personal-bests"].bulkWrite(bwrite, { ordered: false });
|
||||
await MONGODB_KILL["personal-bests"].bulkWrite(bwrite, { ordered: false });
|
||||
}
|
||||
|
||||
+23
-18
@@ -1,7 +1,7 @@
|
||||
import type { GoalDocument, GoalSubscriptionDocument } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { dmf } from "#test-utils/misc.js";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import {
|
||||
@@ -51,10 +51,10 @@ t.test("#UpdateGoalsForUser", (t) => {
|
||||
};
|
||||
|
||||
t.test("Should correctly update goals when user achieves goal.", async (t) => {
|
||||
await db.goals.insert(baseGoalDocument);
|
||||
await MONGODB_KILL.goals.insert(baseGoalDocument);
|
||||
|
||||
await db["goal-subs"].insert(baseGoalSubscriptionDocument);
|
||||
await db["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
await MONGODB_KILL["goal-subs"].insert(baseGoalSubscriptionDocument);
|
||||
await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
|
||||
const ugMap = new Map([["FAKE_GOAL_ID", baseGoalSubscriptionDocument]]);
|
||||
|
||||
@@ -80,7 +80,7 @@ t.test("#UpdateGoalsForUser", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const r = await db["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 });
|
||||
const r = await MONGODB_KILL["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 });
|
||||
|
||||
t.hasStrict(
|
||||
r,
|
||||
@@ -100,19 +100,21 @@ t.test("#UpdateGoalsForUser", (t) => {
|
||||
t.test("Should correctly update goals when user does not achieve goal.", async (t) => {
|
||||
const goal = dmf(baseGoalDocument, { criteria: { value: 2 } });
|
||||
|
||||
await db.goals.insert(goal);
|
||||
await MONGODB_KILL.goals.insert(goal);
|
||||
|
||||
const goalSub = deepmerge(baseGoalSubscriptionDocument, {
|
||||
outOf: 2,
|
||||
outOfHuman: "2",
|
||||
}) as unknown as GoalSubscriptionDocument;
|
||||
|
||||
await db["goal-subs"].insert(goalSub);
|
||||
await MONGODB_KILL["goal-subs"].insert(goalSub);
|
||||
|
||||
// we dont delete _id here because updategoalsforuser
|
||||
// depends on usergoal _id
|
||||
|
||||
await db["personal-bests"].insert(dmf(TestingIIDXSPScorePB, { scoreData: { score: 1 } }));
|
||||
await MONGODB_KILL["personal-bests"].insert(
|
||||
dmf(TestingIIDXSPScorePB, { scoreData: { score: 1 } }),
|
||||
);
|
||||
|
||||
const ugMap = new Map([["FAKE_GOAL_ID", goalSub]]);
|
||||
|
||||
@@ -138,7 +140,7 @@ t.test("#UpdateGoalsForUser", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const r = await db["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 });
|
||||
const r = await MONGODB_KILL["goal-subs"].findOne({ goalID: "FAKE_GOAL_ID", userID: 1 });
|
||||
|
||||
t.hasStrict(
|
||||
r,
|
||||
@@ -196,14 +198,14 @@ t.todo("#GetAndUpdateUsersGoals", (t) => {
|
||||
t.test("#ProcessGoal", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(async () => {
|
||||
await db.goals.insert(HC511Goal);
|
||||
await MONGODB_KILL.goals.insert(HC511Goal);
|
||||
});
|
||||
|
||||
t.test("Should process the users goal if a score has changed.", async (t) => {
|
||||
await db["goal-subs"].insert(HC511UserGoal);
|
||||
await MONGODB_KILL["goal-subs"].insert(HC511UserGoal);
|
||||
|
||||
// score is EX HARD CLEAR by default.
|
||||
await db["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
|
||||
const res = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log);
|
||||
|
||||
@@ -251,7 +253,7 @@ t.test("#ProcessGoal", (t) => {
|
||||
userID: 1,
|
||||
};
|
||||
|
||||
await db["goal-subs"].insert(achievedGoalSub);
|
||||
await MONGODB_KILL["goal-subs"].insert(achievedGoalSub);
|
||||
|
||||
const res = await ProcessGoal(HC511Goal, achievedGoalSub, 1, log);
|
||||
|
||||
@@ -285,7 +287,7 @@ t.test("#ProcessGoal", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should return undefined if there's no score.", async (t) => {
|
||||
await db["goal-subs"].insert(HC511UserGoal);
|
||||
await MONGODB_KILL["goal-subs"].insert(HC511UserGoal);
|
||||
|
||||
const res = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log);
|
||||
|
||||
@@ -295,17 +297,20 @@ t.test("#ProcessGoal", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should return undefined if the progress has not changed.", async (t) => {
|
||||
await db["goal-subs"].insert(HC511UserGoal);
|
||||
await db["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
await MONGODB_KILL["goal-subs"].insert(HC511UserGoal);
|
||||
await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
|
||||
const firstUpdate = await ProcessGoal(HC511Goal, HC511UserGoal, 1, log);
|
||||
|
||||
// ignore this one
|
||||
t.not(firstUpdate, undefined, "Should NOT return undefined.");
|
||||
|
||||
await db["goal-subs"].bulkWrite([firstUpdate!.bwrite]);
|
||||
await MONGODB_KILL["goal-subs"].bulkWrite([firstUpdate!.bwrite]);
|
||||
|
||||
const goalSub = await db["goal-subs"].findOne({ userID: 1, goalID: HC511Goal.goalID });
|
||||
const goalSub = await MONGODB_KILL["goal-subs"].findOne({
|
||||
userID: 1,
|
||||
goalID: HC511Goal.goalID,
|
||||
});
|
||||
|
||||
const secondUpdate = await ProcessGoal(HC511Goal, goalSub!, 1, log);
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { GameGroup, GoalDocument, GoalSubscriptionDocument, integer } from
|
||||
|
||||
import { EvaluateGoalForUser, GetRelevantGoals } from "#lib/targets/goals";
|
||||
import { EmitWebhookEvent } from "#lib/webhooks/webhooks";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
/**
|
||||
* Update a user's progress on all of their set goals.
|
||||
@@ -90,7 +90,7 @@ export async function UpdateGoalsForUser(
|
||||
});
|
||||
}
|
||||
|
||||
await db["goal-subs"].bulkWrite(bulkWrite, { ordered: false });
|
||||
await MONGODB_KILL["goal-subs"].bulkWrite(bulkWrite, { ordered: false });
|
||||
|
||||
return importInfo;
|
||||
}
|
||||
@@ -209,14 +209,14 @@ export async function ProcessGoal(
|
||||
}
|
||||
|
||||
export async function UpdateGoalsInFolder(folderID: string, log: KtLogger) {
|
||||
const goals = await db.goals.find({
|
||||
const goals = await MONGODB_KILL.goals.find({
|
||||
"charts.type": "folder",
|
||||
"charts.data": folderID,
|
||||
});
|
||||
|
||||
log.info(`Updating ${goals.length} goals for ${folderID}`);
|
||||
|
||||
const goalSubs = await db["goal-subs"].find({
|
||||
const goalSubs = await MONGODB_KILL["goal-subs"].find({
|
||||
goalID: { $in: goals.map((e) => e.goalID) },
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { integer } from "tachi-common";
|
||||
|
||||
import { ONE_DAY, ONE_HOUR } from "#lib/constants/time";
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
/**
|
||||
* If a user has no ongoing import, enable the import lock and return true.
|
||||
@@ -13,19 +13,19 @@ import db from "#services/mongo/db";
|
||||
* has a lock.
|
||||
*/
|
||||
export async function CheckAndSetOngoingImportLock(userID: integer) {
|
||||
const lockExists = await db["import-locks"].findOne({
|
||||
const lockExists = await MONGODB_KILL["import-locks"].findOne({
|
||||
userID,
|
||||
});
|
||||
|
||||
if (!lockExists) {
|
||||
await db["import-locks"].insert({
|
||||
await MONGODB_KILL["import-locks"].insert({
|
||||
userID,
|
||||
locked: false,
|
||||
lockedAt: null,
|
||||
});
|
||||
} else if (lockExists.locked && lockExists.lockedAt! + ONE_DAY < Date.now()) {
|
||||
log.warn(`Removed import lock for ${userID} as it is ostensibly stuck.`);
|
||||
await db["import-locks"].update(
|
||||
await MONGODB_KILL["import-locks"].update(
|
||||
{
|
||||
userID,
|
||||
},
|
||||
@@ -38,7 +38,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) {
|
||||
);
|
||||
}
|
||||
|
||||
const lockWasSet = await db["import-locks"].findOneAndUpdate(
|
||||
const lockWasSet = await MONGODB_KILL["import-locks"].findOneAndUpdate(
|
||||
{
|
||||
userID,
|
||||
locked: false,
|
||||
@@ -68,7 +68,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) {
|
||||
* Disable a users import lock.
|
||||
*/
|
||||
export function UnsetOngoingImportLock(userID: integer) {
|
||||
return db["import-locks"].findOneAndUpdate(
|
||||
return MONGODB_KILL["import-locks"].findOneAndUpdate(
|
||||
{
|
||||
userID,
|
||||
locked: true,
|
||||
|
||||
+19
-11
@@ -1,7 +1,7 @@
|
||||
import type { BatchManualScore } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import deepmerge from "deepmerge";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
@@ -56,7 +56,7 @@ t.test("#OrphanScore", (t) => {
|
||||
"Should be a checksum of the orphan's contents.",
|
||||
);
|
||||
|
||||
const dbCheck = await db["orphan-scores"].findOne({
|
||||
const dbCheck = await MONGODB_KILL["orphan-scores"].findOne({
|
||||
orphanID: res.orphanID,
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ t.test("#OrphanScore", (t) => {
|
||||
t.equal(res2.success, false);
|
||||
t.equal(res1.orphanID, res2.orphanID);
|
||||
|
||||
const dbCheck = await db["orphan-scores"].count();
|
||||
const dbCheck = await MONGODB_KILL["orphan-scores"].count();
|
||||
|
||||
t.equal(dbCheck, 1);
|
||||
});
|
||||
@@ -163,7 +163,9 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
"Should successfully import the score.",
|
||||
);
|
||||
|
||||
const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID });
|
||||
const orphan = await MONGODB_KILL["orphan-scores"].findOne({
|
||||
orphanID: orphanDoc.orphanID,
|
||||
});
|
||||
|
||||
t.equal(orphan, null, "Should remove the orphan document.");
|
||||
|
||||
@@ -171,7 +173,7 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should delete the orphan doc and return null if the score is invalid.", async (t) => {
|
||||
await db["orphan-scores"].insert(orphanDoc);
|
||||
await MONGODB_KILL["orphan-scores"].insert(orphanDoc);
|
||||
const res = await ReprocessOrphan(
|
||||
deepmerge(orphanDoc, {
|
||||
data: {
|
||||
@@ -184,7 +186,9 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
|
||||
t.equal(res, null);
|
||||
|
||||
const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID });
|
||||
const orphan = await MONGODB_KILL["orphan-scores"].findOne({
|
||||
orphanID: orphanDoc.orphanID,
|
||||
});
|
||||
|
||||
t.equal(orphan, null, "Should remove the orphan document.");
|
||||
|
||||
@@ -194,7 +198,7 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
t.test(
|
||||
"Should keep the orphan doc and return false if no parents could be found.",
|
||||
async (t) => {
|
||||
await db["orphan-scores"].insert(orphanDoc);
|
||||
await MONGODB_KILL["orphan-scores"].insert(orphanDoc);
|
||||
const res = await ReprocessOrphan(
|
||||
deepmerge(orphanDoc, {
|
||||
data: {
|
||||
@@ -207,7 +211,9 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
|
||||
t.equal(res, false);
|
||||
|
||||
const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID });
|
||||
const orphan = await MONGODB_KILL["orphan-scores"].findOne({
|
||||
orphanID: orphanDoc.orphanID,
|
||||
});
|
||||
|
||||
t.not(orphan, null, "Should not remove the orphan document.");
|
||||
|
||||
@@ -218,12 +224,12 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
t.test(
|
||||
"Should keep the orphan doc and return false if an internal failure was triggered.",
|
||||
async (t) => {
|
||||
await db["orphan-scores"].insert(orphanDoc);
|
||||
await MONGODB_KILL["orphan-scores"].insert(orphanDoc);
|
||||
const GAZER_SHA256 = "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d";
|
||||
|
||||
// force an internal failure by removing all bms songs and then
|
||||
// leaving a valid bms chart to match with.
|
||||
await db.songs.bms.remove({});
|
||||
await MONGODB_KILL.songs.bms.remove({});
|
||||
|
||||
const res = await ReprocessOrphan(
|
||||
deepmerge(orphanDoc, {
|
||||
@@ -242,7 +248,9 @@ t.test("#ReprocessOrphan", (t) => {
|
||||
|
||||
t.equal(res, false);
|
||||
|
||||
const orphan = await db["orphan-scores"].findOne({ orphanID: orphanDoc.orphanID });
|
||||
const orphan = await MONGODB_KILL["orphan-scores"].findOne({
|
||||
orphanID: orphanDoc.orphanID,
|
||||
});
|
||||
|
||||
t.not(orphan, null, "Should not remove the orphan document.");
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { KtLogger } from "#lib/log/log.js";
|
||||
import type { FilterQuery } from "mongodb";
|
||||
import type { GameGroup, ImportTypes, integer } from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetBlacklist } from "#utils/queries/blacklist";
|
||||
import { GetUserWithID } from "#utils/user";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
@@ -53,7 +53,7 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
throw new Error(`Failed to orphan score. ${(err as Error).message}`);
|
||||
}
|
||||
|
||||
const exists = await db["orphan-scores"].findOne({ orphanID });
|
||||
const exists = await MONGODB_KILL["orphan-scores"].findOne({ orphanID });
|
||||
|
||||
if (exists) {
|
||||
log.debug(`Skipped orphaning score ${orphanID} because it already exists.`);
|
||||
@@ -70,7 +70,7 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
|
||||
log.debug(orphanScoreDoc, `Inserting orphanScoreDoc...`);
|
||||
|
||||
await db["orphan-scores"].insert(orphanScoreDoc);
|
||||
await MONGODB_KILL["orphan-scores"].insert(orphanScoreDoc);
|
||||
|
||||
return { success: true, orphanID };
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export async function ReprocessOrphan(
|
||||
|
||||
// @danger - This could go terribly, if there's a mistake in the converterFN we might accidentally
|
||||
// remove a users score.
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ export async function ReprocessOrphan(
|
||||
);
|
||||
} catch (err) {
|
||||
if (IsConverterFailure(err) && err.failureType === "InvalidScore") {
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ export async function ReprocessOrphan(
|
||||
}
|
||||
|
||||
if (converterReturns === null || !converterReturns.success) {
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export async function ReprocessOrphan(
|
||||
log.error(
|
||||
`Orphan ${orphan.orphanID} belongs to ${orphan.userID}, but that user no longer exists in the database. Going to skip this and remove the orphan.`,
|
||||
);
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -189,12 +189,12 @@ export async function ReprocessOrphan(
|
||||
undefined,
|
||||
);
|
||||
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
await MONGODB_KILL["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
return converterReturns;
|
||||
}
|
||||
|
||||
export async function DeorphanScores(query: FilterQuery<OrphanScoreDocument>, log: KtLogger) {
|
||||
const orphans = await db["orphan-scores"].find(query);
|
||||
const orphans = await MONGODB_KILL["orphan-scores"].find(query);
|
||||
|
||||
// ScoreIDs are essentially userID dependent, so this is fine.
|
||||
const blacklist = await GetBlacklist();
|
||||
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
import { type KtLogger, log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import {
|
||||
BMSGazerChart,
|
||||
@@ -56,8 +56,8 @@ t.test("#CreatePBDoc", (t) => {
|
||||
t.test(
|
||||
"(IIDX) Should use the Server Impl Merge FNs to also join the BP PB if necessary.",
|
||||
async (t) => {
|
||||
await db.scores.remove({});
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
await MONGODB_KILL.scores.insert([
|
||||
IIDXScore,
|
||||
deepmerge(IIDXScore, {
|
||||
scoreData: {
|
||||
@@ -130,8 +130,8 @@ t.test("#CreatePBDoc", (t) => {
|
||||
scoreID: "LAMP_PB_ID",
|
||||
});
|
||||
|
||||
await db.scores.remove({});
|
||||
await db.scores.insert([IIDXScore, d]);
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
await MONGODB_KILL.scores.insert([IIDXScore, d]);
|
||||
|
||||
const res = await CreatePBDoc("iidx:SP", 1, Testing511SPA, log);
|
||||
|
||||
@@ -151,7 +151,7 @@ t.test("#CreatePBDoc", (t) => {
|
||||
|
||||
let warnCalled = false;
|
||||
|
||||
await db.scores.remove({});
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
|
||||
const res = await CreatePBDoc("iidx:SP", 1, Testing511SPA, fakeLogger);
|
||||
|
||||
@@ -163,8 +163,8 @@ t.test("#CreatePBDoc", (t) => {
|
||||
});
|
||||
|
||||
t.test("(BMS) Should inherit BP from the best BP score.", async (t) => {
|
||||
await db.scores.remove({});
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
await MONGODB_KILL.scores.insert([
|
||||
TestingBMS7KScore,
|
||||
deepmerge(TestingBMS7KScore, {
|
||||
scoreData: {
|
||||
@@ -215,8 +215,8 @@ t.test("#CreatePBDoc", (t) => {
|
||||
});
|
||||
|
||||
t.test("(BMS) Should inherit graph data from the best lamp.", async (t) => {
|
||||
await db.scores.remove({});
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
await MONGODB_KILL.scores.insert([
|
||||
TestingBMS7KScore,
|
||||
deepmerge(TestingBMS7KScore, {
|
||||
scoreData: {
|
||||
@@ -3,7 +3,7 @@ import type { BulkWriteUpdateOneOperation, FilterQuery, SortOptionObject } from
|
||||
|
||||
import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations";
|
||||
import { GetEveryonesRivalIDs } from "#lib/rivals/rivals";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { DeleteUndefinedProps } from "#utils/misc";
|
||||
import {
|
||||
type ChartDocument,
|
||||
@@ -49,7 +49,7 @@ export async function CreatePBDoc(
|
||||
|
||||
const gptConfig = GetGPTConfig(gpt);
|
||||
|
||||
const defaultMetricPB = await db.scores.findOne(query, {
|
||||
const defaultMetricPB = await MONGODB_KILL.scores.findOne(query, {
|
||||
sort: {
|
||||
[`scoreData.${gptConfig.defaultMetric}`]: -1,
|
||||
},
|
||||
@@ -177,7 +177,7 @@ export async function UpdateChartRanking(game: GameGroup, playtype: Playtype, ch
|
||||
return;
|
||||
}
|
||||
|
||||
await db["personal-bests"].bulkWrite(bwrite, { ordered: false });
|
||||
await MONGODB_KILL["personal-bests"].bulkWrite(bwrite, { ordered: false });
|
||||
}
|
||||
|
||||
async function GetSortedPBs(game: GameGroup, playtype: Playtype, chartID: string) {
|
||||
@@ -199,7 +199,7 @@ async function GetSortedPBs(game: GameGroup, playtype: Playtype, chartID: string
|
||||
};
|
||||
}
|
||||
|
||||
return db["personal-bests"].aggregate([
|
||||
return MONGODB_KILL["personal-bests"].aggregate([
|
||||
{
|
||||
$match: {
|
||||
chartID,
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { Testing511SPA, TestingIIDXSPScore } from "#test-utils/test-data";
|
||||
import crypto from "crypto";
|
||||
@@ -12,12 +12,12 @@ t.test("#ProcessPBs", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should successfully insert a pb into the score-pb database", async (t) => {
|
||||
await db["personal-bests"].remove({});
|
||||
await MONGODB_KILL["personal-bests"].remove({});
|
||||
|
||||
// scores on 511 SPA are pre-loaded into the database
|
||||
await ProcessPBs("iidx", "SP", 1, new Set([Testing511SPA.chartID]), log);
|
||||
|
||||
const pbs = await db["personal-bests"].find({});
|
||||
const pbs = await MONGODB_KILL["personal-bests"].find({});
|
||||
|
||||
t.equal(pbs.length, 1, "Should match the amount of PBs inserted into the DB.");
|
||||
|
||||
@@ -25,9 +25,9 @@ t.test("#ProcessPBs", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should successfully insert multiple pbs into the score-pb database", async (t) => {
|
||||
await db["personal-bests"].remove({});
|
||||
await MONGODB_KILL["personal-bests"].remove({});
|
||||
|
||||
await db.charts.iidx.insert([
|
||||
await MONGODB_KILL.charts.iidx.insert([
|
||||
// @ts-expect-error lol
|
||||
deepmerge(Testing511SPA, {
|
||||
chartID: "test1",
|
||||
@@ -45,7 +45,7 @@ t.test("#ProcessPBs", (t) => {
|
||||
}),
|
||||
]);
|
||||
|
||||
await db.scores.insert([
|
||||
await MONGODB_KILL.scores.insert([
|
||||
// @ts-expect-error lol
|
||||
deepmerge(TestingIIDXSPScore, {
|
||||
chartID: "test1",
|
||||
@@ -73,7 +73,7 @@ t.test("#ProcessPBs", (t) => {
|
||||
log,
|
||||
);
|
||||
|
||||
const pbs = await db["personal-bests"].find({});
|
||||
const pbs = await MONGODB_KILL["personal-bests"].find({});
|
||||
|
||||
t.equal(pbs.length, 4, "Should match the amount of PBs inserted into the DB.");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { KtLogger } from "#lib/log/log.js";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetChartForIDGuaranteed } from "#utils/db";
|
||||
import { type GameGroup, GetGPTString, type integer, type Playtype } from "tachi-common";
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function ProcessPBs(
|
||||
// this *is* bad behaviour, but I don't have a nice way to fix it.
|
||||
// This should be fixed in the future to avoid crashes between these two
|
||||
// calls - but that is unlikely.
|
||||
await db["personal-bests"].bulkWrite(
|
||||
await MONGODB_KILL["personal-bests"].bulkWrite(
|
||||
pbDocs.map((e) => ({
|
||||
updateOne: {
|
||||
filter: { chartID: e.chartID, userID: e.userID },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { KtLogger } from "#lib/log/log.js";
|
||||
|
||||
import { EmitWebhookEvent } from "#lib/webhooks/webhooks";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { ReturnClassIfGreater } from "#utils/class";
|
||||
import deepmerge from "deepmerge";
|
||||
import {
|
||||
@@ -163,7 +163,7 @@ export async function ProcessClassDeltas(
|
||||
}
|
||||
}
|
||||
|
||||
await db["class-achievements"].insert(achievementOps);
|
||||
await MONGODB_KILL["class-achievements"].insert(achievementOps);
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
|
||||
import { EvaluateQuestProgress } from "#lib/targets/quests";
|
||||
import { EmitWebhookEvent } from "#lib/webhooks/webhooks";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
export async function UpdateUsersQuests(
|
||||
importGoalInfo: Array<GoalImportInfo>,
|
||||
@@ -114,7 +114,7 @@ export async function UpdateQuestsForUser(
|
||||
);
|
||||
|
||||
if (bwrite.length !== 0) {
|
||||
await db["quest-subs"].bulkWrite(bwrite, { ordered: false });
|
||||
await MONGODB_KILL["quest-subs"].bulkWrite(bwrite, { ordered: false });
|
||||
}
|
||||
|
||||
return importQuestInfo;
|
||||
@@ -127,7 +127,7 @@ async function GetRelevantQuests(
|
||||
userID: integer,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const questSubs = await db["quest-subs"].find({
|
||||
const questSubs = await MONGODB_KILL["quest-subs"].find({
|
||||
game,
|
||||
playtype: { $in: playtypes },
|
||||
userID,
|
||||
@@ -135,7 +135,7 @@ async function GetRelevantQuests(
|
||||
|
||||
log.debug(`Found ${questSubs.length} quest-subs.`);
|
||||
|
||||
const quests = await db.quests.find({
|
||||
const quests = await MONGODB_KILL.quests.find({
|
||||
questID: { $in: questSubs.map((e) => e.questID) },
|
||||
"questData.goals.goalID": { $in: goalIDs },
|
||||
});
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
import { CDNRetrieve } from "#lib/cdn/cdn";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { FakeSmallBatchManual } from "#test-utils/test-data";
|
||||
import t from "tap";
|
||||
@@ -22,7 +22,7 @@ t.test("#MakeScoreImport", (t) => {
|
||||
importID: "mockImportID",
|
||||
});
|
||||
|
||||
const dbRes = await db.imports.findOne(
|
||||
const dbRes = await MONGODB_KILL.imports.findOne(
|
||||
{ importID: "mockImportID" },
|
||||
{ projection: { _id: 0 } },
|
||||
);
|
||||
@@ -57,7 +57,9 @@ t.test("#MakeScoreImport", (t) => {
|
||||
t.fail(err as any);
|
||||
}
|
||||
|
||||
const importTrack = await db["import-trackers"].findOne({ importID: "mockImportID" });
|
||||
const importTrack = await MONGODB_KILL["import-trackers"].findOne({
|
||||
importID: "mockImportID",
|
||||
});
|
||||
|
||||
t.equal(
|
||||
importTrack,
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import type { ScoreDocument } from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import t from "tap";
|
||||
|
||||
@@ -12,7 +12,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => {
|
||||
|
||||
// empty scoreDB after
|
||||
t.afterEach(async () => {
|
||||
await db.scores.remove({});
|
||||
await MONGODB_KILL.scores.remove({});
|
||||
});
|
||||
|
||||
t.test("Single Queue Test", async (t) => {
|
||||
@@ -33,7 +33,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => {
|
||||
|
||||
t.equal(flushSize, 1, "QueueScoreInsert should append the score to the queue.");
|
||||
|
||||
const dbRes = await db.scores.find({
|
||||
const dbRes = await MONGODB_KILL.scores.find({
|
||||
scoreID: "foo",
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => {
|
||||
|
||||
t.equal(flushRes, 0, "The queue should now be empty.");
|
||||
|
||||
const dbRes = await db.scores.find({
|
||||
const dbRes = await MONGODB_KILL.scores.find({
|
||||
scoreID: { $exists: true },
|
||||
chartID: "test",
|
||||
});
|
||||
@@ -112,7 +112,7 @@ t.test("#QueueScoreInsert, #InsertQueue", async (t) => {
|
||||
|
||||
t.equal(flushRes, 1, "Should flush 1 score document");
|
||||
|
||||
const dbRes = await db.scores.find({
|
||||
const dbRes = await MONGODB_KILL.scores.find({
|
||||
chartID: "foo",
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { integer, ScoreDocument } from "tachi-common";
|
||||
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
const MAX_PIPELINE_LENGTH = 500;
|
||||
|
||||
@@ -64,7 +64,7 @@ export async function InsertQueue(userID: integer) {
|
||||
delete ScoreQueues[userID];
|
||||
|
||||
try {
|
||||
await db.scores.insert(queuedScores);
|
||||
await MONGODB_KILL.scores.insert(queuedScores);
|
||||
} catch (err) {
|
||||
log.warn(
|
||||
{ err },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { KtLogger } from "#lib/log/log.js";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
import {
|
||||
GetGPTConfig,
|
||||
@@ -58,7 +58,7 @@ export function CreateScoreID(
|
||||
}
|
||||
|
||||
export function GetWithScoreID(scoreID: string) {
|
||||
return db.scores.findOne({
|
||||
return MONGODB_KILL.scores.findOne({
|
||||
scoreID,
|
||||
});
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
import type { KtLogger } from "#lib/log/log";
|
||||
import type { ScoreImportJob } from "#lib/score-import/worker/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetMillisecondsSince } from "#utils/misc";
|
||||
import { GetUserWithID } from "#utils/user";
|
||||
import {
|
||||
@@ -133,7 +133,7 @@ export default async function ScoreImportMain<D, C>(
|
||||
);
|
||||
} catch (err) {
|
||||
// Remove all scores from the database for this user which were imported after our timer started.
|
||||
const r = await db.scores.remove({
|
||||
const r = await MONGODB_KILL.scores.remove({
|
||||
userID: user.id,
|
||||
timeAdded: { $gte: startOfImportingScores },
|
||||
});
|
||||
@@ -222,11 +222,11 @@ export default async function ScoreImportMain<D, C>(
|
||||
log.debug(logMessage);
|
||||
}
|
||||
|
||||
await db.imports.insert(ImportDocument);
|
||||
await MONGODB_KILL.imports.insert(ImportDocument);
|
||||
|
||||
// we don't await this because we don't
|
||||
// particularly care about waiting for it.
|
||||
void db["import-timings"].insert({
|
||||
void MONGODB_KILL["import-timings"].insert({
|
||||
importID,
|
||||
timestamp: Date.now(),
|
||||
total: ImportDocument.timeFinished - timeStarted,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ScoreImportJob } from "#lib/score-import/worker/types";
|
||||
|
||||
import { AppendLogCtx, type KtLogger } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { ClassToObject } from "#utils/misc";
|
||||
import {
|
||||
type ChartDocument,
|
||||
@@ -52,7 +52,7 @@ export async function ImportAllIterableData<D, C>(
|
||||
// @optimisable: could filter harder with score.game and score.playtype
|
||||
// stuff.
|
||||
const blacklist = (
|
||||
await db["score-blacklist"].find({
|
||||
await MONGODB_KILL["score-blacklist"].find({
|
||||
userID,
|
||||
})
|
||||
).map((e) => e.scoreID);
|
||||
@@ -345,7 +345,7 @@ async function HydrateCheckAndInsertScore(
|
||||
return null;
|
||||
}
|
||||
|
||||
const existingScore = await db.scores.findOne(
|
||||
const existingScore = await MONGODB_KILL.scores.findOne(
|
||||
{
|
||||
scoreID,
|
||||
},
|
||||
@@ -377,7 +377,7 @@ async function HydrateCheckAndInsertScore(
|
||||
let res;
|
||||
|
||||
if (force) {
|
||||
res = await db.scores.insert(score);
|
||||
res = await MONGODB_KILL.scores.insert(score);
|
||||
} else {
|
||||
res = await QueueScoreInsert(score);
|
||||
}
|
||||
|
||||
+61
-17
@@ -1,6 +1,6 @@
|
||||
import type { ScoreDocument, SessionDocument, UserDocument } from "tachi-common";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingIIDXSPScore, TestingIIDXSPScorePB } from "#test-utils/test-data";
|
||||
import deepmerge from "deepmerge";
|
||||
@@ -17,7 +17,7 @@ const log = CreateScoreLogger(
|
||||
|
||||
t.test("#CreateSessions", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(() => db.sessions.remove({}));
|
||||
t.beforeEach(() => MONGODB_KILL.sessions.remove({}));
|
||||
|
||||
t.test("Should compose sessions from one timestamped score provided.", async (t) => {
|
||||
const res = await CreateSessions(1, "iidx", { SP: [TestingIIDXSPScore] }, log);
|
||||
@@ -31,9 +31,17 @@ t.test("#CreateSessions", (t) => {
|
||||
|
||||
t.equal(res.length, 1);
|
||||
|
||||
const session = await db.sessions.findOne({ userID: 1, game: "iidx", playtype: "SP" });
|
||||
const session = await MONGODB_KILL.sessions.findOne({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
});
|
||||
|
||||
const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" });
|
||||
const sessionCount = await MONGODB_KILL.sessions.count({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
});
|
||||
|
||||
t.equal(sessionCount, 1);
|
||||
|
||||
@@ -66,7 +74,11 @@ t.test("#CreateSessions", (t) => {
|
||||
|
||||
t.strictSame(res, []);
|
||||
|
||||
const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" });
|
||||
const sessionCount = await MONGODB_KILL.sessions.count({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
});
|
||||
|
||||
t.equal(sessionCount, 0);
|
||||
|
||||
@@ -83,11 +95,19 @@ t.test("#CreateSessions", (t) => {
|
||||
|
||||
t.equal(res.length, 2);
|
||||
|
||||
const sessionCount = await db.sessions.count({ userID: 1, game: "iidx", playtype: "SP" });
|
||||
const sessionCount = await MONGODB_KILL.sessions.count({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
});
|
||||
|
||||
t.equal(sessionCount, 1);
|
||||
|
||||
const sessionCountDP = await db.sessions.count({ userID: 1, game: "iidx", playtype: "DP" });
|
||||
const sessionCountDP = await MONGODB_KILL.sessions.count({
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
playtype: "DP",
|
||||
});
|
||||
|
||||
t.equal(sessionCountDP, 1);
|
||||
|
||||
@@ -101,7 +121,7 @@ const start = 1619454485988;
|
||||
|
||||
t.test("#LoadScoresIntoSessions", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(() => db.sessions.remove({}));
|
||||
t.beforeEach(() => MONGODB_KILL.sessions.remove({}));
|
||||
|
||||
t.test("Should create sessions from scores.", async (t) => {
|
||||
const res = await LoadScoresIntoSessions(
|
||||
@@ -121,7 +141,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 1);
|
||||
|
||||
@@ -154,7 +178,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 1);
|
||||
|
||||
@@ -201,7 +229,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 2);
|
||||
|
||||
@@ -212,7 +244,7 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should append to existing sessions.", async (t) => {
|
||||
await db.sessions.insert({
|
||||
await MONGODB_KILL.sessions.insert({
|
||||
sessionID: "EXAMPLE_SESSION_ID",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
@@ -239,7 +271,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 1);
|
||||
|
||||
@@ -256,7 +292,7 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should change the timeStarted of existing sessions if prepending.", async (t) => {
|
||||
await db.sessions.insert({
|
||||
await MONGODB_KILL.sessions.insert({
|
||||
sessionID: "EXAMPLE_SESSION_ID",
|
||||
userID: 1,
|
||||
game: "iidx",
|
||||
@@ -283,7 +319,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 1);
|
||||
|
||||
@@ -300,7 +340,7 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should calculate pbDifferences if a scorePB exists.", async (t) => {
|
||||
await db["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
|
||||
const res = await LoadScoresIntoSessions(1, [TestingIIDXSPScore], "iidx", "SP", log);
|
||||
|
||||
@@ -311,7 +351,11 @@ t.test("#LoadScoresIntoSessions", (t) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const sessions = await db.sessions.find({ game: "iidx", playtype: "SP", userID: 1 });
|
||||
const sessions = await MONGODB_KILL.sessions.find({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
t.equal(sessions.length, 1);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ONE_HOUR } from "#lib/constants/time";
|
||||
import { AppendLogCtx, type KtLogger, log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import { GetChartForIDGuaranteed } from "#utils/db";
|
||||
import { GetScoresFromSession } from "#utils/session";
|
||||
import crypto from "crypto";
|
||||
@@ -99,7 +99,7 @@ function ScoreToSessionScoreInfo(
|
||||
export async function GetSessionScoreInfo(
|
||||
session: SessionDocument,
|
||||
): Promise<Array<SessionScoreInfo>> {
|
||||
const scores = await db.scores.find({
|
||||
const scores = await MONGODB_KILL.scores.find({
|
||||
scoreID: { $in: session.scoreIDs },
|
||||
});
|
||||
|
||||
@@ -253,7 +253,7 @@ export async function LoadScoresIntoSessions(
|
||||
// Find any sessions with +/-2hrs of this group. This is rather exhaustive, and could result in some issues
|
||||
// if this query returns more than one session. We could account for that by smushing sessions together.
|
||||
// This is not possible however, so this is now just a known tachi oddity.
|
||||
const nearbySession = await db.sessions.findOne({
|
||||
const nearbySession = await MONGODB_KILL.sessions.findOne({
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
@@ -276,7 +276,7 @@ export async function LoadScoresIntoSessions(
|
||||
|
||||
infoReturn = { sessionID: session.sessionID, type: "Appended" };
|
||||
|
||||
await db.sessions.update(
|
||||
await MONGODB_KILL.sessions.update(
|
||||
{
|
||||
sessionID: session.sessionID,
|
||||
},
|
||||
@@ -292,7 +292,7 @@ export async function LoadScoresIntoSessions(
|
||||
const session = CreateSession(userID, scoreIDs, groupScores, game, playtype);
|
||||
|
||||
infoReturn = { sessionID: session.sessionID, type: "Created" };
|
||||
await db.sessions.insert(session);
|
||||
await MONGODB_KILL.sessions.insert(session);
|
||||
}
|
||||
|
||||
sessionInfoReturns.push(infoReturn);
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ import type { ImportTypes } from "tachi-common";
|
||||
import { CDNStoreOrOverwrite } from "#lib/cdn/cdn";
|
||||
import { GetScoreImportInputURL } from "#lib/cdn/url-format";
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
import type ScoreImportFatalError from "../score-importing/score-import-error";
|
||||
|
||||
@@ -37,7 +37,7 @@ function SerialiseJobData(jobData: ScoreImportJobData<ImportTypes>): string {
|
||||
* awaited when you await this function, it happens in the background.
|
||||
*/
|
||||
export async function StartTrackingImport(jobData: ScoreImportJobData<ImportTypes>) {
|
||||
await db["import-trackers"].insert({
|
||||
await MONGODB_KILL["import-trackers"].insert({
|
||||
type: "ONGOING",
|
||||
importID: jobData.importID,
|
||||
importType: jobData.importType,
|
||||
@@ -63,7 +63,7 @@ export async function StartTrackingImport(jobData: ScoreImportJobData<ImportType
|
||||
}
|
||||
|
||||
export async function MarkImportAsFailed(importID: string, error: Error | ScoreImportFatalError) {
|
||||
await db["import-trackers"].update(
|
||||
await MONGODB_KILL["import-trackers"].update(
|
||||
{
|
||||
importID,
|
||||
},
|
||||
@@ -84,5 +84,5 @@ export async function MarkImportAsFailed(importID: string, error: Error | ScoreI
|
||||
* in the tracker.
|
||||
*/
|
||||
export async function EndTrackingImport(importID: string) {
|
||||
await db["import-trackers"].remove({ importID });
|
||||
await MONGODB_KILL["import-trackers"].remove({ importID });
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,5 +1,5 @@
|
||||
import { log } from "#lib/log/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingIIDXSPScorePB } from "#test-utils/test-data";
|
||||
import crypto from "crypto";
|
||||
@@ -12,20 +12,20 @@ import { UpdateUsersGamePlaytypeStats } from "./update-ugpt-stats";
|
||||
t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(async () => {
|
||||
await db["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
await MONGODB_KILL["personal-bests"].insert(TestingIIDXSPScorePB);
|
||||
});
|
||||
|
||||
t.test(
|
||||
"Should create new UserGameStats and UserGameSettings if the user has none",
|
||||
async (t) => {
|
||||
await db["game-stats"].remove({});
|
||||
await db["game-settings"].remove({});
|
||||
await MONGODB_KILL["game-stats"].remove({});
|
||||
await MONGODB_KILL["game-settings"].remove({});
|
||||
|
||||
const res = await UpdateUsersGamePlaytypeStats("iidx", "SP", 1, null, log);
|
||||
|
||||
t.strictSame(res, [], "Should return an empty object");
|
||||
|
||||
const gs = await db["game-stats"].findOne();
|
||||
const gs = await MONGODB_KILL["game-stats"].findOne();
|
||||
|
||||
t.hasStrict(
|
||||
gs,
|
||||
@@ -39,7 +39,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
"Should insert an appropriate game-stats object",
|
||||
);
|
||||
|
||||
const settings = await db["game-settings"].findOne();
|
||||
const settings = await MONGODB_KILL["game-settings"].findOne();
|
||||
|
||||
t.hasStrict(settings, {
|
||||
game: "iidx",
|
||||
@@ -53,9 +53,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
);
|
||||
|
||||
t.test("Should update UserGameStats if the user has one", async (t) => {
|
||||
await db["game-stats"].remove({});
|
||||
await MONGODB_KILL["game-stats"].remove({});
|
||||
|
||||
await db["game-stats"].insert({
|
||||
await MONGODB_KILL["game-stats"].insert({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
@@ -66,7 +66,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
// insert some mock scores
|
||||
const ratings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
await db["personal-bests"].insert(
|
||||
await MONGODB_KILL["personal-bests"].insert(
|
||||
ratings.map((e) =>
|
||||
deepmerge(TestingIIDXSPScorePB, {
|
||||
chartID: crypto.randomBytes(20).toString("hex"),
|
||||
@@ -81,7 +81,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
|
||||
t.strictSame(res, [], "Should return an empty object");
|
||||
|
||||
const gs = await db["game-stats"].findOne();
|
||||
const gs = await MONGODB_KILL["game-stats"].findOne();
|
||||
|
||||
t.hasStrict(
|
||||
gs,
|
||||
@@ -99,9 +99,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should return class deltas", async (t) => {
|
||||
await db["game-stats"].remove({});
|
||||
await MONGODB_KILL["game-stats"].remove({});
|
||||
|
||||
await db["game-stats"].insert({
|
||||
await MONGODB_KILL["game-stats"].insert({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
@@ -132,7 +132,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
"Should return the class delta",
|
||||
);
|
||||
|
||||
const gs = await db["game-stats"].findOne();
|
||||
const gs = await MONGODB_KILL["game-stats"].findOne();
|
||||
|
||||
t.hasStrict(
|
||||
gs,
|
||||
@@ -152,9 +152,9 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
});
|
||||
|
||||
t.test("Should return updated class deltas", async (t) => {
|
||||
await db["game-stats"].remove({});
|
||||
await MONGODB_KILL["game-stats"].remove({});
|
||||
|
||||
await db["game-stats"].insert({
|
||||
await MONGODB_KILL["game-stats"].insert({
|
||||
game: "iidx",
|
||||
playtype: "SP",
|
||||
userID: 1,
|
||||
@@ -186,7 +186,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
"Should return the updated class delta",
|
||||
);
|
||||
|
||||
const gs = await db["game-stats"].findOne();
|
||||
const gs = await MONGODB_KILL["game-stats"].findOne();
|
||||
|
||||
t.hasStrict(
|
||||
gs,
|
||||
@@ -2,7 +2,7 @@ import type { KtLogger } from "#lib/log/log.js";
|
||||
import type { ClassDelta, GameGroup, integer, Playtype, UserGameStats } from "tachi-common";
|
||||
|
||||
import { CreateGameSettings } from "#lib/game-settings/create-game-settings";
|
||||
import db from "#services/mongo/db";
|
||||
import MONGODB_KILL from "#services/mongo/db";
|
||||
|
||||
import type { ClassProvider } from "../calculated-data/types";
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
|
||||
// Attempt to find a users game stats if one already exists. If one doesn't exist,
|
||||
// this is this players first import for this game!
|
||||
const userGameStats = await db["game-stats"].findOne({
|
||||
const userGameStats = await MONGODB_KILL["game-stats"].findOne({
|
||||
game,
|
||||
playtype,
|
||||
userID,
|
||||
@@ -49,7 +49,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
updateClasses[`classes.${delta.set}`] = delta.new;
|
||||
}
|
||||
|
||||
await db["game-stats"].update(
|
||||
await MONGODB_KILL["game-stats"].update(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
@@ -63,7 +63,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const hasAnyScores = await db.scores.findOne({
|
||||
const hasAnyScores = await MONGODB_KILL.scores.findOne({
|
||||
game,
|
||||
playtype,
|
||||
userID,
|
||||
@@ -90,7 +90,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
};
|
||||
|
||||
log.info(`Created new gamestats for ${game} (${playtype})`);
|
||||
await db["game-stats"].insert(newStats);
|
||||
await MONGODB_KILL["game-stats"].insert(newStats);
|
||||
await CreateGameSettings(userID, game, playtype);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,31 +20,31 @@ import type { ConverterFunction } from "../../common/types";
|
||||
import type { MytChunithmScore } from "./types";
|
||||
|
||||
const DIFFICULTIES = {
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_UNSPECIFIED]: undefined,
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_BASIC]: "BASIC",
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_ADVANCED]: "ADVANCED",
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_EXPERT]: "EXPERT",
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_MASTER]: "MASTER",
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_ULTIMA]: "ULTIMA",
|
||||
[ChunithmLevel.CHUNITHM_LEVEL_WORLDS_END]: "WORLD'S END",
|
||||
[ChunithmLevel.UNSPECIFIED]: undefined,
|
||||
[ChunithmLevel.BASIC]: "BASIC",
|
||||
[ChunithmLevel.ADVANCED]: "ADVANCED",
|
||||
[ChunithmLevel.EXPERT]: "EXPERT",
|
||||
[ChunithmLevel.MASTER]: "MASTER",
|
||||
[ChunithmLevel.ULTIMA]: "ULTIMA",
|
||||
[ChunithmLevel.WORLDS_END]: "WORLD'S END",
|
||||
};
|
||||
|
||||
const CLEAR_LAMPS = {
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_UNSPECIFIED]: undefined,
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_FAILED]: "FAILED",
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_CLEAR]: "CLEAR",
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_HARD]: "HARD",
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_ABSOLUTE]: "BRAVE",
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_ABSOLUTE_PLUS]: "ABSOLUTE",
|
||||
[ChunithmClearStatus.CHUNITHM_CLEAR_STATUS_CATASTROPHY]: "CATASTROPHY",
|
||||
[ChunithmClearStatus.UNSPECIFIED]: undefined,
|
||||
[ChunithmClearStatus.FAILED]: "FAILED",
|
||||
[ChunithmClearStatus.CLEAR]: "CLEAR",
|
||||
[ChunithmClearStatus.HARD]: "HARD",
|
||||
[ChunithmClearStatus.ABSOLUTE]: "BRAVE",
|
||||
[ChunithmClearStatus.ABSOLUTE_PLUS]: "ABSOLUTE",
|
||||
[ChunithmClearStatus.CATASTROPHY]: "CATASTROPHY",
|
||||
} as const;
|
||||
|
||||
const NOTE_LAMPS = {
|
||||
[ChunithmComboStatus.CHUNITHM_COMBO_STATUS_UNSPECIFIED]: undefined,
|
||||
[ChunithmComboStatus.CHUNITHM_COMBO_STATUS_NONE]: "NONE",
|
||||
[ChunithmComboStatus.CHUNITHM_COMBO_STATUS_FULL_COMBO]: "FULL COMBO",
|
||||
[ChunithmComboStatus.CHUNITHM_COMBO_STATUS_ALL_JUSTICE]: "ALL JUSTICE",
|
||||
[ChunithmComboStatus.CHUNITHM_COMBO_STATUS_ALL_JUSTICE_CRITICAL]: "ALL JUSTICE CRITICAL",
|
||||
[ChunithmComboStatus.UNSPECIFIED]: undefined,
|
||||
[ChunithmComboStatus.NONE]: "NONE",
|
||||
[ChunithmComboStatus.FULL_COMBO]: "FULL COMBO",
|
||||
[ChunithmComboStatus.ALL_JUSTICE]: "ALL JUSTICE",
|
||||
[ChunithmComboStatus.ALL_JUSTICE_CRITICAL]: "ALL JUSTICE CRITICAL",
|
||||
} as const;
|
||||
|
||||
const ConvertAPIMytChunithm: ConverterFunction<MytChunithmScore, EmptyObject> = async (
|
||||
|
||||
@@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { ChunithmUserClient } from "#proto/generated/chunithm/user_grpc_pb";
|
||||
import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/chunithm/user_pb";
|
||||
import { credentials } from "@grpc/grpc-js";
|
||||
import { ChunithmUser, GetPlaylogRequestSchema } from "#proto/generated/chunithm/user_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { ConnectError, createClient } from "@connectrpc/connect";
|
||||
|
||||
import type { ParserFunctionReturns } from "../../common/types";
|
||||
import type { MytChunithmScore } from "./types";
|
||||
|
||||
import {
|
||||
FetchMytTitleAPIID,
|
||||
GetMytHostname,
|
||||
StreamRPCAsAsync,
|
||||
} from "../../common/api-myt/traverse-api";
|
||||
import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api";
|
||||
|
||||
async function* getObjectsFromGrpcIterable(
|
||||
iterable: AsyncIterable<GetPlaylogStreamItem>,
|
||||
): AsyncIterable<MytChunithmScore> {
|
||||
for await (const item of iterable) {
|
||||
yield item.toObject();
|
||||
async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable<MytChunithmScore> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "chunithm", log);
|
||||
const client = createClient(ChunithmUser, CreateMytTransport());
|
||||
const request = create(GetPlaylogRequestSchema, { profileApiId });
|
||||
|
||||
try {
|
||||
for await (const item of client.getPlaylog(request)) {
|
||||
yield item;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ConnectError) {
|
||||
log.error(
|
||||
{ err, code: err.code },
|
||||
`MYT gRPC error streaming Chunithm playlog for userID ${userID}`,
|
||||
);
|
||||
} else {
|
||||
log.error(
|
||||
{ err },
|
||||
`Unexpected MYT error streaming Chunithm playlog for userID ${userID}`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,29 +42,8 @@ export default async function ParseMytChunithm(
|
||||
userID: integer,
|
||||
log: KtLogger,
|
||||
): Promise<ParserFunctionReturns<MytChunithmScore, EmptyObject>> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "chunithm", log);
|
||||
const endpoint = GetMytHostname();
|
||||
const client = new ChunithmUserClient(endpoint, credentials.createSsl());
|
||||
const request = new GetPlaylogRequest();
|
||||
|
||||
request.setProfileApiId(profileApiId);
|
||||
|
||||
let iterable;
|
||||
|
||||
try {
|
||||
const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log);
|
||||
|
||||
iterable = getObjectsFromGrpcIterable(stream);
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`Unexpected MYT error while streaming Chunithm playlog items for userID ${userID}: ${err}`,
|
||||
);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
|
||||
return {
|
||||
iterable,
|
||||
iterable: streamPlaylog(userID, log),
|
||||
context: {},
|
||||
classProvider: null,
|
||||
game: "chunithm",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { GetPlaylogStreamItem } from "#proto/generated/chunithm/user_pb";
|
||||
|
||||
export type MytChunithmScore = GetPlaylogStreamItem.AsObject;
|
||||
export type MytChunithmScore = GetPlaylogStreamItem;
|
||||
|
||||
@@ -17,20 +17,20 @@ import type { ConverterFunction } from "../../common/types";
|
||||
import type { MytMaimaiDxScore } from "./types";
|
||||
|
||||
const DIFFICULTIES = {
|
||||
[MaimaiLevel.MAIMAI_LEVEL_UNSPECIFIED]: undefined,
|
||||
[MaimaiLevel.MAIMAI_LEVEL_BASIC]: "Basic",
|
||||
[MaimaiLevel.MAIMAI_LEVEL_ADVANCED]: "Advanced",
|
||||
[MaimaiLevel.MAIMAI_LEVEL_EXPERT]: "Expert",
|
||||
[MaimaiLevel.MAIMAI_LEVEL_MASTER]: "Master",
|
||||
[MaimaiLevel.MAIMAI_LEVEL_REMASTER]: "Re:Master",
|
||||
[MaimaiLevel.MAIMAI_LEVEL_UTAGE]: "Utage",
|
||||
[MaimaiLevel.UNSPECIFIED]: undefined,
|
||||
[MaimaiLevel.BASIC]: "Basic",
|
||||
[MaimaiLevel.ADVANCED]: "Advanced",
|
||||
[MaimaiLevel.EXPERT]: "Expert",
|
||||
[MaimaiLevel.MASTER]: "Master",
|
||||
[MaimaiLevel.REMASTER]: "Re:Master",
|
||||
[MaimaiLevel.UTAGE]: "Utage",
|
||||
};
|
||||
|
||||
function getLamp(
|
||||
comboStatus: number,
|
||||
isClear: boolean,
|
||||
): ScoreData<"maimaidx:Single">["lamp"] | undefined {
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_UNSPECIFIED) {
|
||||
if (comboStatus === MaimaiComboStatus.UNSPECIFIED) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -38,23 +38,23 @@ function getLamp(
|
||||
return "FAILED";
|
||||
}
|
||||
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_NONE) {
|
||||
if (comboStatus === MaimaiComboStatus.NONE) {
|
||||
return "CLEAR";
|
||||
}
|
||||
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO) {
|
||||
if (comboStatus === MaimaiComboStatus.FULL_COMBO) {
|
||||
return "FULL COMBO";
|
||||
}
|
||||
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_FULL_COMBO_PLUS) {
|
||||
if (comboStatus === MaimaiComboStatus.FULL_COMBO_PLUS) {
|
||||
return "FULL COMBO+";
|
||||
}
|
||||
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT) {
|
||||
if (comboStatus === MaimaiComboStatus.ALL_PERFECT) {
|
||||
return "ALL PERFECT";
|
||||
}
|
||||
|
||||
if (comboStatus === MaimaiComboStatus.MAIMAI_COMBO_STATUS_ALL_PERFECT_PLUS) {
|
||||
if (comboStatus === MaimaiComboStatus.ALL_PERFECT_PLUS) {
|
||||
return "ALL PERFECT+";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { MaimaiUserClient } from "#proto/generated/maimai/user_grpc_pb";
|
||||
import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/maimai/user_pb";
|
||||
import { credentials } from "@grpc/grpc-js";
|
||||
import { GetPlaylogRequestSchema, MaimaiUser } from "#proto/generated/maimai/user_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { ConnectError, createClient } from "@connectrpc/connect";
|
||||
|
||||
import type { ParserFunctionReturns } from "../../common/types";
|
||||
import type { MytMaimaiDxScore } from "./types";
|
||||
|
||||
import {
|
||||
FetchMytTitleAPIID,
|
||||
GetMytHostname,
|
||||
StreamRPCAsAsync,
|
||||
} from "../../common/api-myt/traverse-api";
|
||||
import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api";
|
||||
|
||||
async function* getObjectsFromGrpcIterable(
|
||||
iterable: AsyncIterable<GetPlaylogStreamItem>,
|
||||
): AsyncIterable<MytMaimaiDxScore> {
|
||||
for await (const item of iterable) {
|
||||
yield item.toObject();
|
||||
async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable<MytMaimaiDxScore> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", log);
|
||||
const client = createClient(MaimaiUser, CreateMytTransport());
|
||||
const request = create(GetPlaylogRequestSchema, { profileApiId });
|
||||
|
||||
try {
|
||||
for await (const item of client.getPlaylog(request)) {
|
||||
yield item;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ConnectError) {
|
||||
log.error(
|
||||
{ err, code: err.code },
|
||||
`MYT gRPC error streaming maimai DX playlog for userID ${userID}`,
|
||||
);
|
||||
} else {
|
||||
log.error(
|
||||
{ err },
|
||||
`Unexpected MYT error streaming maimai DX playlog for userID ${userID}`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,29 +42,8 @@ export default async function ParseMytMaimaiDx(
|
||||
userID: integer,
|
||||
log: KtLogger,
|
||||
): Promise<ParserFunctionReturns<MytMaimaiDxScore, EmptyObject>> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "maimaidx", log);
|
||||
const endpoint = GetMytHostname();
|
||||
const client = new MaimaiUserClient(endpoint, credentials.createSsl());
|
||||
const request = new GetPlaylogRequest();
|
||||
|
||||
request.setProfileApiId(profileApiId);
|
||||
|
||||
let iterable;
|
||||
|
||||
try {
|
||||
const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log);
|
||||
|
||||
iterable = getObjectsFromGrpcIterable(stream);
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`Unexpected MYT error while streaming maimai DX playlog items for userID ${userID}: ${err}`,
|
||||
);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
|
||||
return {
|
||||
iterable,
|
||||
iterable: streamPlaylog(userID, log),
|
||||
context: {},
|
||||
classProvider: null,
|
||||
game: "maimaidx",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { GetPlaylogStreamItem } from "#proto/generated/maimai/user_pb";
|
||||
|
||||
export type MytMaimaiDxScore = GetPlaylogStreamItem.AsObject;
|
||||
export type MytMaimaiDxScore = GetPlaylogStreamItem;
|
||||
|
||||
@@ -20,12 +20,12 @@ import type { ConverterFunction } from "../../common/types";
|
||||
import type { MytOngekiScore } from "./types";
|
||||
|
||||
const DIFFICULTIES = {
|
||||
[OngekiLevel.ONGEKI_LEVEL_UNSPECIFIED]: undefined,
|
||||
[OngekiLevel.ONGEKI_LEVEL_BASIC]: "BASIC",
|
||||
[OngekiLevel.ONGEKI_LEVEL_ADVANCED]: "ADVANCED",
|
||||
[OngekiLevel.ONGEKI_LEVEL_EXPERT]: "EXPERT",
|
||||
[OngekiLevel.ONGEKI_LEVEL_MASTER]: "MASTER",
|
||||
[OngekiLevel.ONGEKI_LEVEL_LUNATIC]: "LUNATIC",
|
||||
[OngekiLevel.UNSPECIFIED]: undefined,
|
||||
[OngekiLevel.BASIC]: "BASIC",
|
||||
[OngekiLevel.ADVANCED]: "ADVANCED",
|
||||
[OngekiLevel.EXPERT]: "EXPERT",
|
||||
[OngekiLevel.MASTER]: "MASTER",
|
||||
[OngekiLevel.LUNATIC]: "LUNATIC",
|
||||
};
|
||||
|
||||
function getNoteLamp(
|
||||
@@ -34,8 +34,8 @@ function getNoteLamp(
|
||||
techScore: number,
|
||||
): ScoreData<"ongeki:Single">["noteLamp"] | undefined {
|
||||
if (
|
||||
comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_UNSPECIFIED ||
|
||||
clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_UNSPECIFIED
|
||||
comboStatus === OngekiComboStatus.UNSPECIFIED ||
|
||||
clearStatus === OngekiClearStatus.UNSPECIFIED
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -44,22 +44,22 @@ function getNoteLamp(
|
||||
return "ALL BREAK+";
|
||||
}
|
||||
|
||||
if (comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_ALL_BREAK) {
|
||||
if (comboStatus === OngekiComboStatus.ALL_BREAK) {
|
||||
return "ALL BREAK";
|
||||
}
|
||||
|
||||
if (comboStatus === OngekiComboStatus.ONGEKI_COMBO_STATUS_FULL_COMBO) {
|
||||
if (comboStatus === OngekiComboStatus.FULL_COMBO) {
|
||||
return "FULL COMBO";
|
||||
}
|
||||
|
||||
if (
|
||||
clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_OVER_DAMAGE ||
|
||||
clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_CLEARED
|
||||
clearStatus === OngekiClearStatus.OVER_DAMAGE ||
|
||||
clearStatus === OngekiClearStatus.CLEARED
|
||||
) {
|
||||
return "CLEAR";
|
||||
}
|
||||
|
||||
if (clearStatus === OngekiClearStatus.ONGEKI_CLEAR_STATUS_FAILED) {
|
||||
if (clearStatus === OngekiClearStatus.FAILED) {
|
||||
return "LOSS";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,24 +3,38 @@ import type { EmptyObject } from "#utils/types";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { OngekiUserClient } from "#proto/generated/ongeki/user_grpc_pb";
|
||||
import { GetPlaylogRequest, type GetPlaylogStreamItem } from "#proto/generated/ongeki/user_pb";
|
||||
import { credentials } from "@grpc/grpc-js";
|
||||
import { GetPlaylogRequestSchema, OngekiUser } from "#proto/generated/ongeki/user_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { ConnectError, createClient } from "@connectrpc/connect";
|
||||
|
||||
import type { ParserFunctionReturns } from "../../common/types";
|
||||
import type { MytOngekiScore } from "./types";
|
||||
|
||||
import {
|
||||
FetchMytTitleAPIID,
|
||||
GetMytHostname,
|
||||
StreamRPCAsAsync,
|
||||
} from "../../common/api-myt/traverse-api";
|
||||
import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api";
|
||||
|
||||
async function* getObjectsFromGrpcIterable(
|
||||
iterable: AsyncIterable<GetPlaylogStreamItem>,
|
||||
): AsyncIterable<MytOngekiScore> {
|
||||
for await (const item of iterable) {
|
||||
yield item.toObject();
|
||||
async function* streamPlaylog(userID: integer, log: KtLogger): AsyncIterable<MytOngekiScore> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "ongeki", log);
|
||||
const client = createClient(OngekiUser, CreateMytTransport());
|
||||
const request = create(GetPlaylogRequestSchema, { profileApiId });
|
||||
|
||||
try {
|
||||
for await (const item of client.getPlaylog(request)) {
|
||||
yield item;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ConnectError) {
|
||||
log.error(
|
||||
{ err, code: err.code },
|
||||
`MYT gRPC error streaming Ongeki playlog for userID ${userID}`,
|
||||
);
|
||||
} else {
|
||||
log.error(
|
||||
{ err },
|
||||
`Unexpected MYT error streaming Ongeki playlog for userID ${userID}`,
|
||||
);
|
||||
}
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,29 +42,8 @@ export default async function ParseMytOngeki(
|
||||
userID: integer,
|
||||
log: KtLogger,
|
||||
): Promise<ParserFunctionReturns<MytOngekiScore, EmptyObject>> {
|
||||
const profileApiId = await FetchMytTitleAPIID(userID, "ongeki", log);
|
||||
const endpoint = GetMytHostname();
|
||||
const client = new OngekiUserClient(endpoint, credentials.createSsl());
|
||||
const request = new GetPlaylogRequest();
|
||||
|
||||
request.setProfileApiId(profileApiId);
|
||||
|
||||
let iterable;
|
||||
|
||||
try {
|
||||
const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), request, log);
|
||||
|
||||
iterable = getObjectsFromGrpcIterable(stream);
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`Unexpected MYT error while streaming Ongeki playlog items for userID ${userID}: ${err}`,
|
||||
);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
|
||||
return {
|
||||
iterable,
|
||||
iterable: streamPlaylog(userID, log),
|
||||
context: {},
|
||||
classProvider: null,
|
||||
game: "ongeki",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { GetPlaylogStreamItem } from "#proto/generated/ongeki/user_pb";
|
||||
|
||||
export type MytOngekiScore = GetPlaylogStreamItem.AsObject;
|
||||
export type MytOngekiScore = GetPlaylogStreamItem;
|
||||
|
||||
+13
-27
@@ -1,50 +1,36 @@
|
||||
import type { ClassProvider } from "#lib/score-import/framework/calculated-data/types";
|
||||
import type { WaccaUserClient } from "#proto/generated/wacca/user_grpc_pb";
|
||||
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { WaccaVersion } from "#proto/generated/wacca/common_pb";
|
||||
import { DataRequest, type DataResponse } from "#proto/generated/wacca/user_pb";
|
||||
import { DataRequestSchema, WaccaUser } from "#proto/generated/wacca/user_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { createClient, type Transport } from "@connectrpc/connect";
|
||||
import { WaccaStageUps } from "tachi-common/config/game-support/wacca";
|
||||
|
||||
import { UnaryRPCAsAsync } from "../../common/api-myt/traverse-api";
|
||||
|
||||
export default async function CreateMytWACCAClassHandler(
|
||||
titleApiId: string,
|
||||
userClient: WaccaUserClient,
|
||||
transport: Transport,
|
||||
): Promise<ClassProvider> {
|
||||
const req = new DataRequest();
|
||||
|
||||
req.setApiId(titleApiId);
|
||||
const dataRes: DataResponse = await UnaryRPCAsAsync(userClient.getData.bind(userClient), req);
|
||||
const data = dataRes;
|
||||
const client = createClient(WaccaUser, transport);
|
||||
const req = create(DataRequestSchema, { apiId: titleApiId });
|
||||
const data = await client.getData(req);
|
||||
|
||||
return (_gptString, _userID, _ratings, _logger) => {
|
||||
// Currently (May 2025) Reverse and Plus are supported on Myt.
|
||||
// We look for both Reverse and PLUS version data, PLUS being prioritized if exists.
|
||||
// If / when custom dans are added, this will need to change.
|
||||
const versionDataMap = data.getVersionDataMap();
|
||||
let versionData: DataResponse.VersionData | undefined;
|
||||
const versionData =
|
||||
data.versionData[WaccaVersion.PLUS] ?? data.versionData[WaccaVersion.REVERSE];
|
||||
|
||||
if (versionDataMap.has(WaccaVersion.WACCA_VERSION_PLUS)) {
|
||||
versionData = versionDataMap.get(WaccaVersion.WACCA_VERSION_PLUS);
|
||||
} else if (versionDataMap.has(WaccaVersion.WACCA_VERSION_REVERSE)) {
|
||||
versionData = versionDataMap.get(WaccaVersion.WACCA_VERSION_REVERSE);
|
||||
}
|
||||
|
||||
// rank:
|
||||
// 0 -> none
|
||||
// 1 -> stage I
|
||||
// 2 -> stage II
|
||||
// ...
|
||||
// 14 -> stage XIV
|
||||
if (versionData === undefined || versionData.getRank() === 0) {
|
||||
// rank: 0 = none, 1 = stage I, 2 = stage II, ... 14 = stage XIV
|
||||
if (versionData === undefined || versionData.rank === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const stageEnum = WaccaStageUps[versionData.getRank() - 1];
|
||||
const stageEnum = WaccaStageUps[versionData.rank - 1];
|
||||
|
||||
if (stageEnum === undefined) {
|
||||
throw new ScoreImportFatalError(400, `Unknown stage up value ${versionData.getRank()}`);
|
||||
throw new ScoreImportFatalError(400, `Unknown stage up value ${versionData.rank}`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -9,30 +9,23 @@ import {
|
||||
SongOrChartNotFoundFailure,
|
||||
} from "#lib/score-import/framework/common/converter-failures";
|
||||
import { ParseDateFromString } from "#lib/score-import/framework/common/score-utils";
|
||||
import {
|
||||
type WaccaClearStatus,
|
||||
WaccaMusicDifficulty,
|
||||
type WaccaMusicDifficultyMap,
|
||||
} from "#proto/generated/wacca/common_pb";
|
||||
import { type WaccaClearStatus, WaccaMusicDifficulty } from "#proto/generated/wacca/common_pb";
|
||||
import { FindChartOnInGameID } from "#utils/queries/charts";
|
||||
import { FindSongOnID } from "#utils/queries/songs";
|
||||
|
||||
import type { ConverterFunction } from "../../common/types";
|
||||
import type { MytWaccaScore } from "./types";
|
||||
|
||||
const DIFFICULTIES: Record<
|
||||
WaccaMusicDifficultyMap[keyof WaccaMusicDifficultyMap],
|
||||
Difficulties["wacca:Single"] | undefined
|
||||
> = {
|
||||
[WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_UNSPECIFIED]: undefined,
|
||||
[WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_NORMAL]: "NORMAL",
|
||||
[WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_HARD]: "HARD",
|
||||
[WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_EXPERT]: "EXPERT",
|
||||
[WaccaMusicDifficulty.WACCA_MUSIC_DIFFICULTY_INFERNO]: "INFERNO",
|
||||
const DIFFICULTIES: Partial<Record<WaccaMusicDifficulty, Difficulties["wacca:Single"]>> = {
|
||||
[WaccaMusicDifficulty.UNSPECIFIED]: undefined,
|
||||
[WaccaMusicDifficulty.NORMAL]: "NORMAL",
|
||||
[WaccaMusicDifficulty.HARD]: "HARD",
|
||||
[WaccaMusicDifficulty.EXPERT]: "EXPERT",
|
||||
[WaccaMusicDifficulty.INFERNO]: "INFERNO",
|
||||
};
|
||||
|
||||
function convertClearStatus(
|
||||
status: WaccaClearStatus.AsObject | undefined,
|
||||
status: WaccaClearStatus | undefined,
|
||||
): GetEnumValue<"wacca:Single", "lamp"> {
|
||||
if (status === undefined) {
|
||||
throw new InvalidScoreFailure(`Can't process a score without clearStatus`);
|
||||
|
||||
@@ -3,27 +3,37 @@ import type { EmptyObject } from "#utils/types";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { WaccaUserClient } from "#proto/generated/wacca/user_grpc_pb";
|
||||
import { PlaylogRequest, type PlaylogStreamItem } from "#proto/generated/wacca/user_pb";
|
||||
import { credentials } from "@grpc/grpc-js";
|
||||
import { PlaylogRequestSchema, WaccaUser } from "#proto/generated/wacca/user_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { ConnectError, createClient } from "@connectrpc/connect";
|
||||
|
||||
import type { ParserFunctionReturns } from "../../common/types";
|
||||
import type { MytWaccaScore } from "./types";
|
||||
|
||||
import {
|
||||
FetchMytTitleAPIID,
|
||||
GetMytHostname,
|
||||
StreamRPCAsAsync,
|
||||
} from "../../common/api-myt/traverse-api";
|
||||
import { CreateMytTransport, FetchMytTitleAPIID } from "../../common/api-myt/traverse-api";
|
||||
import CreateMytWACCAClassHandler from "./class-handler";
|
||||
|
||||
async function* getObjectsFromGrpcIterable(
|
||||
iterable: AsyncIterable<PlaylogStreamItem>,
|
||||
): AsyncIterable<MytWaccaScore> {
|
||||
for await (const item of iterable) {
|
||||
// Re: non-null assertion - in GRPC, all fields are always technically optional.
|
||||
// Realistically, it's ok to crash if this is undefined - it should never be.
|
||||
yield item.getInfo()!.toObject();
|
||||
async function* streamPlaylog(apiId: string, log: KtLogger): AsyncIterable<MytWaccaScore> {
|
||||
const client = createClient(WaccaUser, CreateMytTransport());
|
||||
const request = create(PlaylogRequestSchema, { apiId });
|
||||
|
||||
try {
|
||||
for await (const item of client.getPlaylog(request)) {
|
||||
if (!item.info) {
|
||||
log.warn(`Received WACCA playlog stream item with no info — skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
yield item.info;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ConnectError) {
|
||||
log.error({ err, code: err.code }, `MYT gRPC error streaming WACCA playlog`);
|
||||
} else {
|
||||
log.error({ err }, `Unexpected MYT error streaming WACCA playlog`);
|
||||
}
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,56 +42,18 @@ export default async function ParseMytWACCA(
|
||||
log: KtLogger,
|
||||
): Promise<ParserFunctionReturns<MytWaccaScore, EmptyObject>> {
|
||||
const titleApiId = await FetchMytTitleAPIID(userID, "wacca", log);
|
||||
const endpoint = GetMytHostname();
|
||||
let client;
|
||||
|
||||
try {
|
||||
client = new WaccaUserClient(endpoint, credentials.createSsl());
|
||||
} catch (err) {
|
||||
// Note: I don't think this actually does anything on the network, so
|
||||
// it shouldn't really fail. Still, wrap just in case.
|
||||
|
||||
log.error(
|
||||
{
|
||||
userID,
|
||||
err,
|
||||
},
|
||||
`Unexpected MYT during WaccaUserClient creation for ${userID}: ${err}`,
|
||||
);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to connect to MYT.`);
|
||||
}
|
||||
|
||||
const req = new PlaylogRequest();
|
||||
|
||||
req.setApiId(titleApiId);
|
||||
|
||||
let iterable;
|
||||
|
||||
try {
|
||||
const stream = StreamRPCAsAsync(client.getPlaylog.bind(client), req, log);
|
||||
|
||||
iterable = getObjectsFromGrpcIterable(stream);
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`Unexpected MYT error while streaming WACCA playlog items for userID ${userID}: ${err}`,
|
||||
);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to get scores from MYT.`);
|
||||
}
|
||||
|
||||
let classProvider;
|
||||
|
||||
try {
|
||||
classProvider = await CreateMytWACCAClassHandler(titleApiId, client);
|
||||
classProvider = await CreateMytWACCAClassHandler(titleApiId, CreateMytTransport());
|
||||
} catch (err) {
|
||||
log.error(`Unexpected MYT error while fetching player data for userID ${userID}: ${err}`);
|
||||
|
||||
throw new ScoreImportFatalError(500, `Failed to fetch player data from MYT.`);
|
||||
}
|
||||
|
||||
return {
|
||||
iterable,
|
||||
iterable: streamPlaylog(titleApiId, log),
|
||||
context: {},
|
||||
classProvider,
|
||||
game: "wacca",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { PlaylogInfo } from "#proto/generated/wacca/user_pb";
|
||||
|
||||
export type MytWaccaScore = PlaylogInfo.AsObject;
|
||||
export type MytWaccaScore = PlaylogInfo;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user