checkpoint

This commit is contained in:
zk
2026-03-13 17:35:10 +00:00
parent d7bb9541f6
commit 4d489cbdea
326 changed files with 1535 additions and 6344 deletions
+285
View File
@@ -0,0 +1,285 @@
#!/usr/bin/env bun
/**
* ts_machete: Find (and optionally remove) unused dependencies across workspace packages.
* Mirrors the behaviour of `cargo machete` for TypeScript/JS monorepos.
*
* Usage:
* bun .scripts/ts_machete.js # report unused deps
* bun .scripts/ts_machete.js --fix # also remove them from package.json
*/
import path from "node:path";
import fs from "node:fs";
import { Glob } from "bun";
const args = process.argv.slice(2);
const fix = args.includes("--fix");
// ANSI colours
const RED = "\x1b[31m";
const YELLOW = "\x1b[33m";
const GREEN = "\x1b[32m";
const BOLD = "\x1b[1m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";
const cwd = path.resolve(__dirname, "..");
process.chdir(cwd);
const rootPkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Node.js built-in module names (without the node: prefix). */
const NODE_BUILTINS = new Set([
"assert",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"http",
"http2",
"https",
"inspector",
"module",
"net",
"os",
"path",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"repl",
"stream",
"string_decoder",
"sys",
"timers",
"tls",
"trace_events",
"tty",
"url",
"util",
"v8",
"vm",
"worker_threads",
"zlib",
]);
/**
* Given an import specifier, return the npm package name it belongs to,
* or null if it is a relative/local/built-in import that has no package.json entry.
*
* Returns the sentinel "__node_builtin__" for bare or node:-prefixed Node.js modules
* so that @types/node detection still works.
*/
function extractPackageName(specifier) {
// Relative imports and local path aliases (e.g. "#utils/log")
if (specifier.startsWith(".") || specifier.startsWith("#")) {
return null;
}
// node: protocol (e.g. "node:fs")
if (specifier.startsWith("node:")) {
return "__node_builtin__";
}
const bare = specifier.split("/")[0];
// Bare Node.js built-ins imported without the node: prefix (legacy style)
if (NODE_BUILTINS.has(bare)) {
return "__node_builtin__";
}
// Scoped packages: "@scope/pkg" or "@scope/pkg/subpath" → "@scope/pkg"
if (specifier.startsWith("@")) {
const parts = specifier.split("/");
if (parts.length >= 2) {
return `${parts[0]}/${parts[1]}`;
}
return null;
}
// Regular packages: "lodash/fp" → "lodash"
return bare;
}
/** Build-output directory prefixes to exclude when scanning source files. */
const EXCLUDED_PREFIXES = ["node_modules/", "js/", "dist/", "build/", ".cache/"];
/** Return all TS/JS source file paths inside a package directory. */
function getSourceFiles(pkgDir) {
const glob = new Glob("**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}");
const files = [];
for (const rel of glob.scanSync(pkgDir)) {
if (EXCLUDED_PREFIXES.some((prefix) => rel.startsWith(prefix))) {
continue;
}
files.push(path.join(pkgDir, rel));
}
return files;
}
// Pre-compiled patterns for extracting import specifiers from source text.
// We match three syntactic forms:
// 1. Static imports/exports: import ... from "pkg" / export ... from "pkg"
// 2. Dynamic imports: import("pkg")
// 3. CommonJS require: require("pkg")
const IMPORT_PATTERNS = [
/\bfrom\s+["']([^"'\s]+)["']/gm,
/\bimport\(["']([^"'\s]+)["']\)/gm,
/\brequire\(["']([^"'\s]+)["']\)/gm,
];
/**
* Scan all source files in pkgDir and return the set of npm package names
* that are actually imported.
*/
function findUsedPackages(pkgDir) {
const used = new Set();
const files = getSourceFiles(pkgDir);
for (const file of files) {
let content;
try {
content = fs.readFileSync(file, "utf8");
} catch {
continue;
}
for (const pattern of IMPORT_PATTERNS) {
pattern.lastIndex = 0;
let match;
while ((match = pattern.exec(content)) !== null) {
const pkgName = extractPackageName(match[1]);
if (pkgName !== null) {
used.add(pkgName);
}
}
}
}
return used;
}
/**
* Determine whether a listed dependency is "used", given the set of packages
* actually imported in the source tree.
*
* Special handling for @types/* packages: "@types/foo" is considered used when:
* - "foo" itself appears in imports (TypeScript automatically uses @types/foo), OR
* - depName is "@types/node" and any Node.js built-in is imported.
*/
function isDepUsed(depName, usedPackages) {
if (depName.startsWith("@types/")) {
const underlying = depName.slice("@types/".length);
if (underlying === "node") {
return usedPackages.has("__node_builtin__");
}
// "@types/react-router-dom" → underlying "react-router-dom"
return usedPackages.has(underlying);
}
return usedPackages.has(depName);
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
let totalUnused = 0;
let totalFixed = 0;
let packageCount = 0;
for (const workspacePattern of rootPkg.workspaces) {
const glob = new Glob(workspacePattern + "*/package.json");
for (const pkgPath of glob.scanSync(".")) {
// Exclude packages nested inside node_modules (the workspace glob pattern
// produces "typescript/**\/package.json" which recurses into node_modules).
if (pkgPath.includes("node_modules/")) {
continue;
}
const pkgDir = path.dirname(pkgPath);
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
const usedPackages = findUsedPackages(path.join(cwd, pkgDir));
const unusedBySection = {};
for (const section of ["dependencies", "devDependencies"]) {
if (!pkg[section]) {
continue;
}
for (const depName of Object.keys(pkg[section])) {
if (!isDepUsed(depName, usedPackages)) {
(unusedBySection[section] ??= []).push(depName);
}
}
}
const unusedCount = Object.values(unusedBySection).reduce((n, arr) => n + arr.length, 0);
if (unusedCount === 0) {
continue;
}
packageCount++;
console.log(`\n${BOLD}${pkgPath}${RESET} ${DIM}(${pkg.name ?? pkgDir})${RESET}`);
for (const [section, deps] of Object.entries(unusedBySection)) {
console.log(` ${DIM}${section}:${RESET}`);
for (const dep of deps) {
console.log(` ${RED}✗${RESET} ${dep}`);
totalUnused++;
if (fix) {
delete pkg[section][dep];
totalFixed++;
}
}
}
if (fix && unusedCount > 0) {
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t"));
}
}
}
// Summary
console.log("");
if (totalUnused === 0) {
console.log(`${GREEN}${BOLD}✓ No unused dependencies found.${RESET}`);
} else {
const depWord = totalUnused === 1 ? "dependency" : "dependencies";
if (fix) {
console.log(
`${GREEN}${BOLD}✓ Removed ${totalFixed} unused ${depWord} across ${packageCount} package(s).${RESET}`,
);
} else {
console.log(
`${YELLOW}${BOLD}⚠ Found ${totalUnused} unused ${depWord} across ${packageCount} package(s).${RESET}`,
);
console.log(`${DIM} Run with --fix to remove them automatically.${RESET}`);
}
}
process.exit(totalUnused > 0 && !fix ? 1 : 0);
+112 -23
View File
@@ -92,10 +92,9 @@
"json5": "catalog:",
"lodash": "catalog:",
"luxon": "catalog:",
"mei-logger": "catalog:",
"monk": "catalog:",
"node-fetch": "catalog:",
"pino": "^10.3.1",
"pino": "catalog:",
"prudence": "catalog:",
"seq-logging": "catalog:",
"tachi-common": "workspace:*",
@@ -333,7 +332,6 @@
"html-entities": "catalog:",
"iconv-lite": "catalog:",
"lodash.get": "catalog:",
"mei-logger": "catalog:",
"monk": "catalog:",
"node-fetch": "catalog:",
"prudence": "catalog:",
@@ -401,7 +399,7 @@
"node-html-parser": "catalog:",
"nodemailer": "catalog:",
"pg": "catalog:",
"pino": "^10.3.1",
"pino": "catalog:",
"prudence": "catalog:",
"rate-limit-redis": "catalog:",
"redis": "catalog:",
@@ -446,7 +444,6 @@
"bms-table-loader": "catalog:",
"commander": "catalog:",
"fast-xml-parser": "catalog:",
"mei-logger": "catalog:",
"node-fetch": "catalog:",
"tachi-common": "workspace:*",
"winston": "catalog:",
@@ -503,6 +500,7 @@
"@types/google-protobuf": "^3.15.12",
"@types/history": "4.7.11",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^26.0.23",
"@types/json5": "2.2.0",
"@types/lodash": "4.14.172",
"@types/lodash.get": "^4.4.7",
@@ -511,7 +509,7 @@
"@types/mongodb": "3.6.20",
"@types/multer": "1.4.7",
"@types/natural-compare": "^1.4.1",
"@types/node": "18.11.18",
"@types/node": "*",
"@types/node-fetch": "2.5.12",
"@types/nodemailer": "^6.4.4",
"@types/pg": "^8.11.11",
@@ -527,8 +525,8 @@
"@types/semver": "^7.3.9",
"@types/supertest": "2.0.11",
"@types/tap": "15.0.5",
"@typescript-eslint/eslint-plugin": "^8.23.0",
"@typescript-eslint/parser": "^8.23.0",
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"@typescript-eslint/utils": "^8.24.1",
"@typescript/native-preview": "7.0.0-dev.20260309.1",
"@valuabletouch/winston-seq": "1.3.0",
@@ -556,9 +554,12 @@
"discord.js": "13.6.0",
"dotenv": "16.0.0",
"encoding-japanese": "^1.0.30",
"eslint": "^7.29.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^16.0.3",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-perfectionist": "^3.9.1",
"eslint-plugin-react": "^7.37.4",
@@ -591,6 +592,7 @@
"isexe": "^2.0.0",
"istanbul-lib-processinfo": "^2.0.2",
"jackspeak": "^1.4.0",
"jest": "^27.0.4",
"json-2-csv": "3.14.4",
"json5": "2.2.0",
"kysely": "^0.27.5",
@@ -598,7 +600,6 @@
"lodash": "4.17.21",
"lodash.get": "^4.4.2",
"luxon": "^2.3.1",
"mei-logger": "^1.0.1",
"minipass": "^3.1.1",
"mkdirp": "^1.0.4",
"mongodb": "3.6.3",
@@ -617,6 +618,7 @@
"nyc": "^15.1.0",
"opener": "^1.5.1",
"pg": "^8.13.3",
"pino": "^10.3.1",
"prettier": "3.2.5",
"pretty-bytes": "5.6.0",
"process-on-spawn": "^1.0.0",
@@ -643,6 +645,7 @@
"seq-logging": "1.1.1",
"signal-exit": "^3.0.0",
"source-map-support": "^0.5.16",
"standard": "^16.0.3",
"supertest": "6.2.2",
"sync-fetch": "^0.3.1",
"tap-mocha-reporter": "^5.0.0",
@@ -650,10 +653,12 @@
"tap-yaml": "^1.0.0",
"tcompare": "^5.0.6",
"treport": "^2.0.2",
"ts-jest": "^27.0.3",
"ts-node": "^8.5.2",
"ts-protoc-gen": "^0.15.0",
"tsconfig-paths": "3.12.0",
"typescript": "^3.7.2",
"tsd": "^0.17.0",
"typescript": "^4.3.4",
"typescript-eslint": "^8.24.1",
"uuidv7": "^1.1.0",
"vite": "^3.0.7",
@@ -1460,25 +1465,27 @@
"@types/yoga-layout": ["@types/yoga-layout@1.9.2", "", {}, "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@4.33.0", "", { "dependencies": { "@typescript-eslint/experimental-utils": "4.33.0", "@typescript-eslint/scope-manager": "4.33.0", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.1.0", "semver": "^7.3.5", "tsutils": "^3.21.0" }, "peerDependencies": { "@typescript-eslint/parser": "^4.0.0", "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
"@typescript-eslint/experimental-utils": ["@typescript-eslint/experimental-utils@4.33.0", "", { "dependencies": { "@types/json-schema": "^7.0.7", "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", "@typescript-eslint/typescript-estree": "4.33.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, "peerDependencies": { "eslint": "*" } }, "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@4.33.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", "@typescript-eslint/typescript-estree": "4.33.0", "debug": "^4.3.1" }, "peerDependencies": { "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@4.33.0", "", { "dependencies": { "@typescript-eslint/types": "4.33.0", "@typescript-eslint/visitor-keys": "4.33.0" } }, "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"@typescript-eslint/types": ["@typescript-eslint/types@4.33.0", "", {}, "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@4.33.0", "", { "dependencies": { "@typescript-eslint/types": "4.33.0", "@typescript-eslint/visitor-keys": "4.33.0", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", "semver": "^7.3.5", "tsutils": "^3.21.0" } }, "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@4.33.0", "", { "dependencies": { "@typescript-eslint/types": "4.33.0", "eslint-visitor-keys": "^2.0.0" } }, "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg=="],
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260309.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260309.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260309.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260309.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260309.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260309.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260309.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260309.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-ZK+ExK7scBzUCAXCTtAwUm6QENJ+l3tCDQXNCly4WcGUvbIAWdaiNns4brganGN9nrxxRkC9Rx0CrxvIsn9zHA=="],
@@ -2090,6 +2097,8 @@
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-utils": ["eslint-utils@3.0.0", "", { "dependencies": { "eslint-visitor-keys": "^2.0.0" }, "peerDependencies": { "eslint": ">=5" } }, "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"esm": ["esm@3.2.25", "", {}, "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA=="],
@@ -2220,6 +2229,8 @@
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
"functional-red-black-tree": ["functional-red-black-tree@1.0.1", "", {}, "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g=="],
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
@@ -2592,8 +2603,6 @@
"media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
"mei-logger": ["mei-logger@1.0.3", "", { "dependencies": { "winston": "^3.3.3" } }, "sha512-s6dMacOWAfDSQ58idaRpiE/xCVsbHSrixnGs9YZbafLNgZHZ9/ByTsGdogU1GwpGKFxgc9QRDVqKxfUA3FhkHA=="],
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
"memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="],
@@ -3030,6 +3039,8 @@
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
"regexpp": ["regexpp@3.2.0", "", {}, "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="],
"reinterval": ["reinterval@1.1.0", "", {}, "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="],
"relateurl": ["relateurl@0.2.7", "", {}, "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="],
@@ -3334,6 +3345,8 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tsutils": ["tsutils@3.21.0", "", { "dependencies": { "tslib": "^1.8.1" }, "peerDependencies": { "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
@@ -3636,11 +3649,21 @@
"@types/superagent/form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/experimental-utils/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
"@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="],
"@vitejs/plugin-react/@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
@@ -3738,12 +3761,16 @@
"eslint-plugin-import/tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
"eslint-plugin-perfectionist/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"eslint-plugin-perfectionist/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
"eslint-plugin-react/prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="],
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"express-session/cookie": ["cookie@0.4.1", "", {}, "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="],
@@ -3986,6 +4013,14 @@
"tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
"tsutils/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"typescript-eslint/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="],
"typescript-eslint/@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
"typescript-eslint/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
"uvu/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="],
"verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
@@ -4062,7 +4097,17 @@
"@types/minimatch/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"@typescript-eslint/experimental-utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"@vitejs/plugin-react/@babel/core/convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
@@ -4178,6 +4223,24 @@
"test-exclude/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"typescript-eslint/@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"vite-node/vite/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
"vite-node/vite/rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
@@ -4206,7 +4269,15 @@
"@types/minimatch/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"kanel/rimraf/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
@@ -4234,6 +4305,18 @@
"test-exclude/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"typescript-eslint/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"vite-node/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
"vite-node/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
@@ -4250,6 +4333,10 @@
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"kanel/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"log-update/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="],
@@ -4260,6 +4347,8 @@
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"typescript-eslint/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"kanel/rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+20 -7
View File
@@ -1,6 +1,6 @@
services:
mongo:
container_name: mongo
tachi-mongo:
container_name: tachi-mongo
command: "--config /etc/mongod.conf"
image: mongo:5
restart: unless-stopped
@@ -10,17 +10,17 @@ services:
- tachi-volume:/data/db
- "./dev/mongod.conf:/etc/mongod.conf"
redis:
container_name: redis
tachi-redis:
container_name: tachi-redis
command: redis-server --save 60 1 --loglevel warning
image: redis:6
image: redis:7.4.1
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- tachi-redis:/data
postgres:
container_name: postgres
tachi-postgres:
container_name: tachi-postgres
image: postgres:18
restart: unless-stopped
ports:
@@ -32,6 +32,18 @@ services:
volumes:
- tachi-postgres:/var/lib/postgresql
- "./dev/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql"
tachi-s3:
container_name: tachi-s3
image: quay.io/minio/minio:RELEASE.2024-10-29T16-01-48Z
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: password
ports:
- "9000:9000"
- "9001:9001"
volumes:
- tachi-minio:/data
entrypoint: /usr/bin/minio server /data --console-address=':9001'
tachi-dev:
user: "1000:1000"
@@ -56,3 +68,4 @@ volumes:
tachi-redis:
tachi-logs:
tachi-postgres:
tachi-minio:
-1
View File
@@ -104,7 +104,6 @@
"discord.js": "13.6.0",
"express": "4.20.0",
"json5": "2.2.0",
"mei-logger": "^1.0.1",
"monk": "^7.3.4",
"node-fetch": "2.6.7",
"seq-logging": "1.1.1",
-1
View File
@@ -38,7 +38,6 @@
"json5": "catalog:",
"lodash": "catalog:",
"luxon": "catalog:",
"mei-log": "catalog:",
"monk": "catalog:",
"node-fetch": "catalog:",
"pino": "catalog:",
+1 -2
View File
@@ -43,7 +43,6 @@ function ParseBotConfig(fileLoc = "conf.json5"): BotConfig {
try {
const contents = fs.readFileSync(fileLoc, "utf-8");
data = JSON5.parse(contents);
} catch (err) {
log.error({ err }, "Failed to find/parse a valid conf.json5 file. Cannot boot.");
@@ -148,7 +147,7 @@ async function GetServerConfig() {
// this fetch is complete, and it saves us having to do a singleton pattern or worse.
// This *should* be solved with top-level-await, but good luck actually getting
// typescript to output the right stuff here.
const res = await fetch(`${BotConfig.TACHI_SERVER_LOCATION}/api/v1/config`).then((res) =>
res.json(),
);
-1
View File
@@ -6,7 +6,6 @@ import type { DiscordUserMapDocument } from "./documents";
import db from "./mongo";
export function GetUserAndTokenForDiscordID(
discordID: string,
): Promise<DiscordUserMapDocument | null> {
+1 -4
View File
@@ -4,7 +4,6 @@ import { log } from "#utils/log";
import { BotConfig } from "../config";
/**
* Middleware that checks that a webhook request has Authorization set to
* exactly "Bearer $CLIENT_SECRET".
@@ -34,9 +33,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => {
}
if (value !== BotConfig.OAUTH.CLIENT_SECRET) {
log.warn(
`Recieved invalid auth value from ${req.ip}. Has the client secret been changed?`,
);
log.warn(`Recieved invalid auth value from ${req.ip}. Has the client secret been changed?`);
return res.status(403).json({
success: false,
description: "Unauthorised.",
+5 -4
View File
@@ -15,7 +15,6 @@ import { ValidateWebhookRequest } from "./middleware";
export const app: Express = express();
app.use(express.json());
// Let NGINX work its magic.
@@ -88,8 +87,7 @@ app.get("/oauth/callback", async (req, res) => {
const whoamiRes = await TachiServerV1Get<UserDocument>("/users/me", apiToken);
if (!whoamiRes.success) {
log.error({ discordID }
, "Failed to request user with token we just got?");
log.error({ discordID }, "Failed to request user with token we just got?");
return res
.status(500)
.send(
@@ -208,7 +206,10 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne
const expErr: ExpressJSONErr = err as ExpressJSONErr;
if (expErr.status === 400 && "body" in expErr) {
log.info({ url: req.originalUrl, err: err }, `Error in parsing JSON in request body from ${req.url}`);
log.info(
{ url: req.originalUrl, err: err },
`Error in parsing JSON in request body from ${req.url}`,
);
return res.status(400).send({ success: false, description: err.message });
}
+5 -4
View File
@@ -7,7 +7,10 @@ import { URLSearchParams } from "url";
import { BotConfig } from "../config";
import { VERSION_STR } from "../version";
export type APIResponse<T> = (({ body: null } & UnsuccessfulAPIResponse) | SuccessfulAPIResponse<T>) & {
export type APIResponse<T> = (
| ({ body: null } & UnsuccessfulAPIResponse)
| SuccessfulAPIResponse<T>
) & {
statusCode: integer;
};
@@ -132,9 +135,7 @@ export function PrependTachiUrl(url: string, version: "1" = "1"): string {
*/
function LogRequestResult(logUrl: string, res: APIResponse<unknown>): void {
if (!res.success) {
log.warn(
`Request ${logUrl} was unsuccessful: ${res.description} (${res.statusCode})`,
);
log.warn(`Request ${logUrl} was unsuccessful: ${res.description} (${res.statusCode})`);
} else {
log.debug(`Request ${logUrl} was successful: ${res.description} (${res.statusCode})`);
}
-6
View File
@@ -2,12 +2,6 @@ const MAJOR = 2;
const MINOR = 2;
const PATCH = 0;
export const VERSION_INFO = {
major: MAJOR,
minor: MINOR,
patch: PATCH,
};
export const VERSION_STR = `${MAJOR}.${MINOR}.${PATCH}`;
export const VERSION_PRETTY = `v${VERSION_STR}`;
@@ -14,7 +14,7 @@ import { client } from "../main";
import { GetUGPTStats, GetUserInfo } from "../utils/apiRequests";
import { CreateEmbed } from "../utils/embeds";
import { PrependTachiUrl } from "../utils/fetchTachi";
import { log } from "#utils/log"
import { log } from "#utils/log";
import { FormatClass, GetGameChannel } from "../utils/misc";
export async function HandleClassUpdateV1(
@@ -1,6 +1,6 @@
import { BotConfig } from "#config";
import { PrependTachiUrl } from "#utils/fetchTachi";
import { log } from "#utils/log"
import { log } from "#utils/log";
import {
GetGameGroupConfig,
type GoalDocument,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
{"version":"7.0.0-dev.20260309.1","fileInfos":[],"options":{"allowJs":true,"allowImportingTsExtensions":true,"composite":true,"declaration":true,"declarationMap":true,"jsx":4,"module":7,"noFallthroughCasesInSwitch":true,"noImplicitAny":false,"outDir":"./js","rootDir":"./","skipLibCheck":true,"strict":true,"strictNullChecks":true,"sourceMap":true,"target":9,"tsBuildInfoFile":"./.tsbuildinfo","useDefineForClassFields":true}}
+1 -1
View File
@@ -10,7 +10,7 @@ const { Command } = require("commander");
const fs = require("fs");
const path = require("path");
const logger = require("./logger");
const logger = require("./log");
const program = new Command();
+15
View File
@@ -0,0 +1,15 @@
import pino from "pino";
const useJson = process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true";
export const log = pino({
level: process.env.LOG_LEVEL ?? "info",
...(useJson
? {}
: {
transport: {
options: { colorize: true },
target: "pino-pretty",
},
}),
});
-3
View File
@@ -1,3 +0,0 @@
const { CreateLogger } = require("mei-logger");
module.exports = CreateLogger("tachi-database-seed-scripts");
-1
View File
@@ -37,7 +37,6 @@
"html-entities": "catalog:",
"iconv-lite": "catalog:",
"lodash.get": "catalog:",
"mei-logger": "catalog:",
"monk": "catalog:",
"node-fetch": "catalog:",
"prudence": "catalog:",
@@ -8,12 +8,7 @@ import {
type integer,
type SongDocument,
} from "../../../common/src";
import {
CreateChartID,
GetFreshSongIDGenerator,
ReadCollection,
WriteCollection,
} from "../../util";
import { CreateChartID, ReadCollection, WriteCollection } from "../../util";
type LocalizedText = { en: string } & Record<string, string>;
type LocalizedSearchTerms = Record<string, Array<string>>;
@@ -1,3 +1,4 @@
import { log } from "#log";
import { LoadBMSTable } from "bms-table-loader";
import {
BMS_TABLES,
@@ -6,7 +7,6 @@ import {
type TableDocument,
} from "tachi-common";
import logger from "../../logger";
import { MutateCollection, ReadCollection } from "../../util";
const existsTables = ReadCollection("tables.json").map((e) => e.tableID);
@@ -19,9 +19,9 @@ async function UpdateTable(tableInfo: BMSTableInfo) {
return;
}
logger.info(`Fetching ${tableInfo.url} (${tableInfo.name})...`);
log.info(`Fetching ${tableInfo.url} (${tableInfo.name})...`);
const table = await LoadBMSTable(tableInfo.url);
logger.info(`Fetched.`);
log.info(`Fetched.`);
const levels = table.getLevelOrder();
@@ -58,7 +58,7 @@ async function UpdateTable(tableInfo: BMSTableInfo) {
folders.push(realFolder);
logger.info(`Inserted new folder ${tableInfo.prefix}${level}.`);
log.info(`Inserted new folder ${tableInfo.prefix}${level}.`);
}
MutateCollection("folders.json", (f) => {
@@ -80,9 +80,9 @@ async function UpdateTable(tableInfo: BMSTableInfo) {
return t;
});
logger.info(`Bumped table ${tableInfo.name}.`);
log.info(`Bumped table ${tableInfo.name}.`);
logger.info(`Checking meta-folder...`);
log.info(`Checking meta-folder...`);
const f = {
title: tableInfo.name,
@@ -118,7 +118,7 @@ async function UpdateTable(tableInfo: BMSTableInfo) {
MutateCollection("folders.json", (folders) => [...folders, realFolder]);
}
logger.info(`Done.`);
log.info(`Done.`);
}
(async () => {
@@ -1,7 +1,6 @@
import { Command } from "commander";
import { XMLParser } from "fast-xml-parser";
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
import { CreateLogger } from "mei-logger";
import path from "path";
import {
@@ -11,10 +10,9 @@ import {
type integer,
type SongDocument,
} from "../../../common/src";
import { log } from "../../log.js";
import { CreateChartID, ReadCollection, WriteCollection } from "../../util";
const logger = CreateLogger("chunithm/merge-options");
const OMNIMIX_OPTION_NAMES = ["AOMN", "AOLD", "AKON"];
const DISPLAY_VERSIONS = [
"CHUNITHM",
@@ -152,7 +150,7 @@ const newCharts: Array<ChartDocument<"chunithm:Single">> = [];
for (const optionsDir of options.input) {
for (const option of readdirSync(optionsDir)) {
if (!isOmnimixVersion && OMNIMIX_OPTION_NAMES.includes(option)) {
logger.warn(
log.warn(
`Ignoring omnimix option ${option} because the version specified is not an omnimix version.`,
);
continue;
@@ -170,11 +168,11 @@ for (const optionsDir of options.input) {
}
if (!existsSync(musicsDir) || !statSync(musicsDir).isDirectory()) {
logger.warn(`Option at ${optionDir} does not have a "music" directory.`);
log.warn(`Option at ${optionDir} does not have a "music" directory.`);
continue;
}
logger.info(`Scanning music directory ${musicsDir} for songs.`);
log.info(`Scanning music directory ${musicsDir} for songs.`);
for (const music of readdirSync(musicsDir)) {
const musicDir = path.join(musicsDir, music);
@@ -184,14 +182,14 @@ for (const optionsDir of options.input) {
}
if (!statSync(musicDir).isDirectory()) {
logger.warn(`Ignoring ${musicDir} because it is not a directory.`);
log.warn(`Ignoring ${musicDir} because it is not a directory.`);
continue;
}
const musicXmlLocation = path.join(musicDir, "Music.xml");
if (!existsSync(musicXmlLocation) || !statSync(musicXmlLocation).isFile()) {
logger.warn(`Music directory at ${musicDir} does not have a Music.xml file.`);
log.warn(`Music directory at ${musicDir} does not have a Music.xml file.`);
continue;
}
const data = parser.parse(readFileSync(musicXmlLocation)) as MusicXML;
@@ -209,7 +207,7 @@ for (const optionsDir of options.input) {
// Has this song been disabled in-game?
if (musicData.disableFlag) {
if (tachiSongID !== undefined) {
logger.info(
log.info(
`Removing charts of song ${musicData.artistName.str} - ${musicData.name.str} (ID ${tachiSongID}) from version ${options.version}, because disableFlag is enabled.`,
);
@@ -232,14 +230,14 @@ for (const optionsDir of options.input) {
const existingTitle = songTitleMap.get(musicData.name.str);
if (existingTitle) {
logger.warn(
log.warn(
`A song called ${musicData.name.str} already exists in songs-chunithm (ID ${existingTitle.id}). Is this a duplicate with a given inGameID?`,
);
if (options.force) {
logger.warn("--force was requested, adding this song anyways.");
log.warn("--force was requested, adding this song anyways.");
} else {
logger.warn("Must be resolved manually. Use --force to overwrite anyways.");
log.warn("Must be resolved manually. Use --force to overwrite anyways.");
continue;
}
}
@@ -270,7 +268,7 @@ for (const optionsDir of options.input) {
inGameIDToSongIDMap.set(inGameID, tachiSongID);
songMap.set(tachiSongID, songDoc);
logger.info(`Added new song ${songDoc.artist} - ${songDoc.title}.`);
log.info(`Added new song ${songDoc.artist} - ${songDoc.title}.`);
} else if (songMap.has(tachiSongID)) {
const songDoc = songMap.get(tachiSongID)!;
@@ -308,7 +306,7 @@ for (const optionsDir of options.input) {
if (!difficulty.enable) {
if (versionIndex !== -1) {
logger.info(
log.info(
`Removing ${displayName} from version ${options.version} because it has been disabled.`,
);
exists.versions.splice(versionIndex, 1);
@@ -318,19 +316,19 @@ for (const optionsDir of options.input) {
}
if (versionIndex === -1) {
logger.info(`Adding ${displayName} to version ${options.version}.`);
log.info(`Adding ${displayName} to version ${options.version}.`);
exists.versions.push(options.version);
}
if (isLatestVersion && exists.level !== level) {
logger.info(
log.info(
`Chart ${displayName} has had a level change: ${exists.level} -> ${level}`,
);
exists.level = level;
}
if (isLatestVersion && exists.levelNum !== levelNum) {
logger.info(
log.info(
`Chart ${displayName} has had a levelNum change: ${exists.levelNum} -> ${levelNum}`,
);
exists.levelNum = levelNum;
@@ -344,7 +342,7 @@ for (const optionsDir of options.input) {
}
if (difficultyName === "WORLD'S END") {
logger.warn(
log.warn(
`Song ${musicData.artistName.str} - ${musicData.name.str} (inGameID=${musicData.name.id}) contains a WORLD'S END chart, which should be impossible. Refusing to process this difficulty.`,
);
continue;
@@ -370,7 +368,7 @@ for (const optionsDir of options.input) {
// track of that too. Awesome.
existingCharts.set(`${inGameID}-${difficultyName}`, chartDoc);
logger.info(
log.info(
`Added chart ${musicData.artistName.str} - ${musicData.name.str} [${difficultyName}] (${chartDoc.chartID}).`,
);
}
@@ -1,4 +1,4 @@
const logger = require("../logger");
const logger = require("../log");
const { CreateFolderID, MutateCollection, ReadCollection, WriteCollection } = require("../util");
const translateMap = new Map();
@@ -1,4 +1,4 @@
const logger = require("../logger");
const logger = require("../log");
const { CreateGoalID, MutateCollection, ReadCollection, WriteCollection } = require("../util");
const translateMap = new Map();
@@ -1,3 +1,4 @@
import { log } from "#log";
import { execSync } from "child_process";
/* eslint-disable no-control-regex */
import fs, { mkdirSync } from "fs";
@@ -5,7 +6,6 @@ import iconv from "iconv-lite";
import path from "path";
import { type integer } from "../../../../common/src";
import logger from "../../../logger";
import { ParseDotOneFile } from "./dot-one-parser/parser";
import { type IIDXConvertOutput } from "./dot-one-parser/types";
@@ -38,7 +38,7 @@ mkdirSync(ifsOUT, { recursive: true });
* Extracts an IFS file into the ./ifs-output cache directory.
*/
function ifsExtract(ifsPath: string) {
logger.info(`Extracting ${ifsPath}...`);
log.info(`Extracting ${ifsPath}...`);
execSync(`ifstools "${ifsPath}" -o "${ifsOUT}" -y --super-disable`, { stdio: "ignore" });
}
@@ -52,7 +52,7 @@ export async function ParseIIDXData(
omni: boolean,
alwaysExtract: boolean,
) {
logger.info(`Parsing from ${basedir}.`);
log.info(`Parsing from ${basedir}.`);
const mdbPath = path.join(
basedir,
@@ -61,7 +61,7 @@ export async function ParseIIDXData(
omni ? "music_omni.bin" : "music_data.bin",
);
logger.info(`Parsing MDB ${mdbPath}.`);
log.info(`Parsing MDB ${mdbPath}.`);
const buffer = fs.readFileSync(mdbPath);
@@ -197,11 +197,11 @@ export async function ParseIIDXData(
if (buffer.length <= curLoc) {
moreData = false;
}
logger.error(`${dotOnePath} cannot find file. neither exist.`);
log.error(`${dotOnePath} cannot find file. neither exist.`);
continue;
}
logger.verbose(`Parsing data/sound/${songID}/${songID}.1`);
log.verbose(`Parsing data/sound/${songID}/${songID}.1`);
try {
const charts = await ParseDotOneFile(dotOnePath, {
genre,
@@ -215,7 +215,7 @@ export async function ParseIIDXData(
if (chart.difficulty === "LEGGENDARIA") {
if (notecount !== 0 && notecounts[`${chart.playtype}-LEGGENDARIA`]) {
logger.warn(
log.warn(
`${chart.artist} - ${chart.title} has conflicting ${
chart.playtype
} LEGGENDARIAs.
@@ -228,7 +228,7 @@ Picking the INLINE version, as it's likely to be the correct one, but confirm th
notecounts[`${chart.playtype}-${chart.difficulty}`] = notecount;
}
} catch (err) {
logger.error(err);
log.error(err);
curLoc += structSize;
if (buffer.length <= curLoc) {
@@ -266,7 +266,7 @@ Picking the INLINE version, as it's likely to be the correct one, but confirm th
}
}
logger.info(`Done parsing.`);
log.info(`Done parsing.`);
return parsedData;
}
@@ -1,8 +1,8 @@
import { log } from "#log";
import fs from "fs/promises";
import path from "path";
import { type integer } from "../../../../../common/src";
import logger from "../../../../logger";
import {
type BGMObject,
type BPMObject,
@@ -229,7 +229,7 @@ function ParseEvent(
} else if (data.param === 14) {
obj.col = 1;
} else {
logger.warn(
log.warn(
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Unknown column of scratchObj sent to EventParser: ${
@@ -257,7 +257,7 @@ function ParseEvent(
};
} else if (type === "BPM") {
if (data.param === 0) {
logger.warn(
log.warn(
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Invalid value of 0 for denominator in BPM event (numerator was ${
@@ -302,7 +302,7 @@ function ParseEvent(
const window = IIDX_TIMING_TO_RGC[data.param];
if (!window) {
logger.warn(
log.warn(
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Unknown type of timingWindow: ${window}, from .1 param ${data.param}. Skipping.`,
@@ -341,7 +341,7 @@ function ParseEvent(
if (parseContext.isDP) {
if (data.param !== 0 && data.param !== 1) {
logger.warn(
log.warn(
`(${parseContext.songTitle ?? "UNKNOWN"}) Invalid parameter for measurebar of ${
data.param
}. Defaulting to 0 (left hand side).`,
@@ -374,7 +374,7 @@ function ParseEvent(
// charts. Maybe it was some sort of diverging bga instruction at one point?
return null;
} else {
logger.warn(
log.warn(
`(${parseContext.songTitle ?? "UNKNOWN"}) Unknown event type in .1: ${
data.type
} at ${ms.toFixed(2)}ms. Ignoring.`,
@@ -465,14 +465,14 @@ export function ParseDotOne(
const dir = dirs[i];
if (!dir || (dir.offset === 0 && dir.length === 0)) {
logger.verbose(`Skipped directory ${i}, as there was no data.`);
log.verbose(`Skipped directory ${i}, as there was no data.`);
continue; // no data.
}
const difficulty = IIDX_DIFFICULTY_TO_RGC[i];
if (!difficulty) {
logger.warn(
log.warn(
`(${
context.songTitle ?? "UNKNOWN"
}) Data present inside directory ${i}, but this corresponds to an unknown difficulty. Ignoring.`,
@@ -486,7 +486,7 @@ export function ParseDotOne(
};
if (!eventContext.songTitle) {
logger.warn(
log.warn(
"No filename provided in context.filename to IIDXParser, subsequent warnings will have no way of referring to the chart.",
);
}
@@ -1,3 +1,4 @@
import { log } from "#log";
import { Command } from "commander";
import fs from "fs";
import path from "path";
@@ -10,7 +11,6 @@ import {
type SongDocument,
type Versions,
} from "../../../../common/src";
import logger from "../../../logger";
import {
CreateChartID,
GetFreshSongIDGenerator,
@@ -135,7 +135,7 @@ async function ParseIIDXMDB() {
for (const inp of mdbCharts) {
if (isInBlacklist(`S${inp.songID}`)) {
logger.verbose(
log.debug(
`Skipped ${inp.artist} - ${inp.title} (${inp.songID}) as it was in the blacklist.`,
);
continue;
@@ -150,17 +150,17 @@ async function ParseIIDXMDB() {
const titleAlreadyExists = songTitleMap.get(inp.title);
if (titleAlreadyExists) {
logger.warn(
log.warn(
`A song called '${inp.title}' already exists in songs-iidx (songID:${titleAlreadyExists.id}). Is this a duplicate with a different inGameID?`,
);
if (!options.force) {
logger.warn(
log.warn(
`Must be resolved manually. Use --force to blindly overwrite it anyway.`,
);
continue;
} else {
logger.warn(`--force provided, adding it to the DB anyway.`);
log.warn(`--force provided, adding it to the DB anyway.`);
}
}
@@ -181,16 +181,16 @@ async function ParseIIDXMDB() {
altTitles: [],
};
logger.info(`Added new song ${inp.title}.`);
log.info(`Added new song ${inp.title}.`);
if (inp.title.match(/\?/gu)) {
logger.warn(
log.warn(
`${inp.title} has a potentially konami-screwed title. Investigate it manually.`,
);
}
if (inp.artist.match(/\?/gu)) {
logger.warn(
log.warn(
`${inp.artist} - ${inp.title} has a potentially konami-screwed title. Investigate it manually.`,
);
}
@@ -202,7 +202,7 @@ async function ParseIIDXMDB() {
const sxng = songMap.get(anySongIDMatch.songID);
if (!sxng) {
logger.error(`Song ${anySongIDMatch.songID} has charts but no song?`);
log.error(`Song ${anySongIDMatch.songID} has charts but no song?`);
throw new Error(`Song ${anySongIDMatch.songID} has charts but no song?`);
}
song = sxng;
@@ -212,7 +212,7 @@ async function ParseIIDXMDB() {
for (const diffName of diffNames) {
if (isInBlacklist(`C${inp.songID}-${diffName}`)) {
logger.verbose(
log.verbose(
`Ignored ${song.title} (${inp.songID}) ${diffName} as it was in the blacklist.`,
);
continue;
@@ -224,7 +224,7 @@ async function ParseIIDXMDB() {
const level = inp.levels[diffName];
if (level === 0 && notecount && notecount > 0) {
logger.info(
log.info(
`Chart ${song.title} ${diffName} has notecount ${notecount}, but has no level assigned. Skipping.`,
);
continue;
@@ -262,11 +262,11 @@ async function ParseIIDXMDB() {
},
};
logger.info(`Inserting new chart ${inp.title} ${diffName}.`);
log.info(`Inserting new chart ${inp.title} ${diffName}.`);
existingCharts.push(tachiChart);
} else {
if (!notecount) {
logger.warn(
log.warn(
`Chart ${inp.title} ${diffName} already exists, but has no notecount anymore. Not marking it as part of this version.`,
);
continue;
@@ -274,11 +274,11 @@ async function ParseIIDXMDB() {
// chart already exists, diff notecounts.
if (chart.data.notecount !== notecount) {
logger.warn(
log.warn(
`Chart ${inp.title} ${diffName} has a different notecount in the JSON to the data just parsed. Has this chart been edited? OLD: ${chart.data.notecount} -> NEW: ${notecount}.`,
);
if (!options.force) {
logger.warn(
log.warn(
`Must be resolved manually. Use --force to blindly overwrite it anyway.`,
);
continue;
@@ -287,7 +287,7 @@ async function ParseIIDXMDB() {
}
if (chart.levelNum !== level) {
logger.info(
log.info(
`Chart ${inp.title} ${diffName} has had a level change. ${chart.level} -> ${level}. Updating this.`,
);
chart.level = level.toString();
@@ -3,7 +3,7 @@ const fs = require("fs");
const fetch = require("node-fetch");
const path = require("path");
const logger = require("../../logger");
const logger = require("../../log");
const { MutateCollection, ReadCollection } = require("../../util");
const program = new Command();
@@ -1,8 +1,8 @@
import { log } from "#log";
import { execFileSync } from "child_process";
import { Command } from "commander";
import { XMLParser } from "fast-xml-parser";
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
import { CreateLogger } from "mei-logger";
import path from "path";
import {
@@ -11,14 +11,7 @@ import {
GetGamePTConfig,
type SongDocument,
} from "../../../common/src";
import {
CreateChartID,
GetFreshSongIDGenerator,
ReadCollection,
WriteCollection,
} from "../../util";
const logger = CreateLogger("merge-options");
import { CreateChartID, ReadCollection, WriteCollection } from "../../util";
const VERSION_DISPLAY_NAMES = [
"maimai",
@@ -152,7 +145,7 @@ for (const chart of existingCharts) {
const song = songMap.get(chart.songID);
if (song === undefined) {
logger.error(
log.error(
`CONSISTENCY ERROR: Chart ID ${chart.chartID} does not belong to any songs! (songID was ${chart.songID})`,
);
process.exit(1);
@@ -208,18 +201,16 @@ if (options.vgmsBinary) {
const res = JSON.parse(stdout);
if (res.sampleRate !== 48000) {
logger.warn(
`Sample rate of ${cuePath} is not 48000Hz (${res.sampleRate}Hz)`,
);
log.warn(`Sample rate of ${cuePath} is not 48000Hz (${res.sampleRate}Hz)`);
}
const duration = Number((res.numberOfSamples / res.sampleRate).toFixed(3));
durationMap.set(cueName, duration);
logger.info(`Cue file ${cueName} has duration ${duration} seconds.`);
log.info(`Cue file ${cueName} has duration ${duration} seconds.`);
} catch (e) {
logger.error(`Error parsing song duration: ${e}`);
log.error(`Error parsing song duration: ${e}`);
}
}
}
@@ -251,11 +242,11 @@ for (const optionsDir of options.input) {
const musicsDir = path.join(optionDir, "music");
if (!existsSync(musicsDir)) {
logger.warn(`Option at ${optionDir} does not have a "music" folder.`);
log.warn(`Option at ${optionDir} does not have a "music" folder.`);
continue;
}
logger.info(`Scanning music directory ${musicsDir} for songs.`);
log.info(`Scanning music directory ${musicsDir} for songs.`);
for (const music of readdirSync(musicsDir)) {
if (!music.match(/music\d+$/u)) {
@@ -271,7 +262,7 @@ for (const optionsDir of options.input) {
const musicXmlLocation = path.join(musicDir, "Music.xml");
if (!existsSync(musicXmlLocation)) {
logger.warn(`Music folder at ${musicDir} does not have a Music.xml file.`);
log.warn(`Music folder at ${musicDir} does not have a Music.xml file.`);
continue;
}
@@ -280,7 +271,7 @@ for (const optionsDir of options.input) {
const inGameID = Number(musicData.name.id);
if (isInBlacklist(`S${inGameID}`)) {
logger.verbose(
log.verbose(
`Ignored ${musicData.artistName.str} - ${musicData.name.str} (inGameID ${inGameID}) as it is in the blacklist.`,
);
continue;
@@ -296,7 +287,7 @@ for (const optionsDir of options.input) {
// Has this song been disabled in-game?
if (musicData.disable || Number(musicData.eventName.id) === 0) {
if (tachiSongID !== undefined) {
logger.info(
log.info(
`Removing charts of song ID ${tachiSongID} from version ${options.version}, because the disable flag in Music.xml is enabled.`,
);
@@ -335,7 +326,7 @@ for (const optionsDir of options.input) {
const duration = durationMap.get(`music${musicData.cueName.id.padStart(6, "0")}`);
if (!duration) {
logger.warn(
log.warn(
`Unknown duration for music ID ${inGameID}, cue ID ${musicData.cueName.id}.`,
);
}
@@ -360,7 +351,7 @@ for (const optionsDir of options.input) {
songTitleArtistMap.set(`${songDoc.title}-${songDoc.artist}`, tachiSongID);
songMap.set(tachiSongID, songDoc);
logger.info(
log.info(
`Added new song ${songDoc.artist} - ${songDoc.title} (inGameID ${inGameID}, tachiSongID ${tachiSongID}).`,
);
} else {
@@ -388,7 +379,7 @@ for (const optionsDir of options.input) {
}
if (isInBlacklist(`C${inGameID}-${difficultyName}`)) {
logger.verbose(
log.verbose(
`Ignoring ${musicData.artistName.str} - ${musicData.name.str} [${difficultyName}] (inGameID ${inGameID}) as it is in the blacklist.`,
);
continue;
@@ -415,18 +406,18 @@ for (const optionsDir of options.input) {
const displayName = `${musicData.artistName.str} - ${musicData.name.str} [${exists.difficulty}] (${exists.chartID})`;
if (exists.data.inGameID === null) {
logger.info(`Adding inGameID ${inGameID} for chart ${displayName}.`);
log.info(`Adding inGameID ${inGameID} for chart ${displayName}.`);
exists.data.inGameID = inGameID;
} else if (exists.data.inGameID !== inGameID) {
logger.warn(
log.warn(
`The chart ${displayName} already exists in charts-maimaidx under a different inGameID (${exists.data.inGameID} != ${inGameID}). Is this a duplicate with a different inGameID?`,
);
if (options.force) {
logger.warn("Overwriting anyways, because --force has been requested.");
log.warn("Overwriting anyways, because --force has been requested.");
exists.data.inGameID = inGameID;
} else {
logger.warn(
log.warn(
"Must be resolved manually. Use --force to overwrite anyways.",
);
}
@@ -440,14 +431,14 @@ for (const optionsDir of options.input) {
if (isLatestVersion) {
if (exists.level !== level) {
logger.info(
log.info(
`Chart ${displayName} has had a level change: ${exists.level} -> ${level}.`,
);
exists.level = level;
}
if (exists.levelNum !== levelNum) {
logger.info(
log.info(
`Chart ${displayName} has had a levelNum change: ${exists.levelNum} -> ${levelNum}.`,
);
exists.levelNum = levelNum;
@@ -476,7 +467,7 @@ for (const optionsDir of options.input) {
newCharts.push(chartDoc);
logger.info(
log.info(
`Inserted new chart ${musicData.artistName.str} - ${musicData.name.str} [${chartDoc.difficulty}] (${chartDoc.chartID}).`,
);
}
@@ -1,4 +1,4 @@
import { CreateLogger } from "mei-logger";
import { log } from "#log";
import { type ChartDocument, type Difficulties, type SongDocument } from "../../../common/src";
import {
@@ -8,8 +8,6 @@ import {
WriteCollection,
} from "../../util";
const logger = CreateLogger("parse-maimaidx-dataset");
type BaseDifficulty = "adv" | "bas" | "exp" | "mas" | "remas";
type MaimaiDXDifficulty = `${"" | "dx_"}lev_${BaseDifficulty}`;
@@ -109,7 +107,7 @@ async function ParseInternalLevelData(
const dx = Number(dxMatch[1]!);
if (dx !== 0 && dx !== 1) {
logger.warn(`DX should be 0 or 1, found ${dxMatch[1]} in ${line}`);
log.warn(`DX should be 0 or 1, found ${dxMatch[1]} in ${line}`);
continue;
}
@@ -119,7 +117,7 @@ async function ParseInternalLevelData(
const tmp = JSON.parse(lvMatch[1]!);
if (!Array.isArray(tmp) || tmp.some((c) => typeof c !== "number")) {
logger.warn(`lv should be an array of numbers, found ${lvMatch[1]} in ${line}`);
log.warn(`lv should be an array of numbers, found ${lvMatch[1]} in ${line}`);
continue;
}
@@ -138,7 +136,7 @@ async function ParseInternalLevelData(
lv.remas = tmp[4];
}
} catch (e) {
logger.warn(`lv should be an array of numbers, found ${lvMatch[1]} in ${line}`);
log.warn(`lv should be an array of numbers, found ${lvMatch[1]} in ${line}`);
continue;
}
@@ -159,11 +157,11 @@ async function ParseMaimaiDXDataset() {
const existingSongMap = new Map(songs.map((s) => [`${s.title}-${s.artist}`, s]));
const existingChartMap = new Map(charts.map((c) => [`${c.songID}-${c.difficulty}`, c]));
logger.info(`Fetching official song information from ${DATA_URL}...`);
log.info(`Fetching official song information from ${DATA_URL}...`);
const datum: Array<MaimaiDXSong> = await fetch(DATA_URL).then((r) => r.json());
logger.info(`Fetching internal level information from ${INTERNAL_LEVEL_URL}...`);
log.info(`Fetching internal level information from ${INTERNAL_LEVEL_URL}...`);
const internalLevelData = await ParseInternalLevelData(INTERNAL_LEVEL_URL);
const internalLevelDataMap = new Map(
@@ -182,7 +180,7 @@ async function ParseMaimaiDXDataset() {
if (version > CURRENT_VERSION_NUM) {
// Skipping songs that are newer than currently supported version.
logger.warn(
log.warn(
`Ignoring song ${data.artist} - ${data.title}, which is newer than CURRENT_VERSION_NUM.`,
);
continue;
@@ -221,7 +219,7 @@ async function ParseMaimaiDXDataset() {
songs.push(songDoc);
logger.info(`Added new song ${songDoc.artist} - ${songDoc.title} (ID ${tachiSongID}).`);
log.info(`Added new song ${songDoc.artist} - ${songDoc.title} (ID ${tachiSongID}).`);
}
for (const [key, difficulty] of Object.entries(DIFFICULTY_TO_TACHI_DIFFICULTY) as [
@@ -260,7 +258,7 @@ async function ParseMaimaiDXDataset() {
}
if (existingChart.level !== level) {
logger.info(
log.info(
`Chart ${displayName} has had a level change: ${existingChart.level} -> ${level}.`,
);
existingChart.level = level;
@@ -268,7 +266,7 @@ async function ParseMaimaiDXDataset() {
}
if (existingChart.levelNum !== levelNum) {
logger.info(
log.info(
`Chart ${displayName} has had a levelNum change: ${existingChart.levelNum} -> ${levelNum}.`,
);
existingChart.levelNum = levelNum;
@@ -293,7 +291,7 @@ async function ParseMaimaiDXDataset() {
charts.push(chartDoc);
logger.info(`Added new chart ${data.artist} - ${data.title} [${difficulty}].`);
log.info(`Added new chart ${data.artist} - ${data.title} [${difficulty}].`);
}
}
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
import type { DeepPartial } from "#utils/types";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -44,8 +44,6 @@ const scoreData: ScoreData<"arcaea:Touch"> = {
const mockScore = mkMockScore("arcaea", "Touch", TestingArcaeaSheriruthFTR, scoreData);
const mockPB = mkMockPB("arcaea", "Touch", TestingArcaeaSheriruthFTR, scoreData);
const logger = CreateLogCtx(__filename);
t.test("Arcaea Implementation", (t) => {
t.test("Grade Deriver", (t) => {
const f = (score: number, expected: string) =>
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -45,8 +45,6 @@ function scoreToPercent(score: number) {
return (100 * score) / max;
}
const logger = CreateLogCtx(__filename);
for (const [game, playtype, impl] of [
["bms", "7K", BMS_7K_IMPL],
["bms", "14K", BMS_14K_IMPL],
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -38,8 +38,6 @@ const scoreData: ScoreData<"chunithm:Single"> = {
const mockScore = mkMockScore("chunithm", "Single", CHUNITHMBBKKChart, scoreData);
const mockPB = mkMockPB("chunithm", "Single", CHUNITHMBBKKChart, scoreData);
const logger = CreateLogCtx(__filename);
t.test("CHUNITHM Implementation", (t) => {
t.test("Grade Deriver", (t) => {
const f = (score: number, expected: any) =>
@@ -22,14 +22,12 @@ import {
type SongDocument,
} from "../../../../common/src";
import db from "../../external/mongo/db";
import CreateLogCtx from "../../lib/logger/logger";
import { log } from "../../lib/logger/log";
import { IsNullish } from "../../utils/misc";
import { CreatePBMergeFor } from "../utils/pb-merge";
import { SessionAvgBest10For } from "../utils/session-calc";
import { GoalFmtScore, GoalOutOfFmtScore, GradeGoalFormatter } from "./_common";
const logger = CreateLogCtx(__filename);
interface PBScoreDocumentWithSong extends PBScoreDocument<"ddr:DP" | "ddr:SP"> {
song: SongDocument<"ddr">;
top?: number;
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -18,8 +18,6 @@ import {
} from "../../../../common/src";
import { GITADORA_DORA_IMPL, GITADORA_GITA_IMPL } from "./gitadora";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics[GPTStrings["gitadora"]] = {
lamp: "CLEAR",
percent: 45,
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkFakePBIIDXSP, mkFakeScoreIIDXSP } from "#test-utils/misc";
@@ -23,8 +23,6 @@ const baseMetrics: ProvidedMetrics["iidx:DP" | "iidx:SP"] = {
score: 1000,
};
const logger = CreateLogCtx(__filename);
const max = Testing511SPA.data.notecount * 2;
function percentToScore(percent: number) {
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -12,8 +12,6 @@ import type { ProvidedMetrics, ScoreData } from "../../../../common/src";
import { JUBEAT_SINGLE_CONF } from "../../../../common/src/config/game-support/jubeat";
import { JUBEAT_IMPL } from "./jubeat";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics["jubeat:Single"] = {
lamp: "CLEAR",
musicRate: 84.21,
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -12,8 +12,6 @@ import type { integer, ProvidedMetrics, ScoreData } from "../../../../common/src
import { MAIMAI_SINGLE_CONF } from "../../../../common/src/config/game-support/maimai";
import { MAIMAI_IMPL } from "./maimai";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics["maimai:Single"] = {
lamp: "CLEAR",
percent: 97.01,
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -11,8 +11,6 @@ import type { ProvidedMetrics, ScoreData } from "../../../../common/src";
import { MAIMAI_DX_SINGLE_CONF } from "../../../../common/src/config/game-support/maimai-dx";
import { MAIMAIDX_IMPL } from "./maimaidx";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics["maimaidx:Single"] = {
lamp: "CLEAR",
percent: 97.012,
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -34,8 +34,6 @@ const scoreData: ScoreData<"museca:Single"> = {
const mockScore = mkMockScore("museca", "Single", TestingMusecaChart, scoreData);
const mockPB = mkMockPB("museca", "Single", TestingMusecaChart, scoreData);
const logger = CreateLogCtx(__filename);
t.test("MUSECA Implementation", (t) => {
t.test("Grade Deriver", (t) => {
const f = (score: number, expected: any) =>
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc, UpdateChartRanking } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -44,8 +44,6 @@ const scoreData: ScoreData<"ongeki:Single"> = {
const mockScore = mkMockScore("ongeki", "Single", TestingOngekiChart, scoreData);
const mockPB = mkMockPB("ongeki", "Single", TestingOngekiChart, scoreData);
const logger = CreateLogCtx(__filename);
t.test("ONGEKI Implementation", (t: any) => {
t.test("Grade Deriver", (t: any) => {
const f = (score: number, expected: string) =>
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -37,8 +37,6 @@ const scoreData: ScoreData<"popn:9B"> = {
const mockScore = mkMockScore("popn", "9B", TestingPopnChart, scoreData);
const mockPB = mkMockPB("popn", "9B", TestingPopnChart, scoreData);
const logger = CreateLogCtx(__filename);
t.test("Pop'n Implementation", (t) => {
t.test("Grade Deriver", (t) => {
const f = (score: number, expected: any) =>
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -15,8 +15,6 @@ import {
} from "../../../../common/src";
import { SDVX_IMPL } from "./sdvx";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics["sdvx:Single"] = {
lamp: "CLEAR",
score: 9_003_000,
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -16,8 +16,6 @@ import {
} from "../../../../common/src";
import { USC_CONTROLLER_IMPL, USC_KEYBOARD_IMPL } from "./usc";
const logger = CreateLogCtx(__filename);
const baseMetrics: ProvidedMetrics[GPTStrings["usc"]] = {
lamp: "CLEAR",
score: 9_003_000,
@@ -1,6 +1,6 @@
import type { DeepPartial } from "#utils/types";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
import db from "#services/mongo/db";
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
@@ -38,8 +38,6 @@ const scoreData: ScoreData<"wacca:Single"> = {
const mockScore = mkMockScore("wacca", "Single", TestingWaccaPupaExp, scoreData);
const mockPB = mkMockPB("wacca", "Single", TestingWaccaPupaExp, scoreData);
const logger = CreateLogCtx(__filename);
t.test("WACCA Implementation", (t) => {
t.test("Grade Deriver", (t) => {
const f = (score: number, expected: any) =>
@@ -1,6 +1,6 @@
import type { PrudenceError } from "prudence";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { ServerConfig, TachiConfig } from "#lib/setup/config";
import { type TachiAPIClientDocument, UserAuthLevels } from "../../../../common/src";
@@ -11,8 +11,6 @@ import { Random20Hex } from "#utils/misc";
import { FormatPrError } from "#utils/prudence";
import fjsh from "fast-json-stable-hash";
const logger = CreateLogCtx(__filename);
type DefaultClients = Array<Omit<TachiAPIClientDocument, "author" | "clientSecret">>;
// Defines some Tachi API Clients that should come default with a Tachi
@@ -315,7 +313,7 @@ async function LoadClients(clients: DefaultClients) {
});
if (!firstAdmin) {
logger.warn(
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}.
@@ -351,9 +349,7 @@ If you have no users, go create an account using the frontend, then run pnpm mak
try {
DatabaseSchemas["api-clients"](realClient);
} catch (err) {
logger.error(
`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`,
);
log.error(`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`);
continue;
}
@@ -364,6 +360,6 @@ If you have no users, go create an account using the frontend, then run pnpm mak
await db["api-clients"].insert(realClient);
logger.info(`Loaded/Modified new built-in client ${client.name}.`);
log.info(`Loaded/Modified new built-in client ${client.name}.`);
}
}
+6 -8
View File
@@ -1,6 +1,6 @@
import type { Response } from "express";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { ServerConfig } from "#lib/setup/config";
import fs from "fs";
import mkdirp from "mkdirp";
@@ -13,8 +13,6 @@ const readFilePromise = promisify(fs.readFile);
const writeFilePromise = promisify(fs.writeFile);
const rmFilePromise = promisify(fs.rm);
const logger = CreateLogCtx(__filename);
/**
* Joins a file location against the location of the CDN static store.
*
@@ -23,7 +21,7 @@ const logger = CreateLogCtx(__filename);
*/
export function CDNFileSystemRoot(fileLoc: string) {
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "LOCAL_FILESYSTEM") {
logger.severe(
log.error(
`Attempted to run CDNFileSystemRoot, but was not using LOCAL_FILESYSTEM as a CDN.`,
{ fileLoc, conf: ServerConfig.CDN_CONFIG },
);
@@ -43,7 +41,7 @@ export function CDNFileSystemRoot(fileLoc: string) {
* As in, this ruins the purpose of a CDN! make sure you have one running.
*/
export function CDNRetrieve(fileLoc: string) {
logger.debug(`Retrieving path ${fileLoc} locally.`);
log.debug(`Retrieving path ${fileLoc} locally.`);
return readFilePromise(CDNFileSystemRoot(fileLoc));
}
@@ -56,7 +54,7 @@ export function CDNRedirect(res: Response, fileLoc: string) {
throw new Error(`Invalid fileLoc - did not start with /.`);
}
logger.debug(`CDN Redirecting to ${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}.`);
log.debug(`CDN Redirecting to ${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}.`);
res.redirect(`${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}`);
}
@@ -65,7 +63,7 @@ export function CDNRedirect(res: Response, fileLoc: string) {
* Stores a file at fileLoc. If it already exists, overwrite it.
*/
export async function CDNStoreOrOverwrite(fileLoc: string, data: string | Buffer): Promise<void> {
logger.debug(`Storing or overwriting path ${fileLoc}.`);
log.debug(`Storing or overwriting path ${fileLoc}.`);
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "LOCAL_FILESYSTEM") {
const loc = CDNFileSystemRoot(fileLoc);
@@ -83,7 +81,7 @@ export async function CDNStoreOrOverwrite(fileLoc: string, data: string | Buffer
* Removes a file at this CDN location.
*/
export async function CDNDelete(fileLoc: string) {
logger.verbose(`Deleting path ${fileLoc}.`);
log.verbose(`Deleting path ${fileLoc}.`);
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "LOCAL_FILESYSTEM") {
const loc = CDNFileSystemRoot(fileLoc);
+5 -7
View File
@@ -1,15 +1,13 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { ServerConfig } from "#lib/setup/config";
import { DeleteObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
const logger = CreateLogCtx("S3 Client");
let s3: S3Client | null = null;
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "S3_BUCKET") {
const data = ServerConfig.CDN_CONFIG.SAVE_LOCATION;
logger.info(`Using S3_BUCKET as CDN location.`, { bootInfo: true });
log.info(`Using S3_BUCKET as CDN location.`, { bootInfo: true });
s3 = new S3Client({
endpoint: data.ENDPOINT,
region:
@@ -25,10 +23,10 @@ if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "S3_BUCKET") {
* Pushes a file to the configured S3 Bucket. Overwrites if already exists.
*/
export function PushToS3(path: string, content: string | Buffer) {
logger.debug(`Saving content on S3 at ${path}.`);
log.debug(`Saving content on S3 at ${path}.`);
if (!s3 || ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "S3_BUCKET") {
logger.severe(
log.error(
`Attempted to push to S3, but CDN_CONFIG.SAVE_LOCATION.TYPE was not S3_BUCKET?`,
ServerConfig.CDN_CONFIG,
);
@@ -51,7 +49,7 @@ export function PushToS3(path: string, content: string | Buffer) {
*/
export function DeleteFromS3(path: string) {
if (!s3 || ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "S3_BUCKET") {
logger.severe(
log.error(
`Attempted to delete from S3, but CDN_CONFIG.SAVE_LOCATION.TYPE was not S3_BUCKET?`,
ServerConfig.CDN_CONFIG,
);
+1 -49
View File
@@ -1,51 +1,3 @@
import { execSync } from "child_process";
import fs from "fs";
// Note that Version Info is intentionally identical between all variants of tachi
// viz. boku and kamai share versioning. This is because they
// essentially share codebases.
import CreateLogCtx from "#lib/logger/logger";
import { Env } from "#lib/setup/config";
import path from "path";
import semver from "semver";
const logger = CreateLogCtx(__filename);
// we want to read the monorepo root package.json.
const packageJSON = JSON.parse(
fs.readFileSync(path.join(__dirname, "../../../../package.json"), "utf-8"),
) as { version: string };
const semverInfo = semver.parse(packageJSON.version);
if (!semverInfo) {
logger.warn(
`Couldn't parse semverInfo out of package.json? Got a string of '${packageJSON.version}', but couldn't parse. Defaulting to v0.0.0-unknown.`,
);
}
let commit: string | null = null;
try {
if (Env.COMMIT_HASH) {
commit = Env.COMMIT_HASH;
} else {
// This fetches the HEAD of our current running branch. This is useful for debugging.
// Note that git returns this with a trailing newline, so we have to trim that off.
commit = execSync("git rev-parse --short HEAD").toString("utf-8").trim();
}
} catch (err) {
logger.warn(`Failed to read what commit we're on -- Using null as our commit version.`, {
err,
});
}
export const VERSION_INFO = {
major: semverInfo?.major ?? 0,
minor: semverInfo?.minor ?? 0,
patch: semverInfo?.patch ?? 0,
commit,
};
export const VERSION_STR = semverInfo?.raw ?? "0.0.0-unknown";
export const VERSION_PRETTY = `v${VERSION_STR} [${commit}]`;
export const VERSION_PRETTY = `v${Env.VERSION} [${Env.COMMIT_HASH ?? "unknown commit"}]`;
+10 -12
View File
@@ -1,14 +1,12 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { Env, ServerConfig } from "#lib/setup/config";
import bunyan from "bunyan";
import nodemailer, { type SentMessageInfo, type Transporter } from "nodemailer";
const logger = CreateLogCtx(__filename);
let transporter: Transporter | undefined;
if (ServerConfig.EMAIL_CONFIG) {
logger.info(`Connecting to email server...`, { bootInfo: true });
log.info(`Connecting to email server...`, { bootInfo: true });
const conf = ServerConfig.EMAIL_CONFIG;
try {
@@ -23,18 +21,18 @@ if (ServerConfig.EMAIL_CONFIG) {
transporter.verify((err) => {
if (err) {
logger.crit(`Could not connect to email server.`, { err });
log.fatal(`Could not connect to email server.`, { err });
throw err;
} else {
logger.info(`Successfully connected to email server.`, { bootInfo: true });
log.info(`Successfully connected to email server.`, { bootInfo: true });
}
});
} catch (err) {
logger.crit(`Failed to create email client.`, { err });
log.fatal(`Failed to create email client.`, { err });
throw err;
}
} else {
logger.warn(`No EMAIL_CONFIG present in conf, emails will not be sent from the server.`, {
log.warn(`No EMAIL_CONFIG present in conf, emails will not be sent from the server.`, {
bootInfo: true,
});
}
@@ -46,16 +44,16 @@ export function SendEmail(
textContent: string,
): Promise<SentMessageInfo> | undefined {
if (Env.NODE_ENV === "test") {
logger.debug(`Stubbed out SendEmail as env was test.`);
log.debug(`Stubbed out SendEmail as env was test.`);
return;
}
if (!transporter || !ServerConfig.EMAIL_CONFIG) {
logger.debug(`Stubbed out SendEmail as no EMAIL_CONFIG was set.`);
log.debug(`Stubbed out SendEmail as no EMAIL_CONFIG was set.`);
return;
}
logger.verbose(`Sending email to ${to}.`);
log.verbose(`Sending email to ${to}.`);
return transporter
.sendMail({
@@ -68,7 +66,7 @@ export function SendEmail(
headers: transporter.options.headers,
})
.catch((err: unknown) => {
logger.info(`Failed to send email to ${to}.`, {
log.info(`Failed to send email to ${to}.`, {
err,
subject,
textContent,
@@ -1,10 +1,8 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import db from "#services/mongo/db";
import type { GameGroup, integer, Playtype } from "../../../../common/src";
const logger = CreateLogCtx(__filename);
/**
* Create GameSettings for a UGPT (which contains their preferences).
*/
@@ -16,7 +14,7 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt
});
if (exists) {
logger.error(
log.error(
`Cannot create ${userID} ${game} ${playtype} game-settings as one already exists?`,
);
@@ -51,5 +49,5 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt
rivals: [],
});
logger.info(`Created game settings for ${userID} (${game} ${playtype}).`);
log.info(`Created game settings for ${userID} (${game} ${playtype}).`);
}
@@ -1,7 +1,7 @@
import type { BMSTableHead, RawBMSTableEntry } from "bms-table-loader";
import type { Request, Response } from "express-serve-static-core";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { GetRivalUsers } from "#lib/rivals/rivals";
import { ServerConfig, TachiConfig } from "#lib/setup/config";
import { GetRelevantSongsAndCharts } from "#utils/db";
@@ -25,8 +25,6 @@ import {
type TableDocument,
} from "../../../../common/src";
const logger = CreateLogCtx(__filename);
// Instead of just supporting existing tables, Tachi should also be able
// to emit its own, custom BMS tables. These may be dynamic.
@@ -48,7 +46,7 @@ function AppendAndConvertChartsToBMSBody(
md5: chart.data.hashMD5,
});
} else {
logger.warn(`BMS Chart md5=${chart.data.hashMD5} has no parent song.`);
log.warn(`BMS Chart md5=${chart.data.hashMD5} has no parent song.`);
body.push({
level,
md5: chart.data.hashMD5,
@@ -80,7 +78,7 @@ export async function TachiTableToBMSTableJSON(
const folder = folderMap.get(folderID);
if (!folder) {
logger.warn(
log.warn(
`Table '${table.title}' refers to folder '${folderID}', yet no such folder exists in the db?`,
);
continue;
@@ -229,7 +227,7 @@ export async function HandleBMSTableHeaderRequest(
return res.status(200).send(header);
} catch (err) {
logger.error(`Failed to load header.json for table ${bmsTable.tableName}.`, {
log.error(`Failed to load header.json for table ${bmsTable.tableName}.`, {
bmsTable,
err,
});
@@ -257,7 +255,7 @@ export async function HandleBMSTableBodyRequest(
return res.status(200).send(body);
} catch (err) {
logger.error(`Failed to load body.json for table ${bmsTable.tableName}.`, {
log.error(`Failed to load body.json for table ${bmsTable.tableName}.`, {
bmsTable,
err,
});
@@ -1,11 +1,9 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import db from "#services/mongo/db";
import { PoyashiBPI } from "rg-stats";
import type { ChartDocument, integer, Playtypes } from "../../../../common/src";
const logger = CreateLogCtx(__filename);
interface PlaylistEntry {
entry_id: integer;
difficulty: "another" | "beginner" | "hyper" | "leggendaria" | "normal";
@@ -55,7 +53,7 @@ function ChartsToPlaylistFormat(
if (el === undefined) {
// wut, chart has an empty array of ingameids?
logger.warn(`Chart '${chart.chartID}' has an empty array of inGameIDs.`);
log.warn(`Chart '${chart.chartID}' has an empty array of inGameIDs.`);
continue;
}
@@ -1,16 +1,14 @@
import type http from "http";
import type https from "https";
import { rootLogger } from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { CloseScoreImportQueue } from "#lib/score-import/worker/queue";
import { monkDB } from "#services/mongo/db";
import { ClosePgConnection } from "#services/pg/db.js";
import { CloseRedisConnection } from "#services/redis/redis";
const logger = rootLogger;
export function HandleSIGTERMGracefully(instance?: http.Server | https.Server) {
logger.info("SIGTERM Received, closing program.", { shutdownInfo: true });
log.info("SIGTERM Received, closing program.", { shutdownInfo: true });
if (instance) {
instance.close(() => CloseEverythingElse());
@@ -20,19 +18,19 @@ export function HandleSIGTERMGracefully(instance?: http.Server | https.Server) {
}
async function CloseEverythingElse() {
logger.info("Closing Mongo Database.", { shutdownInfo: true });
log.info("Closing Mongo Database.", { shutdownInfo: true });
await monkDB.close();
logger.info("Closing database...", { shutdownInfo: true });
log.info("Closing database...", { shutdownInfo: true });
await ClosePgConnection();
logger.info("Closing Redis Connection.", { shutdownInfo: true });
log.info("Closing Redis Connection.", { shutdownInfo: true });
await CloseRedisConnection();
logger.info("Closing Score Import Queue.", { shutdownInfo: true });
log.info("Closing Score Import Queue.", { shutdownInfo: true });
await CloseScoreImportQueue();
logger.info("Everything closed. Waiting for process to exit naturally.", {
log.info("Everything closed. Waiting for process to exit naturally.", {
shutdownInfo: true,
});
}
+6 -10
View File
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import {
CheckAndSetOngoingImportLock,
UnsetOngoingImportLock,
@@ -8,8 +8,6 @@ import db from "#services/mongo/db";
import type { ImportDocument } from "../../../../common/src";
const logger = CreateLogCtx(__filename);
interface OngoingImportError {
tag: "ONGOING_IMPORT";
}
@@ -22,16 +20,14 @@ interface OngoingImportError {
* If this results in sessions being deleted, it will delete them.
*/
export async function RevertImport(importDoc: ImportDocument): Promise<OngoingImportError | null> {
logger.info(`Received revert-import request for import '${importDoc.importID}'`, { importDoc });
log.info(`Received revert-import request for import '${importDoc.importID}'`, { importDoc });
const scores = await GetImportScores(importDoc);
const hasNoOngoingImport = await CheckAndSetOngoingImportLock(importDoc.userID);
if (hasNoOngoingImport) {
logger.info(
`User ${importDoc.userID} tried to revert an import while they had one ongoing.`,
);
log.info(`User ${importDoc.userID} tried to revert an import while they had one ongoing.`);
return {
tag: "ONGOING_IMPORT",
@@ -41,7 +37,7 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
try {
await DeleteMultipleScores(scores);
logger.info(
log.info(
`Deleted ${scores.length} scores as part of reverting import '${importDoc.importID}'.`,
{ importDoc },
);
@@ -49,9 +45,9 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
try {
await db.imports.remove({ importID: importDoc.importID });
logger.info(`Reverted and deleted import '${importDoc.importID}'.`);
log.info(`Reverted and deleted import '${importDoc.importID}'.`);
} catch (err) {
logger.severe(
log.error(
`Deleted scores that were part of import, but failed to remove the actual import? There is a stale import with ID '${importDoc.importID}', which must be removed manually.`,
{ importDoc, err },
);
+2 -6
View File
@@ -1,11 +1,9 @@
import { ONE_MONTH } from "#lib/constants/time";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { ServerConfig } from "#lib/setup/config";
import type { UserDocument } from "../../../../common/src";
const logger = CreateLogCtx(__filename);
/**
* Users are only allowed to invite so many users, and their invites are
* trickled out in bursts of INVITE_BATCH_SIZE.
@@ -15,9 +13,7 @@ const logger = CreateLogCtx(__filename);
*/
export function GetTotalAllowedInvites(user: UserDocument) {
if (!ServerConfig.INVITE_CODE_CONFIG) {
logger.warn(
`No INVITE_CODE_CONFIG set, but tried to get total allowed invites? Returning 0.`,
);
log.warn(`No INVITE_CODE_CONFIG set, but tried to get total allowed invites? Returning 0.`);
return 0;
}
@@ -1,11 +1,9 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { PullDatabaseSeeds } from "#lib/seeds/repo";
/* eslint-disable no-await-in-loop */
import db from "#services/mongo/db";
import { WrapScriptPromise } from "#utils/misc";
const logger = CreateLogCtx(__filename);
/**
* The tachi-server may have its BMS or PMS database update. It needs to sync this
* information back with the seeds.
@@ -14,12 +12,12 @@ export async function BacksyncBMSPMSSongsAndCharts() {
const repo = await PullDatabaseSeeds();
for (const game of ["bms", "pms"] as const) {
logger.info(`Fetching ${game} songs from DB.`);
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({});
logger.info(`Found ${songs.length} ${game} songs.`);
log.info(`Found ${songs.length} ${game} songs.`);
await repo.WriteCollection(`songs-${game}`, songs);
@@ -28,10 +26,10 @@ export async function BacksyncBMSPMSSongsAndCharts() {
// tries to read even more stuff.
songs = null;
logger.info(`Fetching ${game} charts from DB.`);
log.info(`Fetching ${game} charts from DB.`);
let charts = await db.anyCharts[game].find({});
logger.info(`Found ${charts.length} ${game} charts.`);
log.info(`Found ${charts.length} ${game} charts.`);
await repo.WriteCollection(`charts-${game}`, charts);
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { PullDatabaseSeeds } from "#lib/seeds/repo";
import { WrapScriptPromise } from "#utils/misc";
import fetch from "node-fetch";
@@ -7,8 +7,6 @@ import type { ChartDocument } from "../../../../common/src";
const AI_URL = "https://bms.hexlataia.xyz/tables/json/ai.json";
const logger = CreateLogCtx(__filename);
interface AITableEntry {
level: string;
md5: string;
@@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */
import type { FilterQuery } from "mongodb";
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { DeorphanIfInQueue } from "#lib/orphan-queue/orphan-queue";
import db from "#services/mongo/db";
import { InitaliseFolderChartLookup } from "#utils/folder";
@@ -15,8 +15,6 @@ import {
type Playtypes,
} from "../../../../common/src";
const logger = CreateLogCtx(__filename);
/**
* Tables might have updates that remove charts from their table.
*
@@ -30,7 +28,7 @@ async function HandleTableRemovals(
prefix: string,
) {
if (tableEntries.length === 0) {
logger.info(
log.info(
`No entries in table ${prefix}, skipping removals to prevent instantly wiping the table.`,
);
return;
@@ -47,7 +45,7 @@ async function HandleTableRemovals(
"data.tableFolders.table": prefix,
})) as unknown as Array<ChartDocument<"bms:7K" | "bms:14K">>;
logger.info(
log.info(
`Found ${existingCharts.length} existing chart(s) in the database for table ${prefix}.`,
);
@@ -89,7 +87,7 @@ async function HandleTableRemovals(
return;
}
logger.info(`Removing ${toRemove.length} chart(s) from ${prefix}.`);
log.info(`Removing ${toRemove.length} chart(s) from ${prefix}.`);
// remove this table info from all of the charts that no longer
// exist in the table.
@@ -115,7 +113,7 @@ async function ImportTableLevels(
let success = 0;
const total = tableEntries.length;
logger.info(`Handling removals for ${playtype}:${prefix}...`);
log.info(`Handling removals for ${playtype}:${prefix}...`);
await HandleTableRemovals(tableEntries, playtype, prefix);
const md5s = tableEntries.filter((e) => e.checksum.type === "md5").map((e) => e.checksum.value);
@@ -183,7 +181,7 @@ async function ImportTableLevels(
}
if (!chart) {
logger.warn(
log.warn(
`No chart exists in table for (${td.checksum.type}=${td.checksum.value} Possible title: ${td.content.title} ${prefix}${td.content.level}`,
);
failures++;
@@ -228,16 +226,16 @@ async function ImportTableLevels(
success++;
}
logger.info(`Finished updating table ${prefix}.`);
logger.info(`${success} Success | ${failures} Failures | ${total} Total.`);
log.info(`Finished updating table ${prefix}.`);
log.info(`${success} Success | ${failures} Failures | ${total} Total.`);
}
export async function UpdateTable(tableInfo: BMSTableInfo) {
const table = await LoadBMSTable(tableInfo.url);
logger.info(`Bumping levels...`);
log.info(`Bumping levels...`);
await ImportTableLevels(table.body, tableInfo.prefix, tableInfo.playtype);
logger.info(`Levels bumped.`);
log.info(`Levels bumped.`);
}
export async function SyncBMSTables() {
@@ -245,13 +243,13 @@ export async function SyncBMSTables() {
try {
await UpdateTable(table);
} catch (err) {
logger.error(`Failed to update table ${table.name} (${table.url}).`, { err });
log.error(`Failed to update table ${table.name} (${table.url}).`, { err });
}
}
logger.info(`Re-initialising folder-chart-lookup, since changes may have been made.`);
log.info(`Re-initialising folder-chart-lookup, since changes may have been made.`);
await InitaliseFolderChartLookup();
logger.info(`Done.`);
log.info(`Done.`);
}
if (require.main === module) {
@@ -1,17 +1,15 @@
// Attempt to deoprhan lost scores.
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { DeorphanScores } from "#lib/score-import/framework/orphans/orphans";
import { WrapScriptPromise } from "#utils/misc";
const logger = CreateLogCtx(__dirname);
export async function DeorphanScoresMain() {
const { success, failed, removed } = await DeorphanScores({}, logger);
logger.info(`Finished attempting deorphaning.`);
log.info(`Finished attempting deorphaning.`);
logger.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`);
log.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`);
}
if (require.main === module) {
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { TachiConfig } from "#lib/setup/config";
import { DedupeArr } from "#utils/misc";
import { Queue, Worker } from "bullmq";
@@ -69,19 +69,17 @@ if (TachiConfig.TYPE !== "kamai") {
});
}
const logger = CreateLogCtx("JOB_RUNNER");
/**
* Initalises a tachi-server job runner.
* This runs the list of jobs defined in jobConfig.jobs.
*/
export function InitialiseJobRunner() {
logger.info(`Booting up Job Runner.`);
log.info(`Booting up Job Runner.`);
const names = jobs.map((e) => e.name);
if (DedupeArr(names).length !== names.length) {
logger.crit(`Jobs has duplicate name fields, refusing to run.`, () => {
log.fatal(`Jobs has duplicate name fields, refusing to run.`, () => {
process.exit(1);
});
}
@@ -98,12 +96,12 @@ export function InitialiseJobRunner() {
const worker = new Worker("Job Runner", async (j) => {
const { jobName } = j.data as { jobName: string };
logger.info(`Running job ${jobName}.`);
log.info(`Running job ${jobName}.`);
const jobInfo = jobNameMap.get(jobName);
if (!jobInfo) {
logger.severe(`Unknown job name ${jobName}, couldn't find a run function?`);
log.error(`Unknown job name ${jobName}, couldn't find a run function?`);
return false;
}
@@ -112,7 +110,7 @@ export function InitialiseJobRunner() {
return true;
});
logger.info(`Initialised ${jobs.length} jobs (${jobs.map((e) => e.name).join(", ")}).`);
log.info(`Initialised ${jobs.length} jobs (${jobs.map((e) => e.name).join(", ")}).`);
return worker;
}
+10 -12
View File
@@ -1,12 +1,10 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import db from "#services/mongo/db";
import { GetMillisecondsSince } from "#utils/misc";
import { GetAllRankings } from "#utils/user";
import type { UserGameStats, UserGameStatsSnapshotDocument } from "../../../../common/src";
const logger = CreateLogCtx(__filename);
// get the time of this midnight. it's possible this script eclipses itself when weird timezone
// nonsense happens. we'll have to see.
const currentTime = new Date().setUTCHours(0, 0, 0, 0);
@@ -21,7 +19,7 @@ export async function UGSSnapshot() {
const alreadyExists = await db["game-stats-snapshots"].findOne({ timestamp: currentTime });
if (alreadyExists) {
logger.warn(
log.warn(
`There already exists snapshots at this time. Has this script been ran twice on one day? Ignoring request.`,
);
@@ -30,7 +28,7 @@ export async function UGSSnapshot() {
);
}
logger.info(`Snapshotting UserGameStats.`);
log.info(`Snapshotting UserGameStats.`);
try {
await db["game-stats"]
@@ -40,7 +38,7 @@ export async function UGSSnapshot() {
.each(async (ugs: UserGameStats, { pause, resume }) => {
pause();
logger.debug(`Snapshotting ${ugs.userID} ${ugs.playtype} ${ugs.game}.`);
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 }),
@@ -57,7 +55,7 @@ export async function UGSSnapshot() {
batchWrite.push(ugsSnapshot);
if (batchWrite.length >= 500) {
logger.verbose(`Flushed batch.`);
log.verbose(`Flushed batch.`);
await db["game-stats-snapshots"].insert(batchWrite);
batchWrite = [];
@@ -70,22 +68,22 @@ export async function UGSSnapshot() {
await db["game-stats-snapshots"].insert(batchWrite);
}
logger.info(
log.info(
`Successfully snapshotted all data as of ${new Date(
currentTime,
).toString()}. Took ${GetMillisecondsSince(timeStart)} ms.`,
);
} catch (err) {
// if we panic, we need to revert whatever we did.
logger.severe(`FATAL IN UGS-SNAPSHOT - Possibly failed midway through snapshotting.`, {
log.error(`FATAL IN UGS-SNAPSHOT - Possibly failed midway through snapshotting.`, {
err,
});
logger.info(`Removing all snapshots at this timestamp (${currentTime}).`);
log.info(`Removing all snapshots at this timestamp (${currentTime}).`);
await db["game-stats-snapshots"].remove({ timestamp: currentTime });
logger.info(`Removed.`);
log.info(`Removed.`);
throw err;
}
@@ -98,7 +96,7 @@ if (require.main === module) {
})
.catch((err: unknown) => {
// This is a severe error, not an error. Running the UGS snapshot every day is necessary.
logger.severe(`Failed to snapshot user game stats.`, { err });
log.error(`Failed to snapshot user game stats.`, { err });
setTimeout(() => {
process.exit(1);
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { BacksyncCollection, PullDatabaseSeeds } from "#lib/seeds/repo";
import db from "#services/mongo/db";
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
@@ -14,8 +14,6 @@ import type {
Versions,
} from "../../../../common/src";
const logger = CreateLogCtx(__filename);
const difficultyResolve: Record<string, [Playtypes["iidx"], Difficulties["iidx:DP" | "iidx:SP"]]> =
{
3: ["SP", "HYPER"],
@@ -56,12 +54,12 @@ interface PoyashiProxyData {
export async function UpdatePoyashiData() {
const repo = await PullDatabaseSeeds();
logger.info("Fetching data from proxy...");
log.info("Fetching data from proxy...");
const data = (await fetch("https://proxy.poyashi.me/?type=bpi").then((r) =>
r.json(),
)) as PoyashiProxyData;
logger.info("Fetched data.");
log.info("Fetched data.");
const iidxSongs: Array<SongDocument<"iidx">> = await repo.ReadCollection("songs-iidx");
const iidxCharts: Array<ChartDocument<"iidx:DP" | "iidx:SP">> =
@@ -114,7 +112,7 @@ export async function UpdatePoyashiData() {
const tachiSong = FindSongOnTitle(d.title);
if (!tachiSong) {
logger.warn(`Cannot find song ${d.title}?`);
log.warn(`Cannot find song ${d.title}?`);
continue;
}
@@ -122,7 +120,7 @@ export async function UpdatePoyashiData() {
const tachiChart = FindChartWithPTDFVersion(tachiSong.id, playtype, diff, "29");
if (!tachiChart) {
logger.warn(
log.warn(
`Cannot find chart ${tachiSong.title} (${tachiSong.id}) ${playtype}, ${diff}?`,
);
continue;
@@ -131,14 +129,12 @@ export async function UpdatePoyashiData() {
const kavg = Number(d.avg);
if (kavg < 0) {
logger.warn(
`${tachiSong.title} (${playtype} ${diff}). Invalid kavg ${d.avg}, Skipping.`,
);
log.warn(`${tachiSong.title} (${playtype} ${diff}). Invalid kavg ${d.avg}, Skipping.`);
continue;
}
if (d.removed === true) {
logger.info(`Skipping removed chart ${tachiSong.title}.`);
log.info(`Skipping removed chart ${tachiSong.title}.`);
continue;
}
@@ -164,15 +160,15 @@ export async function UpdatePoyashiData() {
await repo.WriteCollection("charts-iidx", iidxCharts);
if (updatedChartIDs.length !== 0) {
logger.info(`Finished applying BPI changes. These changes will be backsynced later.`);
log.info(`Finished applying BPI changes. These changes will be backsynced later.`);
logger.info(`Recalcing scores.`);
log.info(`Recalcing scores.`);
await RecalcAllScores({
game: "iidx",
chartID: { $in: updatedChartIDs },
});
logger.info(`Finished recalcing scores.`);
log.info(`Finished recalcing scores.`);
}
await repo.Destroy();
@@ -1,4 +1,4 @@
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { BacksyncCollection } from "#lib/seeds/repo";
/* eslint-disable no-await-in-loop */
import db from "#services/mongo/db";
@@ -9,8 +9,6 @@ import { FindSongOnTitle } from "#utils/queries/songs";
import { decode } from "html-entities";
import { parse } from "node-html-parser";
const logger = CreateLogCtx(__filename);
export async function UpdateDPTiers() {
const rawHTML = await fetch("https://zasa.sakura.ne.jp/dp/run.php").then((r) => r.text());
@@ -40,7 +38,7 @@ export async function UpdateDPTiers() {
});
}
logger.info(`Got DP tier data. Applying it.`);
log.info(`Got DP tier data. Applying it.`);
const updatedSongIDs = new Set();
@@ -49,7 +47,7 @@ export async function UpdateDPTiers() {
const song = await FindSongOnTitle("iidx", d.songTitle);
if (!song) {
logger.warn(`Couldn't find song with title ${d.songTitle}.`);
log.warn(`Couldn't find song with title ${d.songTitle}.`);
return;
}
@@ -84,9 +82,7 @@ export async function UpdateDPTiers() {
);
if (updatedSongIDs.size !== 0) {
logger.info(
`${updatedSongIDs.size} songs were changed. Recalcing the relevant scores now.`,
);
log.info(`${updatedSongIDs.size} songs were changed. Recalcing the relevant scores now.`);
await RecalcAllScores({
game: "iidx",
@@ -94,12 +90,12 @@ export async function UpdateDPTiers() {
songID: { $in: [...updatedSongIDs.values()] },
});
logger.info(`Recalced those scores.`);
log.info(`Recalced those scores.`);
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update DP Tierlist");
}
logger.info("Done.");
log.info("Done.");
}
/**
@@ -2,7 +2,7 @@ import type { ChartDocument, Difficulties, integer } from "../../../../common/sr
/* eslint-disable no-await-in-loop */
import CreateLogCtx from "#lib/logger/logger";
import { log } from "#lib/logger/log.js";
import { BacksyncCollection } from "#lib/seeds/repo";
import db from "#services/mongo/db";
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
@@ -12,8 +12,6 @@ import { FindSongOnTitle } from "#utils/queries/songs";
import fetch from "node-fetch";
import { p } from "prudence";
const logger = CreateLogCtx(__filename);
interface SP12Data {
id: integer;
title: string;
@@ -49,7 +47,7 @@ async function FetchSP12Data() {
});
if (err) {
logger.error(`Got invalid/unexpected content from sp12.`, { unvalidatedRJ });
log.error(`Got invalid/unexpected content from sp12.`, { unvalidatedRJ });
throw new Error(`Got invalid/unexpected content from sp12.`);
}
@@ -66,7 +64,7 @@ async function FetchSP12Data() {
try {
chart = await HumanisedTitleLookup(sh.title);
} catch (err) {
logger.error((err as Error).message);
log.error((err as Error).message);
continue;
}
@@ -189,21 +187,21 @@ async function FetchSP12Data() {
},
);
logger.info(`Saved ${sh.title} value ${key} = ${val} (${text}).`);
log.info(`Saved ${sh.title} value ${key} = ${val} (${text}).`);
}
}
if (updatedChartIDs.length !== 0) {
logger.info(`Finished applying SP12 changes. Recalcing.`);
logger.info("These changes will be backsynced by a separate script.");
log.info(`Finished applying SP12 changes. Recalcing.`);
log.info("These changes will be backsynced by a separate script.");
logger.info(`Recalcing scores.`);
log.info(`Recalcing scores.`);
await RecalcAllScores({
game: "iidx",
chartID: { $in: updatedChartIDs },
});
logger.info(`Finished recalcing scores.`);
log.info(`Finished recalcing scores.`);
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update SP12 Tierlist");
}
+31
View File
@@ -0,0 +1,31 @@
import pino from "pino";
export type KtLogger = pino.Logger;
// Use JSON log lines when LOG_JSON=1 (dev/prod); otherwise pretty format for localdev.
// Deliberately process.env here instead of importing ENV to avoid a circular dep.
const useJson = process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true";
export const log = pino({
level: process.env.LOG_LEVEL ?? "info",
...(useJson
? {}
: {
transport: {
options: { colorize: true },
target: "pino-pretty",
},
}),
});
export function AppendLogCtx(context: string, parent: KtLogger = log): KtLogger {
return parent.child({ context });
}
export function ChangeRootLogLevel(level: string): void {
log.level = level;
}
export function GetLogLevel(): string {
return log.level;
}
@@ -1,62 +0,0 @@
import { Env, ServerConfig } from "#lib/setup/config";
import t from "tap";
import CreateLogCtx, { ChangeRootLogLevel, GetLogLevel, rootLogger, Transports } from "./logger";
const LOG_LEVEL = Env.LOG_LEVEL;
t.test("Logger Tests", (t) => {
const logger = CreateLogCtx(__filename);
if (!Transports[0]) {
throw new Error(`No transports were defined? Can't perform logger tests.`);
}
// lol
Transports[0].level = "debug";
logger.debug("Debug Message Test");
logger.verbose("Verbose Message Test");
logger.info("Info Message Test");
logger.warn("Warning Message Test");
logger.error("Error Message Test");
logger.severe("Severe Message Test");
logger.crit("Critical Message Test");
Transports[0].level = process.env.LOG_LEVEL ?? "info";
logger.debug("This message shouldn't appear.");
t.end();
});
t.test("#GetLogLevel", (t) => {
t.equal(GetLogLevel(), LOG_LEVEL);
ChangeRootLogLevel("crit");
t.equal(GetLogLevel(), "crit");
ChangeRootLogLevel(LOG_LEVEL);
t.equal(GetLogLevel(), LOG_LEVEL);
t.end();
});
t.test("#ChangeRootLogLevel", (t) => {
t.test("Should work with the root logger.", (t) => {
rootLogger.info("The below message should appear.");
ChangeRootLogLevel("verbose");
rootLogger.verbose("== SHOULD APPEAR ==");
ChangeRootLogLevel("info");
rootLogger.info("The below message SHOULD NOT appear.");
rootLogger.verbose("== SHOULD NOT APPEAR ==");
t.end();
});
t.end();
});
-197
View File
@@ -1,197 +0,0 @@
import { Env, TachiConfig } from "#lib/setup/config";
import { EscapeStringRegexp } from "#utils/misc";
import SafeJSONStringify from "safe-json-stringify";
import winston, { format, type LeveledLogMethod, type Logger, transports } from "winston";
export type KtLogger = { severe: LeveledLogMethod } & Logger;
const level = process.env.LOG_LEVEL;
const formatExcessProperties = (meta: Record<string, unknown>, limit = false) => {
let i = 0;
for (const [key, val] of Object.entries(meta)) {
// this is probably fine
if (val instanceof Error) {
meta[key] = { message: val.message, stack: val.stack };
}
i++;
}
if (!i) {
return "";
}
const content = SafeJSONStringify(meta);
return ` ${limit ? StrCap(content) : content}`;
};
function StrCap(string: string) {
if (string.length > 300) {
return `${string.slice(0, 297)}...`;
}
return string;
}
const formatExcessPropertiesNoStack = (
meta: Record<string, unknown>,
omitKeys: Array<string> = [],
limit = false,
) => {
const realMeta: Record<string, unknown> = {};
for (const [key, val] of Object.entries(meta)) {
if (omitKeys.includes(key)) {
continue;
}
// this is probably fine
if (val instanceof Error) {
realMeta[key] = { message: val.message };
} else if (!key.startsWith("__") && !key.startsWith("!")) {
realMeta[key] = val;
}
}
if (Object.keys(realMeta).length === 0) {
return "";
}
const content = SafeJSONStringify(realMeta);
return ` ${limit ? StrCap(content) : content}`;
};
const tachiPrintf = format.printf(
({ level, message, context = "tachi-root", timestamp, ...meta }) =>
`${timestamp} [${
Array.isArray(context) ? context.join(" | ") : context
}] ${level}: ${message}${formatExcessProperties(meta)}`,
);
const tachiConsolePrintf = format.printf(
({ level, message, context = "tachi-root", timestamp, hideFromConsole, ...meta }) =>
`${timestamp} [${
Array.isArray(context) ? context.join(" | ") : context
}] ${level}: ${message}${formatExcessPropertiesNoStack(
meta,
hideFromConsole as Array<string>,
true,
)}`,
);
winston.addColors({
crit: ["bgRed", "black"],
severe: ["bgBrightRed", "black"],
error: ["red"],
warn: ["yellow"],
info: ["cyan"],
verbose: ["white"],
debug: ["white"],
});
const baseFormatRoute = format.combine(
format.timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
);
const defaultFormatRoute = format.combine(
baseFormatRoute,
format.errors({ stack: false }),
tachiPrintf,
);
const consoleFormatRoute = format.combine(
baseFormatRoute,
format.errors({ stack: false }),
tachiConsolePrintf,
format.colorize({
all: true,
}),
);
const tports: Array<winston.transport> = [
new transports.Console({
format: consoleFormatRoute,
}),
];
export const rootLogger = winston.createLogger({
levels: {
// entire process termination is necessary
crit: 0,
// something is wrong, and more than one function is affected (such as a failed assertion that is definitely expected to be true).
severe: 1,
// function call (or related process) has failed unexpectedly
error: 2,
// function call has hit something it didn't want, but can recover
warn: 3,
// something has happened that is expected, but worth logging
info: 4,
// something has happened
verbose: 5,
// glorified console.log debugging
debug: 6,
},
level,
format: defaultFormatRoute,
transports: tports,
defaultMeta: {
__ServerName: TachiConfig.NAME,
__Worker: !!process.env.IS_WORKER,
},
}) as KtLogger;
function CreateLogCtx(filename: string, lg = rootLogger): KtLogger {
const replacedFilename = filename.replace(
new RegExp(`^${EscapeStringRegexp(process.cwd())}/((js|src)/)?`, "u"),
"",
);
const logger = lg.child({
context: [replacedFilename],
}) as KtLogger;
// @hack, defaultMeta isn't reactive -- won't be updated unless we do this.
logger.defaultMeta = { ...(logger.defaultMeta ?? {}), context: [replacedFilename] };
return logger;
}
export function AppendLogCtx(context: string, lg: KtLogger): KtLogger {
const newContext = [...lg.defaultMeta.context, context];
return lg.child({ context: newContext }) as KtLogger;
}
export function ChangeRootLogLevel(
level: "crit" | "debug" | "error" | "info" | "severe" | "verbose" | "warn",
) {
rootLogger.info(`Changing log level to ${level}.`);
for (const tp of rootLogger.transports) {
tp.level = level;
}
}
export function GetLogLevel() {
return (
rootLogger.transports.map((e) => e.level).find((e) => typeof e === "string") ??
Env.LOG_LEVEL
);
}
export const Transports = rootLogger.transports;
export default CreateLogCtx;
@@ -1,127 +0,0 @@
import db from "#services/mongo/db";
import ResetDBState from "#test-utils/resets";
import t from "tap";
import { ApplyMigration, FAKE_MIGRATION, FindUnappliedMigrations } from "./migrations";
t.test("#FindUnappliedMigrations", (t) => {
t.beforeEach(ResetDBState);
t.test("Should find all unapplied migrations", async (t) => {
const res = await FindUnappliedMigrations();
t.equal(
res.includes("fake-migration"),
true,
"Should contain fake-migration since it isn't applied.",
);
t.end();
});
t.test("Shouldn't return pending migrations", async (t) => {
await db.migrations.insert({
migrationID: "fake-migration",
status: "pending",
});
const res = await FindUnappliedMigrations();
t.equal(res.includes("fake-migration"), false);
t.end();
});
t.test("Shouldn't return applied migrations", async (t) => {
await db.migrations.insert({
migrationID: "fake-migration",
status: "applied",
appliedOn: 1000,
});
const res = await FindUnappliedMigrations();
t.equal(res.includes("fake-migration"), false);
t.end();
});
t.end();
});
t.test("#ApplyMigration", (t) => {
t.beforeEach(ResetDBState);
t.test("Should apply a migration.", async (t) => {
await ApplyMigration(FAKE_MIGRATION);
const dbRes = await db.users.findOne({
id: 1,
});
t.equal(
// @ts-expect-error Our migration is a fake one that adds __wasMigrated
// to all user documents. There's nothing special about this property name.
dbRes?.__wasMigrated,
true,
"Migration should be applied on the users.",
);
t.end();
});
t.test("Should panic and reset if something goes wrong.", async (t) => {
try {
await ApplyMigration({
id: "bad-migration",
up: () => {
throw new Error("Failed to migrate.");
},
down: () =>
db.users.update({}, { $set: { __wasUnmigrated: true } }, { multi: true }),
});
} catch (e) {
const err = e as Error;
t.equal(err.message, "Was going to exit with statusCode 1, but we're in testing.");
}
const dbRes = await db.users.findOne({
id: 1,
});
t.equal(
// @ts-expect-error Our un-migration adds this property to check if it worked.
dbRes?.__wasUnmigrated,
true,
"Migration should be applied on the users.",
);
t.end();
});
t.test(
"Should panic and panic again if un-migration failed, with a scarier log message.",
async (t) => {
try {
await ApplyMigration({
id: "bad-migration",
up: () => {
throw new Error("Failed to migrate.");
},
down: () => {
throw new Error("Failed to undo migration.");
},
});
} catch (e) {
const err = e as Error;
t.equal(err.message, "Was going to exit with statusCode 1, but we're in testing.");
}
t.end();
},
);
t.end();
});
@@ -1,280 +0,0 @@
import type { Migration } from "#utils/types";
import CreateLogCtx from "#lib/logger/logger";
import { Env, TachiConfig } from "#lib/setup/config";
import db from "#services/mongo/db";
import UserFollowersMigration from "./migrations/add-following-to-users";
import AddLockedAt from "./migrations/add-lockedat";
import UGPTAddPreferredRanking from "./migrations/add-preferredRanking-to-ugpt";
import UGPTRivalsMigration from "./migrations/add-rivals-to-ugpt";
import ChunithmFixPreferredDefaultEnum from "./migrations/chunithm-fix-preferred-default-enum";
import ChunithmLampSplit from "./migrations/chunithm-lamp-split";
import FixUndefinedBMSData from "./migrations/fix-undefined-bms-data";
import JoinINFCastHourCharts from "./migrations/join-inf-casthour-charts";
import JoinINFCastHourSongs from "./migrations/join-inf-casthour-songs";
import KtRatingToCuratorSkill from "./migrations/ktRating-to-curator-skill";
import MaimaiDXFixLamps from "./migrations/maimaidx-fix-lamps";
import MarkFervidexAsWithoutIntent from "./migrations/mark-fervidex-as-without-intent";
import MarkKsHookAsWithoutIntent from "./migrations/mark-kshook-as-without-intent";
import NullLR2HookFailedBPs from "./migrations/null-lr2hook-failed-bps";
import OngekiV2 from "./migrations/ongeki-v2";
import RemoveAbove100pc from "./migrations/remove-above-100pc";
import RemoveIIDXBeginners from "./migrations/remove-iidx-beginners";
import RemoveIIDX2dxtraBeginners from "./migrations/remove-iidx-extra-beginners";
import RemoveMultifolderStats from "./migrations/remove-multifolder-stats";
import RemoveRandom from "./migrations/remove-random";
import RemoveSessionViews from "./migrations/remove-session-views";
import RmHot from "./migrations/rm-hot";
// import SdvxMaxxiveGoals from "./migrations/sdvx-maxxive-goals";
// import SdvxMaxxiveLampRearrange from "./migrations/sdvx-maxxive-lamp-rearrange";
import SessionsToScoreIDs from "./migrations/sessions-to-scoreIDs";
import UpdateJubeatPreferredTables from "./migrations/update-jubeat-preferred-tables";
import V3PropsMigration from "./migrations/v3-props";
import V3ScoresMigration from "./migrations/v3-scores";
const logger = CreateLogCtx(__filename);
// NOTE: This is declared here, and NOT in test-utils. If any runtime file attempts to depend
// on test-utils, the server will not boot.
export const FAKE_MIGRATION: Migration = {
id: "fake-migration",
up: () => db.users.update({}, { $set: { __wasMigrated: true } }, { multi: true }),
down: () => db.users.update({}, { $unset: { __wasMigrated: 1 } }, { multi: true }),
};
// Migrations are stored in an array because they have some concept of order
// That is, migrations should ideally be applied in a fixed order just to avoid
// any potential unsavoury interactions.
// If we're testing, we should pull fake migrations instead to ensure the tests
// stay consistent
const REGISTERED_MIGRATIONS: Array<Migration> =
Env.NODE_ENV === "test"
? [FAKE_MIGRATION]
: [
UGPTRivalsMigration,
RemoveMultifolderStats,
UGPTAddPreferredRanking,
UserFollowersMigration,
RemoveSessionViews,
SessionsToScoreIDs,
V3PropsMigration,
V3ScoresMigration,
AddLockedAt,
];
// only apply type-specific migrations if we're not in testing
if (Env.NODE_ENV !== "test") {
// kamaitachi specific migrations
if (TachiConfig.TYPE !== "boku") {
REGISTERED_MIGRATIONS.push(
RemoveIIDXBeginners,
UpdateJubeatPreferredTables,
JoinINFCastHourCharts,
JoinINFCastHourSongs,
MarkFervidexAsWithoutIntent,
MarkKsHookAsWithoutIntent,
KtRatingToCuratorSkill,
RemoveIIDX2dxtraBeginners,
RmHot,
ChunithmLampSplit,
OngekiV2,
ChunithmFixPreferredDefaultEnum,
// SdvxMaxxiveLampRearrange,
// SdvxMaxxiveGoals,
MaimaiDXFixLamps,
);
}
// bokutachi specific migrations
if (TachiConfig.TYPE !== "kamai") {
REGISTERED_MIGRATIONS.push(
NullLR2HookFailedBPs,
FixUndefinedBMSData,
RemoveRandom,
RemoveAbove100pc,
);
}
}
function CreateMigrationLookupMap(migrations: Array<Migration>) {
const map = new Map<string, Migration>();
for (const mig of migrations) {
if (map.get(mig.id)) {
logger.crit(
`Multiple migrations are registered for ${mig.id}. Cannot safely apply migrations.`,
() => {
// note, we want to exit in testing here, this is fine.
process.exit(1);
},
);
}
map.set(mig.id, mig);
}
return map;
}
// That said, we still make a lookup map so we can efficiently find registered
// migrations.
const MIGRATION_LOOKUP = CreateMigrationLookupMap(REGISTERED_MIGRATIONS);
/**
* Return the migrationID of all migrations that have not been applied.
*/
export async function FindUnappliedMigrations() {
// An unapplied migration is one that is acknowledged in the codebase
// (see REGISTERED_MIGRATIONS), but not stored inside the mongodb instance.
const migrations = await db.migrations.find(
{},
{
projection: {
migrationID: 1,
},
},
);
const appliedIDs = new Set(migrations.map((e) => e.migrationID));
const notApplied = [];
for (const migration of REGISTERED_MIGRATIONS) {
if (!appliedIDs.has(migration.id)) {
notApplied.push(migration.id);
}
}
return notApplied;
}
export async function ApplyUnappliedMigrations() {
const unapplied = await FindUnappliedMigrations();
if (unapplied.length === 0) {
logger.verbose(`Found no migrations to apply. Not bothering.`);
return;
}
logger.info(
`Found ${
unapplied.length
} migration(s) that have not been applied yet. These are '${unapplied.join("', '")}'.`,
{ unapplied },
);
for (const migrationID of unapplied) {
// These things need to apply in lockstep.
// Note that if any migration fails, this will exit at CRIT level
// and not continue.
// eslint-disable-next-line no-await-in-loop
await ApplyMigrationByID(migrationID);
}
}
export function ApplyMigrationByID(migrationID: string) {
const migration = MIGRATION_LOOKUP.get(migrationID);
if (!migration) {
logger.error(
`Attempted to apply migration ${migrationID}, but that migration doesn't exist?`,
);
throw new Error(
`Attempted to apply migration ${migrationID}, but that migration doesn't exist?`,
);
}
return ApplyMigration(migration);
}
/**
* Applies a migration. Will **EXIT TACHI** if a migration fails to apply, as this implies
* database level inconsistencies which could cause severe problems.
*/
export async function ApplyMigration(migration: Migration) {
const migrationID = migration.id;
logger.info(`Received request to apply migration '${migrationID}'.`);
// Lock the migration here:
// If the migration is already applied (i.e. migrationID is set and pending/applied)
// don't bother applying again
// If the migration isn't, set it as pending and continue. This is race-condition
// safe.
const wasApplied = await db.migrations.findOneAndUpdate(
{ migrationID },
{
$setOnInsert: {
migrationID,
status: "pending",
},
},
{
returnOriginal: true,
upsert: true,
},
);
if (wasApplied) {
logger.error(`Tried to apply migration ${migrationID}, but it was already applied.`);
throw new Error(`Tried to apply migration ${migrationID}, but it was already applied.`);
}
try {
await migration.up();
logger.info(`Successfully applied migration '${migrationID}'.`);
await db.migrations.update(
{
migrationID,
},
{
$set: {
status: "applied",
appliedOn: Date.now(),
},
},
);
logger.info(`Successfully set migration state for '${migrationID}' to applied.`);
} catch (err) {
logger.severe(
`Failed to apply migration '${migrationID}'. Attempting to revert migration. Tachi will exit after attempting to revert this migration.`,
{ err },
);
try {
await migration.down();
logger.info(
`Successfully reverted migration. Database state is okay, but Tachi will not boot.`,
);
logger.crit(
`Failed to apply migration '${migrationID}'. Although successfully reverted, Tachi will not boot if a migration fails.`,
);
} catch (err) {
logger.crit(
`FAILED TO REVERT PARTIAL MIGRATION '${migrationID}'. THE DATABASE IS ALMOST CERTAINLY TAINTED. MANUAL INTERVENTION/BACKUP RESTORE NECESSARY.`,
{
err,
},
);
}
// remove the stale migration so it can be re-ran in the future.
await db.migrations.findOneAndDelete({ migrationID });
if (Env.NODE_ENV === "test") {
throw new Error("Was going to exit with statusCode 1, but we're in testing.");
}
logger.crit(`Exiting.`, () => {
process.exit(1);
});
}
}
@@ -1,29 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "add-following-to-users",
up: async () => {
await db["user-settings"].update(
{},
{
$set: {
following: [],
},
},
{ multi: true },
);
},
down: async () => {
await db["user-settings"].update(
{},
{
$unset: { following: 1 },
},
{ multi: true },
);
},
};
export default migration;
@@ -1,31 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "add-lockedat",
up: async () => {
await db["import-locks"].update(
{},
{
$set: {
lockedAt: null,
},
},
{ multi: true },
);
},
down: async () => {
await db["import-locks"].update(
{},
{
$unset: {
lockedAt: 1,
},
},
{ multi: true },
);
},
};
export default migration;
@@ -1,29 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "add-preferredRanking-to-ugpt",
up: async () => {
await db["game-settings"].update(
{},
{
$set: {
"preferences.preferredRanking": null,
},
},
{ multi: true },
);
},
down: async () => {
await db["game-settings"].update(
{},
{
$unset: { "preferences.preferredRanking": 1 },
},
{ multi: true },
);
},
};
export default migration;
@@ -1,29 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "add-rivals-to-ugpt",
up: async () => {
await db["game-settings"].update(
{},
{
$set: {
rivals: [],
},
},
{ multi: true },
);
},
down: async () => {
await db["game-settings"].update(
{},
{
$unset: { rivals: 1 },
},
{ multi: true },
);
},
};
export default migration;
@@ -1,26 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "chunithm-fix-preferred-default-enum",
up: async () => {
await db["game-settings"].update(
{
game: "chunithm",
"preferences.preferredDefaultEnum": "lamp",
},
{
$set: {
"preferences.preferredDefaultEnum": "noteLamp",
},
},
{ multi: true },
);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,593 +0,0 @@
import type { Migration } from "#utils/types";
import CreateLogCtx from "#lib/logger/logger";
import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs";
import { CreateFullScoreData } from "#lib/score-import/framework/score-importing/derivers";
import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id";
import { CreateGoalID } from "#lib/targets/goals";
import type { GetEnumValue } from "../../../../../common/src/types/metrics";
import {
GetGPTString,
type GoalDocument,
type GoalSubscriptionDocument,
type integer,
type ScoreData,
type ScoreDocument,
} from "../../../../../common/src";
/* eslint-disable no-await-in-loop */
import db, { monkDB } from "#services/mongo/db";
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
const logger = CreateLogCtx(__filename);
const OLD_LAMP_INDEXES = {
FAILED: 0,
CLEAR: 1,
"FULL COMBO": 2,
"ALL JUSTICE": 3,
"ALL JUSTICE CRITICAL": 4,
} as const;
const NEW_CLEAR_LAMP_INDEXES = {
FAILED: 0,
CLEAR: 1,
HARD: 2,
BRAVE: 3,
ABSOLUTE: 4,
CATASTROPHY: 5,
} as const;
const NEW_CLEAR_LAMPS = ["FAILED", "CLEAR", "HARD", "BRAVE", "ABSOLUTE", "CATASTROPHY"] as const;
const NEW_NOTE_LAMP_INDEXES = {
NONE: 0,
"FULL COMBO": 1,
"ALL JUSTICE": 2,
"ALL JUSTICE CRITICAL": 3,
} as const;
const NEW_NOTE_LAMPS = ["NONE", "FULL COMBO", "ALL JUSTICE", "ALL JUSTICE CRITICAL"] as const;
async function FastUpdateSessions() {
const sessions = await db.sessions.find({ game: "chunithm", playtype: "Single" });
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
.get("temp-update-map")
.find({});
const lookup = new Map(newScoreIDRefs.map((r) => [r.old, r.new]));
const newScoreIDs = new Set(newScoreIDRefs.map((r) => r.new));
await Promise.allSettled(
sessions.map(async (session) => {
const updatedScoreIDs: Array<string> = [];
for (const oldScoreID of session.scoreIDs) {
const newScoreID = lookup.get(oldScoreID);
// If the migration died here before, we might run into sessions where the scoreIDs
// are already the migrated ones. Therefore, we only emit errors for IDs that are
// actually non-existent.
if (!newScoreID && !newScoreIDs.has(oldScoreID)) {
logger.warning(
`[session ${session.sessionID}] No such score ${oldScoreID} exists in lookup, nor is it a new scoreID. Ignoring this score.`,
);
continue;
}
updatedScoreIDs.push(newScoreID ?? oldScoreID);
}
await db.sessions.update(
{ sessionID: session.sessionID },
{
$set: { scoreIDs: updatedScoreIDs },
},
);
}),
);
}
async function FastUpdateImports() {
const imports = await db.imports.find({ game: "chunithm", playtypes: "Single" });
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
.get("temp-update-map")
.find({});
const lookup = new Map(newScoreIDRefs.map((r) => [r.old, r.new]));
const newScoreIDs = new Set(newScoreIDRefs.map((r) => r.new));
await Promise.allSettled(
imports.map(async (importDoc) => {
const updatedScoreIDs: Array<string> = [];
for (const oldScoreID of importDoc.scoreIDs) {
const newScoreID = lookup.get(oldScoreID);
// If the migration died here before, we might run into sessions where the scoreIDs
// are already the migrated ones. Therefore, we only emit errors for IDs that are
// actually non-existent.
if (!newScoreID && !newScoreIDs.has(oldScoreID)) {
logger.warning(
`[import ${importDoc.importID}] No such score ${oldScoreID} exists in lookup, nor is it a new scoreID. Ignoring this score.`,
);
continue;
}
updatedScoreIDs.push(newScoreID ?? oldScoreID);
}
await db.imports.update(
{ importID: importDoc.importID },
{
$set: { scoreIDs: updatedScoreIDs },
},
);
}),
);
}
function MoveLamps(
scoreData: { lamp?: string } & ScoreData<"chunithm:Single">,
): ScoreData<"chunithm:Single"> {
// This score has already been migrated.
if (scoreData.clearLamp && scoreData.noteLamp && !scoreData.lamp) {
return scoreData;
}
const oldLamp = scoreData.lamp;
let noteLamp: GetEnumValue<"chunithm:Single", "noteLamp">;
let clearLamp: GetEnumValue<"chunithm:Single", "clearLamp">;
// If the old score had a combo lamp, we assume that the score was at least a clear.
// While it is theoretically possible for there to be a failed all justice, the chance
// is small.
if (oldLamp === "FAILED" || oldLamp === "CLEAR") {
noteLamp = "NONE";
clearLamp = oldLamp;
} else if (
oldLamp === "FULL COMBO" ||
oldLamp === "ALL JUSTICE" ||
oldLamp === "ALL JUSTICE CRITICAL"
) {
noteLamp = oldLamp;
clearLamp = "CLEAR";
} else {
noteLamp = "NONE";
clearLamp = "FAILED";
}
const newScoreData = {
...scoreData,
noteLamp,
clearLamp,
};
delete newScoreData.lamp;
return newScoreData;
}
const migration: Migration = {
id: "chunithm-lamp-split",
up: async () => {
logger.info("Migrating CHUNITHM scores to new lamps...");
const gptString = GetGPTString("chunithm", "Single") as "chunithm:Single";
await EfficientDBIterate(
// @ts-expect-error filter assures we're getting only chuni scores
db.scores,
(score: { scoreData: { lamp?: string } } & ScoreDocument<"chunithm:Single">) => {
// @ts-expect-error it does exist sometimes
delete score._id;
const newScore = {
...score,
scoreData: MoveLamps(score.scoreData),
};
const newScoreID = CreateScoreID(
gptString,
newScore.userID,
newScore,
newScore.chartID,
);
// To make this faster, we update only the relevant parts of the scores instead
// of having it go through the UpdateScore process, which requires querying
// a chart from the database every UpdateScore call.
//
// We skip calculating calculatedData since that is outside the scope
// of this lamp move; no CHUNITHM calculated metrics depend on lamp.
newScore.scoreID = newScoreID;
newScore.scoreData = CreateFullScoreData(
gptString,
newScore.scoreData,
// @ts-expect-error This is intentional, CHUNITHM derivers do not
// depend on the chart.
null,
logger,
);
return {
oldScoreID: score.scoreID,
newScore,
};
},
async (changes) => {
const changesPrime = [];
for (const change of changes) {
const scoreExists = await db.scores.findOne({
scoreID: change.newScore.scoreID,
});
if (scoreExists) {
continue;
}
changesPrime.push(change);
}
await db.scores.bulkWrite(
changesPrime.map((c) => ({
updateOne: {
filter: {
scoreID: c.oldScoreID,
},
update: {
$set: c.newScore,
},
},
})),
);
await monkDB.get("temp-update-map").insert(
changes.map((c) => ({
old: c.oldScoreID,
new: c.newScore.scoreID,
})),
);
},
{
game: "chunithm",
playtype: "Single",
"scoreData.noteLamp": { $exists: false },
"scoreData.clearLamp": { $exists: false },
},
);
logger.info("Reconstructing PBs...");
const usersWithChuniScores = await db.scores.distinct("userID", {
game: "chunithm",
playtype: "Single",
});
// We don't use UpdateAllPBs here because it re-PBs all of a user's
// games and playtypes which is fairly wasteful.
for (const userID of usersWithChuniScores) {
const chartIDs = await db.scores.distinct("chartID", {
userID,
game: "chunithm",
playtype: "Single",
});
if (chartIDs.length === 0) {
continue;
}
logger.info(`PBing #${userID}'s scores.`);
await ProcessPBs("chunithm", "Single", userID, new Set(chartIDs), logger);
}
logger.info("Updating references to scores...");
await FastUpdateSessions();
await FastUpdateImports();
await monkDB.get("temp-update-map").drop();
logger.info("Updating blacklisted scores...");
await EfficientDBIterate(
// @ts-expect-error this works because of the filter restricting game and playtype
db["score-blacklist"],
(blacklistedScore: {
score: ScoreDocument<"chunithm:Single">;
scoreID: string;
userID: integer;
}) => {
const newScore = {
...blacklistedScore.score,
scoreData: MoveLamps(blacklistedScore.score.scoreData),
};
const newScoreID = CreateScoreID(
gptString,
blacklistedScore.userID,
newScore,
newScore.chartID,
);
newScore.scoreID = newScoreID;
newScore.scoreData = CreateFullScoreData(
gptString,
newScore.scoreData,
// @ts-expect-error This is intentional. CHUNITHM derivers
// do not need a chart.
null,
logger,
);
return {
oldScoreID: blacklistedScore.scoreID,
newScoreID,
newScore,
};
},
async (changes) => {
await db["score-blacklist"].bulkWrite(
changes
.filter((c) => c.oldScoreID !== c.newScoreID)
.map((c) => ({
updateOne: {
filter: {
scoreID: c.oldScoreID,
},
update: {
$set: {
scoreID: c.newScoreID,
score: c.newScore,
},
},
},
})),
);
},
{
"score.game": "chunithm",
"score.playtype": "Single",
"score.scoreData.noteLamp": { $exists: false },
"score.scoreData.clearLamp": { $exists: false },
},
);
logger.info("Updating stat showcases...");
await db["game-settings"].bulkWrite([
...(["FULL COMBO", "ALL JUSTICE", "ALL JUSTICE CRITICAL"] as const).map((lamp) => ({
updateMany: {
filter: { game: "chunithm", playtype: "Single" } as const,
update: {
$set: {
"preferences.stats.$[e].metric": "noteLamp",
"preferences.stats.$[e].gte": NEW_NOTE_LAMP_INDEXES[lamp],
},
},
arrayFilters: [
{
"e.mode": "folder",
"e.metric": "lamp",
"e.gte": OLD_LAMP_INDEXES[lamp],
},
],
},
})),
...(["FAILED", "CLEAR"] as const).map((lamp) => ({
updateMany: {
filter: { game: "chunithm", playtype: "Single" } as const,
update: {
$set: {
"preferences.stats.$[e].metric": "clearLamp",
"preferences.stats.$[e].gte": NEW_CLEAR_LAMP_INDEXES[lamp],
},
},
arrayFilters: [
{
"e.mode": "folder",
"e.metric": "lamp",
"e.gte": OLD_LAMP_INDEXES[lamp],
},
],
},
})),
// Unfortunately we don't have enough data to accurately migrate
// over chart lamp showcases, so we'll automatically migrate everyone
// over to noteLamp, which is what people usually showcase.
{
updateMany: {
filter: { game: "chunithm", playtype: "Single" } as const,
update: {
$set: {
"preferences.stats.$[e].metric": "noteLamp",
},
},
arrayFilters: [
{
"e.mode": "chart",
"e.metric": "lamp",
},
],
},
},
]);
logger.info("Updating goals...");
await EfficientDBIterate(
db.goals,
(goal: GoalDocument) => {
const oldGoalID = goal.goalID;
if (goal.criteria.value <= OLD_LAMP_INDEXES.CLEAR) {
goal.criteria.key = "clearLamp";
// goal.criteria.value remains unchanged
} else {
goal.criteria.key = "noteLamp";
// old lamps have FC = 2, AJ = 3, AJC = 4
// new noteLamps have FC = 1, AJ = 2, AJC = 3
goal.criteria.value = goal.criteria.value - 1;
}
const newGoalID = CreateGoalID(
goal.charts,
goal.criteria,
goal.game,
goal.playtype,
);
// @ts-expect-error it exists
delete goal._id;
return {
oldGoalID,
newGoalID,
goal,
};
},
async (changes) => {
await db.goals.bulkWrite(
changes.map((c) => ({
updateOne: {
filter: {
goalID: c.oldGoalID,
},
update: {
$set: {
goalID: c.newGoalID,
criteria: c.goal.criteria,
},
},
},
})),
);
await monkDB.get("temp-update-goal-map").insert(
changes.map((c) => ({
old: c.oldGoalID,
new: c.newGoalID,
goal: c.goal,
})),
);
},
{ game: "chunithm", playtype: "Single", "criteria.key": "lamp" },
);
logger.info("Updating goal subscriptions...");
const updatedGoalIDs: Array<{ goal: GoalDocument; new: string; old: string }> = await monkDB
.get("temp-update-goal-map")
.find({});
const updatedGoalIDMap = new Map(updatedGoalIDs.map((u) => [u.old, u]));
await EfficientDBIterate(
db["goal-subs"],
(subscription: GoalSubscriptionDocument) => {
// We're 100% sure the goal ID exists in updatedGoals due to the query.
const { goal, new: newGoalID } = updatedGoalIDMap.get(subscription.goalID)!;
if (goal.criteria.key !== "clearLamp" && goal.criteria.key !== "noteLamp") {
throw new Error(`Received invalid criteria key ${goal.criteria.key}`);
}
// We don't update goal progress for goals that are "absolute" | "proportion"
// since the progress is the number of items already done, and not the lamp.
// Simply migrate them over to the new goal ID.
if (goal.criteria.mode !== "single") {
return {
userID: subscription.userID,
goalID: subscription.goalID,
newGoalID,
progress: subscription.progress,
progressHuman: subscription.progressHuman,
outOf: subscription.outOf,
outOfHuman: subscription.outOfHuman,
};
}
let newProgress: integer | null;
let newProgressHuman: string | undefined;
const newOutOfHuman =
goal.criteria.key === "clearLamp"
? NEW_CLEAR_LAMPS[goal.criteria.value]
: NEW_NOTE_LAMPS[goal.criteria.value];
if (!newOutOfHuman) {
logger.severe(`Received invalid criteria value for a goal of mode "single".`, {
mode: goal.criteria.mode,
key: goal.criteria.key,
value: goal.criteria.value,
});
throw new Error(
`Received invalid progress for a goal of mode "single". criteria_mode=${goal.criteria.mode} criteria_key=${goal.criteria.key} criteria_value=${goal.criteria.value}`,
);
}
if (subscription.progress === null) {
newProgress = null;
newProgressHuman = "NO DATA";
} else if (goal.criteria.key === "clearLamp") {
// Clamp back to CLEAR if the user has made better progress (e.g. FULL COMBO).
newProgress = Math.min(NEW_CLEAR_LAMP_INDEXES.CLEAR, subscription.progress);
newProgressHuman = NEW_CLEAR_LAMPS[newProgress];
} else {
// Clamp back to NONE if the user has made worse progress (e.g. FAILED).
// Otherwise, the -1 maps the old progress onto the new progress nicely:
// CLEAR: 1 -> NONE: 0
// FULL COMBO: 2 -> FULL COMBO: 1
// ...
newProgress = Math.max(NEW_NOTE_LAMP_INDEXES.NONE, subscription.progress - 1);
newProgressHuman = NEW_NOTE_LAMPS[newProgress];
}
if (!newProgressHuman) {
logger.severe(`Received invalid progress for a goal of mode "single".`, {
criteria: {
mode: goal.criteria.mode,
key: goal.criteria.key,
},
progress: subscription.progress,
newProgress,
});
throw new Error(
`Received invalid progress for a goal of mode "single". criteria_key=${goal.criteria.key} criteria_mode=${goal.criteria.mode} progress=${subscription.progress} newProgress=${newProgress}`,
);
}
return {
userID: subscription.userID,
goalID: subscription.goalID,
newGoalID,
progress: newProgress,
progressHuman: newProgressHuman,
outOf: goal.criteria.value,
outOfHuman: newOutOfHuman,
};
},
async (updates) => {
await db["goal-subs"].bulkWrite(
updates.map((u) => ({
updateOne: {
filter: {
userID: u.userID,
goalID: u.goalID,
},
update: {
$set: {
goalID: u.newGoalID,
progress: u.progress,
progressHuman: u.progressHuman,
outOf: u.outOf,
outOfHuman: u.outOfHuman,
},
},
},
})),
);
},
{ goalID: { $in: updatedGoalIDs.map((u) => u.old) } },
);
await monkDB.get("temp-update-goal-map").drop();
},
down: () => {
throw new Error("Reverting this change is not possible.");
},
};
export default migration;
@@ -1,37 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "fix-undefined-bms-data",
up: async () => {
await db.songs.bms.update(
{
"data.tableString": { $exists: false },
},
{
$set: {
"data.tableString": null,
},
},
{ multi: true },
);
await db.charts.bms.update(
{
"data.aiLevel": { $exists: false },
},
{
$set: {
"data.aiLevel": null,
},
},
{ multi: true },
);
},
down: () => {
throw new Error(`Cannot revert migration.`);
},
};
export default migration;
@@ -1,47 +0,0 @@
import type { Migration } from "#utils/types";
import UpdateScore from "#lib/score-mutation/update-score";
/* eslint-disable no-await-in-loop */
import db from "#services/mongo/db";
// old chart -> new chart
const mappings = [
["4ff8e32a6eefee5e476f6c8261db5c66e2496b51", "1de27f681eb00b8d422658b66507c5f438379e40"],
["827b1f8cccce51d3e2e0fd2c0b1117a47b530c70", "96bf5cd64056bca7e870233eb1096e8bb24d24b7"],
["43a7b6386da6b0c5d2d577c9f09ab83d516038a2", "764f6c14f4be643fe4d6e7715a77d4f5789a255e"],
["ad0a143262671e970c3e148b62d4aaebec17f604", "2981d53beda743071c0174b3235e8b811c227231"],
["69093ac77208326f1261b3dcc38f73d054b0400e", "f7fb763afd99981232c92a41a6c3276692551101"],
["2f8bac0550e95c87f4ff4c931c49d118622d320d", "1d4025230032b8ee6ce787e80756286a1626dc04"],
["2b2dc7bc90eb7715fda937a263878dc16c3df507", "78f3fcd2765233c1d588ea588d1416a45065b272"],
["3cf992f0615ab51903d45fda6d4fda5ed5ec5c7c", "9b6eac3a3f152a43e3ef297130677daa42d895fd"],
["fb4a6ca054b9697a34841afc52f52d7d100a5779", "6264bb33e8ab53e226cd514696789dd330ab13fc"],
["4ee3a65c0ab0968ed3c52bf90753cfa7620fee94", "03efef860d7aa122dd601853d99b73622b560aa6"],
["28c1ca0d1ef50f56d05e3143a1cde25e9b25a1ef", "1e112b872267c0b66fb64671ef2c8aedff8bcf7e"],
["1cdb02868c5a23ca4c3131f9cf0403b7ef178b83", "bbe64b3d1158db2f1255eb342bafe165ba2871e5"],
["29d7c72e8ed01173f7f68fb8102706694ba80619", "9d96ec36c37f7f723d3851618e7daef6d58a5176"],
["4d3d3ae0760c520b471a6c4025fd4517e792fdd5", "e42f30c04bcdd07ccf46470c8ce321814c69f62f"],
["5d2b11fc4d26b965dd30094c06548a55a670408c", "ee22f91a9be8826ef3b371ef9c3ab518d34753b0"],
["46d3a5c245898f8fbc2492e293a8dadcfaff363c", "92efb4e0d1b036e385cb5a73a0ace0e12baaa0cc"],
["39ed6d4a5c7a4557ab00266fae2363a6094fc0e1", "02218c7580fffe984d5db15e3ac9703c7c5ff42c"],
["6e5231055e517dbe6f67d68f572286b1c647eceb", "ef2359505d0be7492dacc4b416d7528f669b4228"],
] as const;
const migration: Migration = {
id: "join-inf-casthour-charts",
up: async () => {
for (const [oldChartID, newChartID] of mappings) {
const scores = await db.scores.find({
chartID: oldChartID,
});
for (const score of scores) {
await UpdateScore(score, { ...score, chartID: newChartID });
}
}
},
down: () => {
throw new Error(`Cannot undo migration.`);
},
};
export default migration;
@@ -1,37 +0,0 @@
import type { Migration } from "#utils/types";
import UpdateScore from "#lib/score-mutation/update-score";
/* eslint-disable no-await-in-loop */
import db from "#services/mongo/db";
// old songID -> new songID
const mappings = [
[2100, 2042],
[2130, 1997],
[2142, 2000],
] as const;
// i messed up the last migration, by only updating the chartID and not the song ID.
const migration: Migration = {
id: "join-inf-casthour-songs",
up: async () => {
for (const [oldSongID, newSongID] of mappings) {
const scores = await db.scores.find({
songID: oldSongID,
});
for (const score of scores) {
await UpdateScore(score, {
...score,
songID: newSongID,
});
}
}
},
down: () => {
throw new Error(`Cannot undo migration.`);
},
};
export default migration;
@@ -1,15 +0,0 @@
import type { Migration } from "#utils/types";
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
const migration: Migration = {
id: "ktRating-to-curator-skill",
up: async () => {
await RecalcAllScores({ game: "museca" });
},
down: () => {
throw new Error(`Not possible to revert.`);
},
};
export default migration;
@@ -1,106 +0,0 @@
import type { Migration } from "#utils/types";
import CreateLogCtx from "#lib/logger/logger";
import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs";
import UpdateScore from "#lib/score-mutation/update-score";
import db from "#services/mongo/db";
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
import type { integer, ProvidedMetrics, ScoreDocument } from "../../../../../common/src";
const logger = CreateLogCtx(__filename);
const migration: Migration = {
id: "maimaidx-fix-lamps",
up: async () => {
const affectedUserCharts: Map<integer, Set<string>> = new Map();
const affectedCharts: Set<string> = new Set();
// @ts-expect-error query assures we're getting maimaidx:Single scores
const invalidScores: Array<ScoreDocument<"maimaidx:Single">> = await db.scores.find({
game: "maimaidx",
playtype: "Single",
"scoreData.lamp": "FAILED",
"scoreData.percent": { $gte: 80 },
});
invalidScores.push(
// @ts-expect-error query assures we're getting maimaidx:Single scores
...(await db.scores.find({
game: "maimaidx",
playtype: "Single",
"scoreData.lamp": "CLEAR",
"scoreData.percent": { $lt: 80 },
})),
);
for (const score of invalidScores) {
// @ts-expect-error just in case
delete score._id;
if (affectedUserCharts.has(score.userID)) {
affectedUserCharts.get(score.userID)!.add(score.chartID);
} else {
affectedUserCharts.set(score.userID, new Set([score.chartID]));
}
affectedCharts.add(score.chartID);
let lamp: ProvidedMetrics["maimaidx:Single"]["lamp"];
if (score.scoreData.percent === 101) {
lamp = "ALL PERFECT+";
} else if (
score.scoreData.percent >= 100.5 &&
score.scoreData.judgements.great === 0 &&
score.scoreData.judgements.good === 0 &&
score.scoreData.judgements.miss === 0
) {
lamp = "ALL PERFECT";
} else if (
score.scoreData.judgements.good === 0 &&
score.scoreData.judgements.miss === 0
) {
lamp = "FULL COMBO+";
} else if (score.scoreData.judgements.miss === 0) {
lamp = "FULL COMBO";
} else if (score.scoreData.percent >= 80) {
lamp = "CLEAR";
} else {
lamp = "FAILED";
}
await UpdateScore(
score,
{
...score,
scoreData: {
...score.scoreData,
lamp,
},
},
/* updateOldChart=*/ false,
/* skipUpdatingPBs=*/ true,
);
}
for (const [userID, chartIDs] of affectedUserCharts.entries()) {
logger.info(`PBing #${userID}'s scores.`);
await ProcessPBs("maimaidx", "Single", userID, chartIDs, logger);
}
// maimai DX CiRCLE rating recalc
await RecalcAllScores({
game: "maimaidx",
"scoreData.lamp": {
$in: ["ALL PERFECT", "ALL PERFECT+"],
},
});
},
down: () => {
throw new Error(`Unable to revert transaction.`);
},
};
export default migration;
@@ -1,22 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "mark-fervidex-as-without-intent",
up: async () => {
await db.imports.update(
{
importType: "ir/fervidex",
},
{
$set: { userIntent: false },
},
);
},
down: () => {
throw new Error(`Unable to revert transaction.`);
},
};
export default migration;
@@ -1,22 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "mark-kshook-as-without-intent",
up: async () => {
await db.imports.update(
{
importType: { $in: ["ir/kshook-sv6c", "ir/kshook-sv6c-static"] },
},
{
$set: { userIntent: false },
},
);
},
down: () => {
throw new Error(`Unable to revert transaction.`);
},
};
export default migration;
@@ -1,32 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
import { UpdateAllPBs } from "#utils/calculations/recalc-scores";
const migration: Migration = {
id: "null-lr2hook-failed-bps",
up: async () => {
await db.scores.update(
{
importType: "ir/lr2hook",
"scoreMeta.gauge": "HARD",
lamp: { $in: ["FAILED", "NO PLAY"] },
},
{
$set: {
"scoreData.optional.bp": null,
},
},
{
multi: true,
},
);
await UpdateAllPBs(undefined, { game: "bms" });
},
down: () => {
throw new Error(`Unable to revert transaction, some scores may have been nulled.`);
},
};
export default migration;
@@ -1,69 +0,0 @@
import type { Migration } from "#utils/types";
import { rootLogger } from "#lib/logger/logger";
import UpdateScore from "#lib/score-mutation/update-score";
import db from "#services/mongo/db";
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
import { ONGEKI_NOTE_LAMPS, type ScoreDocument } from "../../../../../common/src";
const migration: Migration = {
id: "ongeki-v2",
up: async () => {
const failedScores = [];
await EfficientDBIterate(
db.scores,
(score) => score as ScoreDocument<"ongeki:Single">,
async (scores: Array<ScoreDocument<"ongeki:Single">>) => {
for (const score of scores) {
// @ts-expect-error just incase
delete score._id;
try {
const newScore = {
...score,
};
if (newScore.scoreData.score === 1010000) {
newScore.scoreData.noteLamp = "ALL BREAK+";
newScore.scoreData.enumIndexes.noteLamp =
ONGEKI_NOTE_LAMPS.ALL_BREAK_PLUS;
}
if (
"platScore" in newScore.scoreData.optional &&
typeof newScore.scoreData.optional.platScore === "number"
) {
newScore.scoreData.platinumScore =
newScore.scoreData.optional.platScore;
delete newScore.scoreData.optional.platScore;
} else {
newScore.scoreData.platinumScore ??= 0;
}
await UpdateScore(score, newScore);
} catch (err) {
rootLogger.warn(`Error applying ${score.scoreID}:`);
rootLogger.warn(err);
rootLogger.warn("Continuing through the error.");
failedScores.push(score.scoreID);
}
}
},
{ game: "ongeki" },
);
if (failedScores.length > 0) {
throw new Error(
`${failedScores.length} failed to be migrated. Resolve these manually, please.`,
);
}
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,21 +0,0 @@
import type { Migration } from "#utils/types";
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
import db from "#services/mongo/db";
const migration: Migration = {
id: "remove-above-100pc",
up: async () => {
const toDelete = await db.scores.find({
game: "bms",
"scoreData.percent": { $gt: 100.0 },
});
await DeleteMultipleScores(toDelete);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,316 +0,0 @@
import type { Migration } from "#utils/types";
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
import db from "#services/mongo/db";
const migration: Migration = {
id: "remove-iidx-beginners",
up: async () => {
// copied from seeds before they were removed
// the charts themselves are not guaranteed to be in the db
// so they have to be hardcoded.
const beginners = [
"71865a2b6d3581decf076ae83c6621302c4bb271",
"fc143b488d1af894fdd0c5be145e1d613aaf5214",
"5e7128485017b67e85cc75432326c9656a33f310",
"5798dd1c2a16e6ef3e4469f7a2bdea8a7bc1cd8f",
"dafa3f6511a3150015ffaaac3f4a008aacc0ebe1",
"62c5cb9228f39e2d6d222ecf84aa709c5e2271ce",
"e852f83964486ef81558b84dd4b554ffc9caf174",
"963edf351b3c6f29c09c54e55ec2c3dd041340a9",
"17514921a23abf2b76983cf50e79b3ff600763b1",
"950c0181ee33d10d3af448c00657702d1a479b8c",
"7e70836d0a3001aa7bd5e9488660f31746be60d8",
"888259e7cc44e2bf2e032898f6a9e85cd4fcad1b",
"02aea8a2135311a7a1f9404e6a12f138ecfae092",
"87b65dbf2965949644c72515a202bb4363662b6b",
"d1e92965be52b5e887089383e0a687d20098e5b4",
"139d5851baf82d75731d9629a25fdee2b75d41c5",
"d903739755a1de064e646280260f7dabe16fbc2e",
"48ee9417cbc4ebad782d372d63548085f65ed660",
"d8ee3c7e42227d9f660598508531e30a990bae39",
"2a2406e6c5236448615f177eb5ac830c353bde12",
"fa424066e32730fe29faad0b0d25aa6323ea6129",
"b38485e92c09f6736ab7e1737c0ed7a191fcdba8",
"241a27876b22646ccb85b6f53a61d7a46fc93168",
"0f39a16ff18d509f44dcf4e37850269477475e77",
"1a44bf2a27033a89af7dad853d846c7bba9f9a48",
"c94536933f21c0ceb2f0b68b4b4bc35849f1d04b",
"4dd7495c32b560f50ad54bec6753c010feb9381c",
"46b18a37648c06bf480487c2b62f5caa8995281a",
"2a9b158e5974fb2f8707843493099b7fc728a320",
"db81c35d160fc5870f5957361a8b32add1bc318b",
"d399af231b377289e1d575337a43609c17998e35",
"e9ca3d286a330bb4ce29e89334c465c243af29c2",
"794ae8f3b8b1b62d0ff9f32c890bd5ccb887d0a0",
"c22c1a520f7dd87be4d1ba1c1ec59a4d572951ff",
"55471eca15caaa14b6dd9f4eaa7bcede2b6f8e7b",
"9f8deae94e16a9b075a8a5290a14df5bb1d349cc",
"5716dddd0316b932fd8b3a9c530cd624c1c50bdf",
"4eafaf1009423849c3709936e1a0b361a0c5e530",
"3f4cda22aa09aad1959993570447e107b6d3cf4c",
"b194cb8b2aa80189567a7bab0949d067792a9086",
"498b6d1388ce9f9672ba038a6b4b02aae4c8cc4c",
"24b4c560ee3daa9a286cca100246b3dc06e96cf0",
"5770f819940914328b74fc7616b69562285eed11",
"6b0831dbde8a52ae377be6aaddea37ec0771a49f",
"2f92bf3627e07e836a5d68aa538f33b3563ed167",
"75373c0c4d1dfef62de50bbb25085f729a20a72a",
"0cca3d6b4361195e883c79017e9850bc767b4f22",
"dcd047a218e17011c7ab340236af62be64893781",
"d821202823731335567f32730bfed70d5bfd1368",
"439bb68fc96706a86a98d437e4f03e032597bc7c",
"79c7ad40e434a5bc0cb139c9e6e0ad06730e9ea1",
"2d549a71666934fcf86d9d827c5f2f6959d49fa7",
"ccb99f9d00ae04b7a0fd0754c9b3024ec527f718",
"21bcddc1191acaf1911fc173d23eb81c939bbe2e",
"10a78c2b7ecf9f5f9c3a11f2385977dc789fbb4e",
"ec7d6764c543dccfbe097a014b6b8839000b76de",
"be19c987cdc067369151829a9889bcae2799fe95",
"b382c1cf9732e132b2fb7800d2d82a93b2497789",
"c70cf629f97a2623d5cff716185350f85b07435a",
"5a94628384b43f68cfee44df205bc0a3edbd1913",
"1c964098d2954f4c8d2e7b723d77a2d1f79f7317",
"a31d41fe3bfd21c0c6e71f8d6df4e556be329123",
"b0b3e92576de8d9090472d118c424e08d1bdd826",
"d667a2e1f226c09e2549393240e00878a8cc4edf",
"e1dbe5d59d4a776ecbffeb0b165c715aae2995b1",
"fb24f2688473c46c04d666d27a9210c871a8191f",
"cb61bd200eea0936d5278b3b28e1033c36713341",
"bcd1fc3e53e2c1c001079159a26767a07f8e628e",
"871db812f3ee5002266edfe7b2cf48b4b2451302",
"01b60f5125ef5123c3840943c047446d167f3e3e",
"10f3292e7f1c91bb9c3e7d8c7b1b43fc8d9d929f",
"6ab72d59a8967bb6265dc82c28ac054d1f86684d",
"2ea729f82867a2c29d34c88ef0cde22358702b00",
"7f5b5f3e3640e2848985b85a574dffbf95a37030",
"7c78e67927f620c06d30887e29cc26683f3748f6",
"b34d86e91d32590d39dcb62216d1a26ea6bdfbef",
"be31fc23b03c53aa58a3a7b599a5b8b2e7ee2ede",
"3f5b76fcb0e9db6146e2f216771250d89bcb6942",
"0568ca6e19a3660b74bbf6fb97453d57b8bd0c83",
"ff1d34af1db5a089505f84a9aa2407b95a64f29b",
"0484e8f3ab6c6dae1311ff4f2980cf76b438aaf9",
"66624f8162c7949744e84f3ef3abb314fcb2da8e",
"531d5a70c4b02da03ee3be0d1efcffb78fe4ef43",
"5df850385272324559182a07d6a2a6b84bbd32bf",
"8ea6931b5382a6460e052410990f8e7c6d2fbcbc",
"8c20bfdaa71322bd572f38c95ffe22b8e65a9fc7",
"fcda03d3fb068fb58c4ae4234582d313ee22eb9d",
"c030db51ec510815e28f0ba181a650de492d2bb5",
"f865fcc658bce3ee89e19dac88486546eb4a5648",
"4de8a44831d3d730d24a174a3c78fa4d17271b87",
"1dfb262a9455e010e635989e480affaf61cd0113",
"49ac835cf5e5cd96a095df0f7220f2532a8c3632",
"3cb97b11313657f9f59cd1ce859c6c642e7a0afe",
"a7286b7086f8e19d78c7cc3fe73249d752fe9fb4",
"982d79f972da4e0a98c73d7d6709b931aa9940a6",
"1757d3c8c1f2e5392ec8aa6adb31a5a85ce1754f",
"c14ac167a2141d14609878cb8b5a0bacaab18468",
"876c1d284d63b272725a33d83b061e8d187bd60d",
"7d495aad1d8e07a499e30373b44e2e1257c21281",
"aa87f48af5a7a658e3f518f07cf5a45ea6750d5e",
"8c88175d419658c9f0ca1ac0994bbd40675027be",
"f98896e0dcdf7994c2ed38f452b1bf3dadd345f8",
"20941a1f2f707ce1a3587be135e42f453230f337",
"dbece3e1d537f91b2d0a4bfbb6d1ce98cf08a20c",
"25c2c3b947ff45795f1aeacf257a872dad8a65a5",
"6e071685bb2aa43878945fad560c68ce8db7c57d",
"88a1445681b2f70f0e84c30a66737dc942a42005",
"c32334b6f121993235082be73afb8137d0c0da19",
"5b49b8de2b70283e2364418ae50af0586a4373c2",
"009aa903a4b9fac40138deb8c5162280a747ae66",
"5b4bf383e44746468c21bf800eac0ef13e72d2f4",
"8c8917854d96a23a40b844beca846f7211c26be6",
"e73a369bbc639f8bfca9b00ec167c772300fcae2",
"3fb05d8b7e68c340f0257c1c405e8a9c75d03119",
"f4af6d0da9cc5f21b8dcb447bc79dcaec0d82965",
"06acbead44e406c41e8827186850950096ffe6e5",
"e6b480eba3d7aaca43f05fcc243cae558f846b72",
"91076b64d7aae8f053a9cc1b6849fbefcde68717",
"8536c1167ee235c1c9c07740af55ef45348bc0fb",
"275e441a1589572ce76067e7db8a51755d6133b3",
"d13ce9d0b458d2d77ee92957a68df649a4ee723a",
"29f7e78c0b9062cb8b12b327253a6dd0358612e1",
"fda142a18547351ad1085c7a82fc0546a5aeba57",
"9b7367e990ab8251597dc85b9a8ffe46284881d0",
"53978253c493013d7e5f6cb4161cba6691414fb2",
"719b865582fe63cc16e4a376a28296853c19ec95",
"f41c638800df3f5ed079ed64ba1cc692e6976c49",
"c29c03903449bbe998640c7e9db74a76ec7fe06a",
"675ebf4acb10bffce2bf11a5d058ee22a04efc17",
"42fe786b7af412634ab8c2d939bad0d9dc29e38e",
"e0ea98985e05cf5f806cf8215ad3a1ee15423451",
"72c1a989bef60be4a8c1c62a3efe1e92233f6a7c",
"772756db2182cfbca706ee62357ebd2539f81c16",
"0493b956a14beca895b3f0b5e2d7e3471387ee63",
"f7c14287e59c6cc41cd4f6296a51694b6a5c8249",
"0e8875810b6b895b37e028a0f9a75ad14d17e28b",
"61322a1d7dc669604c9a8621b60eeb887e60dbd6",
"b83b48401a4352c533bfefde2f19b87815e88394",
"9f99251bf88c0efecb6cbcc62d5cb64be4c82a9d",
"507b2342d683a10cac317346fd5c743b129fb786",
"f17367f08b15d4af2823534180b290128852fb6c",
"738a4332f3aa8ef2c2ca698444621f7657c611cb",
"03877a6ee4f639c8449065d6e35709b22e9224af",
"e6b3645a8543654224140ac5854ab4584911b822",
"99a6807e22830dabd3f3aa5631dda0fd38940220",
"1af1a81e2ac30f5e21ac1eb39ebb970894c1db95",
"b026831b99bcb0c4b259c3446f969d2e75978474",
"204b94938a992ee3539324c70b922dd4b05d4156",
"0b8722317353af3bbb28d8578b08c682a3a442aa",
"ed8b651f6fbfbcb5bf258147d3bfe276f8e183bf",
"daec5d11791b7e87c8a67968ce2cd9a066167f6e",
"389e82aeebbe95519bf2957265f4e7126d90d27a",
"e1b25bb9af0b24562b51cd857b9fadb697ec6a0b",
"007d459a4ee5a2b29fdb5396d291a151b4915c58",
"29aa55ecfa77f792166b12a8188a9d1468af66df",
"519a1e30b9f563abb78654798be8304e55b1ef2c",
"a0b67afb6b3335a81dfe400d2ebf32d7d83f1924",
"edaa8463ac326baa34c99976c36f31a957e62190",
"7ba5b22204cbcdeaab550a7e37a8744a7760dd9e",
"34bdf077fcc2bbcc01cea8a2a7956dc988212815",
"b0f7f654a1663d21eca4665f9f98912c655065b0",
"81aa485038cec17ac15c3f39c8127cdb027e038c",
"be9b86ac9a898efa539ad74ae3106a4dc82d5aa1",
"868fa4e239b59a6eaeede9ea17fe15c466f018d5",
"27270db6941d575ae94c028df1b21eec448fb9e9",
"d7eaf8be7fafdd96f635954521c3e917fb9ad7c5",
"7551c2a33656c3065755e66519d245efcc6f7330",
"3625c061aaf90c384868b7f3745f7b77edc63b27",
"f68fd5dda2bfe7e5c8bcb961918ca87b5c8d4347",
"5d0ce8c1973e14aad82dd51c07fca8cdd5ad3dbb",
"356c75587a1d45c1bb02aab987ac15350e3dc665",
"cd84e01b39898103907e183b79b99d49da36fd50",
"8f76cd178ef94b5784b6a8011a71870b17705a77",
"9ee1128a70428a4ec7938173d6ba0f577d6b3069",
"86639d28029e7c8eb0246db1de83fa9134fb9997",
"c0aa7fcbc44e33a937bf21d761f9b5d348dc8cbe",
"451714744633ac72d15922ad651deabc611d1db9",
"42be14e5a7b8991d4e7be715dc43d3fce0326021",
"e653ffb2f2ea14e1bcdedbfaa9b7780e009c3dad",
"7962ec3bb553bf9103e7a186a02627a0f2086b8f",
"0a89606183c4d5aea122d6406a5c3eb2be895ec8",
"8f406d3aad3b5e1ac37486c71af86249f2f577c3",
"9c1ae44ea3ec24e3bb3a60a68fa8d1211f6d1bf8",
"27536e3ea51722536e236b916eb7d91c9ca98fa0",
"281ddcbba655858fdd5a408ff8c4ae1ed24ce0f4",
"0fcbce768b9f939b31c04132b00f18a27fc10fd3",
"3d483bdced0dced981b810c6a48caa78ffe7253a",
"a962cb8891965e5921b1f4910e99a36e688a9ac1",
"038db5c9c451e3e36ac5a9e48912fbad87502731",
"d376224b9c1ef9af6fee83ef01b2ee04c20319f5",
"50b1a2eaf172613919159f92bfb3d457174495fd",
"e710068ab927ee86ad0d1151531c2ae34b121321",
"9b0da3ed3be75dd75d445b503d3a95e743b2cff8",
"074bd51bbc30fa82ebe182fe9acaeedb8c7ef438",
"110b33a9b62d44d9792e0b5489e7595414e32464",
"83fa7c43089640b59f9ebd14ab76eed341284b26",
"a9de15a3d57fb813133aee02a6ae0d356e269c08",
"e35b6405fcf935e87fcfa50def7fa0f80efeb726",
"0261c38912717678437bfccaf9dc82bc70261e02",
"c9800a30504106c706326feea7567160ba67dad6",
"797d205f3ba57f5b7a95d9c94c0ade68012a16f6",
"6dac6a3ddb3ceec1f6c495d27143a77f6c5c838a",
"1af6993dbcb5b1dfa7719273104789f392473e0f",
"18e8111348312a2e2767dc7efb60f1f3df510ba5",
"1b0610afe3d5bcc4a1974d0ab5e8771ec2e1136c",
"1a5406ccf15a24d00a98b9a8032b935f107db3c7",
"d82fbd903ee1e18c6a472e18d6216b0f3ae062d1",
"3f5db998c2229ebb746b6bea276b30cf35e67209",
"fc642d958803578c20d4c23dc4c22a81c3fed837",
"5187b5640d7ca637dcc81752afa8c7186ba5b63a",
"9ca79b85d01b26e17e01e9d2afa72f902e3cff1c",
"6c8eac6e855e2ea8aeab605c070227fee4886f52",
"fbd5bb18023140aa47441db95c5c6e425eb36755",
"9037b528e4c59fb11f127e91e9dfce6981be93f9",
"f824c4a8333300fb48b2a6f3180fdb7079e5df3f",
"5ddd3993d2511402aad650bf8151f103d051e8fb",
"f6a7743e006826f1bc0e67689a42ce59273903d0",
"41510d24645438f3f7e26841985b57395ba4d63b",
"ee7620a802a13bbdc0aab6ee9ed5be64af55deb7",
"27dab68221b340a8e32117c2614206156641efb9",
"915d5cda9e21ae6d6dd50475bbd5beaed9088c8e",
"5f07b4e354fd330e2af82442d6e4feb9aa537dd9",
"3a3e57b1de5abf4249926cc8f71be71b8008fe2e",
"a7f5d9980188a6f146193447655d2b9a9c9a9b0b",
"1bf603dc03acca6369e66c91477c6d5493720595",
"7f1a82b40bae1b97c97505c2e038607e68d916ee",
"97956b06fab7cea820753c1cb538071f99173a02",
"b774f50e8fe309ffa9518296e0dca96e2b6aa329",
"f148efed4c6dd03e6568167db4b94b5b0b91ff68",
"d0275578f535d5644ada372923d749d86e2c91d1",
"6004ca07f5dcd4b3539237fe48e11c3f07bc9c83",
"d91b28a8d208c21fec5012f60d7052c0504043a4",
"10e6520b81372c1f9215b41fd8de26dfc26265c9",
"644d0643cac05a8c35a9ae75096684cb810453a1",
"24d417effd3daabaf210d1c198c3cdb242eda0a1",
"2f1d1a4ba1132a51624070e96699c9113e74e02e",
"1b88e59648977946136b592352c92bbbf399acb6",
"8a6b62892cfea4101ad0c7eefc465e14d7d46e09",
"1eb88833f3619c907715160ba3db08a35f535a70",
"4308833a3e042140f6f575f05e76e30e47a16e72",
"05d449abf2a7c10c21d25db1fdce1f37547d41e2",
"b1b4d7a79c8a616ce2929b073b389f9359bba734",
"942bbf29e46a42c8d513504a5fcd3fb311da7557",
"75b4172c7cf619d2862f85b5b94b0ebb50d70273",
"0280d845615cd24cf32f766e251508f381c6978c",
"f9e30a6b8ca6c0e401c88bda7700892ec068f3c4",
"abb2b4d61bddcae851fc0b207ee2253d9640e1f2",
"f547dc535fa50270b0dde4ae612a6eafa13d612d",
"682c0454c7b3dea6dcee516c6262410e8938c849",
"05f092dbcc41c1babe03956cd7da15ca6f997d12",
"819272234eef7fd5a3c6513a88a4c03636095584",
"1efed0d0ad95ff26142c576e63b8c602d39e0bfc",
"d06a6fa2fc3ed12b2662a609a8fb6ce054c22353",
"0669ea3d4a5faf5fafb5a70003ef2859c815bcb6",
"773e810fd95414af969b79682bbd66f82a0f478c",
"2480042e8eca6c91c5eda1f44b69134d09acfbaa",
"e058be2882fc01a2185212edcee770bc59346f05",
"0b7eee74be4866ebc23fc9355ce7d622435352a6",
"95f0e6f558090371b657342e3fd9a3faedea4a7f",
"dce6e24b4b72502995246ee0044953fcb8e7ea31",
"daa8492c68c6a41bb8a44f184bff4166f5cd421d",
"495865634d8c25f8e470cdf3864b62d175d3fc52",
"73eedaacedcbe3d6fa6dbd61175e571c45dde5db",
"65e718fbfa6dab8e8d5476cf134b607206e6d965",
"f121729bc1bfc399056f2c75cb733686814720a0",
"97459484cb0e0f41a1185c6e99fb994d00994aeb",
"bb76acff886184885cd262993aa4881773345ccd",
"961a83bbd5bd5f21490fe0c4415c5c43564981fb",
"b0df9236b4470ccb92db426fa17f3355514499b0",
"006950c9d7150c418c2a9affcb0fac1df9d5837e",
"3d98209e094cb09f932183109c68aff8f6789120",
"6539c449fb725fc647a01128392f71acd01c892e",
"bb0f7986f49ba638c092ad7849964c43095571f0",
"3b709254daa125c7f8440227c3fdcadf7583e616",
"1bc75b9f978cfcb5f86be6b78de0670a228194ec",
"3f9e7bf39c71bd1e781565e97311ec0bc9ca4dd8",
"00e731f4917e805c8df7252228197bfd86dc3e85",
"8e00ebfa454c91687b899f63cf224957966b32d4",
"5a2ddf992d135a647e77ceefcdf8fd50b8f1b503",
"7d4518eb1728f4d33a6d4a8b06ed666c11dfdfb7",
"ee61b3f43ff8309923ee62386d8058578feb6992",
"6f0079dc782cd267ee7f2bc1efac0b4e1b959ac7",
"e24a499d0a46c2d5021260a0c858fcd62525f544",
"3b82acd434b9bec22e76cd3ba276ce1b8e286318",
"7c32caa24c73f52cc39f3af98d451f75bb2e8773",
"e72249d9b6675f00c6411fa659d6b877cb94f32c",
"29c57eeea632fcb862099db5e24cc45f13e6e0c3",
"704301142885cbb14ea45de3b3cf758bfd97a3f4",
"3451b5d2b387e7103b7fa855ca8899d4c0e04abc",
];
const toDelete = await db.scores.find({
game: "iidx",
chartID: { $in: beginners },
});
await DeleteMultipleScores(toDelete);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,989 +0,0 @@
import type { Migration } from "#utils/types";
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
import db from "#services/mongo/db";
const migration: Migration = {
id: "remove-iidx-extra-beginners",
up: async () => {
// copied from seeds before they were removed
// the charts themselves are not guaranteed to be in the db
// so they have to be hardcoded.
const beginners = [
"661ef517b026d69617787d3330b8e9dec4267e9d",
"5db25449c7b2ce780c864d3714a652ac763cf9dd",
"badefcb014e1a7eb180544d559962a4ddb761c58",
"d8bc08dacbecc4a57d05f501ebaa828e2a676df0",
"076c5aa10170468a2a771cbb461b30ca8b5d9ccc",
"9152feb1f2cdfc49f6972e626e5377cfbe9a3afc",
"afecfe333c2c0fc6087f14ab0d99cd479aba763e",
"5ba20d81aa60a6341e60685bf5d855c47b2725ef",
"bd92be1e0dc5f9cb2d6c6f05322c1f1d1e8d26b3",
"603cc46b10b5afe78a80e9a3da195d3ea748288c",
"9eb7434051156021b23e219bd5bf7504d415858b",
"565ee5e53365ff98196ee5a75985a58962702445",
"8384d234c4cf1c4c6ac50c2187384a816088d7ec",
"45f4d12f5268c2bd1b8bc13add5b08d0f160cb7c",
"78624d9d5d0ce4dc54b31187e448046d53237f1e",
"0301762d5593914dfce8478ed0a67abbbf642fc0",
"1c98b4e3a051058ec13123c91c0c600c0cc515ee",
"4bcb67d0267ad6734aeaacccbd6699ade3eea7c1",
"634fcb687087fb6e6d33440a573fc70736e4a2ee",
"774cf1e81d2e73a50c9f9b565f1ed1ffa16cac68",
"4391de0a3768d01ca40c4d25a304c81c50d874bf",
"3d5bf64fc577446099fb7ddd6f91b7bb441a8b41",
"a4d99811a54b103af446673221f03531ecbffcf5",
"d50e8aaede0de6cc485d9fe9c6fffb21f91c2530",
"4b74fc4ef182f589995db1c132fd4897cb9685fa",
"36cb496960ff9df38ee2f89996851ca42790c315",
"912ac090102b9ad9a05a87261907f3edc3f82691",
"2b393fa0857931aac303a26a54a5a955cfcc9299",
"b549d6670cc30624b8e3f87f97dd0d1cd1702968",
"25795d571d54ddfb27be5b2fc92c65e6ea68bba3",
"4b1ccae70b551f3e98e8fdc55c11f6c809f6cdd5",
"d26a29b485506f1761bbcfc99fe1963514c01028",
"4e03a64a613442ef5c18a58fccaff8889189e9d2",
"c435b0e66a6ead55b199d60c39cbf9ffd462a324",
"649fb4ee4e0a85ae2d108afb3f6ad36fde48b584",
"132021f620225d183e0f012b34ba6ebd66a402c6",
"18e9074f7a049c6023729de1e1edb9844a5afea7",
"8584a69f97e352ae8098492281542ec64e19589f",
"d2c16b237907d891cc16a1621e9788becfef6f21",
"3df0d37f3bf27ce2b8755541b407b6a2497820f4",
"e633d900cde7f44cb92d8bd976e5230fc10f9561",
"41ce5b44f364810496d689e751d939cd6da7646d",
"9c423859fa5467376a7a08cb96e6cb242282aed6",
"2dd6630dee52bea24bec2b021ff93b0b85d69377",
"d68e42c9b0e47c049985333178505be8de03d5f0",
"7c5667140ac1849cf14ae3771160bb07c2b54af7",
"4fe8b90c77832a5d656d69f046205dc4a6c03844",
"aea3e2128fe4ec63da7f4ece53dcf7498ff7aae1",
"5a47dc5639816e0fb9ec09ac83d53750d4cd4913",
"03a25566d002590ad1e9c3d59c76b8f954973377",
"34c2d0fe6a02f36052f17608b74694bdc729f309",
"86fac1a7a61da348f632273baaf89400b08000bc",
"2a0f07945050fc10c4095dcca54b4d53ef6beb4d",
"545c87b32cc0e2c5f93152431b8a011e59cd523c",
"90c0d2395690e64bffb295c59aaf867bea1de36d",
"842c48ccd6c3acdacc7f25d38e694bd91be90cc4",
"bfbb0c4f035698dda3e481ef97d5f4239c6039b8",
"739f198a65dc013b85e057bad0ea6cfb485eeeee",
"aadef0e3f5d1cf853b549b91b6e96b8d404b4c9e",
"1be0fd1abc68d0d89f0a57645255ed6369fc516a",
"95a83da1367ae85c138a13b45c936c22a94b6eea",
"eef1462f4c8d4f7427d82cd8b5a1f8de4cb46f9c",
"951f61227587b464bf95b04c936ed81f1e017213",
"c9cc718f69740e43a3f4c175fcdd8ceadff89246",
"a051c7c54f30fec226a8cf818acdf177cf6303b0",
"f83bafd60b4e4f6fb487bb22891ddd3cffb65ee1",
"d4371d5fbce357fb9051e14cb840ff260f9e314d",
"6baa57136dd329a61f9696b9ab9646409f126c41",
"51250776fef8c940b849c416f85cdde31852efa5",
"bdb69fd8088eb0559924c0db6410657aad1b168f",
"92b0807fd7b28a018f591e825c5a5a7e08b57c70",
"9e14a7c0e548bfef46b4541935110d4ac0f7ad31",
"4c2e7fa313c532fd7c9d10e773b6f8b5ebb007d8",
"aa8b7fd55db286e1aaaeac6f10e04663b625faf7",
"bddb0f0674c4f46ac566f6c16f67fc168c85ab4e",
"1a1d9f07d0eb6aa9185e081a51caf3ed0ec810a3",
"5590b2fbf8c9b6f64bbded8609891b7c7ef4d58b",
"b373944c0f39817bbc95d3473a6780460a6a1925",
"ddaf62ed398483f92b16f24c5d9bc3b22e0b9463",
"5b611dfd1c7501c53e33925decdf7e82731bd351",
"a57bb36f065df9bc877cf49992ea97a02ace31fb",
"d6c7f16b0f7858faf3405702c0ed204aca6c50a4",
"3a55cec3b6a4e7431f0121473c83f0ee0f660241",
"b079edccf0e90c80286587b5b619587922df2b26",
"bb896e6d8b5a74988cb2e7999beaafb6fc34b7f8",
"f1e9be1f667be2ec00f65450583908d369bca3e7",
"c4e32f10002bdd99d18f683dfebd5d774e932ae0",
"2f609a11fae78c10cd406fc0ab42848a24c05886",
"ee97d7fcd90786be6fa331997d6c36b4d53112d3",
"55449c569329c31abfb01f0712b4f9ecacaf8420",
"9b4b295b2b953f6a67981957d0a53aab55e8dadc",
"d43971c7afbfbb71efa3a6b3071586a67f9358c1",
"321486a05d7e28fca044d88b95cbdf4be1ab56f7",
"1dd1bfd3f24521a37440e5b5b3a2b1614781e726",
"110cc1c334849305240d4571d58c68dc0f49e16a",
"f34b22a54026a1e286a79053c6c0cbd8aac82111",
"bd05e161f1bd2d7c0815d763a51f25a1328019ee",
"b9dd1bd65a79ab68299cbb40cf930f9f9e0f0566",
"cb1c16e68b7beb3c955631643fdb6b2a3cf51f25",
"eb240a2118e7ba270b574ff913ee9b1948c87f84",
"b253b7d18813ef655ec9645ed74d06bea3497600",
"de2c9d8835122cdbc83653548240193f157e4c56",
"c608109064b0a3d7700fa94cccc365d7da1b065f",
"ef50dcce52d277246cbd1eb2ad0f63b086b15613",
"bbbede4a4341a5e48d9ce2272cb2a35dcabe8236",
"af7adf981622e4fb1e4160f583af5a1e36adc1a8",
"35be3c96ad2a423729b158318c49506e52720340",
"20542febf4e69e212d8aaba8303f5fc4a1309839",
"52f0c154a646232609d3604970c42e021a45bc25",
"824640e1b06f8b5a1c0fdf997e21397e1b21234a",
"38129eb41a24bd45d6d7e81fffc247751cf6b4f1",
"1c8eee750da7f77482c68bc2a5615ec113a2a139",
"2b7b3ebe2cb3e276418aa0993c4151be2cae2510",
"efa019fdffba654a58861a2c7ef4e0b482cf6740",
"4abffcad8ac89a23816831d6de29a04c306fd634",
"003e1d384c306515cab3087c8b0fc69ee4b846ed",
"d7149e80a0520654115c279e2e568777d0b4c7ba",
"987f4d414415647dfad38a0c62754798e64cc622",
"9e402abd7dc68d002ac29efe741bae1770c0ac5c",
"761983a6a52dc19145a20839f24967fe6dd5e58b",
"3b5c6488f14c7bf7ad26dca9bab646c43b0db033",
"7e0e8f7df56fde7535e804640f48a91ba24300e2",
"97f509f5aa3b4f012eb30ca19c032342058fb5ae",
"f317126b5fe11b7983080dfaa3769238fafd4d25",
"154495d34250b918ec43b8e5dc1319a994dc4ea8",
"5f4b69f18f43d68a05c62fd76e0926fc5ace659f",
"b36f6b80497a5aecbbaf587c984b36ed7f5fd37c",
"67080be1cb2331dadc3a4ee797b8cd75243ed950",
"7d9c61075b1a32dcbe93ff54539fae9ac1b21285",
"8bac128c26535144ccf702c28f71d0b1565f6ad6",
"14e86070032a42ef1c80c176cc48c31845d7c3ff",
"23917e570bbc0949afd97372ae0a5907765f0e37",
"d9d92cbae7abd8d50c1ad69eb0462e87ebb02066",
"92cbcb207d3c75e30ee25a0c2ce4c4cdbe7dfac1",
"f565df60602320e715ce5bb07095446e25e93a26",
"acbfd89b1303d235dce0c8f5d3039b90a0763d59",
"3ace93e28d251c458506f0acde582742ee5a7e5e",
"2d8dc46b4a621aaa954cdf9b1432d15c5f5c4783",
"0287936bbb7e60d70922f895c81f42a55a81436b",
"db9355e671e5af59ff8712927f0386621a911874",
"f47cb021b4ebda91d0404c4f30280e6396cd4be5",
"b1a4c2a959c1b1fdbd20579c1b874d40e8355dfa",
"8a1932353f141cc44dfdfa297243e38eff6bc88d",
"41a61e4be75e329d7e52f034dd03b38cbfc0dc31",
"314c5c3647b2b1ea8d5d4b67c9bde434c08ea40f",
"2e0261d7fd00c0f4f76e82255658b6e27209ba98",
"00c20b66b9c7a286f918e37ad0ba3690307d0288",
"73dda04a4c1eb86e5b9316cdaca7fdf8f9dd045e",
"4e0e1b44ebd373615fc21de1a8e2afddeab30a0f",
"869a59b0728b5fd3d0bee8b35cd72e363267e588",
"3ce2b6ad46886b244106c6c6ddbe50e9c3ad47c7",
"f1930ae5bdc25bdd6bdc34956389959ec6fc6b9a",
"8d71628f1d281f69e2162dfb0c6d6ac55482a0c0",
"79959cff3137fe640c3c13975fb4bc20b5f81983",
"f6873d250b57e3246ccb019c15f0325207a27199",
"b3cda4c1989ae7ea78d1ae2105f828f0b5c5bd30",
"bf0381fcaa3ea0635b5b4937ff41f71ee68e527e",
"ef0c0c8a265d075183015a8d817aa28dd3d6490e",
"97d9f87e888c23587b7690c9a42c1a1b1ff77321",
"6005b8ac86e3ee274193b21d148c32ac7e8ce21b",
"fe9e0497e0d059b530e2be559e0789b101f82886",
"fb148b2fe9b982420f478cb60f335afc46917405",
"7e3f2df88518a060f5bdd1d41041b061868e42b5",
"8eb325bfbca0a98e9d265c9650f1a73c1b7d3f3d",
"1f5dc62094fa8e5f70818a6a6052ad7a3c339867",
"5809be7f3ab9678af36a85ad9b1ad152d5ef6b97",
"6f6386629a02a3c05c66cf3b76d52eea9dfc1f66",
"32fa465f6ddbf7ff85df79bfd702c7b4871b2ce5",
"359febfe39a747bdb0d23a4e3946fcae340c3d8a",
"f1e607a001b8a82f2eddd1e4135e0686fb3c9c02",
"434543abcfe7dc2b8e31eada49227f5d24df9496",
"71b138941abef6865e796733767edb216b02e533",
"4d5a45e2b16cc6d1d39b5cb4fbbeadae28bd2818",
"ea59c3e921437da14e3b3a3fea0b261f5f8911f2",
"4133265895f20fb034c7827f635926730c277e6d",
"32f07bcae894a62660b3583954c88913758d2fcc",
"e53b65318b548eefb4b8b6427d6de008bc4419ef",
"5cec40c1ce58d4824ba7157361995897e822aa90",
"5d3fd69ce84dcaf7acb58ffb85bc264ca979853a",
"6bd02e8f9871463ae8ce18331a38ca6f669fab85",
"3a0a45380dfa73a523b0aa505bd95157e3f6ecda",
"89c3b3417577d5a212ed74116eccb0eae65e7071",
"88bf55fcfff10bede9503e170ad72e378a91dfb9",
"143a8bed833222c83ee284ba42159ed420932c0a",
"05b99fe6781edd9b2e730074cb4e26ab8e0edb25",
"c9e0c22e41a885dfb9edee81b2bd24411599017c",
"5e2fe3883337b15f47b279c483e47c57ac63272a",
"e351184f283bce39e43ce6a3c7bd26af5689262e",
"6ff4668de182a47e0b1a6fb534824971448a0b06",
"05565ae23927a7227578f7c9ad837d759aa36e07",
"8573e508b23c69304b0d60a8f65ef881397aaada",
"1d345afde76156f620f9385594f517a3b66704ef",
"274412110cff255b4e83d4b2ab034806a487d391",
"c23a69ca5157222526cc8e979dfe4d0c0e030144",
"5f0d2b7424016a0d16e783d5392ef9cb0802d1c5",
"d670727f4c3edee023ebfc5bc3779b9ee61fec0a",
"87037822b3b959a98e4875b2685e6e0284824818",
"fc7389fb7453bab1c40abcda82390b5ea52bf7ec",
"58147614cca9f43f4d392508da8f5a6eef850a6e",
"b98e20a4e21542edc04447f63a7f65f6daa5c944",
"f3e64f88953eeb2d1e3a809a05d8ed2d7cd13f25",
"61aa9f77e538f87a72b720726685c0452ea1e6ad",
"fc32afb41e6a53a7c17dcadae04648ca1698391d",
"e3994c1be06d17c4311245ba3554445245ef50d3",
"85b881645fc988984bff9d650ad6ce3f15954d32",
"e6722a337ef56712401fe2289ba91fc170f95d5e",
"48c637ec5619e2b0623e8934ac602527b139db80",
"b3a17ff07ec43bf551b2dddfe1e1d3bad0de788c",
"d2f7d85edf14d6def97d44d22edac72c52cfa530",
"84b8ee4c6c565535568e6707e327f48fcb1c02dc",
"931db4091942668efd050a8acf4a4cd3278fc607",
"e1ae1c80a1b63c5451757a6097c7d65837339f74",
"b413600118f500b3bd8bc4bd87a2e9ce66653e93",
"bd2f959f0feb0ff2b38c9e69a982bae0724a6808",
"e74e59833d1311b9696cf8418a0a5dead1c5d61a",
"e81953d7042d923453f2ee38aff65d7ae06c0920",
"3082f75036b6196b4b8dda1d04b87e8cea33cc8a",
"18108c442d57ae8688a7be265cd2309e52eb3823",
"5d94fb94c26235f084a3a165967f3a2052690399",
"63772bd386708944660ffe118fdb911477bf163a",
"4b9b2c93e24d64cc827cf307836bf1ea645259d5",
"2a364f95e38f094fdffe931d0ef0d95e065e8ad9",
"5c9d79af48b880abaa035bcb608a4af7dc11874b",
"cc9127f801536f308fd5be2fc3fb529a01de2f3d",
"5721c3bc13cd1444d1eca0d75e0fecdb31477c28",
"4e4f535c744ab77b64e0c774af9d3c603dccc6b9",
"6c5e4ab388a2384dba1c07521a6dd453d2472339",
"8d02f8458554f10a1fc2935f9edf1a0fc1bffe41",
"647e23e587dc009b97a42f1d695bc2f4940187ba",
"d8fbf3e52eba573c04721cbab25409f64f3f863a",
"aa3c7075dbe567c49f93c12dbc837ff18032c1f6",
"15766bf8720c9cfc38a877679737558fd92b2873",
"60da87f2cc15cd560107cebaeb4cea0306b24f40",
"acefef2e119d69a1b8c652f54a90dd71f7eb2da4",
"25de55c00eb1e2f66a289e7dd9f55f1c18b6e0f0",
"dce02a2af13f45e303849f3d01422c79b3c517fa",
"9131b7f5299abc4a3898cf011cb60d5b2767a176",
"c4abc38f4caf6b8205ef2c06c5262900bd25ace1",
"158dfe41824261305e91d8c8856aba160266d9ec",
"9b03fcb6e7055450550de1f77278dc2b8e631006",
"ada22a32b9c0a4046b852a1054b940625292c24c",
"fab9f586312463b0e691603bd1423fe6009a758d",
"9a753509e951225ec29323de2f0fcc57996664bd",
"89e00a559782f55944a444e197e5b77a9a80933c",
"9eb7806961df7931a853cca6b34e0e93b67aed00",
"981a6e6028402d8d8b1413b2db687a43f0742265",
"856de204915853d5a5621924621b4989eb0d6f4d",
"5cf850241f14347e435708c4864a4d5437def899",
"912e8d6ca87a83e1f5f2492e281ee4e9320e351e",
"abe2585d5a74f7183f067322593902f183d1f5ab",
"17ce6c85cff578e8f6332a3ddda4da15a83a951c",
"c3cf771dcbb5820063eb92cddd3d1f9ca552b49c",
"9217aad3be0c2e9137551675796bcf30bf86385c",
"b3242f5366b7569ce510bf2867d71f148659c5ce",
"3b4eac036e2ead87527b848c4cb09b103099ed93",
"73270bde4f47973b4741beffbbf984ddf4722fd6",
"0b7c24db33a65216c7071ecc2c9038066d531cf1",
"6c5c8cb009458d586c1f6bba31c3597bf93d7058",
"135353d0d80d7f4d53303e9274c683b194ef7fb9",
"bbc2f1233f6c257f96b6c52c60b86048521280ec",
"3bb3fada8e9ec4bccb3524ce141118edb2ee4ec2",
"ba92a9a30086b080879eb2e6659b1bdfb45fe7cc",
"c61739fdd80d2694ff1eeb0c4affa1e309a2c7d2",
"6c978544bd31102016ba757cc9eca9c2b977e48c",
"495167b4b7ed2132c75f29217226d01edf159b9d",
"05e4a77c9d5224792fc1d523ca73533b11b99c49",
"49a3cdf900f1956dcca818c06402f3c6083cc064",
"6e6fe05a55d14096d7dc6666971fb3bafe984981",
"6a35a6dc7c7a0ad54feb97d7ffbb68d96c6c1fe2",
"445048a42ffd2b99f40159227ad55ab102ed9517",
"f943ae18952b4b652ad0d7ac03ee662a2b996cd4",
"1d97254882f0c942bcc6ae94f398eabe3529e5c2",
"98771b9cb9eeec788cfb96f07b318c5862185691",
"469b93b630ae13a522653a6a09dfb152297a1103",
"191e94fef9e39ad64ac7053609b316d6bcd88998",
"6f2c87359cc182065f00647e9e2c8ea53a47100a",
"211a2590e0ec315770fb0dc951811bf63a31be16",
"6776022cbc158164cad300a220f4124e8aed9fa3",
"1b4dd9046af2f02e4db4f63a062cc7566a723ad2",
"c86dd9bf95aee32f3e2769a64a50aacacd0d2cc5",
"4fffa6d8809bb6d43a0cc3f9d81cba06f60c35c9",
"053309e6273ba4ae18db581240ee274a9624650d",
"642a16109de71c902315d1d52ca4f93eb2be146c",
"d31b02d2867ff6ca0a6c6f48aef52474a4dc33f6",
"fcc468d222de0ab95460739dff3de57eed8d1717",
"20fb5e4da4cf16c47773f2bc913d6214658f2f4d",
"982b6c05cfb5337d75e646b4ae328ba2bdad91c4",
"2bb0b45659c549b4b5562c3ae1c53646fb4e6be5",
"1bf8d06211232d3af4dcacb4358503ad1477972a",
"ec4fd5b625658ebeec8efd0dabbbfd7257c2f329",
"73199f0cac9fb5d3bde33b7ac7b5b37f37e709f9",
"dbc23d272c7934a19f1518d946fdba5739db6293",
"a7f48ffc54f93fb84ed661ce04efdf53c8cbe98d",
"58da1cfdd00b03e0f5a3c3863052840651c67943",
"28219e8374b133be408424c19a5e64fe817cad39",
"b7436a00af93cd59078bd3d271cfc6e9534a9c5b",
"b591163919446bd5cee65afb537e387952092057",
"623bdc242e7997932f879d80b112549441fa26f3",
"953cfb211fcb78cc7ca316587335588f92ab8dfe",
"672fe1d3522588ee4f4876ddbb9a336697514215",
"b372fb595269347a4c1423c5a5dec047e8c64219",
"d31813d7f861d8cbd0c27b967b85f6dde08626a5",
"0dcee6320f0942298ef1109d912a64f26b852f59",
"e07df555b0143ffd7f64c4b8fe2a03d0a87653d4",
"798df9d8129e134e6fbeb6a2676f8fbd95b87303",
"431e01faed05ed856dd7471370ed60f697273735",
"2feaec1c81fecff2bf227c09eba8b5d143ada31d",
"7bb9c4bc7557d5c086f26fa1d394e2bc4cd61459",
"a77ffc29ecc0cfab3880efbbdf7833aa7f12cbac",
"4f47f6732cb15a760acc918d4d5ee9923638e6ad",
"9298a270beac1b2bdf7098e8c370b81e56a76aee",
"930a72a66717026c08111ec41e0d987613321c59",
"c55043fdcf0a12f423e70094441f8a246fb5a518",
"69b7c73fd53cb7c003cedb6515a03b6484a5ff1a",
"473d7db7fe74d5ef48b8cdb6f78c3a0308e7cbe8",
"f33029333409e829c0c2425ea2e2c5313f17ad6a",
"f07d57305fa377a856e33db230b12a7cb72d830f",
"50504cc2b0bc8c61086c664488eebb4b096c5f1c",
"cc4512dbb6496610bd80decda249fe94fd5490b4",
"d78f91d6ce8de4b8db77a2acbbda9df163d1263c",
"8bb316892e5c7df55b5b450f3ed7d0b14e110e76",
"09e7035a1cfe261abe911e2b6e885d167b241402",
"256377f02c7378720e60dc129435355c06868fda",
"29a0dd24b4427ce98be10c4f0085977bfd18253f",
"5d040c18fac3ed826ef827ef5f52ae6a29aaf227",
"fc491a68d0e3fe56dc43b00e511cd2736df87f6e",
"645d83e627654293ae0faee6118cc4de741da862",
"e41eef2ecd3865c087413b05018b3ad1c7525291",
"92cdeebc7767d89208eab5bb9307d0c27ba42cbc",
"9dc2df9bf9d4487856ffc005088a36b81ce3d60a",
"9bfcfabf6928e7136dfd4d3ca02f5818a34f24c7",
"c4afd4d18fa93aaf6a0919ffeaaca8400724e874",
"03398570a0f6f9cc6e801c1a3b7d358ce4e0674d",
"16c65e5861ea9037919213a38abc2c9b3c63db3d",
"eaacaf57ca8b2eb7ab21a0c9f0d6ddd612e7dfe0",
"20d99e144f08bd704961f4a10a6a00356f40841e",
"9625ac84fa3f6d9c937f2db84da2c20ada875ff1",
"1a5e02d14ee35a4e262ec6df346b589c7d6cd849",
"e1ccf35f312e670bcef36f3fa7dde64720b6c2e6",
"efb361a3210ca28e880a057a200d8dfe29f8a49a",
"0560f93794f79accda85f5d5dc1febfe001950b1",
"eee7f0cf957bd6de9661af3160216e6bd7503402",
"389ec863fd47bbf8202903a888c9892b5d4e4601",
"49212d5592ae021321b1d0b317e63755476d3f93",
"27f30b6f8aa4668d569cfdf121f3fb23dbaeeae1",
"48c42a48c8493257f9137ec541057f25b9205105",
"d552cdb76740ad4655a0af177294e94df9fef11e",
"9729d7c0ca63e5ca7ea529beb50ea28331aed3bc",
"dd98309f2dc82f2ced97ee22c46190ec2967a22b",
"3055361001656483c48c5f57329d3837803357fd",
"f56b0e71751e5352f089e5c5cc7946c1b680195b",
"41068945d7673f9cc6f4ff0d614f5c4c2e86a72b",
"6a9f7cb1585630f4f419a239118085c1332ce97e",
"b9e23d53a9ec08f08e39f503d1d91d76c6c4e92b",
"411b2a0a91f5406a67e22239c380d54e417c6c14",
"cd101799f67fa4bea0836bf1cae7f56483f85961",
"c751963865e2d389a8c05dfb33b3fb66588b1e28",
"7bd8bda5c695ecd83ad414b105ade4a4d443bfe0",
"ac16a90821566a47b7fec44ba207a3dc23e5c998",
"0e7bcc798f848bb3f393dec56c27b31a32d7a1e3",
"f55b652322417f6cf79f9a45fe5ffe8a7b986001",
"cb32c897e85aaad6463a985a059cd8975f756c0a",
"41c2db43f7c1ece6ca8760c1c8149e8f7c90014c",
"64f1800df8001f9b70fa3fc2d9f0a2a3591fe227",
"4ec95aa11b6bdd235e7eae759850aa64047d86a2",
"df188c438cb0619c51203ee144cc021ae1f5cc75",
"02f7bb9f4eef89d4eb1ec7d966f68a804b082dcf",
"1bd753ee8d55f543d0f1b36c0d71968fa6dca97b",
"8df5ae837397aa7de54b185de1375f935a07d9ac",
"068e54406bd9cfe6fa2c1ba25c4604249005c957",
"52e5841906b62148d4b32b613de6d2ca037ab763",
"1b375b046c1b8741785b98afcdeb0c317d550df2",
"275ee7f8f35ddaf883a90b2280f80863fb8079e1",
"ce264cc6c0ffce8f346729f8829893589dadc223",
"47a67a3b625528f1e50e5f61730ed3ba2202e1ea",
"876820530a58d11d6578b47c7e89011c3bd61b6b",
"d2994fa15cd04cf0319ca8d788a3444e514f4ca8",
"938f8c47a37f4854f04d5f092dd54017538486fb",
"2a2eb914361a101bf47377da5f7255ebc45506aa",
"5de52db3efccb482e36110b436e5927a81b5eefc",
"52fd672afa633036888512d7429c43d0fdf1260e",
"493f083dd62c8ea8136a715deb0b47e81889015f",
"f217354944876d478eef34ab6fd9fc792855eeee",
"d3485eef775f8967e6dd8cb54403880a910aa30c",
"a5f179871a2f87324c9de852357b148d244e462b",
"746b7c3f420a41e19e1bda63d71fbc5304e9107c",
"a3346c541418b5b171e821b40546a5506985b658",
"5214ad784bd487634342ed5accb0b6e50b9a3e52",
"1d55635f9d8cd1d0e9db7263050b5d59eabae9a7",
"f38e13a3402f4f07646ad24b9b84a914f086e99c",
"97af01d4ea8aa264b33abd8b5eee7cc656b7691c",
"c78b0a814460e3c27bbe8cdc28cb02fd0776d5b2",
"a862e6bc181e1b06de773c053a3e849537c83e29",
"fdc0a4e501a197c7e0f77a0a59b168a06231e958",
"60f514dd88ae987b9ffbc6fc85f6cd5d86d9add0",
"e805ef6643cf4322914df94d44b05c2c33a313a9",
"916f52e2757722cde0c6b7122e796cf666f3c29b",
"ac6e415f395c3363d4da7c63fa7a4ba5fb18b53c",
"3163fa6bc1ccbf0b9dbc5806ad253436911bab13",
"4c2599bf6b7a18f7ce95a9ab68f6a9e86d55d511",
"03c9f7c81707056b56aace3306bde3cae3e850f2",
"d448dbb537b8058cae0c1059876b6698ca35b3be",
"7a3fc6bc3c6f46d3dcfbe220f62dca5f855f6d62",
"d1e8d96d3568513a240e43b4737b09cb79ed6403",
"df7edca301064a23ecc40cd5068a9e2b8b847a5d",
"c93270bbaeb82fa7dc9d09f91f97c7a1504bd77a",
"c025eef1993f9b7cc49f478b75e7e3b118e2012f",
"7b87015f33c5d910b23e0c57077d92b9503e27f6",
"84fdd626a18709f6546dd61172feb573e5875716",
"a97094b96b0448fdbe01936a6b62ff35825c0eae",
"b4641c57260ddd9b0c766d348045b012b4631f17",
"c6e5b39ff3bb039f12e337ad66b57d9fffb11b7d",
"f1ba2bee42aee0d9ea6ffdc8835e259cb55fe4a7",
"91ce3cf27118a44c531308f7aed4f871d1c99e68",
"ed0a3685464b70287baa989d6763c359fd2852c4",
"abf2d7e317003083b78c92fd1d28a1d54cd89b3c",
"60fbfc13555a22ad9d5a17a7ef6c909f3dc798b2",
"a765548ce6c964d23b368a40bd86374dec50d1ab",
"6fc4ddab97a40617df0a3d23f65f6c6157d88713",
"2e31ac1f46fc3d9875110445c2f02852a3406aa5",
"fce62cfb8c7bf2a837f43e746657e6df3f9336bc",
"db4823ff5b70e5ccc16d3a0e80dda9c2bba8d472",
"4c3b715944ae9ab07bd0bf6eb89b7f4f9ad48a85",
"fb1f7514e58c52426e822cac7307413cc458e3f0",
"07cd9c1e430831b7616aa0874e861b886d97ff6a",
"fb352bb389abe213843ab5c53c70cffde93352aa",
"a3a04027cdcc6b6fcb4065246f720cf8d707003e",
"8091d2c5b9812f6a5827dcd084240ad8b6046469",
"a4d373d199174113de05c1cf7c95acba08485aa2",
"baa07c18513ad734dfa100170a03e1199f3c882f",
"885b7375fe79df4f7afa2a05ba70239be5451c19",
"ce5455a72e679a6fd858cf7406e6e39056c85ea2",
"161fbcf59a5ca4ddfcdd2d84d543369957599c4b",
"2a2067531b2372d2847211086620da44beb6aa02",
"d58f0b7902969a83e02ce67a965427135beb3902",
"442e0a043467292e3e064021ae24b957c7535ae4",
"dc406d4f6487a7a2d8f668a4b53dd5cf95e4fc5e",
"5662b4d2b6c7b23c9423ee39415abce3515d8967",
"106b2dcaeb426c7f1b72163fef3a414ebe1cef21",
"1fbf6b6bc47284ab5aecbc917bc5c69e9930c438",
"2fb2bff8de52f5414712a3a8fc06f6245ea38682",
"0202b3c5e522907af1b198dc1422dfed65503054",
"0faf0ed0f848cebe614b7922b87ecee481157497",
"9125c893a2f6031f897bb4041a2b806329fdf5af",
"d59639f546370774513085fb1ab54121e2b2a56f",
"bebbb9856a927d3a0bdb641658a7aac32ca491bb",
"47f46650d837c9d7e54e1fd72fb318a04d8f179a",
"0f048ed4b8b914406c1f709d83c179013efce9be",
"1c3e6c3274ed59d3549f49a24d2cb1f94be81e85",
"d421b2165778b73bd0cc82221240774d6a0a920b",
"5d7ac6111f7c4e98f65612aeb6a4bae64511b164",
"96a5e71e931a8ca3b2db1850f416951aa11b6220",
"a66b8e11b98d78957213794bd9548686178d2891",
"8a5b5c3bd7a8dbc79f2b745610602ce2dc081e71",
"93667b009651ff896cd41cfb7e885a15a250bf29",
"9bbc1956990fab45fe2f678a5435472d7d39bcad",
"58220a1b88eb0dc1383fcb09e49433447c0c2b03",
"a84b40ef648c7ddfae0ed4e894c2e9352dbd868b",
"cd56842cbc2628eb71ddff63bfb7532c01858018",
"866fc4cfb176739e10e23f34af5c96b1312c6ea6",
"45309953bb82e3fe69426f5ae2ddbc5c4b81a8dd",
"67b3cedb4987ac68a8108b2b5a87f838c0de8c41",
"93a758c7f11ca6264286e41164bf74c4f7aa470f",
"5956b45826849de26ff9b879ac93b7802b9826e8",
"00d855c1225cb42604ef03ce6b06484e503737f5",
"47be771c26fc8d1af7b97db5e3ea2a4f42718074",
"546099ad054195fea9c6cb62c800ff7b5abb27d6",
"099d5a2d4591c10c1d67865780e66cb02ae743d8",
"9304109ec4b04b4d30567308e22195ae4374acb3",
"5b786550ad0343f10d9cc6fb2eb626589ae72933",
"a688d1c49dcb65ba5fe31b7b19c12603dc8f5cc2",
"80c10985281060da276f06009b7320c66122db40",
"96e4fb1b132c3d4084abf7945da6cfccf942ff56",
"7e3e4fbc0cceb92148122b5d689f4543ac4cf30b",
"7cab88bdb4a73edd53e4e82a3cc3c2e949661e65",
"5bd79b641f7653ef38825dd5145087499fa303df",
"ad8446426d232f83b1956ee8809196d6c2478f25",
"6b2fd95d06558bd242db1987a557fb045947bde6",
"a1bb65ceb97b73d98812f89952a8dd3d91c06f29",
"6fb049764a8e1c61e68f867a9fd42cf582ac2639",
"7e50f8f6d3ea4ff4d699d8a46350ca9b478f4df7",
"46b4f4f87bbc76dd6cd035a868500673ddf3d907",
"05d0726345dd072d5b95b8136c881910e00efbc0",
"4ad8f54b1cdd1fd453a784a7e56c573e02d17091",
"418eecd61315cd3b7fad281b9bfb22c873736815",
"b555e65b9e1da944ac12aa4c6f14295b49cf866d",
"c30601770bfcdfcd3c093137894f0bd7cf57e601",
"30c3e4e96b10722e0a3f1a27004ec9481d833d36",
"fd00f81c491bc35c63ddb72cb108be6bbe4e0494",
"75c82f898ac88fdf55f4c32940ee366e6bea12f3",
"0313dd5fa669f78262a823f0b2a1f0ea44787e6e",
"eaabfaf4932b7790ea966a65b6f9a9f16c2b96fb",
"25dc6d1aa70263bfc0fbade85fbf1cbba951206d",
"07418fe7bbddb437a0180b261f55e38ae89fedc2",
"db8cdb15305b833c03d7af35e03abdf946136cf4",
"4e7c2df5c4c5f8a4d40269ad01d66c09a12feb93",
"d324d6a8adc8374e0ae6d550e71092cb6665de73",
"e97adea34e4be1b6dcc5169d5d238f818f727810",
"6296657c5167e8dbc89dadd5a84fe32c13189c98",
"be57e4235074dc1981362ba89537d994f4703371",
"ba3501692905f9cbdf9b3e7cc987f4863e48ff6e",
"df939fa5faac2a9ec813f7b3d1ef856bac138cee",
"0e7c38a84643ffcf94567dede478191da80ecf8e",
"20faf996c508db4769ebc33a5d601d2a061d9eba",
"5288d0bb27932cdef3c947c0db5c0c3bd6c04f03",
"bc37702d1eca33414b1aaef1d1296b50d4dfcb53",
"60ca7c8fd036c1563daab42ea17e2682399f594d",
"3103a5e27c9fa7c8e6a4716ad7baa099c685ec97",
"5c83b690204fbcc631dfb9ce08e3ad8bb2013cc7",
"744e26330f76d58b3361d65c7a6bae617c5de3a9",
"ca701a0c86244af58cc0011e28881b4476680ad9",
"62bedd62745a4438e51f163274093be24a51c85c",
"2894400b7d8b2eebd2c58da54fcc11f26a83e2ad",
"1691cbd8cbf8607d0f595cb49ff539cf1aa5649a",
"2c4ebd5c09687709501ecf3325b5b73567276db5",
"2b155dd8d78695230d9ea3f0ff6869abedbce850",
"61c5df33f5e90a5cbb42b490b75646045fec5b29",
"24220318ce27141e01489a7133d36a5ec89d38d6",
"40769bcdd436403cc6cda2b6e4619f5c88ca3062",
"c6ee190a242f6cad9ed672c2bb2ddd5f24c513b2",
"6396e4b80acbe113bfa305f04459cc926192afd7",
"fce0e0b21211906978bc2f33e2aa4dab27aa034c",
"c2a5cf13b012a01878b792406e29e7f104a7aeb1",
"89c38e25dbaa133562781c5b75700bfa85f7307f",
"7318411156bc0e9607cb023986999bc371a9b295",
"7159c95a40648b20cbdbb52d0e0e99c4b299aed8",
"61308a0b2204bfaf77f2e7841b9c05163e6c4faf",
"c65cef23eba65016011d4eaacd9547849105e813",
"a274c5325bcaff83388233005d6fcef54c7c711d",
"edbda78c45a5540ad3a20ee0403206872f56ee45",
"5eee152a701b74de55f941d8d68a4747089349a6",
"586a20b2add3c93c9746f44da16eccbe5bb3ac63",
"e4b807374e0033f90e57323b6462ad376711c2ec",
"e211be66d052e4ed0e51c77407ec6346aa719738",
"ac10c29b29f6939ffe241d9ba642f8145226c61a",
"3e7df2ef0c608726ea6eb664815ce7e31358ea03",
"2a028d2a3a32b779a4243a5eb2322e6088626b1c",
"7c97ef5d1e22a60fa800c20abeb2a2c09492e00c",
"a1ac9f40c425b6d02627616d04d732e6e883a513",
"9defa08a5d66e06bd02a14d816d41524e9026361",
"c0bd6c7da6ac49acf8e38ea86f1f38f583df449c",
"7824bdab89f911cbc9c4844e122432c7d61b05c0",
"c2f62061343cb08f0bc4ba7e90c825a41dab3064",
"711898933be17e4f14756c2fa23275fcab81b267",
"94205d18851257af1118c50224e1ac4caba8fbd4",
"4c3c1350bae96ef643c72a6b1a2827ca9661a49a",
"59f170bd88b2ee6714f2de5da63680b5ae4d62b7",
"9b6ed450d5b8ad9241573b22fdcb488d1a8fdbfe",
"f44234ea03ad99515f943073a98a897e306be782",
"22ec0849d2c56385d4c9cf64a33c17c1f7122bec",
"a50326b6ef3299b881bd90d613f97d0782a5361a",
"72320aa416f668905b37773566754a82dd3c5b9e",
"9d4da973037050f21bc3bc594c37d38a2c488aed",
"def66308dca8c9a4a148d46548fbcc538a0ddd18",
"6899d9b70875150486b4c10fceabb4bbf231a82d",
"e84b3d6a2fba29231491789741dd1ebc013a31d9",
"ba3b49c09b97b28c5e35ad2ceba88586d3f92e09",
"757ba44fa70d1b3258f49d8a4612d52390988632",
"a7cbdd470a30c4de7354778539fdb4a50e016f99",
"8ae8b6e2ddfba263e0ab388379c40227c164b8ca",
"b32b8178ebf63838e46200a741da5569ea5f0697",
"9b281f385d5b7c767c2990ececeeae33a2692b4c",
"4c39ec49cb5cc0f7ea4ef2153d7a80e871d294ed",
"0ce4f6eb81686d1c4bd9060c33fe568a1ee5e140",
"749a7513deab3d925d528aa4ee67524e7d5d85a1",
"aa1ed59fda540ac6ace91dd17a455f56e05f0201",
"6ed3b764e569e7ea0e8345d897481422554f9d82",
"7fcf8e13dd639a3246fa85a773da91aa16f03862",
"73bc3f5d3cf932a2afe24614e29df75f3adca969",
"3b4d1ac660029ff0ab7f67901eeac3d7eb78264c",
"a55b643b9e0dedb6da9e2e292bfcddbce73ea856",
"f920cff2e0eb5d8eaa1963ab6c0a5f7ead426eec",
"2ee672c1923e29cd256ba6b164be9378558bae18",
"8f3b0986577fc71c259a90dcb12facebed94a4dd",
"28286b8566bcfe223e27de8e8528672ef7686552",
"60cc5472c70dedacc9c35329d348b3512f0b7684",
"59204498f84611716ae173e699d88152a85a2aba",
"b2aa6d13e04c18918b563d55854e7672ac5ea25f",
"3ff385bd4659b3806e35752500b2a7d18438a3b9",
"df2eee9c3f81384843708244bc5b9282bdfbaf44",
"347b6bc26a3de4bb584bca65bbdbc97f9a7c822c",
"04825179dfe97f8bda20c25683e7a04b2a5a26ea",
"86726851c5813fa12b359902a7d3cb4245f9846d",
"f571ccc44a8a2ce2ec4f3492ab70246dd3528230",
"1cb0471af86f9ec7c8ebf28d89035d3360e5a760",
"de7cd46ecfe8ffc1845f54399798c2bd3435d27a",
"abe63bdab5657026379ae3dc3314d1d74441ff3c",
"f6ee583dd1a29034340995e692ae57318a7bfdbb",
"fc49498fd063fdcd2928f8c151f80ca2cbcc1591",
"3494924ac518bb1edf39385728de55c4a478da5b",
"b935632b67f7dca5013f77f1ada6ee6800204370",
"9347f7d33cf1d6428b7fb79e0b515cef51d55708",
"7ae2ea9dcbfae8bc13f2a5d6b71ae82961c687db",
"3de907f5c95f33d28232c4a50c893256e9a4624e",
"189e164fa8d4691f652c8e809525a2487807c8ac",
"60d077985f65f86dd9623dab0c5ebd4026113dfa",
"2d0d0b1a815375825ae0b702f966404fe5632ea4",
"f960e47109712881dffa6732dcb8103c18748c5a",
"82d55eb13fee9545214b3bbf58091c2499e022fb",
"a2bf54eb5b3e5acb847c1ac16d06a82689f846cc",
"bffead1a879079ed8daba69bf509723be372be18",
"99adaf337cab34f56383c1fd8d2c4cdf53bf66a2",
"fbcebfd1a6f9f395f2384914e4d6c942fd93dd69",
"6535447739d1cbb2d008662a2c543a5538da4261",
"6447f4b736947c2d3bb6f3377bdc57c102a30e37",
"fa3b2a1ceedc63a3cf804942b0242ef81de62405",
"a02661b0b6bd3535eee8afca694c52e033d08730",
"05b736f44e3f625bf9c3d4b2a2eea63a8df7d91d",
"2056b07b25e55cdc4693cf754f979bd06510ad68",
"1a669306c84aa8c3e1b5504beb510d6a4354f0a4",
"490af3c6aa59844fb758ba43132309bd8407a802",
"4202a38283d46d27a70ea139e557e3ace5afbfe2",
"c25d4d2acf1608b6d1fd460e90f781d32dba55d9",
"1cee6f3b8d08dac6fcfa0ce30d1bbc67a7fe0c9a",
"e7261017e8744ddb8a8b3145111ddfc160942388",
"bc09dd500ae7b60067c928ed20ce6967a0c8cba0",
"0b9997111802fd4708e4a90cb6fbd0fb968eec7d",
"73d903a6e969e4c55c996f3c2e71fd737aab0a9a",
"81d310901256725dc3dbe658fe9768239299952d",
"7117d019e61351bd711bc04d536ee673519e8866",
"a4de4109230439a3f0f9532785cc17386cdc5d5b",
"5203d0816976e5539ee2a53f80338140984777ff",
"f5f3d43962e494689b6cfc49029f54c49f36cc20",
"32b489dfacd741e7945a2bd851f6b6d63f0997a1",
"c6c96367123866ae305ad3b7a6d14bccdf278430",
"b67ae3d116b48b86b7a2a0556fdf43a567c6fcdb",
"ea2dac39522405c5977892564edc489f0859870a",
"f5f1a6731486a3241814aa32760ddda22cad09d7",
"267d052b683ec1e8e9cb03fff7594ab5e16ebd40",
"fd8356698432d629e82c1f0430809f5d4b3978ca",
"ba23700b82e09b5574d9f51941266ad5cda7c5c6",
"416e5da53fb50464ede3efc00b59b646e7f2bd1e",
"5b93aabe50964908a2be04131b74ab52b4580ac6",
"55d832cfe5719b4d3312d9db4afac6819ff2350f",
"cbd3be2d22f81cca65c63ee55f632075a4a947cc",
"519c1bb35a1bef899bf70b03813197b09877ddde",
"2765beacf45a8d1eb902ab06d53aeefcaca60d67",
"730e38144f7dd8c6df14211965277d615973d815",
"e224031d709c56998298b1558b8ab48353087d0a",
"4d82f48ca4bab4928292903984b13646577e6fe1",
"5c2eba1e9996149317458b0a44fc37f080809eb1",
"792754253877a3be70481e94324f2d9860fd6ce6",
"f918e7c4fc2d02e9e1ac7e5c7a664b23acdb0ec6",
"765d6c16779dd07e05a5a430b460a709141534eb",
"f741c54d8e099de1b13c053b3c0bc14a12ab57ce",
"2957dba2823f4c1df3a36446aa6770333694978c",
"6450b0b2055288605b075775525722161dee9382",
"91130087e53328532c6a23f74451b5b6b7b31bda",
"52bc45485d0d489c98db495f6f9fd28247f90e9a",
"fc099f901f55f79daae2f91226cdd4b722895482",
"257c8f051d6110360c2902d12d2d5c8598177864",
"46651694e48ced51ad3daf2c842f3f089810c760",
"5e0fef152403f7c969538cde36e1afbffe062ef5",
"a8ef5a35dfaec15bbd3ad45ce0278dc67c2a1cb0",
"1568cba236677c952731fa99d4cb77f7ff295bcc",
"5ef980270be1a44d3f174e3a32a9ee320a7207ae",
"ae9984829aa0e9bca22c11c19792079188896b33",
"4c56d19327f821e40028a3c85b3a57d7ab95e1cd",
"087ccd321532fb9e6d0b4dc6b41fb5da9f2a3d07",
"5929efe3039f964b08f366a22553acc9b17fd39c",
"7ef896f73a531269ab7e0a3929b7d0fae4d77ebc",
"a096f3f657c09450891e95becaaffc80e6be8573",
"afa9f40f6f847e7e5d01d6b34248ed1375310a38",
"d4dc74f00cf43e0f0cc1fb23c9351fc194066a7e",
"d3b0ea6fd747a5a7b4197a3ccbd8c58f87d2e622",
"a08b48e9461237bcd3f31396d0b5ef95809c0c48",
"23439f0f1332f4c28a5470ea70b5639bb6274173",
"8b95ced29cf8bcee89b7cb8bf304e2ac19af3229",
"9b02d601852578bedcec852becccae7a2f2f3676",
"f75b4ca108cbdff0f70c593c00cd4e2ddb6524ec",
"19671ca0b42c3de113d239b613fbca3cd9698297",
"47aee42e8df5e26964a6afcdad13d9adb264a046",
"90d6353d339470c65ad41bfcf908c4211b6114f3",
"c424a84d1767d76e0548e7f9449e589daca5402d",
"436caccea52b061196e47378566811e1bbd8bfa6",
"547d444254e461df5f557ed90a68c623635d9d61",
"aa7437cb15a217316ce9f9298933dc3b241dbb9d",
"049353a3d3473900d6f1b8787b52dd4006e3ea16",
"3dbd3bbb0c5ed182a7fd5d4964e0a1795024397b",
"0b77d01cc53ff095342e0ede84b31950db2c1c21",
"2a8ecde4d001d6e2370c9e4dfc94fc9cdafda543",
"c9365acb116ac4e0f13f786a968404839a9941ab",
"8c496d1b4fc14f90ef43d6dc93cea0033353c519",
"bf62083c34967e933125a6f8c953790e712935f6",
"5a260ffb21cfadae9aa3a80e148b1e838aff66d5",
"81e7ede501486351847f116581824d6797bbeced",
"69663fae94e0d7abd4e3a659f490d3171678327f",
"a76f630d135721108db5e0f0bc7010a03adade0d",
"77bc193da7be8218ee8225903e334bc5da140033",
"820a23e16c599168d552f4068614e6b84aeed204",
"eb6480e87aeecb950d6214170059f9de44e12142",
"3643de2fd794fba4d9fc711ebb6f8d7bb853305a",
"2ec1ffe2c17c4401bda89f6ade266f7d11de0b2b",
"c935b1892cecf78cb8ac0f931c06eb4b8cd6fbed",
"8d8c245a046603d55dacf21c675ff8d08f2f6c49",
"399e9d5a67d1fefba95590f47e758deee3582702",
"88b167dac82c76e39d070fd29c7c01ced2c06394",
"e84e6faf60ef18890db8b038e59953fa8ee269a8",
"7b2bda1731540468ecfbe63bd3bb2f559781699f",
"2cc977a3ae387c509a4744c5d2413fe23eec7434",
"b3563a6a6abf98000e8bee4e5f8d278a5669db4f",
"7900b7cedf6e2e6ab5ad94139957511249fc98b7",
"29e2d91a6bb63e0cb3e95abcdd38fb84311e8325",
"3299188a53081b1fbde608eb9b6c5fdc3c65b223",
"4f6fb80e9a3d05ce3902c4bcec690f6d31da9db8",
"76f808e48c852aa1e3d29d45bc56fb943f91302f",
"cfc2fc795bb58d69c560e86d0647f913cca9d575",
"aab9f8af4bf136634492f92eb276ad8511691c97",
"95cc8f92750ce0594128163f25b94439806ebc9d",
"684dd0f19b0c00b04f722193edb78c13f0e0bf6a",
"d5de1b4c9dce7fd3fd5a623a19cd7188417e8ca7",
"d5e6591aade627f43228d73f9effa3fac1d7a073",
"e8fda62a36f6cb18f6e8e7c24c97e741f2cc15ff",
"dce3b7610b26c123cc399586971e40f5d7204a32",
"5c4a64afbc7fa8816124d33e63912481535801f9",
"cc1b1e771754ced2c5052a45a487dbab139d5e2a",
"c3a38aa8fa0f1c1b19ba06cc8731d605ebae4037",
"0da6321df53c79cd7be4ae52131f70c06b483a90",
"e5cb93bc9c9e319fde3c2a0c618fab884e4de1c1",
"8493deed55bd9241020d52e49d87102a5a262980",
"14f8742e937dba15b5676cfa2b2f7fa23084b508",
"745d186a53397b981e4d76cb50ac5c1578d5ef3f",
"ed9c4c150fe525c51c902f12797bd1a51c2d30c2",
"52842828e9b02efb10ab687917e4fcfea8afef43",
"88ebf01384c8a82eaa8e3fb3c4b8be411c8da681",
"eec189c881245d66d71842a6699c1becc3322dd4",
"c96080731dc25a5b439a76880922fa933fde1e1b",
"e1086bdba41cb1dcb3c7fa7bedc3a9913609378b",
"3005f5c28ee615d8fafb0410ef74afab21c0a250",
"37d6280f625db75e8677a5f7edddb26b1c80c223",
"61e62d047ba5037e6959744681709ad631c28d21",
"01599eead34323fd3d6172594c3abb009c710236",
"bf329f3d4ff25f3753c6ee413ae0c114afca8ec3",
"3f8c0c124807e1b554329635d26349c40bb373b8",
"99849f2b41b6c5d1bfdccf9bae54880c086c9147",
"d722a780c36be715a27644933bd053a33e0ab8ed",
"b2e7b1cc84d2a938a44438c1dbb1964fdf826917",
"9cb78a414c185c55bc653b8a6640a5da98603e07",
"81c2e8569ce844d35ff5076e767e5667823e2c7a",
"a33c11a246478d1704b61bf06b40490395a35225",
"70ef10d8b4d0b396fcf3d145926be9b3f33c1d09",
"1e91949e730d4b370fc7573e78fabd9743530586",
"2d8610c7e48b387a90a51fe26123a268da29ab9d",
"145b1d913f5cf742c08086845343b5fcf8c988aa",
"8a2a73aed2b157c0ab69550c31250c02c886558f",
"6b3ec12d6a6bea8c2bc9b375f99995b136104b29",
"dcb87acc18da271f4b7d668ff928d175f5e8214b",
"acfd42912e25f21786807c6e8b21114d0e08d1f7",
"d2dbaab513c1f67b989c51f86ea48585ffa19fe8",
"a95ad3047c03d113d1180fadf2711a3749f72e97",
"d7cc4a32063b7ad4372eae8bd02d016c03e22cc6",
"529783fd8948ab10ee41e5febc1413b0a306fdc2",
"728b726e5bda1731a9dc12fff708981eee424b23",
"e41b7dbaa3ce306dde66ed59fb535acd96bf9798",
"d428b2e059a734a50295517d29ba892b63215e5e",
"9eeaa086c83ee78c2ea79d3f0e7693bd10f7e34c",
"659f727b4a6734b8e2f523907b7e22917889e578",
"aa250c7b435485e150fc797ba1e9b20b6945c19e",
"1717219aaedc95c384bb7f13624fc6278d152948",
"5420879e60a0a73a15b2095de34ac4ed59983561",
"43a557fe4212f2fdea2bc774a6943f70247e6325",
"87de1176c7e0316c3aee8b27b59b6886018cd966",
"eff8acd9ea8570d7fa641865ae7532a8f69ebb5d",
"4effcec62202f5e8dd1618bd6859847d8014a310",
"95423f26cee41b6ab01b2466b09c0fb421f6aacb",
"66418ba2eec57654dec8c6ce08d94105347f45a4",
"36a13cf846b5d88a9823e686089ff812a639265b",
"a9c4e592c9b6cc65565d6276e76bf8495aeb20ed",
"9798b69aa6111f75cc8f649fc7ca64d7b51850ad",
"82786c1dc19bc326df4a8c4904203e2297b2be2a",
"6075ab3f0d54667549b0e73d3b1ceee29fa89d31",
"691bdcb475375321ff12b6914f73e41e724b1bd5",
"a214fb4c8c629cc9e6430316ed036503cdb79664",
"222e65439aec018e46817b9301c0a13d9e64fd13",
"ab4d72b9fcf370cc87f3c14be57c02b7149caa83",
"d90f5722a2624dc55a01cf5a243d25727407fe19",
"9c1398288cea88446e6cf0ea5d2634aadd7ec057",
"00e0e4864c4fcfb1a303f655e549d8a46d53fcc7",
"6a933e4c1e1dbd4072a01ac549deec81cb39be6d",
"ec5e0773a3199a94e32892444611c345a38ac8ef",
"4355112f572f124e42bbc74aa9e615b9f362d522",
"cf55fde290dcdf2cbe91cd7f3421e763d9f6db62",
"19dbbbb5d9b076e3e184e18cdbbdc395c4323be8",
"4a4d19686af4dc058c7b20e717255b5fe295a398",
"c052d869700ff60057fb93dd791836e3886a5398",
"f6208aa606ffc6f1fa06383534abc5303087094c",
"99e3b71ff8e92a36bdc1fb9a2495e1eab2841414",
"f3d6193e684ebfb5ec0df9f96df3264c4685c561",
"92f6124c73bae0da8673adeac94c025c25cf291c",
"7ad6d768773a77b6d86279327b8d83ad6d04664d",
"fce8d87884a1dc2f81b1b8f0778dafbc3a52da6c",
"f7abe720edbdd905a325b6c1f17a707f6b25a88a",
"f5ee28189c92ecb3ac8e39731937f025c7fcade4",
"1b25cef981a7d3e84bd77bc8d75b4e8a9d42da0d",
"33cbfd39a6b231a6cf41cfe9ae91550e988abc2e",
"4238b24b1df8dd2c01aa9719560e363f6705fe52",
"5e3d3593b5fc8aa790b6072bcffa440a8102dbef",
"873fc83e5afe2d7a58a7b3bdf1d9fb9b43ad678e",
"620f0aa2d9134b94cf8396bb1793a03f1da11cbc",
"3f1629ebf3cc02af5cf666b51206b8fbf8c020a1",
"cf47fc3f9511af4fbb9041e1b533cf00a8db7c85",
"b7bcc338e00e48ba4ec7808fa04e17f72bb0c6ee",
"958f4f0ad68ea2bad4a71cbf3d07e6f01f7800fa",
"9c1758498dac3963ed1fc474454012146fafbf28",
"44ea1a0c03c82feb9e561ecdf6663244e95f5c6a",
"d19ee330ad6fed7b7d2fbdf0dd5c96673863a606",
"dcba1860a53be99e2b98725681ef971d38f6adcc",
"bb73501ef6fe994d0b6d6e92a3fee6d02cc34a86",
"a634edc54f31a151e075043918a1ab1ef667d75c",
"70d9a7d460bb90d00228e363ae0e3a02a990fa03",
"196fe4d6d4ccdc2f6742bbd4ff7d53dd576a31d1",
"08a02a21f8b27fadfbae8f80fa08eaa8453b0da8",
"46bdfe3bb814d5aee9e4cb8a3df9d4c0c0155676",
"bc611d3207c7c83274029ed75e0d7a0842871182",
"a2be919ee2e8305377a8a88fb10ef1520dc0792c",
"e3285a3654eff5d2d4ac9bd00b445d92027d3c3f",
"b0df17f74cfbedc15d3d37718db0631def860638",
"3fe8c5ec18180d2d7f7826d9c26d2a36dd1e3a7f",
"f19dd315d535b2e5547252d3d781b0da27fa5cf7",
"d8e23fa6edc48c20c750563a2271524f5c7edd0d",
"43f6f591ac952854f52cefd0f45fa230f589c8b6",
"f706cef7fe4e1dfefbf64886307711ac27e88633",
"1b15a74007771eb9146c8e06476892c92c1d111e",
"01595d5229194fe3dda764e884d4432d71fedbad",
"3376cb6f201ad88d908be08bc068e1137d54edab",
"77170527d2de9b9d7c3ba8371021b78468745cb8",
"f7828300974982844f7903c9acb6f2933966ea8f",
"113d3e05e114d71b4e7a294b5e1fdd9ac964996a",
"3dd13035cf8ed8fa4be49cedeeab8852150c7132",
"933a5e1f1cdedfac2922d1701acc3ecb665521c3",
"dfaa9fffe27e0b8898b4a97bbc95778a88a43037",
"882451528aa8e48d2b4e3c2fb45b18b69e743bd4",
"3998d7313448f2835a07aff2ca30beb6d076b5c6",
"fac84d9ab171ec5b3e3345e60eb86f4b84d915c9",
"e6323f4d8d15c51f983dd030070caf931d66b80b",
"2b51fce7c00c0fea47408441ecfc98823068286b",
"5c36d5180cd58a51b7f8ec410f1b0e74f53b565e",
"a1ad36ac70b427e676097c87229d13336774b37f",
"0224e08d32f8642f39503fef0df6f1237b1c0583",
"15a2cfac5cae8f54e33278f0df714a33b3d0ade9",
"640f1149b5976278c28ab96a2b6e56259cdde4b0",
"fd81804ecc7a9e12fa55976196d7201a07f22fe6",
"0653caa316c76415416cb1b939ec0d52294bee76",
"aaad037f557a9f0550a1c17425c87e02f1247b18",
"b8befc039f6b083e4f15a14757303f36ccde447b",
"1a8661a9a2209d92fe047a5c8600d7be12cd126b",
"b7ce808b24e6f895aadbd78e202d144839559790",
"8092c4698e9b797cdb24b3cd53b230b6e342dcc6",
"b872f67e8d25cd1f8b0781976114f5826a2d5914",
"9ca4133308e64d363d6a1662b6f8b794285a7d99",
"412786052a477ad8f6db40c783b18d26385580c4",
"5fc77d1ba542bf5783132458b181ae2977a1f482",
"f1118ccf09145691c7f9a5af00ef9c304ebbf899",
"a1d74f34ada315db6d91acfbd4cf67e8df2422e2",
"7448859d90ec18d5a2f1f3da04343b6cc5f33711",
"0bc6003615fa23c5a0edfb8f6a8ffbf0ff27be43",
"fd3992b114340a09f01b0345fcd1cc94e6643185",
"870d1ed56d764380bd36692571243423cd025688",
"affc3e75b3feada02a7ffcf690843b83c1d8deec",
"6acdb2a34170e8151993867b7e34896e887600e8",
"9c4b5f193b38d0d19234a2472249e7062d58ca13",
"6605b6a3589c4df2ec70d81da3c151eb2daec254",
"58df496ff6a19a5deeef5d6e3b064f3bfa7ea350",
"bc7474c739a0d0daa5fad5ccf171e168aa52a82d",
"2b43573acd47d8dfad89c01218e9ec581ce95067",
"73028241cc6f6f6004b4448056e2b3692016e93f",
"191b283db4c19a1fd596997a4e48f9a64163d6fb",
"c1cf36701ec466960cbc46e729ef03978cdb7140",
"959f8dead8def7554482ff590f02c6cbfab55a52",
"a849ed976b7f464b322acd6997407bbd94744016",
"2bfe00fa4a09d6cb4109ec7b825910c2379a711d",
"e844c9f7850945c17a9ab01ca3f8809fd627ca93",
"15a897a7790aebe8751703953fe76fa631c84e41",
"3f040f7a287f0700aac10df1d9e923c1781d067f",
"4fa1a2c395aac52f0ee79608dc000832a6239a08",
"bb7a799e9ef50a1c5133bb017cc3871f99f2ea80",
"b686668366529c0969834c00a8e3ac957ab92b48",
"d2a1cb43dfcf5fc4b4eaec33dda1cf2d5ed5dcb7",
"4092a7ee4201b434a8d0931a0af6130eefe85222",
"a771348eda9592b9bc300b10cc71d9d08e54ac50",
"a749b75db4ae419d490b2320b0d50f7163375e84",
"b852cbe4e70f83b62015709b4b829f150b25a4d8",
"dbd294d1537f586af756d0223c8509946fe19c72",
"b59a11c4329a2b86b8261d1d68f6986dbf7a1750",
"aa618f9613d155e32e8a9cc234ecaef1942fe26d",
"53c39bf13c20a523a3aec7b43374228363af1c23",
"873cd59031929efd8b4d80be6ec9dc119cf7ff63",
"ca95fee72c33adb37add739f4c2d09e81ac715fb",
"3ffdb9590ae3d5372d92748c6e4f71bdd7d9c0b6",
"88d32bd5c7d0677744733b0e372614eed4a83771",
"2a4fe0216e7d4183ceacf097c889e456ee06bcc3",
"d557f3208e0ffe408c8f63efe696f0484aed7cd4",
"a39cbef0ed86ab17b5ae980cbdb3a627546562ef",
"0321b945180f1a6f1c0a7cc02cd9b9a5f5f9d347",
"03c8e5e03c6538e3b9d13d958cda825156cb02f2",
"e75bff7bbe762ad020151db8cae641a5a141252c",
"75cbe4a9a1d86a8fd12428434112e077f8617d83",
"3c9d72392a5f7675a100beb3238dc191ae6adac6",
"0b5aa8252ef054ca1a867a009283a56ee14feb03",
"8d2e5c9895663805202b8bda9121748ced63b0a1",
"6ea012cb3073e1ff2450b7a284f7800fc64acc97",
"07b7e823a22e198f9bae145819a21a5e39615774",
"c4f6d1764dd71c7c1ffab960087908e0ee1ad562",
"dce23af94b0de5c12752b312c98b9a817aa9dcc9",
"225279b710df7296cbeed02b596b56115e9d98c5",
"d4838241c2f7136d5cbe0bc83076507dc3a39b99",
"4c3254437f705b33869810913fbd9ccad29ccff7",
"6a61fca4e7d8efabaf07e346b2d4deb27d95e5a7",
"18edfff26b39300f03e396bbc604d517387a28d4",
"ce4ddc63b688bbee53b8e1cccd12fe2f271af558",
"05d0601b66880d011b162230c5a3845bb9f22283",
"58fe4103287f33c11e5e08c7a5b5efe715d402a8",
"28a67cdfde3d57d974789c01588e434ec4b41245",
"5cc1e0b3d3a611d216cab6f12836da0cf72749ea",
"460cee911aedeceab401b6e835193ec3770a7b98",
"b0cd7fa11857007b96a6f78826e11035870ad99a",
"0c9b9f3b58aada909f2c4b53c9bbc677b48a01c7",
"8dd6a95bbb3938960713d1432f1ca201fe769d38",
"ca65fc832ce0591fba2373066723657af1b35ea5",
"658914b3780029f78e7db8c114650272579534c5",
"da8ba2bbae6239c366a8c69b8b4192751caae48e",
"a4bcafd5dcdf7dc991383618d166492152cf742c",
"dd2f5c849a2526fd9f3947be6fba0f798d0ea05d",
"b9f40cbc79238063354ebda0ecc5f17f30bbf7d6",
"30084963244dd8b1ae7aa35fc698bd813fea8067",
"2f2d8797e98f240e009b6ea9ac2dc642d7cfb436",
"8fa41bcb155b247b4b98029d26a233bfd915fa5d",
"cfa98ee1bef7ba2b4fe531de38ba46a7266f9d3f",
"44fc9a4f3981b742e22769e56b317ae76528f8c3",
"444b9e41e4e09b539bdba1f2e2a6b935d72c744b",
"669a94001b8666c725674dfe634c706dcc4088e3",
"d3b0c191dc5e53bd1c25519c66fc3e33dd05d781",
"1ad560bd6f9e702cc48035a699418f68e852a7dd",
"acb09fb1bb00c3f527c14cc58aeb61ebeff86e24",
"ef046f2e39af156ae4e227c4761918531e811998",
"524b1abe9e01621788528b89dfc77f98114406d8",
"9b7725d7f5070abbd63aecbc43ad3c95ca451cb2",
"1d8876c9d1068f2df16d514009191c487a1f5652",
"8ba0908a9e885a955ea3159f3f62373f60f6e8ad",
"731244cbab264eec570d725903a7af17726eac02",
"c584df0ee552d355e6b0f000f6219084c304b475",
"2bf817a2113a376f73c7639ebc562357cee4750c",
"93a87c51f4d5dc4742e840a3fe38a510d66f7f11",
"3ddc3885f58963d2bb8bfcf1ddbb3febf89fa5b1",
"0687d129962bd2d906b6d9326c8d0318d2bc98a1",
"784d0ff036b5d812003ade8756e825c8382e2006",
"eca6008ac22644bfa7784922026516ca2d6a52bd",
"1686924230e7bb08c458c9c640588c2bb2d97346",
"834dcad3ef464483b316470bead5adbdb6ef1d61",
"199e9dc4f1ca75724b52a531959b30d121a5647d",
"c36ffa60a42b70d66ad7b7e4c412a5e3512e893f",
"d42f2beb114523527a111e2088cf2a76418b4cea",
"e08fb8cba473cfbe29a67db6aadfc08155b6fac9",
"3605f62e6b0411134d4085b7857fff4041ea26bf",
"26c0c877692664b43461c9962b7591d33efad1a0",
"d6d744471b895081826d81118f1e5cefad2f05bf",
"2d7f5fef141b66e9a9714c120136891b011410f5",
"d68077b9380db564151c842be4c8b53f5de1e1d5",
"3ba847423badafd9e7a648cd9d9902018d1627d3",
"9d562785f7232ae7c7b3498a6e0095820190c968",
"e0be45044b759131d7f226bdfc3d17ba7214dbc9",
"54e14e1b5d6ee0bd6d4d348a7e32b0975624db2d",
];
const toDelete = await db.scores.find({
game: "iidx",
chartID: { $in: beginners },
});
await DeleteMultipleScores(toDelete);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,30 +0,0 @@
import type { Migration } from "#utils/types";
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
import db from "#services/mongo/db";
const migration: Migration = {
id: "remove-jubeat-night",
up: async () => {
// copied from seeds before they were removed
// the charts themselves are not guaranteed to be in the db
// so they have to be hardcoded.
const nightSongIDs = [
724, 723, 1026, 1028, 1043, 1030, 1014, 1032, 1013, 1046, 1029, 1031, 1006, 1047, 1018,
1037, 1008, 1021, 1023, 1035, 1005, 1034, 1036, 1038, 1040, 1025, 1017, 1015, 1011,
1049, 1027, 1045, 1019, 1016, 1020, 1042, 1009, 1007, 1012, 1010, 1044, 1022, 1004,
1024, 1039, 1041, 1033, 1048,
];
const toDelete = await db.scores.find({
game: "jubeat",
songID: { $in: nightSongIDs },
});
await DeleteMultipleScores(toDelete);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,28 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "remove-multifolder-stats",
up: async () => {
await db["game-settings"].update(
{
"preferences.stats.mode": "folder",
"preferences.stats.folderID": { $type: "array" },
},
{
$pull: {
"preferences.stats": {
folderID: { $type: "array" },
},
},
},
{ multi: true },
);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,571 +0,0 @@
/* eslint-disable no-await-in-loop */
// ^ pulled from my local raja install. some of these are in the db.
import type { Migration } from "#utils/types";
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
import db from "#services/mongo/db";
const HAS_RANDOM_MD5 = [
"b94b380dbf8ebb9e132f9314dfc90501",
"68cdc6f205aaf61a6f7d534df9a1ca89",
"55b9007054f81b9c90afe4ceb487d1a2",
"a808756a7ccadc5fa8bd2811acdc5757",
"28fd26d7e8b10ebee2c9fe66f6c2f3e1",
"3acc17d29dfa493069a4f190f28201de",
"c0633baaf71683c2e958abc05cc227c4",
"5001337d252b64edadbc5654e2653a41",
"e7cde366b201c84d3e802718fc6642b2",
"2d693e4081da35517527806454a563c3",
"2b91d8ce1cdbc9a44f71c922f5b4207d",
"f7f6b9e9390bb2744290b4673e111022",
"472590a92b09bb1250ee5b7b6f209373",
"0dc76d5e5fddc6768b1a2090a4afe35f",
"5106e14f3f7dec7bfa4177cc6a3fe815",
"3c9b0770aeedce6f973a4776a9ba7fd4",
"ee665f640d74d3447718baadf490d24f",
"13aeb61f696554359f5defd70379d677",
"5a9c567289fdacda73b4b28d125e5c13",
"e81ac8710f19eef8ee3f055427987e5a",
"dee90bfd129388419e72f0df85b53bbc",
"97c0cf8cd7c184d76593e12083bad836",
"f168af78895b093cc0fdffeb771cebac",
"9dfd19c33b08c9f710ca6a43bc0670f2",
"6c86021350b7b105995502dd24cc9bf3",
"7450fecbdecb5b657ecf0885d64c731f",
"7977867add6f1888200ce81f75ee255f",
"28bd2e6da722861c9d209e4c28ac152b",
"beec0ec093eaf6c9d3f0c55ff573d526",
"178b00a163dda5688ed4be49a88465ca",
"2c23d407ecdffd405d5c9c95cad7b91e",
"09b7fb5be08b8d07594e523a29405867",
"03b8718bf088f6951cf4e2e631738a22",
"5c160092005c193ab138ff0fe46b9ab9",
"8c0d6af3f1f4e2e73f1ef97d4fc2cd40",
"90bfa7091b38bc322af3ec08cc455e43",
"b3ec50bc5d0c8bf9938aeb39a215139f",
"6c771af5d84f3623b1271b31b058959d",
"c80816ef8f631d72a2dfbb4634079934",
"c00c50a59ace750cd8cc943897b39959",
"a8bf592f5ebd7869933ec1b5dd07333e",
"088a4dbae11c802dcbd0b313eeb69068",
"726cf167d76d14c40ef22c6684614107",
"4b445b51fe38e7a3ec5421cb50cdae1d",
"e420e30e57ba5d1b6e688864001684af",
"570c7e899e1cade06de5ebcbf52d9c02",
"9506d011abed48e851b79a7a5691d143",
"11c506b28a8492bc8d8f39bd423115e0",
"937e6e8c5c22398dca8bbccf6f519ea6",
"ef50e27377664ee1e2a29ae3a3c91b53",
"aba3b40c23f6d0a8c19aed91a1022d27",
"02d3ce07e8862a502a8e9a9353039713",
"46781572ce1711fb04c6f065bacf02a9",
"3482fe1a9ac768c9f43995507afaf2d7",
"72f120200fde9fc056bc65599a7a4906",
"f898064566edbbed05e2208c10203a9c",
"72e67be42857a55beb4d5490d43365f1",
"f60e07a810f5e8fcff965b20226e064d",
"2ab44688b54eba07d575e8df7155a206",
"b7209e898b880b413674236422891f41",
"057180cd0d9edc8d6f3ba80be414779c",
"bb279f79c74b788372ac513e8c97cb5e",
"830d10ad7eff9f489ca1a3f1226cc19b",
"bc3dc318370357a91a8d89eb3bc73def",
"011b37018971ee57a0956e50f3e67cf2",
"73f88511307ab1c632c4bb01449a501f",
"f80bc6c1205cd658d6e3a9262a058793",
"5669e90e9a3909ff03208f509a229f47",
"06c8de3fa651c6218e3540002567d9a4",
"45a8ce75415aa4f204ead4afa041ac75",
"9303858450c0668322c5015576e6aac7",
"2c9d5684208571436b3cde863c546602",
"cdf6cdc493c3cac3d5b9efb3457324bf",
"77e8cc866aac02ea056b2c232513f0cb",
"8f009e68251be9ad2603b19f8dec849d",
"8050d463f7efba554901fa3bc578f10a",
"7f8aef4f43a54065fd6c8ac0d1a361d9",
"4e269cd830d68e3e1497691be41fbb62",
"18dd57edd1dc9b969de7cec2ced0c41c",
"db2ce3c79016eb7a05104a26df2eb02c",
"bbee066aa3666bf0e812015515d40500",
"0624e226b1341db8457aaa3f608370c5",
"620c162aec244bc3a42e6daf3786cc18",
"138dc09661f886a51b210af6a551487e",
"df79c7967a9840964cabf2cabb2765f3",
"1fe19d30f345ca2ca95c15be90a7a8a0",
"73354263fa3beefec2635627b92551ab",
"e9a266efc4ec71fddbc4639ac626f23a",
"5dc8a641979b741fd17939c8121626f3",
"994fabe27b2d38152d5a1bb6d5e8de39",
"b25de77b496ecef8a48ab0b334d01ad2",
"619c8357df2399ae18e22c4e36b2be95",
"3bbec1005ef59916b0c0e6ca931c1756",
"049327db798151b4e25a6f8ef8a3fc3d",
"04c57f5a3826c3d3b4d704df486e21a1",
"188ac44b91c8f97436dd74b2d6825f2e",
"ff2d2ffa4ae22da44b8cc3f20597a899",
"778aeea01fdea30a4287e7b2eb19ee11",
"2ea031596696506e9d6d58354399e453",
"507bc8bb28eaa904942a630b09155b32",
"ee6dc9c8d2073f8b37d9f71b1c6a1d0e",
"6e98007ab76c890a7dc7a7b9a7f0ee21",
"9f6c6c2b7ea20a8189bfc90b25c2c273",
"f1b7eb1f64d98474f1fcbd0ba91fe3a3",
"c81c0b1698dbfbefe90ce6d2b502b63d",
"a5e08dc3211f3a5f5daaf56025050097",
"7ea4e79c5c8a71394b00d5ba845cc217",
"523862f70a3cec67ebe0a3267252140f",
"db3a25e763c3b4d970ec29e195fae473",
"4cb86b641a7ffdd5bcd0c3bd1ce00244",
"dace2eb74865e7280765ecd7e2398cfc",
"45f0574c38325760ed49b5020c6e8ece",
"657b5ff7cf6aabbaca438308e57db88e",
"ac82aca2b52ce00bdcb1b43091f8b77c",
"8adb5d61edb6fcb349cbb37b37c00de5",
"eeac12de1dfa182ceb518f431cb268aa",
"52bccda6a2a4c1235410dec3d467208f",
"a0d0614e9e0f4cdec2175ce89b094fca",
"5b0ba8f75ab1a58937a2c9eb7a68a7b6",
"fd7c600950424c69f075b84f7e821413",
"87323a23d5aee737f6bf8765570160c5",
"95543acc75936a31bfea37b88e17a86b",
"bcb0386396ed1eb128658dde13057229",
"3d5397f8c68912e477492003187a9072",
"67a169717caa40eec50ab1355e501965",
"6d53512547d425c0dbe574b5f65e59b7",
"83a96c2e76540a42859c795dbdee6768",
"87667ee3acde34b662b003938545c434",
"5945cddb62ebfe1119fd4686fd3742be",
"106827d8ef85f9d7bcd7b757e7347d80",
"ed5b2787a89d4a43c9f6337fcc110ad9",
"0a5a008e261a79a2a66126b67a980c87",
"17a2dd033d1161456b62f2c0b11e12c5",
"6f1d541e9e92ba6dd06a9836d922a039",
"2ba24954dd2e06b318cb043a287a89ca",
"307293dc6a70835717084ea2caa2e4fd",
"5259db5547f5b153fd82ed7c2da187ce",
"a5d55133fd47bf414da89de1f28f8415",
"08922898c9a4b7e6240e27202e6a325e",
"576045481be4bb3400d45d9dc3eed006",
"74c83032660dd26a4389095d7c480e4a",
"e6f384ed7a0889e9df5aee06c2a46b9f",
"83d685a5239f28db9eab94d846b44016",
"7de28e26e5579f11cf1f805907f234fd",
"76a43be54dad1bd972ed1c6a357fed86",
"74e311b2a2229fdbd1b1268766948d52",
"22b7b4da78dbf2eac4575deda4534fe4",
"c09e67e807d3be0d14695c3060ad7956",
"aaa6a9021bffee4f930ae91f32774113",
"bb9072b6b0460a5feb96a6a5dad45875",
"ab42a6ca7b0352d960ac1c1a6a9041d9",
"938660483afa26f132df20da002a549e",
"942a2ef62f2cb219046b2007775c46bc",
"75cde6cceabe2b0bb5f4b1731b97dd08",
"3b073741055654a2bcf0fb925489c869",
"458a168843bb988e840b8bd5312aa6ed",
"229e6053b6cc2a97bc2f37ed58b035c2",
"37ab461684ef428de94387be689b25a1",
"82fbb000f431997c84cd25dd230471c9",
"29233a1ae18aeb55104573533a1d8788",
"fda9c90f716be783b598609b93642596",
"34ebe51b1053a5043061c73e1164ebe9",
"a0c354b7dc48e1b4bdbde40721a6a9d6",
"c725ff7f7b7a99ad01339cf6d05f6af5",
"968d17b52500bc20415d9669f11e69f5",
"7ae3d2d7314e5e404e543ef4d9a4add2",
"3e1c9c40ac31e2f2f509182e596e232b",
"02f573770990a9af045a07c81ffbb59e",
"a205c9b3199f0a61c9981eccb4dc34b6",
"1c68375cac73c18be626a1b9f59e5eeb",
"41d5fd0d8a43eef42665703508b47e99",
"b40d9e637e3ce07a3b4054ae7b453e8a",
"725276e227259ff5613b7cba51903662",
"117d4598613c124b363561118808a13d",
"623c71599979807ce585c0b520fb79ca",
"715fa80618eeb3c0e2aa8219ef5a5207",
"f9060a24e376900064eb89cc124e0d94",
"1823e859b25257450139c44c670cf6a9",
"96ce2a1b3db373a89f22f53c97291a98",
"52201e88ad8175c93632d21eb11ccaca",
"bb71bcd0811e1c4ae68649d8157cc84b",
"09a397d277e23a5b4756f6422d357a45",
"572a212cf25e7ec13d95d36aa2350a24",
"61e43cd4eee6b231b063acb89048e27e",
"520a4d6fec68a28dbe13accb42a1a8a4",
"6c611a667e7a5f0cc757448e17a2fdc7",
"5e8182cfe19b53c915aa89afc9e069c0",
"7926ffe1f6915ea0b2e3e56de7ff52bf",
"686765b33d03faa0b19c19d8e147b391",
"0ffa538bbed25d43c3b8d11e66287160",
"799e7598d530456cb394c663dd88f590",
"ff654863671f01edc77a8b3058fe8758",
"04d1578d5550091630d2835870e39d9d",
"1820f45f501a633a01b4e991732f50be",
"ac035ea64b6502a5a9b36b37ce0b1b33",
"f735c0a5ff59d6faa177a4bb418e12f3",
"864406f20586a91bc6a28db7218a01ed",
"66743c12c927a8c1dd80e9d6b4c74e6a",
"6d20b030dd391e2d3d6a39860e16307c",
"bb01d921e6816e0cf8be789908d4c44f",
"9699fa026cb94b3c120114dd3311d70a",
"3ddbbae842e07855879b04b7ced50f06",
"73852585ef0a02b5ae2221977f134d12",
"dff93f5258e9ff8e34ca245df485bb40",
"b9ea3d3671badf0620e49416e5aa21be",
"9cea969b996bc6291e3b1781a61a7373",
"00351165ad4deb19dd03e059138a4a8d",
"726cf7fc681e4b95d48c39ce2d8740cc",
"ddafb40446a20a17b804ddf74dc127a2",
"886b376a46a7f9c397b62e539ed294bc",
"a540c995e5a4cc114ee93db84ecd0d8d",
"2a1b6a32b52bc46641ac25ea4b4a6828",
"cf0819a3ef3d59dc77e66c7e8eef4e47",
"be956ee713848ae7f11c8cdd044bdd84",
"a7a92f35284ecec49a0369fc2ba89daa",
"4010caafe71464630760391f1652046b",
"2d2d3268e83a8caccada4c0cd60a66ad",
"8a54a27a27bba973b1dbee516de4a9f2",
"3f132456a2ec89cc2e8b3a49fca1e74a",
"19342db445a0ba93a98d1c4c351f3e0c",
"0d8e9aab1b701c0420af4781f391576a",
"bb6db2432a7d440fb326e3c7ba0aa023",
"be4a9651fc5992be75c593a50f17e4fa",
"15ff6e8c810293548926104e65402c75",
"4d926fc787feaaf5ca79a5a8bb7ba76c",
"2570d6d58449f7299fc8c4660a6520b3",
"06b349050c015c66a24fdfde68c2a7b1",
"c47a24a1d5d06995679337ab8d658ba4",
"82470c719b1c5c67720775556f135fd9",
"c172fe29c37bc226ebbead5b55cd7ef9",
"73cf8af05f23baf56f43b85e74c28e4b",
"32e4fd1200f6650068c2143931afbba9",
"64134628009c0335f1b42222dc6ebf28",
"51aa4bb15c69386f0e01dd913487b1b6",
"23631ff772d634847c10bcf86f103b99",
"af6c77b4f516310f109af58adccb6c41",
"ccc86f0e28a4f5539f7cb005c9d43d06",
"0a360fe1d0420809fe967af8d51d81f2",
"6c2adb8118b1c073d0b70fd730accc9d",
"c51449041d7c70ee43145f6183b9c777",
"f5c8ffdfcb8fa5a509807d9f6197754e",
"7320658d347306fbbb8063b2ae6470d3",
"32ead63e9a5eb5b5e78bb91e24145603",
"8520d99bcd3ebc73b56890ce1581c25d",
"2615ff7950813b9af883b5a9616c444c",
"08684508f408e3bb8ab7246a773a8802",
"b553fef79ac2299a887c8eab80edbbc6",
"cdcf3d6785d6df87cc11f8e3186789fc",
"064f3800e0fa782fbe23bb13b88a3fad",
"7d9369c46503bdb00fa2d879675bb2a5",
"5e3046fc8a3deaf9e805a2cdba7b9286",
"d5f9e96ba7e019acde3c9863d25b0e37",
"21d5a1fa9996251bf7eb9e589cac6de1",
"06cc5247f7d1d95e1cca84d4ffb7d26b",
"520e23729ba9420da3d177ccdb7fb1f3",
"fbfd4a93aa3e91e758d6a767bfc1e782",
"3bd4480e0aaabd1bd0edc5cf6eb124e3",
"36d0cbc6f0427a3eca843b4efef7fcc1",
"770f14f022857cac0069aa2277c7937a",
"016f9de5b8338c522417313caba5746e",
"5b0ed334acd5ed0dfbc74e0ecfd46df6",
"c5ca232ab116e1c90b9f28665a2a97b7",
"d21691ebf9f690a360ccd269819464f2",
"d87f1b2a7436a13b4203c8269a5ce82a",
"be5d99e035c49b1ad1537e1a3363702b",
"47f172131f9afbf9ccc3ae58c2ab1766",
"29ac7babc977e0aff5ec52ab38b888f3",
"031d5e6b1ba8b705de216ce2ee738a03",
"a4fab81cbcb9a5c382afc99f9efa654b",
"28846fa5c003b5228dea15d9588b26ef",
"a1a8fc59d659db3421fc992e6ed43ebb",
"70cbd8da12ab2d623ea64a7ff59d6017",
"38c455db117cd47abe7ec84adcb82703",
"384e6e5390d91bf026ad92de3e736b67",
"20c07f23c94241c0544bedab4a27616c",
"f7768c8027f456fd023e0047bab85e50",
"6d5000b7960b444e735bcde5bbadcf67",
"27819ccc024698ce25f09c0d8fac7fba",
"7283f3d39e9f81f882cdd3a88a7c1975",
"5bf6405dd62de5d1a62ce5a869b0ff7a",
"aa271d610c9fbfda90456e83014a1219",
"98e5a0cfe3865a392c456a39d04a0d1e",
"e655c37f44efcbc6d9911a2d21de0057",
"e99804eaaee76e3ff71e5d0e1206cdc6",
"f81c6a8cb8bd3274f00f91f4d2311978",
"41355bba3a9a90dde0ca91883c94d7de",
"7746715309f78a5da6d1b6f98e11fbaf",
"4c57ace7a707683c873b652d4177c12a",
"4f7c6afaa207e58111e35be7511353ec",
"5af1f5e15d88dd0f947510dc0a27713b",
"8fa0805bcff15df07c6a485278bb1e67",
"bb71ab72d9008b92825b043932927990",
"5c9afbc6178718517a407d7d6349b6a9",
"ca16b49b82ecb338ba2e49be83f3e768",
"b4530667334ccdec608611afa283d263",
"f30aab1f10f73d3fd66dc590eabe072e",
"9207526277f1bc81817769eff74dc6b9",
"6d255bcaaf17dbd177d45215f584f276",
"cc879c77bfa5d15fd31be930e87cbf1e",
"cf8288d8f28b79a93819fe227a76ddb2",
"ff5303531b1ffe2f921c61a4aa03323f",
"4f73f087afc31294ed956c2600b37b1c",
"668215e653a481fa0086e45bfa933d3d",
"4c78e318b619e825b88ffe160eebeb5e",
"7690972aad49eeb701d2d18a745a98b6",
"8c7940010a72f0a48a037fe8391cd2fb",
"2ea3b62d1813e049596a9b0fda6b3e4e",
"7785ee8a88113c583146aa8dc23f946e",
"0f4c8e00fc2b26a8a29f4db770183dfa",
"1093a140b83ac76ad325c203a531f65e",
"c582e68cb6ed95348e8f6fcf074ff2fc",
"c6032361913d4785ed481f48b33f731a",
"217faeaad9304408b161e701285a3b8e",
"f45d0372f347002b98720d9453ddf1a2",
"b4d4c66327a1cdab0c07cd0093a974a8",
"954d8356dced1f1a3720da4f0046a398",
"911df550a51c8a181ef32b9753555dfb",
"bda7df95e666eafe33a8f2abe2fea634",
"0e0c4d35a061ad013a41b6e7d1c98b98",
"bf5b76dae267f0395505cfec63b50646",
"76bf70a68ab95a425ff8dcac46131132",
"c7e70ebf1632e663996b35118c160c8e",
"318c1b8c660d6af6d17fd6273b245af9",
"a0dfd7d70a53e4752d39e09b23877cff",
"e6f289a63cfe387475d8163aedcc81ad",
"1e5e699b986f4c23d09eff0e217085b8",
"833a5c48288f4952e9c61e6b0b930fa7",
"8ed1cf85741113aaf857e2965d4e4272",
"6dd9ab107eb20bbaeea02fa3ec0f33d2",
"a7b7eaf135d71e3022ad76ec107b48ff",
"073af39e97efb74768bb3183da953566",
"c3f863d0bdbafc92c0ca3faaed93ced4",
"08af1b56c7fc6425374e9872ff785b09",
"c0d0a55c63e6e33eec91128dc058ba23",
"91cbd62d57c8933f3c411118a81ab04d",
"a8cbfae060e9ce8f94a5c5cbcbee8df9",
"8f5b21d65d86a840a009eead6b8884cc",
"b1afdd65bac87ffd1c04793a0ab972aa",
"e40c4f069de5d95683ccdb5c0628b2ef",
"6491aef47c7959bce2e0a884abeb3bb2",
"f5e004e2bc642ca31f3abe2710c8a881",
"e1579a648e8e94d490c78527672dbffa",
"9635c20a64553e6333a134f256e0d8f6",
"709925f349a00398e02fdd0bb1ca3a2a",
"f79d18e08ad731408737f94fd5ec311e",
"fa49527db20263c08efbda57d32fb60f",
"abeb2e43db5870bfc7f67c95a5d0715e",
"1c7921492252eda86523556ee1b31cf0",
"86c17d99c0ba64ecc8dae0d1c374801b",
"413ee29a922e595bdc831d55d7bf869c",
"c1346e4458a976189e1f766edc58e3c6",
"77e3c9a12c3f439d8a688cd7104c6945",
"1edba00a2065fea20698be50c53aa469",
"108b628730e6b6d95a934dc753ba7800",
"765df7084717a95d8b661d94470d9874",
"b6f9d18e20f9fbfcc0f58a8249701c39",
"6c37a50657fc642ceba428745aa05524",
"645d96cd1ab254cb2960882c91e59818",
"51a91086bbf25cb202e7d90a90a96a23",
"a7cc74f1090aaf4c7f8ba15d47b94607",
"22db653c956f93fe86edc58e3d891d33",
"082f11ad48e6915edbcf73d1ce5f4d14",
"0052b5b88344ebe4964cd74f3468e33e",
"3d279760a58611179383a54938fee4c1",
"e7703438b9827715d48364f56b2394ae",
"3ce79f5b99aca6020af533fe9e3591c4",
"97e28d0cf7e7b90e5c8e28a824e39b20",
"03e342c00179c3afb0fe2a1959a40f2e",
"677c9d5e8606dbabed966fd9998d36a5",
"03122db996d2dd586190ecede173e042",
"31d0ab82d1381d6c404a4b4c2d312df5",
"210fa9270878d6779e3197642122c897",
"55af1f915c8305b6009ead4a3a5ca206",
"ea8d7e974991f0fd7ce49f1a6958b908",
"cb4acc5d6ba761d28d0eaf5998ccfbc3",
"2cd1ba926f08e0b8921ee731d4ad45d2",
"59cecd5ff719d4dbff1d5db7896073f8",
"23fcfe052943f99ae0be66429e2fed13",
"8ab828468c65071c469377994d333fd0",
"bb60899434542599246a5506f91acc70",
"9511650eaa7a03e0395bda03f81c8e46",
"25402db80324219bf47190e565826947",
"3fa08036424eb4abd45dff9e5a44d053",
"d227f25d2fec870921f490264cf10d4d",
"b673e3f2b80a49f5d1a302688ad6c507",
"a1089fb46ad31d4f2e5bdcb89c4ca450",
"a4cab8697eb3d01da02e8f919a6aa623",
"b31f456ef56d61c4caf94e7f08caddc0",
"9bcc87ca47c213d47c910eb42784a36b",
"b10d304e187eb51636281dcca6998bb4",
"a5a9774d73b86d4417da071e86d3e3c0",
"6ce588f19710e366eebadae627eee1fc",
"578dc4c94adf0c34b45663eb4a85a838",
"bf84f07538cc95f066691f26be070348",
"caf153b8021978ae8f9df99c2af233d9",
"89dfa8569d80c9740b4688c0c4817333",
"3a90490dd7f7ef066bf28b74f81e8c1f",
"ed7212665806b5001731bf221739ed36",
"652e1e096bad409f4284acb9e9eabd19",
"25356f8d57abcdf0b06b8e5602e0ccf1",
"03cc9703cd331fb9e79976c73ba65913",
"1824aa65b7e6c8b7723e635ad6639101",
"2c0120f93b519fe5182b422962212e15",
"8615b9ae85fdbf7e7a6738e8e3e1bd45",
"b01d663a2a02c660981056d7f16df586",
"906f62aa308315508487282538d96db1",
"d749670a542f3d85d257c694e997e316",
"df81bc8022bd51e2e17dd9738b452041",
"a8caeb99af594f40f7fff48b32066f89",
"8f263eb262bd4615303303cebf86782f",
"4d1b54f56bde5b2279a44fdfe096413f",
"21da00d34b00b6a3abb2dd0c680d1c37",
"0c10a05600ea952624c929253bf4ce8a",
"6df46e4c7f11a01eb2a362c02f17ed22",
"7bf5124eddcd6122b2709f56f045b7cf",
"9ff5bdd19778f4fa67984b5b7492e036",
"a60ba550973bfcd1415b8ed5b12de272",
"828df338ec04cb2f304eea9bf640999c",
"10b1f6d46172ca42292dd53533670daa",
"97aec799b90fd5bea919ad7eb8ae1e5e",
"010c173afbaa3eb6d78005836f687a10",
"2c1518c284eef37d9710dca5236d545e",
"ab6b23baf829811a17ac367aae043b19",
"e8c0d51d4086da5ab5c8f61e69397615",
"cff001e1b62d1b725ce16bf5a1065d14",
"34477cc17921837cb65f5bff8d53f55c",
"82f760de665244c7dc41f988660c0a77",
"778abd1a5c5422e7b8d52506fad0c4c3",
"6d86d5c5e9545eade5e5c40a0e7d9959",
"1b823369559fe3222934b016b661ae12",
"3dd185ee23db47cb9357f808a3d5892a",
"a1dc473d8b85302571f25b6a93269d26",
"f551b1b02371ea764b1a7816a16b9cde",
"b7d73f3a3240338cb956b7f75bd11a83",
"20f53f1fa841a187328da52185aeafe3",
"ad0af27bbe31f4a95f63d38ce3097a5c",
"743b0415fb37ed722ebc8fdcd6566b1c",
"99049f4e27c1075f5c2a8bee2a6078da",
"204d1e686918408ff168d13ffc06a572",
"6f6a7ac25ae4614ff7842849a2ee9d94",
"85ca525f01d26e432f9595ba148438f6",
"6d1522b0beb17d68ad4230b0158c4652",
"f7a42d6350eba9acc454dd6c08068c54",
"0da0c5262f4dca9033a7a7cd565b9d10",
"dbbcacbb8629807a8a022ae0c2051041",
"b40fa988b67df4f490895064fec7dac7",
"c87da7fa9431c9bf6173dcfc86bae31b",
"d5a32f44c958cefcdf3348be4de3ac6a",
"a3d87f9c67d70809291a10136fdd304e",
"b9e82e3fbdc7bdb4130cc00735503fb2",
"ee1b7fefc7a213cd3bc73b84e32f38b2",
"c4c038a06d11fd8101a1776764d27518",
"db080178c87a02266d23f07cd72d30b2",
"d0b25179c2eca995999f1eec6c266a4f",
"2b26cd9c0535473865d2242f9399ce8b",
"6dc87b9326fd2ff9e6a9e6e0bb330fb3",
"c4496fd117d8ce36f7052f03ccad1670",
"bba80ab1558408c357ad47aaaaeafe38",
"7481c11cb646d723c3b9ee6f0fae9330",
"bfc3ce3b1248483331c53d172bd2fbd0",
"9e4546091b7bf30d44473ada7a17634b",
"6503febe7505e7a128e4496c38ecdf1a",
"d230bd356b8efd6a1a0634b85676a07f",
"6e4beda3e0d14554270df61ebc78231d",
"7440252c5b23d6ec6e354a7cfc02afe5",
"7c55fa1d99d2cf5df085dfc292bb580d",
"d6f3b316e695d8fb421cb7dd1e236991",
"0a2cac0dcb4a896ee57f3abbddb19b8b",
"240526792717478c5ce8e89adc8f66f9",
"5d5266e37a48f0066c266418621c6e2b",
"8e23b3b768cf3df7b0a753607386ce92",
"20465d43926878705d88c9d31d5f5128",
"448c9f9ca3f7f13842d83a303a83a6e4",
"803aa630470ff5fe98c216f6dd307ec1",
"d4bb28b4788a28000c24bd20fd224168",
"80c30fd234cefa21fd37528b1f28fcca",
"9f36eb1cd54566e72ddecb7177c9662a",
"f25028ffcfd67f948323c209862fd81a",
"ac643a14932b315b1c55df2a79a6b9ed",
"2f4dadff83681e3487ee4120c502c8ee",
"8e68432df47e80760d47eae1bec44271",
"52997076c191e305515bf647e8a7f39c",
"6fabd22dbb92abf8f19ef7925b49018e",
"d736f396d6b8009ab3da1507298bd252",
"44eee6eefef6b5ab50953600e5216e58",
"007d43c295692443836fcb1c856a46df",
"55fb67ad0d29d3efd8cc4f4961c8cd9d",
"058edd56fcaf4f3e5b1f99da9901c8b5",
"67b805c0cef73db3706b1329775af537",
"834c3067f4149288a2e52a1aa992e908",
"37e9d2336b1321846741c784e2edf0b7",
"6091e0c0c2d192b489bdd3c852a3e08c",
"e1c5046950883c22740491ac7e9593b2",
"3cc29dc8878988d88875f7f48cfd06f4",
"3f0cbc3c2b5f3fc7d124ea0194594403",
"cdb6ba798a0249f78f6564c611606fd5",
"18a80a2c808396a7e5c92b1708bb26b8",
"dcd7de44b633139e72badc2bddb08b06",
"9e0da4fd7049a852eb895d42f5f6b233",
"3e76a01e9e73bea957eee0319f87da8b",
"64f3177822b9315319f1703606b6b8ae",
"a44fa7b7845808821af63c46378ee43a",
"9cbea4427d7e8caff0d28f9d7600cdec",
"c4d2162d5badfa27d969b03babf9698b",
"dc38dee4ece34ef9c253e0002e4aaa8e",
"f814e597a8c9a33f65061178cb2d7eb4",
"79d5a69c0c2fe77163f16ba16d810730",
"1c0adfdbdcda89c4c0472be4a6b36f0b",
"e9cd7a3495bce7deef0917766bd4ae9c",
"929dd6c71919f79896296cb65eda4e13",
"7fea808fa65ee1a8b692d6949e76de62",
"a697c5cc8c4dcbc81b5349a8299cfd2f",
"9382ec316f372840b9f07ab68adfd4be",
"a4923efc2333c32956995ae7a84224c9",
"0f3c5b204ec882447dc8aae513321508",
"c9d8c8b2eedc262a501f38d17ff8576b",
"e39d2cac795946660a0109410650131e",
"9a8324ed4219be3da821d7d7d51403de",
"c403cdc94083cf5038eecde2158f27b9",
"c1af93053920fe633a0071072a279669",
"4474fde06c283fbc09c86fd7c634b15f",
"7d008c39d752b615d703068aced8118f",
"7c305b8a003d4578b58cd26236f29d10",
"8729dd951263fa4636e54ff191b864c4",
"f8ccac3dd7cb79ae7129a4c8b0e26958",
"aceb234bd3ef312f8480f299690c8d85",
"2699862b096e1aa6fe6da4ebb4849753",
"13203d1e1076b42d3e4bb429870c5ada",
"63b8a6743660cb6ba3c8513435a20312",
"4cea997415cc8f2be6b61667b01913f3",
"64c61e76a8173efa4cf0b9b5554813ed",
"78182197c48a3db307ecbc4d9df83954",
"aee0711c7175fd5a41bfbb6b55ad3ab1",
];
const migration: Migration = {
id: "remove-random",
up: async () => {
for (const md5 of HAS_RANDOM_MD5) {
const chart = await db.charts.bms.findOne({ "data.hashMD5": md5 });
if (!chart) {
continue;
}
const scores = await db.scores.find({
chartID: chart.chartID,
});
await DeleteMultipleScores(scores);
await db.charts.bms.remove({
chartID: chart.chartID,
});
}
},
down: () => {
throw new Error(`Cannot undo.`);
},
};
export default migration;
@@ -1,27 +0,0 @@
import type { Migration } from "#utils/types";
import db, { monkDB } from "#services/mongo/db";
const migration: Migration = {
id: "remove-session-views",
up: async () => {
await db.sessions.update(
{},
{
$unset: {
views: 1,
},
},
{ multi: true },
);
// remove session view cache (which no longer exists, so we have to
// access it raw with monk).
await monkDB.get("session-view-cache").drop();
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,51 +0,0 @@
import type { Migration } from "#utils/types";
/* eslint-disable no-await-in-loop */
import db from "#services/mongo/db";
const migration: Migration = {
id: "rm-hot",
up: async () => {
const toRemove = [
["gitadora", "skill"],
["wacca", "rate"],
["maimai", "rate"],
["maimaidx", "rate"],
] as const;
for (const [game, met] of toRemove) {
await db["game-settings"].update(
{
game,
"preferences.preferredProfileAlg": met,
},
{
$set: {
"preferences.preferredProfileAlg": null,
},
},
{ multi: true },
);
await db["game-stats"].update(
{ game },
{
$unset: { [`ratings.${met}`]: 1 },
},
{ multi: true },
);
await db["game-stats-snapshots"].update(
{ game },
{
$unset: { [`ratings.${met}`]: 1, [`rankings.${met}`]: 1 },
},
{ multi: true },
);
}
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;
@@ -1,40 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "sdvx-maxxive-lamp-rearrange",
up: async () => {
await db.goals.update(
{
game: "sdvx",
"criteria.key": "lamp",
"criteria.value": 4,
name: /PERFECT ULTIMATE CHAIN/u,
},
{
$set: {
"criteria.value": 5,
},
},
);
await db.goals.update(
{
game: "sdvx",
"criteria.key": "lamp",
"criteria.value": 3,
},
{
$set: {
"criteria.value": 4,
},
},
);
},
down: () => {
throw new Error("not doing this");
},
};
export default migration;
@@ -1,59 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
const migration: Migration = {
id: "sdvx-maxxive-lamp-rearrange",
up: async () => {
await db.scores.update(
{
game: "sdvx",
"scoreData.lamp": "ULTIMATE CHAIN",
},
{
$set: {
"scoreData.enumIndexes.lamp": 4,
},
},
);
await db.scores.update(
{
game: "sdvx",
"scoreData.lamp": "PERFECT ULTIMATE CHAIN",
},
{
$set: {
"scoreData.enumIndexes.lamp": 5,
},
},
);
},
down: async () => {
await db.scores.update(
{
game: "sdvx",
"scoreData.lamp": "ULTIMATE CHAIN",
},
{
$set: {
"scoreData.enumIndexes.lamp": 3,
},
},
);
await db.scores.update(
{
game: "sdvx",
"scoreData.lamp": "PERFECT ULTIMATE CHAIN",
},
{
$set: {
"scoreData.enumIndexes.lamp": 4,
},
},
);
},
};
export default migration;
@@ -1,47 +0,0 @@
import type { Migration } from "#utils/types";
import db from "#services/mongo/db";
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
import type { SessionDocument } from "../../../../../common/src";
const migration: Migration = {
id: "sessions-to-scoreIDs",
up: async () => {
await EfficientDBIterate(
db.sessions,
(s: any) => {
// this is completely unsafe but i don't care
s.scoreIDs = s.scoreInfo.map((e: any) => e.scoreID);
return s as SessionDocument;
},
async (updates: Array<SessionDocument>) => {
await db.sessions.bulkWrite(
updates.map((e) => ({
updateOne: {
filter: {
sessionID: e.sessionID,
},
update: {
$set: {
scoreIDs: e.scoreIDs,
},
$unset: {
scoreInfo: 1,
},
},
},
})),
);
},
{ scoreIDs: { $exists: false } },
);
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
export default migration;

Some files were not shown because too many files have changed in this diff Show More