mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-28 06:08:13 -05:00
Migrate Prettier/Eslint/Stylelint setup to Biome (#1772)
* Initial * CSS lint * Test CI * Add 1v1, 2v2, and 3v3 Tags (#1771) * Initial * CSS lint * Test CI * Rename step --------- Co-authored-by: xi <104683822+ximk@users.noreply.github.com>
This commit is contained in:
@@ -1,139 +1,139 @@
|
||||
/* eslint-disable no-console */
|
||||
import "dotenv/config";
|
||||
|
||||
import invariant from "~/utils/invariant";
|
||||
import { sql } from "~/db/sql";
|
||||
import type { XRankPlacement } from "~/db/types";
|
||||
import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists";
|
||||
import { xRankSchema } from "./schemas";
|
||||
import { syncXPBadges } from "~/features/badges/queries/syncXPBadges.server";
|
||||
import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { xRankSchema } from "./schemas";
|
||||
|
||||
const rawJsonNumber = process.argv[2]?.trim();
|
||||
invariant(rawJsonNumber, "jsonNumber is required (argument 1)");
|
||||
const jsonNumber = Number(rawJsonNumber);
|
||||
invariant(
|
||||
Number.isInteger(jsonNumber),
|
||||
"jsonNumber must be an integer (argument 1)",
|
||||
Number.isInteger(jsonNumber),
|
||||
"jsonNumber must be an integer (argument 1)",
|
||||
);
|
||||
|
||||
type Placements = Array<
|
||||
Omit<XRankPlacement, "playerId" | "id"> & { playerSplId: string }
|
||||
Omit<XRankPlacement, "playerId" | "id"> & { playerSplId: string }
|
||||
>;
|
||||
|
||||
const modes = ["splatzones", "towercontrol", "rainmaker", "clamblitz"] as const;
|
||||
const modeToShort = {
|
||||
splatzones: "SZ",
|
||||
towercontrol: "TC",
|
||||
rainmaker: "RM",
|
||||
clamblitz: "CB",
|
||||
splatzones: "SZ",
|
||||
towercontrol: "TC",
|
||||
rainmaker: "RM",
|
||||
clamblitz: "CB",
|
||||
} as const;
|
||||
const regions = ["a", "p"] as const;
|
||||
|
||||
void main();
|
||||
|
||||
async function main() {
|
||||
const placements: Placements = [];
|
||||
const placements: Placements = [];
|
||||
|
||||
wipeMonthYearPlacements(resolveMonthYear(jsonNumber));
|
||||
for (const mode of modes) {
|
||||
for (const region of regions) {
|
||||
for (const includeWeapon of [false]) {
|
||||
placements.push(
|
||||
...(await processJson({
|
||||
includeWeapon,
|
||||
mode,
|
||||
region,
|
||||
number: jsonNumber,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
wipeMonthYearPlacements(resolveMonthYear(jsonNumber));
|
||||
for (const mode of modes) {
|
||||
for (const region of regions) {
|
||||
for (const includeWeapon of [false]) {
|
||||
placements.push(
|
||||
...(await processJson({
|
||||
includeWeapon,
|
||||
mode,
|
||||
region,
|
||||
number: jsonNumber,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addPlacements(placements);
|
||||
syncXPBadges();
|
||||
console.log(`done reading in ${placements.length} placements`);
|
||||
addPlacements(placements);
|
||||
syncXPBadges();
|
||||
logger.info(`done reading in ${placements.length} placements`);
|
||||
}
|
||||
|
||||
async function processJson(args: {
|
||||
mode: (typeof modes)[number];
|
||||
region: (typeof regions)[number];
|
||||
includeWeapon: boolean;
|
||||
number: number;
|
||||
mode: (typeof modes)[number];
|
||||
region: (typeof regions)[number];
|
||||
includeWeapon: boolean;
|
||||
number: number;
|
||||
}) {
|
||||
const result: Placements = [];
|
||||
const result: Placements = [];
|
||||
|
||||
const url = `https://splatoon3.ink/data/xrank/xrank.detail.${args.region}-${
|
||||
args.number
|
||||
}.${args.mode}${args.includeWeapon ? ".weapons" : ""}.json`;
|
||||
const url = `https://splatoon3.ink/data/xrank/xrank.detail.${args.region}-${
|
||||
args.number
|
||||
}.${args.mode}${args.includeWeapon ? ".weapons" : ""}.json`;
|
||||
|
||||
console.log(`reading in ${url}...`);
|
||||
logger.info(`reading in ${url}...`);
|
||||
|
||||
const json = await fetch(url).then((res) => res.json());
|
||||
const validated = xRankSchema.parse(json);
|
||||
const json = await fetch(url).then((res) => res.json());
|
||||
const validated = xRankSchema.parse(json);
|
||||
|
||||
const array =
|
||||
validated.data.node.xRankingAr ??
|
||||
validated.data.node.xRankingCl ??
|
||||
validated.data.node.xRankingLf ??
|
||||
validated.data.node.xRankingGl;
|
||||
invariant(array, "array is null");
|
||||
const array =
|
||||
validated.data.node.xRankingAr ??
|
||||
validated.data.node.xRankingCl ??
|
||||
validated.data.node.xRankingLf ??
|
||||
validated.data.node.xRankingGl;
|
||||
invariant(array, "array is null");
|
||||
|
||||
for (const { node: placement } of array.edges) {
|
||||
const weaponId = Number(atob(placement.weapon.id).replace("Weapon-", ""));
|
||||
if (!mainWeaponIds.includes(weaponId as MainWeaponId)) {
|
||||
throw new Error(`Invalid weapon ID: ${weaponId}`);
|
||||
}
|
||||
for (const { node: placement } of array.edges) {
|
||||
const weaponId = Number(atob(placement.weapon.id).replace("Weapon-", ""));
|
||||
if (!mainWeaponIds.includes(weaponId as MainWeaponId)) {
|
||||
throw new Error(`Invalid weapon ID: ${weaponId}`);
|
||||
}
|
||||
|
||||
const { month, year } = resolveMonthYear(args.number);
|
||||
const { month, year } = resolveMonthYear(args.number);
|
||||
|
||||
result.push({
|
||||
name: placement.name,
|
||||
badges: placement.nameplate.badges
|
||||
.map((badge) => (badge ? atob(badge.id).replace("Badge-", "") : "null"))
|
||||
.join(","),
|
||||
bannerSplId: Number(
|
||||
atob(placement.nameplate.background.id).replace(
|
||||
"NameplateBackground-",
|
||||
"",
|
||||
),
|
||||
),
|
||||
nameDiscriminator: placement.nameId,
|
||||
power: placement.xPower,
|
||||
rank: placement.rank,
|
||||
region: args.region === "p" ? "JPN" : "WEST",
|
||||
title: placement.byname,
|
||||
weaponSplId: weaponId as MainWeaponId,
|
||||
month,
|
||||
year,
|
||||
mode: modeToShort[args.mode],
|
||||
playerSplId: parsePlayerId(placement.id),
|
||||
});
|
||||
}
|
||||
result.push({
|
||||
name: placement.name,
|
||||
badges: placement.nameplate.badges
|
||||
.map((badge) => (badge ? atob(badge.id).replace("Badge-", "") : "null"))
|
||||
.join(","),
|
||||
bannerSplId: Number(
|
||||
atob(placement.nameplate.background.id).replace(
|
||||
"NameplateBackground-",
|
||||
"",
|
||||
),
|
||||
),
|
||||
nameDiscriminator: placement.nameId,
|
||||
power: placement.xPower,
|
||||
rank: placement.rank,
|
||||
region: args.region === "p" ? "JPN" : "WEST",
|
||||
title: placement.byname,
|
||||
weaponSplId: weaponId as MainWeaponId,
|
||||
month,
|
||||
year,
|
||||
mode: modeToShort[args.mode],
|
||||
playerSplId: parsePlayerId(placement.id),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
function parsePlayerId(encoded: string) {
|
||||
const parts = atob(encoded).split("-");
|
||||
const last = parts[parts.length - 1];
|
||||
invariant(last, "last is null");
|
||||
const parts = atob(encoded).split("-");
|
||||
const last = parts[parts.length - 1];
|
||||
invariant(last, "last is null");
|
||||
|
||||
return last;
|
||||
return last;
|
||||
}
|
||||
|
||||
function resolveMonthYear(number: number) {
|
||||
const start = new Date("2023-03-15");
|
||||
// 2 is the first X Rank month
|
||||
// 3 is the length of x rank season
|
||||
const monthsToAdd = (number - 2) * 3;
|
||||
const start = new Date("2023-03-15");
|
||||
// 2 is the first X Rank month
|
||||
// 3 is the length of x rank season
|
||||
const monthsToAdd = (number - 2) * 3;
|
||||
|
||||
start.setMonth(start.getMonth() + monthsToAdd);
|
||||
start.setMonth(start.getMonth() + monthsToAdd);
|
||||
|
||||
return {
|
||||
month: start.getMonth() + 1,
|
||||
year: start.getFullYear(),
|
||||
};
|
||||
return {
|
||||
month: start.getMonth() + 1,
|
||||
year: start.getFullYear(),
|
||||
};
|
||||
}
|
||||
|
||||
const addPlayerStm = sql.prepare(/* sql */ `
|
||||
@@ -176,26 +176,26 @@ const addPlacementStm = sql.prepare(/* sql */ `
|
||||
`);
|
||||
|
||||
function addPlacements(placements: Placements) {
|
||||
sql.transaction(() => {
|
||||
for (const placement of placements) {
|
||||
addPlayerStm.run({ splId: placement.playerSplId });
|
||||
addPlacementStm.run(placement);
|
||||
}
|
||||
})();
|
||||
sql.transaction(() => {
|
||||
for (const placement of placements) {
|
||||
addPlayerStm.run({ splId: placement.playerSplId });
|
||||
addPlacementStm.run(placement);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function wipeMonthYearPlacements({
|
||||
month,
|
||||
year,
|
||||
month,
|
||||
year,
|
||||
}: {
|
||||
month: number;
|
||||
year: number;
|
||||
month: number;
|
||||
year: number;
|
||||
}) {
|
||||
const wipeMonthYearPlacementsStm = sql.prepare(/* sql */ `
|
||||
const wipeMonthYearPlacementsStm = sql.prepare(/* sql */ `
|
||||
delete from "XRankPlacement"
|
||||
where "month" = @month
|
||||
and "year" = @year
|
||||
`);
|
||||
|
||||
wipeMonthYearPlacementsStm.run({ month, year });
|
||||
wipeMonthYearPlacementsStm.run({ month, year });
|
||||
}
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const placements = z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
rank: z.number(),
|
||||
rankDiff: z.union([z.string(), z.null()]),
|
||||
xPower: z.number(),
|
||||
weapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
image3d: z.object({ url: z.string() }),
|
||||
image2d: z.object({ url: z.string() }),
|
||||
image3dThumbnail: z.object({ url: z.string() }),
|
||||
image2dThumbnail: z.object({ url: z.string() }),
|
||||
subWeapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
specialWeapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
weaponTop: z.boolean(),
|
||||
__isPlayer: z.string(),
|
||||
byname: z.string(),
|
||||
nameId: z.string(),
|
||||
nameplate: z.object({
|
||||
badges: z.array(
|
||||
z.union([
|
||||
z.object({
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
z.null(),
|
||||
]),
|
||||
),
|
||||
background: z.object({
|
||||
textColor: z.object({
|
||||
a: z.number(),
|
||||
b: z.number(),
|
||||
g: z.number(),
|
||||
r: z.number(),
|
||||
}),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
__typename: z.string(),
|
||||
}),
|
||||
cursor: z.string(),
|
||||
}),
|
||||
),
|
||||
pageInfo: z.object({ endCursor: z.string(), hasNextPage: z.boolean() }),
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
rank: z.number(),
|
||||
rankDiff: z.union([z.string(), z.null()]),
|
||||
xPower: z.number(),
|
||||
weapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
image3d: z.object({ url: z.string() }),
|
||||
image2d: z.object({ url: z.string() }),
|
||||
image3dThumbnail: z.object({ url: z.string() }),
|
||||
image2dThumbnail: z.object({ url: z.string() }),
|
||||
subWeapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
specialWeapon: z.object({
|
||||
name: z.string(),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
weaponTop: z.boolean(),
|
||||
__isPlayer: z.string(),
|
||||
byname: z.string(),
|
||||
nameId: z.string(),
|
||||
nameplate: z.object({
|
||||
badges: z.array(
|
||||
z.union([
|
||||
z.object({
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
z.null(),
|
||||
]),
|
||||
),
|
||||
background: z.object({
|
||||
textColor: z.object({
|
||||
a: z.number(),
|
||||
b: z.number(),
|
||||
g: z.number(),
|
||||
r: z.number(),
|
||||
}),
|
||||
image: z.object({ url: z.string() }),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
__typename: z.string(),
|
||||
}),
|
||||
cursor: z.string(),
|
||||
}),
|
||||
),
|
||||
pageInfo: z.object({ endCursor: z.string(), hasNextPage: z.boolean() }),
|
||||
});
|
||||
|
||||
// e.g. https://splatoon3.ink/data/xrank/xrank.detail.a-2.clamblitz.json
|
||||
export const xRankSchema = z.object({
|
||||
data: z.object({
|
||||
node: z.object({
|
||||
__typename: z.string(),
|
||||
xRankingAr: placements.optional(),
|
||||
xRankingCl: placements.optional(),
|
||||
xRankingLf: placements.optional(),
|
||||
xRankingGl: placements.optional(),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
data: z.object({
|
||||
node: z.object({
|
||||
__typename: z.string(),
|
||||
xRankingAr: placements.optional(),
|
||||
xRankingCl: placements.optional(),
|
||||
xRankingLf: placements.optional(),
|
||||
xRankingGl: placements.optional(),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user