Add home, settings, history, schedules and battle history types

This commit is contained in:
Samuel Elliott 2022-09-09 13:28:18 +01:00
parent 4a95c231d3
commit 0821235a89
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
6 changed files with 829 additions and 9 deletions

View File

@ -3,7 +3,6 @@ export interface BulletToken {
bulletToken: string;
lang: string;
is_noe_country: 'true' | unknown;
// ...
}
/** /graphql */
@ -97,3 +96,712 @@ export enum RequestId {
VsHistoryDetailPagerRefetchQuery = '994cf141e55213e6923426caf37a1934',
VsHistoryDetailQuery = 'cd82f2ade8aca7687947c5f3210805a6',
}
interface Colour {
a: number;
b: number;
g: number;
r: number;
}
interface Weapon {
name: string;
image: {
url: string;
};
id: string;
}
interface SubWeapon {
name: string;
image: {
url: string;
};
id: string;
}
interface SpecialWeapon {
name: string;
image: {
url: string;
};
id: string;
}
interface WeaponSet extends Weapon {
subWeapon: SubWeapon;
specialWeapon: SpecialWeapon;
}
interface ExtendedSpecialWeapon extends SpecialWeapon {
maskingImage: {
width: number;
height: number;
maskImageUrl: string;
overlayImageUrl: string;
};
}
interface ExtendedWeaponSet extends WeaponSet {
specialWeapon: ExtendedSpecialWeapon;
image3d: {
url: string;
};
image2d: {
url: string;
};
image3dThumbnail: {
url: string;
};
image2dThumbnail: {
url: string;
};
}
interface VsMode {
mode: string; // "REGULAR"
id: string; // "VnNNb2RlLTE="
}
interface Nameplate {
badges: [unknown | null, unknown | null, unknown | null];
background: {
textColor: Colour;
image: {
url: string;
};
id: string;
}
}
export enum Judgement {
WIN = 'WIN',
LOSE = 'LOSE',
}
export enum Species {
INKLINK = 'INKLING',
OCTOLING = 'OCTOLING',
}
interface HeadGear {
__isGear: 'HeadGear';
name: string;
image: {
url: string;
};
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
}
interface ClothingGear {
__isGear: 'ClothingGear';
name: string;
image: {
url: string;
};
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
}
interface ShoesGear {
__isGear: 'ShoesGear';
name: string;
image: {
url: string;
};
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
}
interface GearPower {
name: string; // "Ink Recovery Up", "Unknown"
image: {
url: string;
};
}
/** f8ae00773cc412a50dd41a6d9a159ddd ConfigureAnalyticsQuery */
export interface ConfigureAnalyticsResult {
playHistory: {
udemaeMax: string;
paintPointTotal: number;
gameStartTime: string;
battleNumTotal: number;
xMatchMaxAr: {
rank: string | null;
};
xMatchMaxCl: {
rank: string | null;
};
xMatchMaxGl: {
rank: string | null;
};
xMatchMaxLf: {
rank: string | null;
};
}
}
/** c0429fd738d829445e994d3370999764 useCurrentFestQuery */
export interface CurrentFestResult {
currentFest: unknown | null;
}
/** c1553ac75de0a3ea497cdbafaa93e95b BankaraBattleHistoriesQuery */
export type BankaraBattleHistoriesResult = unknown;
/** 7d8b560e31617e981cf7c8aa1ca13a00 LatestBattleHistoriesQuery */
export interface LatestBattleHistoriesResult {
latestBattleHistories: {
summary: latestBattleHistoriesSummary;
historyGroupsOnlyFirst: {
nodes: LatestBattleHistoryGroupOnlyFirst[];
};
historyGroups: {
nodes: LatestBattleHistoryGroup[];
};
};
currentFest: unknown | null;
}
interface latestBattleHistoriesSummary {
assistAverage: number;
deathAverage: number;
killAverage: number;
lose: number;
perUnitTimeMinute: number;
specialAverage: number;
win: number;
}
interface LatestBattleHistoryGroupOnlyFirst<HasOverlayImage extends boolean = false> {
historyDetails: {
nodes: LatestBattleHistoryGroupOnlyFirstDetails<HasOverlayImage>[];
};
}
interface LatestBattleHistoryGroupOnlyFirstDetails<HasOverlayImage extends boolean = false> {
player: {
weapon: {
specialWeapon: {
maskingImage: {
width: number;
height: number;
maskImageUrl: string;
overlayImageUrl: HasOverlayImage extends true ? string : never;
};
id: string;
};
id: string;
};
id: string;
};
id: string;
}
interface LatestBattleHistoryGroup {
historyDetails: {
nodes: AnyLatestBattleHistoryDetails[];
};
}
interface LatestBattleHistoryDetails {
id: string;
vsMode: VsMode;
vsRule: {
name: string; // "Turf War"
id: string; // "VnNSdWxlLTA="
};
vsStage: {
name: string; // "Mincemeat Metalworks"
id: string; // "VnNTdGFnZS02"
image: {
url: string;
};
};
judgement: Judgement;
player: {
weapon: Weapon;
id: string;
festGrade: unknown | null;
};
knockout: 'NEITHER';
myTeam: {
result: {
paintPoint: number;
paintRatio: number;
score: unknown | null;
};
};
nextHistoryDetail: unknown | null;
previousHistoryDetail: unknown | null;
}
interface AnyLatestBattleHistoryDetails extends LatestBattleHistoryDetails {
udemae: unknown | null;
bankaraMatch: unknown | null;
leagueMatch: unknown | null;
}
/** 51981299595060692440e0ca66c475a1 PrivateBattleHistoriesQuery */
export type PrivateBattleHistoriesResult = unknown;
/** 819b680b0c7962b6f7dc2a777cd8c5e4 RegularBattleHistoriesQuery */
export interface RegularBattleHistoriesResult {
regularBattleHistories: {
summary: latestBattleHistoriesSummary;
historyGroupsOnlyFirst: {
nodes: LatestBattleHistoryGroupOnlyFirst<true>[];
};
historyGroups: {
nodes: RegularBattleHistoryGroup[];
};
};
}
interface RegularBattleHistoryGroup {
lastPlayedTime: string;
historyDetails: {
nodes: LatestBattleHistoryDetails[];
};
}
/** 49dd00428fb8e9b4dde62f585c8de1e0 BattleHistoryCurrentPlayerQuery */
export interface BattleHistoryCurrentPlayerResult {
currentPlayer: {
species: Species;
weapon: {
specialWeapon: {
maskingImage: {
width: number;
height: number;
maskImageUrl: string;
overlayImageUrl: string;
};
id: string;
};
id: string;
};
};
}
/** 29957cf5d57b893934de857317cd46d8 HistoryRecordQuery */
export interface HistoryRecordResult {
currentPlayer: CurrentPlayer;
playHistory: PlayHistory;
}
interface CurrentPlayer {
__isPlayer: 'CurrentPlayer';
byname: string; // "Splatlandian Youth"
name: string;
nameId: string;
nameplate: Nameplate;
weapon: WeaponSet;
headGear: HeadGear;
clothingGear: ClothingGear;
shoesGear: ShoesGear;
}
interface PlayHistory {
currentTime: string;
gameStartTime: string;
udemaeMax: string; // "B-"
xMatchMaxAr: XMatchMax;
xMatchMaxCl: XMatchMax;
xMatchMaxGl: XMatchMax;
xMatchMaxLf: XMatchMax;
winCountTotal: number;
frequentlyUsedWeapons: Weapon[];
paintPointTotal: number;
badges: unknown[];
weaponHistory: {
nodes: WeaponHistorySeason[];
};
recentBadges: unknown[];
allBadges: unknown[];
}
interface XMatchMax {
power: null;
rank: null;
rankUpdateSeasonName: null;
powerUpdateTime: null;
}
interface WeaponHistorySeason {
seasonName: string; // "Drizzle Season 2022"
isMonthly: boolean;
startTime: string; // "2022-09-01T00:00:00Z"
endTime: string; // "2022-09-09T10:13:36Z"
weaponCategories: WeaponHistoryCategory[];
weapons: WeaponHistoryRecord[];
}
interface WeaponHistoryRecord {
weapon: WeaponHistoryWeapon;
utilRatio: number;
}
interface WeaponHistoryWeapon extends Weapon {
weaponId: number;
}
interface WeaponHistoryCategory {
weaponCategory: WeaponCategory;
utilRatio: number;
weapons: WeaponHistoryCategoryRecord[];
}
interface WeaponCategory {
name: string; // "Shooters"
category: string; // "Shooter"
id: string; // "V2VhcG9uQ2F0ZWdvcnktMA=="
}
interface WeaponHistoryCategoryRecord {
weapon: WeaponHistoryCategoryWeapon;
utilRatio: number;
}
interface WeaponHistoryCategoryWeapon extends WeaponHistoryWeapon {
weaponCategory: {
category: string; // "Shooter"
id: string; // "V2VhcG9uQ2F0ZWdvcnktMA=="
};
}
/** 61228d553e7463c203e05e7810dd79a7 SettingQuery */
export interface SettingResult {
currentPlayer: {
name: string;
userIcon: {
url: string;
};
};
}
/** 10e1d424391e78d21670227550b3509f StageScheduleQuery */
export interface StageScheduleResult {
regularSchedules: {
nodes: RegularSchedule[];
};
bankaraSchedules: {
nodes: BankaraSchedule[];
};
xSchedules: {
nodes: XSchedule[];
};
leagueSchedules: {
nodes: LeagueSchedule[];
};
coopGroupingSchedule: {
regularSchedules: {
nodes: CoopRegularSchedule[];
};
bigRunSchedules: {
nodes: unknown[];
};
};
festSchedules: {
nodes: FestSchedule[];
};
currentFest: unknown | null;
currentPlayer: {
userIcon: {
url: string;
};
};
vsStages: {
nodes: VsStageDetail[];
};
}
interface RegularSchedule {
startTime: string; // "2022-09-09T08:00:00Z"
endTime: string; // "2022-09-09T10:00:00Z"
regularMatchSetting: RegularMatchSetting;
festMatchSetting: unknown | null;
}
interface RegularMatchSetting {
__isVsSetting: 'RegularMatchSetting';
__typename: 'RegularMatchSetting';
vsStages: VsStage[];
vsRule: VsRule;
}
interface BankaraSchedule {
startTime: string; // "2022-09-09T08:00:00Z"
endTime: string; // "2022-09-09T10:00:00Z"
bankaraMatchSettings: BankaraMatchSetting[];
festMatchSetting: unknown | null;
}
interface BankaraMatchSetting {
__isVsSetting: 'BankaraMatchSetting';
__typename: 'BankaraMatchSetting';
vsStages: VsStage[];
vsRule: VsRule;
mode: BankaraMatchMode;
}
export enum BankaraMatchMode {
CHALLENGE = 'CHALLENGE',
OPEN = 'OPEN',
}
interface XSchedule {
startTime: string; // "2022-09-09T08:00:00Z"
endTime: string; // "2022-09-09T10:00:00Z"
xMatchSetting: XMatchSetting;
festMatchSetting: unknown | null;
}
interface XMatchSetting {
__isVsSetting: 'XMatchSetting';
__typename: 'XMatchSetting';
vsStages: VsStage[];
vsRule: VsRule;
}
interface LeagueSchedule {
startTime: string; // "2022-09-09T08:00:00Z"
endTime: string; // "2022-09-09T10:00:00Z"
leagueMatchSetting: LeagueMatchSetting;
festMatchSetting: unknown | null;
}
interface LeagueMatchSetting {
__isVsSetting: 'LeagueMatchSetting';
__typename: 'LeagueMatchSetting';
vsStages: VsStage[];
vsRule: VsRule;
}
interface VsStage {
id: string; // "VnNTdGFnZS0xMQ=="
vsStageId: number; // 11
name: string; // "Museum d'Alfonsino"
image: {
url: string;
};
}
interface VsRule {
name: string; // "Turf War", "Rainmaker", "Tower Control", "Splat Zones", "Clam Blitz"
rule: string; // "TURF_WAR", "GOAL", "LOFT", "AREA", "CLAM"
id: string; // "VnNSdWxlLTA=", "VnNSdWxlLTM=", "VnNSdWxlLTI=", "VnNSdWxlLTE=", "VnNSdWxlLTQ="
}
interface CoopRegularSchedule {
startTime: string; // "2022-09-08T08:00:00Z"
endTime: string; // "2022-09-10T00:00:00Z"
setting: CoopNormalSetting;
}
interface CoopNormalSetting {
__typename: 'CoopNormalSetting';
coopStage: CoopStage;
weapons: CoopWeapon[];
}
interface CoopStage {
name: string; // "Sockeye Station"
coopStageId: number; // 2
thumbnailImage: {
url: string;
};
image: {
url: string;
};
id: string; // "Q29vcFN0YWdlLTI="
}
interface CoopWeapon {
name: string; // "Splattershot Jr."
image: {
url: string;
};
}
interface FestSchedule {
startTime: string; // "2022-09-09T08:00:00Z"
endTime: string; // "2022-09-09T10:00:00Z"
festMatchSetting: unknown | null;
}
interface VsStageDetail {
stageId: number; // 1
id: string; // "VnNTdGFnZS0x"
originalImage: {
url: string;
};
name: string; // "Scorch Gorge"
stats: VsStageStats | null;
}
interface VsStageStats {
winRateAr: null;
winRateLf: null;
winRateGl: null;
winRateCl: null;
}
/** dba47124d5ec3090c97ba17db5d2f4b3 HomeQuery */
export interface HomeResult {
currentPlayer: {
weapon: {
image: {
url: string;
};
id: string;
};
};
banners: HomeBanner[];
friends: {
nodes: unknown[];
totalCount: number;
};
footerMessages: unknown[];
}
interface HomeBanner {
image: {
url: string;
width: number;
height: number;
};
message: string;
jumpTo: string;
}
/** cd82f2ade8aca7687947c5f3210805a6 VsHistoryDetailQuery */
export interface VsHistoryDetailResult {
vsHistoryDetail: VsHistoryDetail;
}
interface VsHistoryDetail {
__typename: 'VsHistoryDetail';
id: string;
vsRule: VsRule;
vsMode: VsMode;
player: VsHistoryDetailPlayer;
judgement: Judgement;
myTeam: VsHistoryDetailTeam;
vsStage: {
name: string; // "Mincemeat Metalworks"
image: {
url: string;
};
id: string; // "VnNTdGFnZS02"
};
festMatch: unknown | null;
knockout: 'NEITHER';
otherTeams: VsHistoryDetailTeam[];
bankaraMatch: unknown | null;
xMatch: unknown | null;
duration: number;
playedTime: string;
awards: Award[];
leagueMatch: unknown | null;
nextHistoryDetail: unknown | null;
previousHistoryDetail: unknown | null;
}
interface BaseVsPlayer {
__isPlayer: 'VsPlayer';
byname: string; // "Splatlandian Youth"
name: string;
nameId: string;
nameplate: Nameplate;
id: string;
paint: number;
}
interface VsHistoryDetailPlayer extends BaseVsPlayer {
headGear: VsHistoryDetailPlayerHeadGear;
clothingGear: VsHistoryDetailPlayerClothingGear;
shoesGear: VsHistoryDetailPlayerShoesGear;
}
interface VsHistoryDetailPlayerHeadGear extends HeadGear {
originalImage: {
url: string;
};
brand: GearBrand;
}
interface VsHistoryDetailPlayerClothingGear extends ClothingGear {
originalImage: {
url: string;
};
brand: GearBrand;
}
interface VsHistoryDetailPlayerShoesGear extends ShoesGear {
originalImage: {
url: string;
};
brand: GearBrand;
}
interface VsHistoryDetailTeam {
color: Colour;
judgement: Judgement;
result: {
paintRatio: number;
score: unknown | null;
noroshi: unknown | null;
};
tricolorRole: unknown | null;
festTeamName: unknown | null;
players: VsPlayer[];
order: number;
}
interface VsPlayer extends BaseVsPlayer {
__typename: 'VsPlayer';
isMyself: boolean;
weapon: ExtendedWeaponSet;
headGear: VsPlayerHeadGear;
clothingGear: VsPlayerClothingGear;
shoesGear: VsPlayerShoesGear;
species: Species;
result: {
kill: number;
death: number;
assist: number;
special: number;
noroshiTry: unknown | null;
};
festDragonCert: 'NONE';
}
interface GearBrand {
name: string; // "Forge"
image: {
url: string;
};
id: string; // "QnJhbmQtNQ=="
}
interface VsPlayerHeadGear {
__isGear: 'HeadGear';
name: string;
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
originalImage: {
url: string;
};
thumbnailImage: {
url: string;
};
brand: GearBrand;
}
interface VsPlayerClothingGear {
__isGear: 'ClothingGear';
name: string;
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
originalImage: {
url: string;
};
thumbnailImage: {
url: string;
};
brand: GearBrand;
}
interface VsPlayerShoesGear {
__isGear: 'ShoesGear';
name: string;
primaryGearPower: GearPower;
additionalGearPowers: GearPower[];
originalImage: {
url: string;
};
thumbnailImage: {
url: string;
};
brand: GearBrand;
}
interface Award {
name: string; // "#1 Turf Inker"
rank: string; // "GOLD"
}

View File

@ -5,7 +5,7 @@ import { NintendoAccountUser } from './na.js';
import { defineResponse, ErrorResponse } from './util.js';
import CoralApi from './coral.js';
import { timeoutSignal } from '../util/misc.js';
import { BulletToken, GraphQLRequest, GraphQLResponse, RequestId } from './splatnet3-types.js';
import { BankaraBattleHistoriesResult, BattleHistoryCurrentPlayerResult, BulletToken, CurrentFestResult, GraphQLRequest, GraphQLResponse, HistoryRecordResult, HomeResult, LatestBattleHistoriesResult, PrivateBattleHistoriesResult, RegularBattleHistoriesResult, RequestId, SettingResult, StageScheduleResult, VsHistoryDetailResult } from './splatnet3-types.js';
const debug = createDebug('nxapi:api:splatnet3');
@ -75,23 +75,49 @@ export default class SplatNet3Api {
}
async getHome() {
return this.persistedQuery(RequestId.HomeQuery, {});
return this.persistedQuery<HomeResult>(RequestId.HomeQuery, {});
}
async getCurrentFest() {
return this.persistedQuery(RequestId.CurrentFestQuery, {});
return this.persistedQuery<CurrentFestResult>(RequestId.CurrentFestQuery, {});
}
async getConfigureAnalytics() {
return this.persistedQuery(RequestId.ConfigureAnalyticsQuery, {});
}
async getSettings() {
return this.persistedQuery<SettingResult>(RequestId.SettingQuery, {});
}
async getHistoryRecords() {
return this.persistedQuery(RequestId.HistoryRecordQuery, {});
return this.persistedQuery<HistoryRecordResult>(RequestId.HistoryRecordQuery, {});
}
async getSchedules() {
return this.persistedQuery(RequestId.StageScheduleQuery, {});
return this.persistedQuery<StageScheduleResult>(RequestId.StageScheduleQuery, {});
}
async getBattleHistoryCurrentPlayer() {
return this.persistedQuery<BattleHistoryCurrentPlayerResult>(RequestId.BattleHistoryCurrentPlayerQuery, {});
}
async getLatestBattleHistories() {
return this.persistedQuery<LatestBattleHistoriesResult>(RequestId.LatestBattleHistoriesQuery, {});
}
async getRegularBattleHistories() {
return this.persistedQuery<RegularBattleHistoriesResult>(RequestId.RegularBattleHistoriesQuery, {});
}
async getPrivateBattleHistories() {
return this.persistedQuery<PrivateBattleHistoriesResult>(RequestId.PrivateBattleHistoriesQuery, {});
}
async getVsHistoryDetail(id: string) {
return this.persistedQuery<VsHistoryDetailResult>(RequestId.VsHistoryDetailQuery, {
vsResultId: id,
});
}
static async createWithCoral(nso: CoralApi, user: NintendoAccountUser) {

View File

@ -0,0 +1,85 @@
import createDebug from 'debug';
import Table from '../util/table.js';
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 { Judgement } from '../../api/splatnet3-types.js';
const debug = createDebug('cli:splatnet3: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 getBulletToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
const results = await splatnet.getLatestBattleHistories();
if (argv.jsonPrettyPrint) {
console.log(JSON.stringify(results.data, null, 4));
return;
}
if (argv.json) {
console.log(JSON.stringify(results.data));
return;
}
console.log('Summary', results.data.latestBattleHistories.summary);
const table = new Table({
head: [
'ID',
'Mode',
'Rule',
'Stage',
'Result',
],
});
for (const group of results.data.latestBattleHistories.historyGroups.nodes) {
for (const result of group.historyDetails.nodes) {
const match = Buffer.from(result.id, 'base64').toString().match(/^VsHistoryDetail-(u-[0-9a-z]{20}):RECENT:((\d+T\d+)_([0-9a-f-]+))$/);
const id_str = match ? match[2] : result.id;
table.push([
id_str,
(result.vsMode.mode === 'REGULAR' ? '\u001b[32m' :
// result.vsMode.mode === 'ranked' ? '\u001b[33m' :
// result.vsMode.mode === 'league' ? '\u001b[31m' :
// result.vsMode.mode === 'private' ? '\u001b[35m' :
'') +
result.vsMode.mode + '\u001b[0m',
result.vsRule.name,
result.vsStage.name,
(result.judgement === Judgement.WIN ? '\u001b[32m' : '\u001b[31m') +
result.judgement + '\u001b[0m',
]);
}
}
console.log(table.toString());
}

View File

@ -1,3 +1,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';

View File

@ -39,11 +39,11 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
const schedules = await splatnet.getSchedules();
if (argv.jsonPrettyPrint) {
console.log(JSON.stringify(schedules, null, 4));
console.log(JSON.stringify(schedules.data, null, 4));
return;
}
if (argv.json) {
console.log(JSON.stringify(schedules));
console.log(JSON.stringify(schedules.data));
return;
}

View File

@ -29,7 +29,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
await storage.getItem('NintendoAccountToken.' + usernsid);
const {splatnet, data} = await getBulletToken(storage, token, argv.zncProxyUrl, argv.autoUpdateSession);
const history: any = await splatnet.getHistoryRecords();
const history = await splatnet.getHistoryRecords();
console.log('Player %s#%s (title %s, first played %s)',
history.data.currentPlayer.name,