Merge remote-tracking branch 'upstream/main' into dumb-splashcat-thing
commit
edda191f56
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,3 +1,16 @@
|
||||||
|
## 0.4.4
|
||||||
|
|
||||||
|
feat: send Anarchy (Open) Power
|
||||||
|
|
||||||
|
## 0.4.3
|
||||||
|
|
||||||
|
feat: add `list-method` option
|
||||||
|
([#73](https://github.com/spacemeowx2/s3si.ts/issues/73))
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
|
fix: `coral_user_id` is string
|
||||||
|
|
||||||
## 0.4.1
|
## 0.4.1
|
||||||
|
|
||||||
feat: add support for Challenges
|
feat: add support for Challenges
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,16 @@ Options:
|
||||||
--exporter <exporter>, -e Exporter list to use (default: stat.ink)
|
--exporter <exporter>, -e Exporter list to use (default: stat.ink)
|
||||||
Multiple exporters can be separated by commas
|
Multiple exporters can be separated by commas
|
||||||
(e.g. "stat.ink,file")
|
(e.g. "stat.ink,file")
|
||||||
|
--list-method When set to "latest", the latest 50 matches will be obtained.
|
||||||
|
When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
|
||||||
|
When set to "auto", the latest 50 matches will be obtained. If 50 matches have not been uploaded yet, matches will be obtained from the list of all modes.
|
||||||
|
"auto" is the default setting.
|
||||||
--no-progress, -n Disable progress bar
|
--no-progress, -n Disable progress bar
|
||||||
--monitor, -m Monitor mode
|
--monitor, -m Monitor mode
|
||||||
--skip-mode <mode>, -s Skip mode (default: null)
|
--skip-mode <mode>, -s Skip mode (default: null)
|
||||||
("vs", "coop")
|
("vs", "coop")
|
||||||
--with-summary Include summary in the output
|
--with-summary Include summary in the output
|
||||||
--help Show this help message and exit`,
|
--help Show this help message and exit
|
||||||
```
|
```
|
||||||
|
|
||||||
3. If it's your first time running this, follow the instructions to login to
|
3. If it's your first time running this, follow the instructions to login to
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "s3si-ts",
|
"productName": "s3si-ts",
|
||||||
"version": "0.4.1"
|
"version": "0.4.4"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|
|
||||||
7
s3si.ts
7
s3si.ts
|
|
@ -4,7 +4,7 @@ import { flags } from "./deps.ts";
|
||||||
|
|
||||||
const parseArgs = (args: string[]) => {
|
const parseArgs = (args: string[]) => {
|
||||||
const parsed = flags.parse(args, {
|
const parsed = flags.parse(args, {
|
||||||
string: ["profilePath", "exporter", "skipMode"],
|
string: ["profilePath", "exporter", "skipMode", "listMethod"],
|
||||||
boolean: ["help", "noProgress", "monitor", "withSummary"],
|
boolean: ["help", "noProgress", "monitor", "withSummary"],
|
||||||
alias: {
|
alias: {
|
||||||
"help": "h",
|
"help": "h",
|
||||||
|
|
@ -15,6 +15,7 @@ const parseArgs = (args: string[]) => {
|
||||||
"skipMode": ["s", "skip-mode"],
|
"skipMode": ["s", "skip-mode"],
|
||||||
"withSummary": "with-summary",
|
"withSummary": "with-summary",
|
||||||
"withStages": "with-stages",
|
"withStages": "with-stages",
|
||||||
|
"listMethod": "list-method",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|
@ -30,6 +31,10 @@ Options:
|
||||||
--exporter <exporter>, -e Exporter list to use (default: stat.ink)
|
--exporter <exporter>, -e Exporter list to use (default: stat.ink)
|
||||||
Multiple exporters can be separated by commas
|
Multiple exporters can be separated by commas
|
||||||
(e.g. "stat.ink,file,mongodb")
|
(e.g. "stat.ink,file,mongodb")
|
||||||
|
--list-method When set to "latest", the latest 50 matches will be obtained.
|
||||||
|
When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
|
||||||
|
When set to "auto", the latest 50 matches will be obtained. If 50 matches have not been uploaded yet, matches will be obtained from the list of all modes.
|
||||||
|
"auto" is the default setting.
|
||||||
--no-progress, -n Disable progress bar
|
--no-progress, -n Disable progress bar
|
||||||
--monitor, -m Monitor mode
|
--monitor, -m Monitor mode
|
||||||
--skip-mode <mode>, -s Skip mode (default: null)
|
--skip-mode <mode>, -s Skip mode (default: null)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
HistoryGroups,
|
HistoryGroups,
|
||||||
RankParam,
|
RankParam,
|
||||||
} from "./types.ts";
|
} from "./types.ts";
|
||||||
import { gameId, parseHistoryDetailId } from "./utils.ts";
|
import { battleTime, gameId } from "./utils.ts";
|
||||||
import { getSeason } from "./VersionData.ts";
|
import { getSeason } from "./VersionData.ts";
|
||||||
|
|
||||||
const splusParams = () => {
|
const splusParams = () => {
|
||||||
|
|
@ -193,17 +193,6 @@ function addRank(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const battleTime = (id: string) => {
|
|
||||||
const { timestamp } = parseHistoryDetailId(id);
|
|
||||||
|
|
||||||
const dateStr = timestamp.replace(
|
|
||||||
/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/,
|
|
||||||
"$1-$2-$3T$4:$5:$6Z",
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Date(dateStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
type FlattenItem = {
|
type FlattenItem = {
|
||||||
id: string;
|
id: string;
|
||||||
gameId: string;
|
gameId: string;
|
||||||
|
|
|
||||||
138
src/app.ts
138
src/app.ts
|
|
@ -1,8 +1,8 @@
|
||||||
import { loginManually } from "./iksm.ts";
|
import { loginManually } from "./iksm.ts";
|
||||||
import { MultiProgressBar } from "../deps.ts";
|
import { MultiProgressBar, Mutex } from "../deps.ts";
|
||||||
import { FileStateBackend, Profile, StateBackend } from "./state.ts";
|
import { FileStateBackend, Profile, StateBackend } from "./state.ts";
|
||||||
import { Splatnet3 } from "./splatnet3.ts";
|
import { Splatnet3 } from "./splatnet3.ts";
|
||||||
import { BattleListType, Game, GameExporter } from "./types.ts";
|
import { BattleListType, Game, GameExporter, ListMethod } from "./types.ts";
|
||||||
import { Cache, FileCache } from "./cache.ts";
|
import { Cache, FileCache } from "./cache.ts";
|
||||||
import { StatInkExporter } from "./exporters/stat.ink.ts";
|
import { StatInkExporter } from "./exporters/stat.ink.ts";
|
||||||
import { FileExporter } from "./exporters/file.ts";
|
import { FileExporter } from "./exporters/file.ts";
|
||||||
|
|
@ -20,6 +20,7 @@ export type Opts = {
|
||||||
withSummary: boolean;
|
withSummary: boolean;
|
||||||
withStages: boolean;
|
withStages: boolean;
|
||||||
skipMode?: string;
|
skipMode?: string;
|
||||||
|
listMethod?: string;
|
||||||
cache?: Cache;
|
cache?: Cache;
|
||||||
stateBackend?: StateBackend;
|
stateBackend?: StateBackend;
|
||||||
env: Env;
|
env: Env;
|
||||||
|
|
@ -32,6 +33,7 @@ export const DEFAULT_OPTS: Opts = {
|
||||||
monitor: false,
|
monitor: false,
|
||||||
withSummary: false,
|
withSummary: false,
|
||||||
withStages: true,
|
withStages: true,
|
||||||
|
listMethod: "latest",
|
||||||
env: DEFAULT_ENV,
|
env: DEFAULT_ENV,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,6 +58,103 @@ class StepProgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GameListFetcher {
|
||||||
|
/**
|
||||||
|
* Return not exported game list.
|
||||||
|
* [0] is the latest game.
|
||||||
|
* @param exporter GameExporter
|
||||||
|
*/
|
||||||
|
fetch(exporter: GameExporter): Promise<string[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BattleListFetcher implements GameListFetcher {
|
||||||
|
protected listMethod: ListMethod;
|
||||||
|
protected allBattleList?: string[];
|
||||||
|
protected latestBattleList?: string[];
|
||||||
|
protected allLock = new Mutex();
|
||||||
|
protected latestLock = new Mutex();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
listMethod: string,
|
||||||
|
protected splatnet: Splatnet3,
|
||||||
|
) {
|
||||||
|
if (listMethod === "all") {
|
||||||
|
this.listMethod = "all";
|
||||||
|
} else if (listMethod === "latest") {
|
||||||
|
this.listMethod = "latest";
|
||||||
|
} else {
|
||||||
|
this.listMethod = "auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getAllBattleList() {
|
||||||
|
return this.allLock.use(async () => {
|
||||||
|
if (!this.allBattleList) {
|
||||||
|
this.allBattleList = await this.splatnet.getAllBattleList();
|
||||||
|
}
|
||||||
|
return this.allBattleList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getLatestBattleList() {
|
||||||
|
return this.latestLock.use(async () => {
|
||||||
|
if (!this.latestBattleList) {
|
||||||
|
this.latestBattleList = await this.splatnet.getBattleList();
|
||||||
|
}
|
||||||
|
return this.latestBattleList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async innerFetch(exporter: GameExporter) {
|
||||||
|
if (this.listMethod === "latest") {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getLatestBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.listMethod === "all") {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getAllBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.listMethod === "auto") {
|
||||||
|
const latestList = await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getLatestBattleList(),
|
||||||
|
});
|
||||||
|
if (latestList.length === 50) {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getAllBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return latestList;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(`Unknown listMethod: ${this.listMethod}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch(exporter: GameExporter) {
|
||||||
|
return [...await this.innerFetch(exporter)].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoopListFetcher implements GameListFetcher {
|
||||||
|
constructor(
|
||||||
|
protected splatnet: Splatnet3,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async fetch(exporter: GameExporter) {
|
||||||
|
return [
|
||||||
|
...await exporter.notExported({
|
||||||
|
type: "CoopInfo",
|
||||||
|
list: await this.splatnet.getBattleList(BattleListType.Coop),
|
||||||
|
}),
|
||||||
|
].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function progress({ total, currentUrl, done }: StepProgress): Progress {
|
function progress({ total, currentUrl, done }: StepProgress): Progress {
|
||||||
return {
|
return {
|
||||||
total,
|
total,
|
||||||
|
|
@ -76,6 +175,12 @@ export class App {
|
||||||
env: opts.env,
|
env: opts.env,
|
||||||
});
|
});
|
||||||
this.env = opts.env;
|
this.env = opts.env;
|
||||||
|
|
||||||
|
if (
|
||||||
|
opts.listMethod && !["all", "auto", "latest"].includes(opts.listMethod)
|
||||||
|
) {
|
||||||
|
throw new TypeError(`Unknown listMethod: ${opts.listMethod}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSkipMode(): ("vs" | "coop")[] {
|
getSkipMode(): ("vs" | "coop")[] {
|
||||||
|
|
@ -193,8 +298,10 @@ export class App {
|
||||||
if (skipMode.includes("vs") || exporters.length === 0) {
|
if (skipMode.includes("vs") || exporters.length === 0) {
|
||||||
this.env.logger.log("Skip exporting VS games.");
|
this.env.logger.log("Skip exporting VS games.");
|
||||||
} else {
|
} else {
|
||||||
this.env.logger.log("Fetching battle list...");
|
const gameListFetcher = new BattleListFetcher(
|
||||||
const gameList = await splatnet.getBattleList();
|
this.opts.listMethod ?? "auto",
|
||||||
|
splatnet,
|
||||||
|
);
|
||||||
|
|
||||||
const { redraw, endBar } = this.exporterProgress("Export vs games");
|
const { redraw, endBar } = this.exporterProgress("Export vs games");
|
||||||
const fetcher = new GameFetcher({
|
const fetcher = new GameFetcher({
|
||||||
|
|
@ -213,7 +320,7 @@ export class App {
|
||||||
type: "VsInfo",
|
type: "VsInfo",
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter: e,
|
exporter: e,
|
||||||
gameList,
|
gameListFetcher,
|
||||||
stepProgress: stats[e.name],
|
stepProgress: stats[e.name],
|
||||||
onStep: () => {
|
onStep: () => {
|
||||||
redraw(e.name, progress(stats[e.name]));
|
redraw(e.name, progress(stats[e.name]));
|
||||||
|
|
@ -247,10 +354,7 @@ export class App {
|
||||||
if (skipMode.includes("coop") || exporters.length === 0) {
|
if (skipMode.includes("coop") || exporters.length === 0) {
|
||||||
this.env.logger.log("Skip exporting coop games.");
|
this.env.logger.log("Skip exporting coop games.");
|
||||||
} else {
|
} else {
|
||||||
this.env.logger.log("Fetching coop battle list...");
|
const gameListFetcher = new CoopListFetcher(splatnet);
|
||||||
const coopBattleList = await splatnet.getBattleList(
|
|
||||||
BattleListType.Coop,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { redraw, endBar } = this.exporterProgress("Export coop games");
|
const { redraw, endBar } = this.exporterProgress("Export coop games");
|
||||||
const fetcher = new GameFetcher({
|
const fetcher = new GameFetcher({
|
||||||
|
|
@ -267,7 +371,7 @@ export class App {
|
||||||
type: "CoopInfo",
|
type: "CoopInfo",
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter: e,
|
exporter: e,
|
||||||
gameList: coopBattleList,
|
gameListFetcher,
|
||||||
stepProgress: stats[e.name],
|
stepProgress: stats[e.name],
|
||||||
onStep: () => {
|
onStep: () => {
|
||||||
redraw(e.name, progress(stats[e.name]));
|
redraw(e.name, progress(stats[e.name]));
|
||||||
|
|
@ -401,30 +505,24 @@ export class App {
|
||||||
* @param gameList ID list of games, sorted by date, newest first
|
* @param gameList ID list of games, sorted by date, newest first
|
||||||
* @param onStep Callback function called when a game is exported
|
* @param onStep Callback function called when a game is exported
|
||||||
*/
|
*/
|
||||||
async exportGameList({
|
private async exportGameList({
|
||||||
type,
|
type,
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter,
|
exporter,
|
||||||
gameList,
|
gameListFetcher,
|
||||||
stepProgress,
|
stepProgress,
|
||||||
onStep,
|
onStep,
|
||||||
}: {
|
}: {
|
||||||
type: Game["type"];
|
type: Game["type"];
|
||||||
exporter: GameExporter;
|
exporter: GameExporter;
|
||||||
fetcher: GameFetcher;
|
fetcher: GameFetcher;
|
||||||
gameList: string[];
|
gameListFetcher: GameListFetcher;
|
||||||
stepProgress: StepProgress;
|
stepProgress: StepProgress;
|
||||||
onStep: () => void;
|
onStep: () => void;
|
||||||
}): Promise<StepProgress> {
|
}): Promise<StepProgress> {
|
||||||
onStep?.();
|
onStep?.();
|
||||||
|
|
||||||
const workQueue = [
|
const workQueue = await gameListFetcher.fetch(exporter);
|
||||||
...await exporter.notExported({
|
|
||||||
type,
|
|
||||||
list: gameList,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
.reverse();
|
|
||||||
|
|
||||||
const step = async (id: string) => {
|
const step = async (id: string) => {
|
||||||
const detail = await fetcher.fetch(type, id);
|
const detail = await fetcher.fetch(type, id);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import type { StatInkPostBody, VsHistoryDetail } from "./types.ts";
|
||||||
|
|
||||||
export const AGENT_NAME = "splashcat / s3si.ts";
|
export const AGENT_NAME = "splashcat / s3si.ts";
|
||||||
export const AGENT_VERSION = "1.1.1";
|
export const AGENT_VERSION = "1.1.1";
|
||||||
export const S3SI_VERSION = "0.4.1";
|
export const S3SI_VERSION = "0.4.4";
|
||||||
export const COMBINED_VERSION = `${AGENT_VERSION}/${S3SI_VERSION}`;
|
export const COMBINED_VERSION = `${AGENT_VERSION}/${S3SI_VERSION}`;
|
||||||
export const NSOAPP_VERSION = "2.5.1";
|
export const NSOAPP_VERSION = "2.5.2";
|
||||||
export const WEB_VIEW_VERSION = "4.0.0-d5178440";
|
export const WEB_VIEW_VERSION = "4.0.0-d5178440";
|
||||||
export enum Queries {
|
export enum Queries {
|
||||||
HomeQuery = "7dcc64ea27a08e70919893a0d3f70871",
|
HomeQuery = "7dcc64ea27a08e70919893a0d3f70871",
|
||||||
|
|
@ -12,6 +12,7 @@ export enum Queries {
|
||||||
RegularBattleHistoriesQuery = "3baef04b095ad8975ea679d722bc17de",
|
RegularBattleHistoriesQuery = "3baef04b095ad8975ea679d722bc17de",
|
||||||
BankaraBattleHistoriesQuery = "0438ea6978ae8bd77c5d1250f4f84803",
|
BankaraBattleHistoriesQuery = "0438ea6978ae8bd77c5d1250f4f84803",
|
||||||
XBattleHistoriesQuery = "6796e3cd5dc3ebd51864dc709d899fc5",
|
XBattleHistoriesQuery = "6796e3cd5dc3ebd51864dc709d899fc5",
|
||||||
|
EventBattleHistoriesQuery = "9744fcf676441873c7c8a51285b6aa4d",
|
||||||
PrivateBattleHistoriesQuery = "8e5ae78b194264a6c230e262d069bd28",
|
PrivateBattleHistoriesQuery = "8e5ae78b194264a6c230e262d069bd28",
|
||||||
VsHistoryDetailQuery = "9ee0099fbe3d8db2a838a75cf42856dd",
|
VsHistoryDetailQuery = "9ee0099fbe3d8db2a838a75cf42856dd",
|
||||||
CoopHistoryQuery = "91b917becd2fa415890f5b47e15ffb15",
|
CoopHistoryQuery = "91b917becd2fa415890f5b47e15ffb15",
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,8 @@ export class StatInkExporter implements GameExporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.bankara_power_after = vsDetail.bankaraMatch?.bankaraPower?.power;
|
||||||
|
|
||||||
if (rankBeforeState && rankState) {
|
if (rankBeforeState && rankState) {
|
||||||
result.rank_before_exp = rankBeforeState.rankPoint;
|
result.rank_before_exp = rankBeforeState.rankPoint;
|
||||||
result.rank_after_exp = rankState.rankPoint;
|
result.rank_after_exp = rankState.rankPoint;
|
||||||
|
|
|
||||||
|
|
@ -213,20 +213,20 @@ export async function getGToken(
|
||||||
|
|
||||||
const idToken2: string = respJson?.result?.webApiServerCredential
|
const idToken2: string = respJson?.result?.webApiServerCredential
|
||||||
?.accessToken;
|
?.accessToken;
|
||||||
const coralUserId: number = respJson?.result?.user?.id;
|
const coralUserId: string = respJson?.result?.user?.id?.toString();
|
||||||
|
|
||||||
if (!idToken2 || !coralUserId) {
|
if (!idToken2 || !coralUserId) {
|
||||||
throw new APIError({
|
throw new APIError({
|
||||||
response: resp,
|
response: resp,
|
||||||
json: respJson,
|
json: respJson,
|
||||||
message:
|
message:
|
||||||
`No idToken2 or coralUserId found. Please try again later. ('${idToken2}', '${coralUserId}')`,
|
`No idToken2 or coralUserId found. Please try again later. (${idToken2.length}, ${coralUserId.length})`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return [idToken2, coralUserId] as const;
|
return [idToken2, coralUserId] as const;
|
||||||
};
|
};
|
||||||
const getGToken = async (idToken: string, coralUserId: number) => {
|
const getGToken = async (idToken: string, coralUserId: string) => {
|
||||||
const { f, request_id: requestId, timestamp } = await callImink({
|
const { f, request_id: requestId, timestamp } = await callImink({
|
||||||
step: 2,
|
step: 2,
|
||||||
idToken,
|
idToken,
|
||||||
|
|
@ -414,7 +414,7 @@ async function callImink(
|
||||||
step: number;
|
step: number;
|
||||||
idToken: string;
|
idToken: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
coralUserId?: number;
|
coralUserId?: string;
|
||||||
env: Env;
|
env: Env;
|
||||||
},
|
},
|
||||||
): Promise<IminkResponse> {
|
): Promise<IminkResponse> {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
} from "./types.ts";
|
} from "./types.ts";
|
||||||
import { DEFAULT_ENV, Env } from "./env.ts";
|
import { DEFAULT_ENV, Env } from "./env.ts";
|
||||||
import { getBulletToken, getGToken } from "./iksm.ts";
|
import { getBulletToken, getGToken } from "./iksm.ts";
|
||||||
import { parseHistoryDetailId } from "./utils.ts";
|
import { battleTime, parseHistoryDetailId } from "./utils.ts";
|
||||||
|
|
||||||
export class Splatnet3 {
|
export class Splatnet3 {
|
||||||
protected profile: Profile;
|
protected profile: Profile;
|
||||||
|
|
@ -137,6 +137,12 @@ export class Splatnet3 {
|
||||||
[BattleListType.Bankara]: () =>
|
[BattleListType.Bankara]: () =>
|
||||||
this.request(Queries.BankaraBattleHistoriesQuery)
|
this.request(Queries.BankaraBattleHistoriesQuery)
|
||||||
.then((r) => getIdsFromGroups(r.bankaraBattleHistories)),
|
.then((r) => getIdsFromGroups(r.bankaraBattleHistories)),
|
||||||
|
[BattleListType.XBattle]: () =>
|
||||||
|
this.request(Queries.XBattleHistoriesQuery)
|
||||||
|
.then((r) => getIdsFromGroups(r.xBattleHistories)),
|
||||||
|
[BattleListType.Event]: () =>
|
||||||
|
this.request(Queries.EventBattleHistoriesQuery)
|
||||||
|
.then((r) => getIdsFromGroups(r.eventBattleHistories)),
|
||||||
[BattleListType.Private]: () =>
|
[BattleListType.Private]: () =>
|
||||||
this.request(Queries.PrivateBattleHistoriesQuery)
|
this.request(Queries.PrivateBattleHistoriesQuery)
|
||||||
.then((r) => getIdsFromGroups(r.privateBattleHistories)),
|
.then((r) => getIdsFromGroups(r.privateBattleHistories)),
|
||||||
|
|
@ -168,6 +174,29 @@ export class Splatnet3 {
|
||||||
return await this.BATTLE_LIST_TYPE_MAP[battleListType]();
|
return await this.BATTLE_LIST_TYPE_MAP[battleListType]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all id from all battle list, sort by time, [0] is the latest
|
||||||
|
async getAllBattleList() {
|
||||||
|
const ALL_TYPE: BattleListType[] = [
|
||||||
|
BattleListType.Regular,
|
||||||
|
BattleListType.Bankara,
|
||||||
|
BattleListType.XBattle,
|
||||||
|
BattleListType.Event,
|
||||||
|
BattleListType.Private,
|
||||||
|
];
|
||||||
|
const ids: string[] = [];
|
||||||
|
for (const type of ALL_TYPE) {
|
||||||
|
ids.push(...await this.getBattleList(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeMap = new Map<string, Date>(
|
||||||
|
ids.map((id) => [id, battleTime(id)] as const),
|
||||||
|
);
|
||||||
|
|
||||||
|
return ids.sort((a, b) =>
|
||||||
|
timeMap.get(b)!.getTime() - timeMap.get(a)!.getTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getBattleDetail(
|
getBattleDetail(
|
||||||
id: string,
|
id: string,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
15
src/types.ts
15
src/types.ts
|
|
@ -9,6 +9,7 @@ export type VarsMap = {
|
||||||
[Queries.RegularBattleHistoriesQuery]: [];
|
[Queries.RegularBattleHistoriesQuery]: [];
|
||||||
[Queries.BankaraBattleHistoriesQuery]: [];
|
[Queries.BankaraBattleHistoriesQuery]: [];
|
||||||
[Queries.XBattleHistoriesQuery]: [];
|
[Queries.XBattleHistoriesQuery]: [];
|
||||||
|
[Queries.EventBattleHistoriesQuery]: [];
|
||||||
[Queries.PrivateBattleHistoriesQuery]: [];
|
[Queries.PrivateBattleHistoriesQuery]: [];
|
||||||
[Queries.VsHistoryDetailQuery]: [{
|
[Queries.VsHistoryDetailQuery]: [{
|
||||||
vsResultId: string;
|
vsResultId: string;
|
||||||
|
|
@ -244,6 +245,9 @@ export type VsHistoryDetail = {
|
||||||
bankaraMatch: {
|
bankaraMatch: {
|
||||||
earnedUdemaePoint: null | number;
|
earnedUdemaePoint: null | number;
|
||||||
mode: "OPEN" | "CHALLENGE";
|
mode: "OPEN" | "CHALLENGE";
|
||||||
|
bankaraPower?: null | {
|
||||||
|
power?: null | number;
|
||||||
|
};
|
||||||
} | null;
|
} | null;
|
||||||
festMatch: {
|
festMatch: {
|
||||||
dragonMatchType: "NORMAL" | "DECUPLE" | "DRAGON" | "DOUBLE_DRAGON";
|
dragonMatchType: "NORMAL" | "DECUPLE" | "DRAGON" | "DOUBLE_DRAGON";
|
||||||
|
|
@ -427,6 +431,11 @@ export type RespMap = {
|
||||||
};
|
};
|
||||||
[Queries.BankaraBattleHistoriesQuery]: BankaraBattleHistories;
|
[Queries.BankaraBattleHistoriesQuery]: BankaraBattleHistories;
|
||||||
[Queries.XBattleHistoriesQuery]: XBattleHistories;
|
[Queries.XBattleHistoriesQuery]: XBattleHistories;
|
||||||
|
[Queries.EventBattleHistoriesQuery]: {
|
||||||
|
eventBattleHistories: {
|
||||||
|
historyGroups: HistoryGroups<BattleListNode>;
|
||||||
|
};
|
||||||
|
};
|
||||||
[Queries.PrivateBattleHistoriesQuery]: {
|
[Queries.PrivateBattleHistoriesQuery]: {
|
||||||
privateBattleHistories: {
|
privateBattleHistories: {
|
||||||
historyGroups: HistoryGroups<BattleListNode>;
|
historyGroups: HistoryGroups<BattleListNode>;
|
||||||
|
|
@ -614,10 +623,14 @@ export enum BattleListType {
|
||||||
Latest,
|
Latest,
|
||||||
Regular,
|
Regular,
|
||||||
Bankara,
|
Bankara,
|
||||||
|
Event,
|
||||||
|
XBattle,
|
||||||
Private,
|
Private,
|
||||||
Coop,
|
Coop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ListMethod = "latest" | "all" | "auto";
|
||||||
|
|
||||||
export type StatInkUuidList = {
|
export type StatInkUuidList = {
|
||||||
status: number;
|
status: number;
|
||||||
code: number;
|
code: number;
|
||||||
|
|
@ -822,6 +835,8 @@ export type StatInkPostBody = {
|
||||||
challenge_lose?: number;
|
challenge_lose?: number;
|
||||||
x_power_before?: number | null;
|
x_power_before?: number | null;
|
||||||
x_power_after?: number | null;
|
x_power_after?: number | null;
|
||||||
|
bankara_power_before?: number | null;
|
||||||
|
bankara_power_after?: number | null;
|
||||||
fest_power?: number; // Splatfest Power (Pro)
|
fest_power?: number; // Splatfest Power (Pro)
|
||||||
fest_dragon?:
|
fest_dragon?:
|
||||||
| "10x"
|
| "10x"
|
||||||
|
|
|
||||||
11
src/utils.ts
11
src/utils.ts
|
|
@ -188,3 +188,14 @@ export function urlSimplify(url: string): { pathname: string } | string {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const battleTime = (id: string) => {
|
||||||
|
const { timestamp } = parseHistoryDetailId(id);
|
||||||
|
|
||||||
|
const dateStr = timestamp.replace(
|
||||||
|
/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/,
|
||||||
|
"$1-$2-$3T$4:$5:$6Z",
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Date(dateStr);
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue