mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-04-24 06:56:54 -05:00
Include lifetime inkage challenges and only download updated data by default
This commit is contained in:
parent
0839ff520b
commit
b50717169a
|
|
@ -2,12 +2,13 @@ import createDebug from 'debug';
|
|||
import mkdirp from 'mkdirp';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import * as crypto from 'crypto';
|
||||
import type { Arguments as ParentArguments } from '../splatnet2.js';
|
||||
import { ArgumentsCamelCase, Argv, initStorage, YargsArguments } from '../../util.js';
|
||||
import { getIksmToken } from './util.js';
|
||||
import SplatNet2Api, { ShareColour } from '../../api/splatnet2.js';
|
||||
import { NicknameAndIcon } from '../../api/splatnet2-types.js';
|
||||
import fetch from 'node-fetch';
|
||||
import { Challenge, NicknameAndIcon, Records, Stages } from '../../api/splatnet2-types.js';
|
||||
|
||||
const debug = createDebug('cli:splatnet2:dump-records');
|
||||
|
||||
|
|
@ -29,6 +30,10 @@ export function builder(yargs: Argv<ParentArguments>) {
|
|||
describe: 'Include user records',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
}).option('challenges', {
|
||||
describe: 'Include lifetime inkage challenges',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
}).option('profile-image', {
|
||||
describe: 'Include profile image',
|
||||
type: 'boolean',
|
||||
|
|
@ -39,14 +44,18 @@ export function builder(yargs: Argv<ParentArguments>) {
|
|||
}).option('favourite-colour', {
|
||||
describe: 'Favourite colour to include on profile image',
|
||||
type: 'string',
|
||||
}).option('new-records', {
|
||||
describe: 'Only update user records and profile image if new data is available',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
}).option('hero-records', {
|
||||
describe: 'Include hero (Octo Canyon) records',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
default: false,
|
||||
}).option('timeline', {
|
||||
describe: 'Include timeline records',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
default: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,67 +79,28 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
|||
]);
|
||||
const nickname_and_icons = await splatnet.getUserNicknameAndIcon([records.records.player.principal_id]);
|
||||
|
||||
const updated = argv.newRecords ? new Date(records.records.update_time * 1000) : undefined;
|
||||
|
||||
if (argv.userRecords) {
|
||||
const filename = 'splatnet2-records-' + records.records.unique_id + '-' + Date.now() + '.json';
|
||||
const file = path.join(argv.directory, filename);
|
||||
|
||||
debug('Writing records %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify(records, null, 4) + '\n', 'utf-8');
|
||||
|
||||
const ni_filename = 'splatnet2-ni-' + records.records.unique_id + '-' + Date.now() + '.json';
|
||||
const ni_file = path.join(argv.directory, ni_filename);
|
||||
|
||||
debug('Writing records %s', ni_filename);
|
||||
await fs.writeFile(ni_file, JSON.stringify(nickname_and_icons.nickname_and_icons[0], null, 4) + '\n', 'utf-8');
|
||||
await dumpRecords(argv.directory, records.records.unique_id, records,
|
||||
nickname_and_icons.nickname_and_icons[0], updated);
|
||||
}
|
||||
|
||||
if (argv.profileImage) {
|
||||
const filename = 'splatnet2-profile-' + records.records.unique_id + '-' + Date.now() + '.json';
|
||||
const file = path.join(argv.directory, filename);
|
||||
const image_filename = 'splatnet2-profile-' + records.records.unique_id + '-' + Date.now() + '.png';
|
||||
const image_file = path.join(argv.directory, image_filename);
|
||||
await dumpProfileImage(splatnet, argv.directory, records.records.unique_id, stages,
|
||||
nickname_and_icons.nickname_and_icons[0],
|
||||
argv.favouriteStage, argv.favouriteColour, updated);
|
||||
}
|
||||
|
||||
const stage = argv.favouriteStage ?
|
||||
stages.stages.find(s => s.id === argv.favouriteStage ||
|
||||
s.name.toLowerCase() === argv.favouriteStage?.toLowerCase()) :
|
||||
stages.stages[0];
|
||||
|
||||
if (!stage) {
|
||||
debug('Invalid favourite stage "%s"', argv.favouriteStage);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (!Object.values(ShareColour).includes(argv.favouriteColour)) {
|
||||
argv.favouriteColour = ShareColour.PINK;
|
||||
}
|
||||
|
||||
debug('Fetching profile image URL');
|
||||
const share = await splatnet.shareProfile(stage?.id ?? stages.stages[0].id, argv.favouriteColour as ShareColour);
|
||||
|
||||
debug('Fetching profile image');
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
|
||||
debug('Writing profile image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({
|
||||
share,
|
||||
stage: stage ?? stages.stages[0],
|
||||
colour: argv.favouriteColour,
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing profile image %s', filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
if (argv.challenges) {
|
||||
await dumpChallenges(splatnet, argv.directory, records.records.unique_id,
|
||||
records.challenges.archived_challenges, 1);
|
||||
await dumpChallenges(splatnet, argv.directory, records.records.unique_id,
|
||||
records.challenges.archived_challenges_octa, 2);
|
||||
}
|
||||
|
||||
if (argv.heroRecords) {
|
||||
debug('Fetching hero records');
|
||||
const hero = await splatnet.getHeroRecords();
|
||||
|
||||
const filename = 'splatnet2-hero-' + records.records.unique_id + '-' + Date.now() + '.json';
|
||||
const file = path.join(argv.directory, filename);
|
||||
|
||||
debug('Writing hero records %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify(hero, null, 4) + '\n', 'utf-8');
|
||||
await dumpHeroRecords(splatnet, argv.directory, records.records.unique_id);
|
||||
}
|
||||
|
||||
if (argv.timeline) {
|
||||
|
|
@ -138,6 +108,153 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
|||
const file = path.join(argv.directory, filename);
|
||||
|
||||
debug('Writing timeline %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify(records, null, 4) + '\n', 'utf-8');
|
||||
await fs.writeFile(file, JSON.stringify(timeline, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
export async function dumpRecords(
|
||||
directory: string, user_id: string,
|
||||
records: Records, nickname_and_icon: NicknameAndIcon,
|
||||
updated?: Date
|
||||
) {
|
||||
const latest_filename = 'splatnet2-records-' + user_id + '-latest.json';
|
||||
const latest_file = path.join(directory, latest_filename);
|
||||
|
||||
if (updated) {
|
||||
try {
|
||||
const {timestamp} = JSON.parse(await fs.readFile(latest_file, 'utf-8'));
|
||||
|
||||
if (timestamp > updated.getTime()) {
|
||||
debug('Skipping user records, not updated');
|
||||
return;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
const filename = 'splatnet2-records-' + user_id + '-' + timestamp + '.json';
|
||||
const file = path.join(directory, filename);
|
||||
const ni_filename = 'splatnet2-ni-' + user_id + '-' + timestamp + '.json';
|
||||
const ni_file = path.join(directory, ni_filename);
|
||||
|
||||
debug('Writing records %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify(records, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing records %s', ni_filename);
|
||||
await fs.writeFile(ni_file, JSON.stringify(nickname_and_icon, null, 4) + '\n', 'utf-8');
|
||||
|
||||
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
export async function dumpProfileImage(
|
||||
splatnet: SplatNet2Api, directory: string, user_id: string,
|
||||
stages: Stages, nickname_and_icon: NicknameAndIcon,
|
||||
favourite_stage?: Arguments['favourite-stage'], favourite_colour?: Arguments['favourite-colour'],
|
||||
updated?: Date
|
||||
) {
|
||||
const stage = favourite_stage ?
|
||||
stages.stages.find(s => s.id === favourite_stage ||
|
||||
s.name.toLowerCase() === favourite_stage?.toLowerCase()) :
|
||||
stages.stages[0];
|
||||
|
||||
if (!stage) {
|
||||
debug('Invalid favourite stage "%s"', favourite_stage);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (!Object.values(ShareColour).includes(favourite_colour)) {
|
||||
favourite_colour = ShareColour.PINK;
|
||||
}
|
||||
|
||||
const latest_filename = 'splatnet2-profile-' + user_id + '-latest.json';
|
||||
const latest_file = path.join(directory, latest_filename);
|
||||
|
||||
const etag_data = {
|
||||
stage: (stage ?? stages.stages[0]).id,
|
||||
stage_image: (stage ?? stages.stages[0]).image,
|
||||
colour: favourite_colour,
|
||||
|
||||
name: nickname_and_icon.nickname,
|
||||
image_url: nickname_and_icon.thumbnail_url,
|
||||
};
|
||||
const etag = crypto.createHash('sha256').update(JSON.stringify(etag_data)).digest('base64');
|
||||
|
||||
if (updated) {
|
||||
try {
|
||||
const {timestamp, etag: prev_etag} = JSON.parse(await fs.readFile(latest_file, 'utf-8'));
|
||||
|
||||
if (timestamp > updated.getTime() && etag === prev_etag) {
|
||||
debug('Skipping profile image, not updated');
|
||||
return;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
const filename = 'splatnet2-profile-' + user_id + '-' + timestamp + '.json';
|
||||
const file = path.join(directory, filename);
|
||||
const image_filename = 'splatnet2-profile-' + user_id + '-' + timestamp + '.png';
|
||||
const image_file = path.join(directory, image_filename);
|
||||
|
||||
debug('Fetching profile image URL');
|
||||
const share = await splatnet.shareProfile(stage?.id ?? stages.stages[0].id, favourite_colour as ShareColour);
|
||||
|
||||
debug('Fetching profile image');
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
|
||||
debug('Writing profile image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({
|
||||
share,
|
||||
stage: stage ?? stages.stages[0],
|
||||
colour: favourite_colour,
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing profile image %s', image_filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
|
||||
await fs.writeFile(latest_file, JSON.stringify({timestamp, etag, etag_data}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
async function dumpChallenges(
|
||||
splatnet: SplatNet2Api, directory: string, user_id: string,
|
||||
challenges: Challenge[], season: 1 | 2
|
||||
) {
|
||||
for (const challenge of challenges) {
|
||||
const filename = 'splatnet2-challenge-' + user_id + '-' + challenge.key + '.json';
|
||||
const file = path.join(directory, filename);
|
||||
const image_filename = 'splatnet2-challenge-' + user_id + '-' + challenge.key + '.png';
|
||||
const image_file = path.join(directory, image_filename);
|
||||
|
||||
try {
|
||||
await fs.stat(file);
|
||||
await fs.stat(image_file);
|
||||
debug('Skipping challenge image %s, file already exists', challenge.key);
|
||||
continue;
|
||||
} catch (err) {}
|
||||
|
||||
debug('Fetching challenge image URL for %s', challenge.key);
|
||||
const share = await splatnet.shareChallenge(challenge.key, season);
|
||||
|
||||
debug('Fetching challenge image for %s', challenge.key);
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
|
||||
debug('Writing challenge image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({share}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing challenge image %s', filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
}
|
||||
}
|
||||
|
||||
async function dumpHeroRecords(splatnet: SplatNet2Api, directory: string, user_id: string) {
|
||||
debug('Fetching hero records');
|
||||
const hero = await splatnet.getHeroRecords();
|
||||
|
||||
const filename = 'splatnet2-hero-' + user_id + '-' + Date.now() + '.json';
|
||||
const file = path.join(directory, filename);
|
||||
|
||||
debug('Writing hero records %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify(hero, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ export function builder(yargs: Argv<ParentArguments>) {
|
|||
describe: 'Include coop (Salmon Run) results',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
}).option('check-updated', {
|
||||
describe: 'Only download data if user records have been updated',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -56,30 +60,51 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
|
|||
|
||||
await mkdirp(argv.directory);
|
||||
|
||||
const records = argv.coop ? await splatnet.getRecords() : null;
|
||||
const updated = argv.checkUpdated ? new Date((await splatnet.getRecords()).records.update_time * 1000) : undefined;
|
||||
|
||||
const records = await splatnet.getRecords();
|
||||
|
||||
if (argv.battles) {
|
||||
await dumpResults(splatnet, argv.directory, argv.battleImages, argv.battleSummaryImage);
|
||||
await dumpResults(splatnet, argv.directory, records.records.unique_id,
|
||||
argv.battleImages, argv.battleSummaryImage, updated);
|
||||
}
|
||||
if (argv.coop) {
|
||||
await dumpCoopResults(splatnet, argv.directory, records!.records.unique_id);
|
||||
await dumpCoopResults(splatnet, argv.directory, records.records.unique_id, updated);
|
||||
}
|
||||
}
|
||||
|
||||
async function dumpResults(splatnet: SplatNet2Api, directory: string, images = false, summary_image = images) {
|
||||
export async function dumpResults(
|
||||
splatnet: SplatNet2Api, directory: string, user_id: string,
|
||||
images = false, summary_image = images, updated?: Date
|
||||
) {
|
||||
const latest_filename = 'splatnet2-results-summary-' + user_id + '-latest.json';
|
||||
const latest_file = path.join(directory, latest_filename);
|
||||
|
||||
if (updated) {
|
||||
try {
|
||||
const {timestamp} = JSON.parse(await fs.readFile(latest_file, 'utf-8'));
|
||||
|
||||
if (timestamp > updated.getTime()) {
|
||||
debug('Skipping battle results, user records not updated');
|
||||
return;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
debug('Fetching battle results');
|
||||
const results = await splatnet.getResults();
|
||||
|
||||
const summary_filename = 'splatnet2-results-summary-' + results.unique_id + '-' + Date.now() + '.json';
|
||||
const timestamp = Date.now();
|
||||
const summary_filename = 'splatnet2-results-summary-' + results.unique_id + '-' + timestamp + '.json';
|
||||
const summary_file = path.join(directory, summary_filename);
|
||||
|
||||
debug('Writing summary %s', summary_filename);
|
||||
await fs.writeFile(summary_file, JSON.stringify(results, null, 4) + '\n', 'utf-8');
|
||||
|
||||
if (summary_image) {
|
||||
const filename = 'splatnet2-results-summary-image-' + results.unique_id + '-' + Date.now() + '.json';
|
||||
const filename = 'splatnet2-results-summary-image-' + results.unique_id + '-' + timestamp + '.json';
|
||||
const file = path.join(directory, filename);
|
||||
const image_filename = 'splatnet2-results-summary-image-' + results.unique_id + '-' + Date.now() + '.png';
|
||||
const image_filename = 'splatnet2-results-summary-image-' + results.unique_id + '-' + timestamp + '.png';
|
||||
const image_file = path.join(directory, image_filename);
|
||||
|
||||
debug('Fetching battle results summary image URL');
|
||||
|
|
@ -153,12 +178,29 @@ async function dumpResults(splatnet: SplatNet2Api, directory: string, images = f
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
async function dumpCoopResults(splatnet: SplatNet2Api, directory: string, user_id: string) {
|
||||
export async function dumpCoopResults(splatnet: SplatNet2Api, directory: string, user_id: string, updated?: Date) {
|
||||
const latest_filename = 'splatnet2-coop-summary-' + user_id + '-latest.json';
|
||||
const latest_file = path.join(directory, latest_filename);
|
||||
|
||||
if (updated) {
|
||||
try {
|
||||
const {timestamp} = JSON.parse(await fs.readFile(latest_file, 'utf-8'));
|
||||
|
||||
if (timestamp > updated.getTime()) {
|
||||
debug('Skipping coop results, user records not updated');
|
||||
return;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
debug('Fetching coop results');
|
||||
const results = await splatnet.getCoopResults();
|
||||
|
||||
const timestamp = Date.now();
|
||||
const summary_filename = 'splatnet2-coop-summary-' + user_id + '-' + Date.now() + '.json';
|
||||
const summary_file = path.join(directory, summary_filename);
|
||||
|
||||
|
|
@ -191,4 +233,6 @@ async function dumpCoopResults(splatnet: SplatNet2Api, directory: string, user_i
|
|||
nickname_and_icons,
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
await fs.writeFile(latest_file, JSON.stringify({timestamp}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getToken } from '../../util.js';
|
||||
import SplatNet2Api from '../../api/splatnet2.js';
|
||||
import { WebServiceToken } from '../../api/znc-types.js';
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user