mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-04-25 15:43:51 -05:00
Add commands to list stages, challenges, weapon stats, hero stats, battles and stage schedules from SplatNet 2
This commit is contained in:
parent
28b3468835
commit
f1d262bfa7
|
|
@ -17,7 +17,8 @@ export function builder(yargs: Argv<ParentArguments>) {
|
||||||
return yargs.option('znc-proxy-url', {
|
return yargs.option('znc-proxy-url', {
|
||||||
describe: 'URL of Nintendo Switch Online app API proxy server to use',
|
describe: 'URL of Nintendo Switch Online app API proxy server to use',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
}).option('auto-update-iksm-session', {
|
}).option('auto-update-session', {
|
||||||
|
alias: ['auto-update-iksm-session'],
|
||||||
describe: 'Automatically obtain and refresh the iksm_session cookie',
|
describe: 'Automatically obtain and refresh the iksm_session cookie',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
|
|
|
||||||
92
src/cli/splatnet2/battles.ts
Normal file
92
src/cli/splatnet2/battles.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:battles');
|
||||||
|
|
||||||
|
export const command = 'battles';
|
||||||
|
export const desc = 'List the last 50 regular/ranked/private/festival battles';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const results = await splatnet.getResults();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(results, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(results));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Summary', results.summary);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'#',
|
||||||
|
'Type',
|
||||||
|
'Mode',
|
||||||
|
'Rule',
|
||||||
|
'Stage',
|
||||||
|
'Result',
|
||||||
|
'Inked',
|
||||||
|
'K (A)',
|
||||||
|
'D',
|
||||||
|
'S',
|
||||||
|
'Timestamp',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
results.results.sort((a, b) => a.start_time > b.start_time ? 1 : a.start_time < b.start_time ? -1 : 0);
|
||||||
|
|
||||||
|
for (const result of results.results) {
|
||||||
|
table.push([
|
||||||
|
result.battle_number,
|
||||||
|
result.type,
|
||||||
|
(result.game_mode.key === 'regular' ? '\u001b[32m' :
|
||||||
|
result.game_mode.key === 'ranked' ? '\u001b[33m' :
|
||||||
|
result.game_mode.key === 'league' ? '\u001b[31m' :
|
||||||
|
result.game_mode.key === 'private' ? '\u001b[35m' : '') +
|
||||||
|
result.game_mode.name + '\u001b[0m',
|
||||||
|
result.rule.key,
|
||||||
|
result.stage.name,
|
||||||
|
(result.my_team_result.key === 'victory' ? '\u001b[32m' : '\u001b[31m') +
|
||||||
|
result.my_team_result.name + '\u001b[0m',
|
||||||
|
result.player_result.game_paint_point + 'p',
|
||||||
|
result.player_result.kill_count + ' (' + result.player_result.assist_count + ')',
|
||||||
|
result.player_result.death_count,
|
||||||
|
result.player_result.special_count,
|
||||||
|
new Date(result.start_time * 1000).toISOString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
92
src/cli/splatnet2/challenges.ts
Normal file
92
src/cli/splatnet2/challenges.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:challenges');
|
||||||
|
|
||||||
|
export const command = 'challenges';
|
||||||
|
export const desc = 'List lifetime inkage challenges';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const records = await splatnet.getRecords();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(records.challenges, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(records.challenges));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [text, season, challenges, next, total_paint_point] of [
|
||||||
|
[
|
||||||
|
'Lifetime inkage challenges season 1', 1,
|
||||||
|
records.challenges.archived_challenges, records.challenges.next_challenge,
|
||||||
|
records.challenges.total_paint_point,
|
||||||
|
] as const,
|
||||||
|
[
|
||||||
|
'Lifetime inkage challenges season 2 (Octoling)', 2,
|
||||||
|
records.challenges.archived_challenges_octa, records.challenges.next_challenge_octa,
|
||||||
|
records.challenges.total_paint_point_octa,
|
||||||
|
] as const,
|
||||||
|
]) {
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'ID',
|
||||||
|
'Name',
|
||||||
|
'Turf covered',
|
||||||
|
'Completion',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const challenge of challenges) {
|
||||||
|
table.push([
|
||||||
|
challenge.key,
|
||||||
|
challenge.name,
|
||||||
|
challenge.paint_points + 'p',
|
||||||
|
'100%',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
table.push([
|
||||||
|
'???',
|
||||||
|
'???',
|
||||||
|
next.paint_points + 'p',
|
||||||
|
(Math.round((total_paint_point / next.paint_points) * 10000) / 100) + '%',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(text);
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||||
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
const token: string = argv.token ||
|
const token: string = argv.token ||
|
||||||
await storage.getItem('NintendoAccountToken.' + usernsid);
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateIksmSession);
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
await mkdirp(argv.directory);
|
await mkdirp(argv.directory);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||||
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
const token: string = argv.token ||
|
const token: string = argv.token ||
|
||||||
await storage.getItem('NintendoAccountToken.' + usernsid);
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateIksmSession);
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
await mkdirp(argv.directory);
|
await mkdirp(argv.directory);
|
||||||
|
|
||||||
|
|
@ -123,13 +123,16 @@ export async function dumpResults(
|
||||||
await fs.writeFile(image_file, image);
|
await fs.writeFile(image_file, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const skipped = [];
|
||||||
|
const skipped_images = [];
|
||||||
|
|
||||||
for (const item of results.results) {
|
for (const item of results.results) {
|
||||||
const filename = 'splatnet2-result-' + results.unique_id + '-' + item.battle_number + '-' + item.type + '.json';
|
const filename = 'splatnet2-result-' + results.unique_id + '-' + item.battle_number + '-' + item.type + '.json';
|
||||||
const file = path.join(directory, filename);
|
const file = path.join(directory, filename);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.stat(file);
|
await fs.stat(file);
|
||||||
debug('Skipping battle result %d, file already exists', item.battle_number);
|
skipped.push(item.battle_number);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debug('Fetching battle result %d', item.battle_number);
|
debug('Fetching battle result %d', item.battle_number);
|
||||||
const result = await splatnet.getResult(item.battle_number);
|
const result = await splatnet.getResult(item.battle_number);
|
||||||
|
|
@ -159,26 +162,36 @@ export async function dumpResults(
|
||||||
try {
|
try {
|
||||||
await fs.stat(file);
|
await fs.stat(file);
|
||||||
await fs.stat(image_file);
|
await fs.stat(image_file);
|
||||||
debug('Skipping battle result image %d, file already exists', item.battle_number);
|
skipped_images.push(item.battle_number);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debug('Fetching battle results summary image URL');
|
debug('Fetching battle results image URL');
|
||||||
const share = await splatnet.shareResult(item.battle_number);
|
const share = await splatnet.shareResult(item.battle_number);
|
||||||
|
|
||||||
debug('Fetching battle results summary image');
|
debug('Fetching battle results image');
|
||||||
const image_response = await fetch(share.url);
|
const image_response = await fetch(share.url);
|
||||||
const image = await image_response.buffer();
|
const image = await image_response.buffer();
|
||||||
|
|
||||||
debug('Writing battle results summary image data %s', filename);
|
debug('Writing battle results image data %s', filename);
|
||||||
await fs.writeFile(file, JSON.stringify({
|
await fs.writeFile(file, JSON.stringify({
|
||||||
share,
|
share,
|
||||||
}, null, 4) + '\n', 'utf-8');
|
}, null, 4) + '\n', 'utf-8');
|
||||||
|
|
||||||
debug('Writing battle results summary image %s', filename);
|
debug('Writing battle results image %s', filename);
|
||||||
await fs.writeFile(image_file, image);
|
await fs.writeFile(image_file, image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipped.length) {
|
||||||
|
if (skipped.length === 1) debug('Skipped battle result %d, file already exists', skipped[0]);
|
||||||
|
else debug('Skipped battle results %s, files already exist', skipped.join(', '));
|
||||||
|
}
|
||||||
|
if (skipped_images.length) {
|
||||||
|
if (skipped_images.length === 1) debug('Skipped battle result image %d, file already exists',
|
||||||
|
skipped_images[0]);
|
||||||
|
else debug('Skipped battle result images %s, files already exist', skipped_images.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,13 +220,15 @@ export async function dumpCoopResults(splatnet: SplatNet2Api, directory: string,
|
||||||
debug('Writing summary %s', summary_filename);
|
debug('Writing summary %s', summary_filename);
|
||||||
await fs.writeFile(summary_file, JSON.stringify(results, null, 4) + '\n', 'utf-8');
|
await fs.writeFile(summary_file, JSON.stringify(results, null, 4) + '\n', 'utf-8');
|
||||||
|
|
||||||
|
const skipped = [];
|
||||||
|
|
||||||
for (const item of results.results) {
|
for (const item of results.results) {
|
||||||
const filename = 'splatnet2-coop-result-' + user_id + '-' + item.job_id + '.json';
|
const filename = 'splatnet2-coop-result-' + user_id + '-' + item.job_id + '.json';
|
||||||
const file = path.join(directory, filename);
|
const file = path.join(directory, filename);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.stat(file);
|
await fs.stat(file);
|
||||||
debug('Skipping coop result %d, file already exists', item.job_id);
|
skipped.push(item.job_id);
|
||||||
continue;
|
continue;
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
|
||||||
|
|
@ -234,5 +249,10 @@ export async function dumpCoopResults(splatnet: SplatNet2Api, directory: string,
|
||||||
}, null, 4) + '\n', 'utf-8');
|
}, null, 4) + '\n', 'utf-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipped.length) {
|
||||||
|
if (skipped.length === 1) debug('Skipped coop result %d, file already exists', skipped[0]);
|
||||||
|
else debug('Skipped coop results %s, files already exist', skipped.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
src/cli/splatnet2/hero.ts
Normal file
97
src/cli/splatnet2/hero.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:hero');
|
||||||
|
|
||||||
|
export const command = 'hero';
|
||||||
|
export const desc = 'Show hero (Octo Canyon) records';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const hero = await splatnet.getHeroRecords();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(hero, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(hero));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Summary', hero.summary);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'Mission',
|
||||||
|
'Hero Shot',
|
||||||
|
'Hero Roller',
|
||||||
|
'Hero Charger',
|
||||||
|
'Hero Dualies',
|
||||||
|
'Hero Brella',
|
||||||
|
'Hero Splatling',
|
||||||
|
'Hero Blaster',
|
||||||
|
'Hero Slosher',
|
||||||
|
'Herobrush',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const stage of hero.stage_infos) {
|
||||||
|
table.push([
|
||||||
|
stage.stage.area + '-' + (stage.stage.is_boss ? 'B' : stage.stage.id),
|
||||||
|
stage.clear_weapons[0] ? 'Level ' + stage.clear_weapons[0].weapon_level + ', ' +
|
||||||
|
hrduration(stage.clear_weapons[0].clear_time) : '',
|
||||||
|
stage.clear_weapons[1] ? hrduration(stage.clear_weapons[1].clear_time) : '',
|
||||||
|
stage.clear_weapons[2] ? hrduration(stage.clear_weapons[2].clear_time) : '',
|
||||||
|
stage.clear_weapons[3] ? hrduration(stage.clear_weapons[3].clear_time) : '',
|
||||||
|
stage.clear_weapons[4] ? hrduration(stage.clear_weapons[4].clear_time) : '',
|
||||||
|
stage.clear_weapons[5] ? hrduration(stage.clear_weapons[5].clear_time) : '',
|
||||||
|
stage.clear_weapons[6] ? hrduration(stage.clear_weapons[6].clear_time) : '',
|
||||||
|
stage.clear_weapons[7] ? hrduration(stage.clear_weapons[7].clear_time) : '',
|
||||||
|
stage.clear_weapons[8] ? hrduration(stage.clear_weapons[8].clear_time) : '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Stages');
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function hrduration(duration: number) {
|
||||||
|
const minutes = Math.floor(duration / 60);
|
||||||
|
const seconds = duration - (minutes * 60);
|
||||||
|
|
||||||
|
if (minutes >= 1) {
|
||||||
|
return minutes + 'm' +
|
||||||
|
(seconds ? ' ' + seconds + 's' : '');
|
||||||
|
} else {
|
||||||
|
return seconds + 's';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
export * as user from './user.js';
|
export * as user from './user.js';
|
||||||
|
export * as stages from './stages.js';
|
||||||
|
export * as challenges from './challenges.js';
|
||||||
|
export * as weapons from './weapons.js';
|
||||||
|
export * as hero from './hero.js';
|
||||||
|
export * as battles from './battles.js';
|
||||||
|
export * as schedule from './schedule.js';
|
||||||
export * as dumpResults from './dump-results.js';
|
export * as dumpResults from './dump-results.js';
|
||||||
export * as dumpRecords from './dump-records.js';
|
export * as dumpRecords from './dump-records.js';
|
||||||
export * as monitor from './monitor.js';
|
export * as monitor from './monitor.js';
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,8 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||||
|
|
||||||
i.cached_records = records;
|
i.cached_records = records;
|
||||||
|
|
||||||
|
i.auto_update_iksm_session = argv.autoUpdateSession;
|
||||||
|
|
||||||
console.log('Player %s (Splatoon 2 ID %s, NSA ID %s) level %d',
|
console.log('Player %s (Splatoon 2 ID %s, NSA ID %s) level %d',
|
||||||
records.records.player.nickname,
|
records.records.player.nickname,
|
||||||
records.records.unique_id,
|
records.records.unique_id,
|
||||||
|
|
@ -120,6 +122,8 @@ export class SplatNet2RecordsMonitor {
|
||||||
/** Prevents redownloading user records on the first loop run */
|
/** Prevents redownloading user records on the first loop run */
|
||||||
cached_records: Records | null = null;
|
cached_records: Records | null = null;
|
||||||
|
|
||||||
|
auto_update_iksm_session = true;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public storage: persist.LocalStorage,
|
public storage: persist.LocalStorage,
|
||||||
public token: string,
|
public token: string,
|
||||||
|
|
|
||||||
78
src/cli/splatnet2/schedule.ts
Normal file
78
src/cli/splatnet2/schedule.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:schedule');
|
||||||
|
|
||||||
|
export const command = 'schedule';
|
||||||
|
export const desc = 'Show stage schedules';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const schedules = await splatnet.getSchedules();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(schedules, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(schedules));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [text, schedule] of [
|
||||||
|
['Regular Battle', schedules.regular],
|
||||||
|
['Ranked Battle', schedules.gachi],
|
||||||
|
['League Battle', schedules.league],
|
||||||
|
] as const) {
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'ID',
|
||||||
|
'Start',
|
||||||
|
'Rule',
|
||||||
|
'Stage',
|
||||||
|
'Stage',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const item of schedule) {
|
||||||
|
table.push([
|
||||||
|
item.id,
|
||||||
|
new Date(item.start_time * 1000).toISOString(),
|
||||||
|
item.rule.name,
|
||||||
|
item.stage_a.name,
|
||||||
|
item.stage_b.name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(text);
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/cli/splatnet2/stages.ts
Normal file
67
src/cli/splatnet2/stages.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:stages');
|
||||||
|
|
||||||
|
export const command = 'stages';
|
||||||
|
export const desc = 'List stages';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const stages = await splatnet.getStages();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(stages, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(stages));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'ID',
|
||||||
|
'Name',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
stages.stages.sort((a, b) => parseInt(a.id) > parseInt(b.id) ? 1 : parseInt(a.id) < parseInt(b.id) ? -1 : 0);
|
||||||
|
|
||||||
|
for (const stage of stages.stages) {
|
||||||
|
table.push([
|
||||||
|
stage.id,
|
||||||
|
stage.name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||||
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
const token: string = argv.token ||
|
const token: string = argv.token ||
|
||||||
await storage.getItem('NintendoAccountToken.' + usernsid);
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateIksmSession);
|
const {splatnet, data} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
const [records, stages, activefestivals, timeline] = await Promise.all([
|
const [records, stages, activefestivals, timeline] = await Promise.all([
|
||||||
splatnet.getRecords(),
|
splatnet.getRecords(),
|
||||||
|
|
@ -42,4 +42,6 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
||||||
records.records.player.principal_id,
|
records.records.player.principal_id,
|
||||||
records.records.player.player_rank,
|
records.records.player.player_rank,
|
||||||
records.records.player.player_type);
|
records.records.player.player_type);
|
||||||
|
|
||||||
|
console.log(data.iksm_session);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export async function getIksmToken(storage: persist.LocalStorage, token: string,
|
||||||
|
|
||||||
const existingToken: SavedIksmSessionToken | undefined = await storage.getItem('IksmToken.' + token);
|
const existingToken: SavedIksmSessionToken | undefined = await storage.getItem('IksmToken.' + token);
|
||||||
|
|
||||||
const td = 2 * 24 * 60 * 60 * 1000; // 2 days in ms
|
const td = 24 * 60 * 60 * 1000; // 1 day in ms
|
||||||
const last_used_days_ago = existingToken?.last_used && (existingToken.last_used + td) <= Date.now();
|
const last_used_days_ago = existingToken?.last_used && (existingToken.last_used + td) <= Date.now();
|
||||||
const expired = existingToken && existingToken.expires_at <= Date.now();
|
const expired = existingToken && existingToken.expires_at <= Date.now();
|
||||||
|
|
||||||
|
|
|
||||||
82
src/cli/splatnet2/weapons.ts
Normal file
82
src/cli/splatnet2/weapons.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import createDebug from 'debug';
|
||||||
|
// @ts-expect-error
|
||||||
|
import Table from 'cli-table/lib/index.js';
|
||||||
|
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||||
|
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||||
|
import { getIksmToken } from './util.js';
|
||||||
|
|
||||||
|
const debug = createDebug('cli:splatnet2:weapons');
|
||||||
|
|
||||||
|
export const command = 'weapons';
|
||||||
|
export const desc = 'Show weapon stats';
|
||||||
|
|
||||||
|
export function builder(yargs: Argv<ParentArguments>) {
|
||||||
|
return yargs.option('user', {
|
||||||
|
describe: 'Nintendo Account ID',
|
||||||
|
type: 'string',
|
||||||
|
}).option('token', {
|
||||||
|
describe: 'Nintendo Account session token',
|
||||||
|
type: 'string',
|
||||||
|
}).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 storage = await initStorage(argv.dataPath);
|
||||||
|
|
||||||
|
const usernsid = argv.user ?? await storage.getItem('SelectedUser');
|
||||||
|
const token: string = argv.token ||
|
||||||
|
await storage.getItem('NintendoAccountToken.' + usernsid);
|
||||||
|
const {splatnet} = await getIksmToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
|
||||||
|
|
||||||
|
const records = await splatnet.getRecords();
|
||||||
|
|
||||||
|
if (argv.jsonPrettyPrint) {
|
||||||
|
console.log(JSON.stringify(records.records.weapon_stats, null, 4));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argv.json) {
|
||||||
|
console.log(JSON.stringify(records.records.weapon_stats));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: [
|
||||||
|
'ID',
|
||||||
|
'Name',
|
||||||
|
'Sub',
|
||||||
|
'Special',
|
||||||
|
'Wins',
|
||||||
|
'Losses',
|
||||||
|
'Meter',
|
||||||
|
'H. meter',
|
||||||
|
'Turf inked',
|
||||||
|
'Last used',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const weaponstats of Object.values(records.records.weapon_stats)) {
|
||||||
|
table.push([
|
||||||
|
weaponstats.weapon.id,
|
||||||
|
weaponstats.weapon.name,
|
||||||
|
weaponstats.weapon.sub.name,
|
||||||
|
weaponstats.weapon.special.name,
|
||||||
|
weaponstats.win_count,
|
||||||
|
weaponstats.lose_count,
|
||||||
|
weaponstats.win_meter,
|
||||||
|
weaponstats.max_win_meter,
|
||||||
|
weaponstats.total_paint_point + 'p',
|
||||||
|
new Date(weaponstats.last_use_time * 1000).toISOString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Weapon stats');
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user