Add a command to download all battle results from SplatNet 3

This commit is contained in:
Samuel Elliott 2022-09-09 21:53:46 +01:00
parent 0821235a89
commit 203a54be44
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
4 changed files with 129 additions and 1 deletions

View File

@ -655,6 +655,20 @@ interface HomeBanner {
jumpTo: string;
}
/** 994cf141e55213e6923426caf37a1934 VsHistoryDetailPagerRefetchQuery */
export interface VsHistoryDetailPagerRefetchQueryResult {
vsHistoryDetail: {
__typename: 'VsHistoryDetail';
nextHistoryDetail: {
id: string;
} | null;
previousHistoryDetail: {
id: string;
} | null;
id: string;
};
}
/** cd82f2ade8aca7687947c5f3210805a6 VsHistoryDetailQuery */
export interface VsHistoryDetailResult {
vsHistoryDetail: VsHistoryDetail;

View File

@ -110,16 +110,30 @@ export default class SplatNet3Api {
return this.persistedQuery<RegularBattleHistoriesResult>(RequestId.RegularBattleHistoriesQuery, {});
}
async getBankaraBattleHistories() {
return this.persistedQuery<BankaraBattleHistoriesResult>(RequestId.BankaraBattleHistoriesQuery, {});
}
async getPrivateBattleHistories() {
return this.persistedQuery<PrivateBattleHistoriesResult>(RequestId.PrivateBattleHistoriesQuery, {});
}
async getVsHistoryDetail(id: string) {
async getBattleHistoryDetail(id: string) {
return this.persistedQuery<VsHistoryDetailResult>(RequestId.VsHistoryDetailQuery, {
vsResultId: id,
});
}
async getBattleHistoryDetailPagerRefetch(id: string) {
return this.persistedQuery<VsHistoryDetailResult>(RequestId.VsHistoryDetailPagerRefetchQuery, {
vsResultId: id,
});
}
async getCoopHistory() {
return this.persistedQuery<unknown>(RequestId.CoopHistoryQuery, {});
}
static async createWithCoral(nso: CoralApi, user: NintendoAccountUser) {
const data = await this.loginWithCoral(nso, user);
return {splatnet: this.createWithSavedToken(data), data};

View File

@ -0,0 +1,99 @@
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import createDebug from 'debug';
import mkdirp from 'mkdirp';
import type { Arguments as ParentArguments } from '../splatnet3.js';
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
import { initStorage } from '../../util/storage.js';
import { getBulletToken } from '../../common/auth/splatnet3.js';
import SplatNet3Api from '../../api/splatnet3.js';
const debug = createDebug('cli:splatnet3:dump-results');
export const command = 'dump-results [directory]';
export const desc = 'Download all battle and coop results';
export function builder(yargs: Argv<ParentArguments>) {
return yargs.positional('directory', {
describe: 'Directory to write record data to',
type: 'string',
}).option('user', {
describe: 'Nintendo Account ID',
type: 'string',
}).option('token', {
describe: 'Nintendo Account session token',
type: 'string',
}).option('battles', {
describe: 'Include regular/ranked/private/festival battle results',
type: 'boolean',
default: true,
}).option('coop', {
describe: 'Include coop (Salmon Run) results',
type: 'boolean',
default: true,
});
}
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 getBulletToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
const directory = argv.directory ?? path.join(argv.dataPath, 'splatnet3');
await mkdirp(directory);
if (argv.battles) {
await dumpResults(splatnet, directory);
}
// if (argv.coop) {
// await dumpCoopResults(splatnet, directory);
// }
}
export async function dumpResults(
splatnet: SplatNet3Api, directory: string
) {
debug('Fetching battle results');
const player = await splatnet.getBattleHistoryCurrentPlayer();
const battles = await splatnet.getLatestBattleHistories();
const battles_regular = await splatnet.getRegularBattleHistories();
const battles_anarchy = await splatnet.getBankaraBattleHistories();
const battles_private = await splatnet.getPrivateBattleHistories();
const skipped = [];
// Reverse battle history order so oldest records are downloaded first
for (const group of battles.data.latestBattleHistories.historyGroups.nodes.reverse()) {
for (const item of group.historyDetails.nodes.reverse()) {
const id_str = Buffer.from(item.id, 'base64').toString() || item.id;
const filename = 'splatnet3-result-' + id_str + '.json';
const file = path.join(directory, filename);
try {
await fs.stat(file);
skipped.push(item.id);
} catch (err) {
debug('Fetching battle result %s', id_str);
const result = await splatnet.getBattleHistoryDetail(item.id);
const pager = await splatnet.getBattleHistoryDetailPagerRefetch(item.id);
debug('Writing %s', filename);
await fs.writeFile(file, JSON.stringify({
result: result.data.vsHistoryDetail,
}, null, 4) + '\n', 'utf-8');
}
}
}
if (skipped.length) {
if (skipped.length === 1) debug('Skipped battle result %s, file already exists', skipped[0]);
else debug('Skipped %d battle results, files already exist', skipped.length);
}
}

View File

@ -2,3 +2,4 @@ export * as user from './user.js';
export * as token from './token.js';
export * as schedule from './schedule.js';
export * as battles from './battles.js';
export * as dumpResults from './dump-results.js';