mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-08-22 02:24:47 -05:00
Add a command and functions to get SplatNet 2 X Rank seasons
This commit is contained in:
169
src/api/splatnet2-xrank.ts
Normal file
169
src/api/splatnet2-xrank.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// X Rank seasons
|
||||
//
|
||||
// X Rank seasons are calculated by the web app, not retrieved from the API.
|
||||
//
|
||||
|
||||
export interface Season {
|
||||
/** Season ID use by the API, e.g. "220501T00_220601T00" */
|
||||
id: string;
|
||||
/** Season ID use by the web app, e.g. "2022_05" */
|
||||
key: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
export function* getAllSeasons(sort_ascending = false) {
|
||||
let season = sort_ascending ? FIRST_SEASON : CURRENT_SEASON;
|
||||
|
||||
while (season) {
|
||||
yield season;
|
||||
season = sort_ascending ? getNextSeason(season) : getPreviousSeason(season);
|
||||
}
|
||||
}
|
||||
|
||||
export function getSeason(year: number, month: number): Season | null
|
||||
export function getSeason(start: Date): Season | null
|
||||
export function getSeason(id: string): Season | null
|
||||
export function getSeason(year: number | Date | string, month?: number): Season | null {
|
||||
if (year instanceof Date) {
|
||||
month = year.getUTCMonth() + 1;
|
||||
year = year.getUTCFullYear();
|
||||
}
|
||||
if (typeof year === 'string') {
|
||||
// 180401T00_180601T00
|
||||
const match = year.match(/^(\d{2,})(0\d,1[012])01T00_(\d{2,})(0\d,1[012])01T00$/);
|
||||
if (!match) throw new Error('Invalid season ID');
|
||||
|
||||
year = 2000 + parseInt(match[1]);
|
||||
month = parseInt(match[2]);
|
||||
|
||||
const nextyear = month === 12 ? year + 1 : year;
|
||||
const nextmonth = year === 2018 && month === 4 ? 6 :
|
||||
month === 12 ? 1 : month + 1;
|
||||
if (nextyear !== parseInt(match[3]) || nextmonth !== parseInt(match[4])) throw new Error('Invalid season ID');
|
||||
}
|
||||
|
||||
const start = new Date(Date.UTC(year, month! - 1));
|
||||
|
||||
if (Date.now() < start.getTime()) return null;
|
||||
if (start.getUTCFullYear() < 2018 || (start.getUTCFullYear() === 2018 && start.getUTCMonth() < 3)) return null;
|
||||
|
||||
const end = new Date(Date.UTC(
|
||||
start.getFullYear() + (start.getMonth() === 11 ? 1 : 0),
|
||||
start.getMonth() === 11 ? 0 : start.getMonth() + 1,
|
||||
));
|
||||
|
||||
const id = toSeasonId(start.getUTCFullYear(), start.getUTCMonth() + 1);
|
||||
const key = start.getUTCFullYear() + '_' + ('' + (start.getUTCMonth() + 1)).padStart(2, '0');
|
||||
|
||||
if (start.getUTCFullYear() === 2018 && start.getUTCMonth() === 4) {
|
||||
start.setUTCMonth(3);
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
key,
|
||||
start,
|
||||
end,
|
||||
complete: Date.now() > end.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getFirstSeason() {
|
||||
return getSeason(2018, 5);
|
||||
}
|
||||
|
||||
const FIRST_SEASON = getFirstSeason();
|
||||
const CURRENT_SEASON = getSeason(new Date());
|
||||
|
||||
export function getNextSeason(season: Season): Season | null
|
||||
export function getNextSeason(year: number, month: number): Season | null
|
||||
export function getNextSeason(season: Season | number, month?: number): Season | null {
|
||||
const current_start = typeof season === 'number' ? new Date(Date.UTC(season, month!)) : season.start;
|
||||
|
||||
if (current_start.getUTCFullYear() === 2018 && current_start.getUTCMonth() === 3) {
|
||||
current_start.setUTCMonth(4);
|
||||
}
|
||||
|
||||
const start = new Date(Date.UTC(
|
||||
current_start.getFullYear() + (current_start.getMonth() === 11 ? 1 : 0),
|
||||
current_start.getMonth() === 11 ? 0 : current_start.getMonth() + 1,
|
||||
));
|
||||
|
||||
if (Date.now() < start.getTime()) return null;
|
||||
|
||||
const end = new Date(Date.UTC(
|
||||
start.getFullYear() + (start.getMonth() === 11 ? 1 : 0),
|
||||
start.getMonth() === 11 ? 0 : start.getMonth() + 1,
|
||||
));
|
||||
|
||||
const id = toSeasonId(start.getUTCFullYear(), start.getUTCMonth() + 1);
|
||||
const key = start.getUTCFullYear() + '_' + ('' + (start.getUTCMonth() + 1)).padStart(2, '0');
|
||||
|
||||
return {
|
||||
id,
|
||||
key,
|
||||
start,
|
||||
end,
|
||||
complete: Date.now() > end.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getPreviousSeason(season: Season): Season | null
|
||||
export function getPreviousSeason(year: number, month: number): Season | null
|
||||
export function getPreviousSeason(season: Season | number, month?: number): Season | null {
|
||||
const current_start = typeof season === 'number' ? new Date(Date.UTC(season, month!)) : season.start;
|
||||
|
||||
const start = new Date(Date.UTC(
|
||||
current_start.getFullYear() - (current_start.getMonth() === 0 ? 1 : 0),
|
||||
current_start.getMonth() === 0 ? 11 : current_start.getMonth() - 1,
|
||||
));
|
||||
|
||||
if (start.getUTCFullYear() < 2018 || (start.getUTCFullYear() === 2018 && start.getUTCMonth() < 3)) return null;
|
||||
|
||||
const end = new Date(Date.UTC(
|
||||
start.getFullYear() + (start.getMonth() === 11 ? 1 : 0),
|
||||
start.getMonth() === 11 ? 0 : start.getMonth() + 1,
|
||||
));
|
||||
|
||||
const id = toSeasonId(start.getUTCFullYear(), start.getUTCMonth() + 1);
|
||||
const key = start.getUTCFullYear() + '_' + ('' + (start.getUTCMonth() + 1)).padStart(2, '0');
|
||||
|
||||
if (start.getUTCFullYear() === 2018 && start.getUTCMonth() === 4) {
|
||||
start.setUTCMonth(3);
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
key,
|
||||
start,
|
||||
end,
|
||||
complete: Date.now() > end.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
export function toSeasonId(year: number, month: number) {
|
||||
const nextyear = month === 12 ? year + 1 : year;
|
||||
const nextmonth = month === 12 ? 1 : month + 1;
|
||||
|
||||
if (year < 2000) throw new Error('Invalid season ID');
|
||||
if (nextyear >= 2100) throw new Error('Invalid season ID');
|
||||
|
||||
if (year === 2018 && month === 5) month = 4;
|
||||
|
||||
return ('' + (year - 2000)).padStart(2, '0') +
|
||||
('' + month).padStart(2, '0') +
|
||||
'01T00_' +
|
||||
('' + (nextyear - 2000)).padStart(2, '0') +
|
||||
('' + nextmonth).padStart(2, '0') +
|
||||
'01T00';
|
||||
}
|
||||
|
||||
export enum Rule {
|
||||
SPLAT_ZONES = 'splat_zones',
|
||||
TOWER_CONTROL = 'tower_control',
|
||||
RAINMAKER = 'rainmaker',
|
||||
CLAM_BLITZ = 'clam_blitz',
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { ErrorResponse } from './util.js';
|
||||
import ZncApi from './znc.js';
|
||||
import { ActiveFestivals, CoopResult, CoopResults, CoopSchedules, HeroRecords, NicknameAndIcons, PastFestivals, Records, Result, Results, Schedules, ShareResponse, ShopMerchandises, Stages, Timeline, WebServiceError, XPowerRankingSummary } from './splatnet2-types.js';
|
||||
import { timeoutSignal } from '../util/misc.js';
|
||||
import { toSeasonId, Rule as XPowerRankingRule } from './splatnet2-xrank.js';
|
||||
|
||||
const debug = createDebug('nxapi:api:splatnet2');
|
||||
|
||||
@@ -302,28 +303,6 @@ ${colour}
|
||||
}
|
||||
}
|
||||
|
||||
export function toSeasonId(year: number, month: number) {
|
||||
const nextyear = month === 12 ? year + 1 : year;
|
||||
const nextmonth = month === 12 ? 1 : month + 1;
|
||||
|
||||
if (year < 2000) throw new Error('Invalid season ID');
|
||||
if (nextyear >= 2100) throw new Error('Invalid season ID');
|
||||
|
||||
return ('' + (year - 2000)).padStart(2, '0') +
|
||||
('' + month).padStart(2, '0') +
|
||||
'01T00_' +
|
||||
('' + (nextyear - 2000)).padStart(2, '0') +
|
||||
('' + nextmonth).padStart(2, '0') +
|
||||
'01T00';
|
||||
}
|
||||
|
||||
export enum XPowerRankingRule {
|
||||
SPLAT_ZONES = 'splat_zones',
|
||||
TOWER_CONTROL = 'tower_control',
|
||||
RAINMAKER = 'rainmaker',
|
||||
CLAM_BLITZ = 'clam_blitz',
|
||||
}
|
||||
|
||||
export function toLeagueId(date: Date, type: LeagueType) {
|
||||
const year = date.getUTCFullYear();
|
||||
const month = date.getUTCMonth() + 1;
|
||||
|
||||
@@ -9,3 +9,4 @@ export * as schedule from './schedule.js';
|
||||
export * as dumpResults from './dump-results.js';
|
||||
export * as dumpRecords from './dump-records.js';
|
||||
export * as monitor from './monitor.js';
|
||||
export * as xRankSeasons from './x-rank-seasons.js';
|
||||
|
||||
62
src/cli/splatnet2/x-rank-seasons.ts
Normal file
62
src/cli/splatnet2/x-rank-seasons.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import createDebug from 'debug';
|
||||
import Table from '../util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { getAllSeasons } from '../../api/splatnet2-xrank.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:x-rank-seasons');
|
||||
|
||||
export const command = 'x-rank-seasons';
|
||||
export const desc = 'Show X Rank seasons';
|
||||
|
||||
export function builder(yargs: Argv<ParentArguments>) {
|
||||
return yargs.option('sort', {
|
||||
describe: 'Sort',
|
||||
type: 'string',
|
||||
choices: ['asc', 'desc'],
|
||||
default: 'asc',
|
||||
}).option('json', {
|
||||
describe: 'Output raw JSON',
|
||||
type: 'boolean',
|
||||
}).option('json-pretty-print', {
|
||||
describe: 'Output pretty-printed JSON',
|
||||
type: 'boolean',
|
||||
});
|
||||
}
|
||||
|
||||
type Arguments = YargsArguments<ReturnType<typeof builder>>;
|
||||
|
||||
export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||
const sort_ascending = argv.sort !== 'desc';
|
||||
|
||||
if (argv.json || argv.jsonPrettyPrint) {
|
||||
const result = {
|
||||
seasons: [...getAllSeasons()],
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result, null, argv.jsonPrettyPrint ? 4 : 0));
|
||||
return;
|
||||
}
|
||||
|
||||
const table = new Table({
|
||||
head: [
|
||||
'ID',
|
||||
'Key',
|
||||
'Start',
|
||||
'End',
|
||||
'Status',
|
||||
],
|
||||
});
|
||||
|
||||
for (const season of getAllSeasons(sort_ascending)) {
|
||||
table.push([
|
||||
season.id,
|
||||
season.key,
|
||||
season.start.toLocaleString('en-GB'),
|
||||
season.end.toLocaleString('en-GB'),
|
||||
season.complete ? 'Complete' : 'Calculating',
|
||||
]);
|
||||
}
|
||||
|
||||
console.log(table.toString());
|
||||
}
|
||||
11
src/index.ts
11
src/index.ts
@@ -8,11 +8,20 @@ export * as f from './api/f.js';
|
||||
|
||||
export {
|
||||
default as SplatNet2Api,
|
||||
XPowerRankingRule as SplatNet2XPowerRankingRule,
|
||||
LeagueType as SplatNet2LeagueType,
|
||||
LeagueRegion as SplatNet2LeagueRegion,
|
||||
ShareColour as SplatNet2ProfileColour,
|
||||
toLeagueId as toSplatNet2LeagueId,
|
||||
} from './api/splatnet2.js';
|
||||
export {
|
||||
Season as SplatNet2XRankSeason,
|
||||
Rule as SplatNet2XPowerRankingRule,
|
||||
getAllSeasons as getSplatNet2XRankSeasons,
|
||||
getSeason as getSplatNet2XRankSeason,
|
||||
getNextSeason as getSplatNet2NextXRankSeason,
|
||||
getPreviousSeason as getSplatNet2PreviousXRankSeason,
|
||||
toSeasonId as toSplatNet2XRankSeasonId,
|
||||
} from './api/splatnet2-xrank.js';
|
||||
export * as splatnet2 from './api/splatnet2-types.js';
|
||||
export {
|
||||
default as NooklinkApi,
|
||||
|
||||
Reference in New Issue
Block a user