diff --git a/src/api/splatnet3-types.ts b/src/api/splatnet3-types.ts index c06d359..17c2833 100644 --- a/src/api/splatnet3-types.ts +++ b/src/api/splatnet3-types.ts @@ -146,8 +146,7 @@ interface ExtendedSpecialWeapon extends SpecialWeapon { overlayImageUrl: string; }; } -interface ExtendedWeaponSet extends WeaponSet { - specialWeapon: ExtendedSpecialWeapon; +interface ExtendedWeapon extends Weapon { image3d: { url: string; }; @@ -161,6 +160,10 @@ interface ExtendedWeaponSet extends WeaponSet { url: string; }; } +interface ExtendedWeaponSet extends WeaponSet { + subWeapon: SubWeapon; + specialWeapon: ExtendedSpecialWeapon; +} interface VsMode { mode: string; // "REGULAR" @@ -624,10 +627,14 @@ export interface DetailFestRecordDetailResult { url: string; }; teams: DetailFestTeam[]; - playerResult: unknown | null; - myTeam: unknown | null; + playerResult: FestPlayerResult | null; + myTeam: { + color: Colour; + teamName: string; + id: string; + } | null; isVotable: boolean; - undecidedVotes: NodeListTotal; + undecidedVotes: NodeListTotal | null; }; currentPlayer: { name: string; @@ -638,7 +645,7 @@ export interface DetailFestRecordDetailResult { } interface DetailFestTeam { - result: unknown | null; + result: FestTeamResult | null; id: string; teamName: string; color: Colour; @@ -646,29 +653,98 @@ interface DetailFestTeam { url: string; }; myVoteState: FestVoteState | null; - preVotes: Votes; - votes: Votes; + preVotes: Votes | null; + votes: Votes | null; role: FestTeamRole | null; } +interface FestTeamResult { + __typename: 'FestTeamResult'; + isWinner: boolean; + horagaiRatio: number; + isHoragaiRatioTop: boolean; + voteRatio: number; + isVoteRatioTop: boolean; + regularContributionRatio: number; + isRegularContributionRatioTop: boolean; + challengeContributionRatio: number; + isChallengeContributionRatioTop: boolean; +} + +interface FestPlayerResult { + grade: string; // "Gear Ruler" + horagai: number; + regularContributionAverage: number; + regularContributionTotal: number; + challengeContributionAverage: number | null; + challengeContributionTotal: number | null; + maxFestPower: number | null; +} + /** 0eb7bac3d8aabcad0e9d663ee5b90846 DetailFestRefethQuery */ export type DetailFestRefetchResult = DetailFestRecordDetailResult; /** 92f51ed1ab462bbf1ab64cad49d36f79 DetailFestVotingStatusRefethQuery */ export type DetailFestVotingStatusRefetchResult = DetailVotingStatusResult; -/** 53ee6b6e2acc3859bf42454266d671fc DetailVotingStatusQuery */ -export interface DetailVotingStatusResult { - fest: { - __typename: 'Fest'; - id: string; - lang: string; - teams: DetailVotingStatusTeam[]; - undecidedVotes: NodeList; - }; +/** 58bdd28e3cf71c3bf38bc45836ee1e96 DetailRankingQuery */ +export interface DetailRankingResult { + fest: DetailRankingFest; } -interface DetailVotingStatusTeam> { +interface DetailRankingFest { + id: string; + lang: string; + teams: DetailRankingTeam[]; + __typename: 'Fest'; +} + +interface DetailRankingTeam { + color: Colour; + id: string; + image: { + url: string; + }; + result: { + rankingHolders: NodeList; + }; + teamName: string; +} + +interface FestRankingHolder { + byname: string; + festPower: number; + id: string; + name: string; + nameId: string; + nameplate: Nameplate; + rank: number; + weapon: FestRankingHolderWeapon; + __isPlayer: 'FestRankingHolder'; +} + +interface FestRankingHolderWeapon extends ExtendedWeapon { + originalImage: { + url: string; + }; + subWeapon: SubWeapon; + specialWeapon: SpecialWeapon; +} + +/** 53ee6b6e2acc3859bf42454266d671fc DetailVotingStatusQuery */ +export interface DetailVotingStatusResult { + fest: DetailVotingStatusFest; +} + +interface DetailVotingStatusFest { + __typename: 'Fest'; + id: string; + lang: string; + teams: DetailVotingStatusTeam[]; + undecidedVotes: NodeList | null; +} + +interface DetailVotingStatusTeam | null> { id: string; teamName: string; image: { @@ -709,15 +785,17 @@ interface FestRecord { image: { url: string; }; - playerResult: unknown | null; + playerResult: Pick | null; teams: FestRecordTeam[]; - myTeam: unknown | null; + myTeam: Pick | null; } interface FestRecordTeam { id: string; teamName: string; - result: unknown | null; + result: { + isWinner: boolean; + } | null; } /** 73b9837d0e4dd29bfa2f1a7d7ee0814a FestRecordRefetchQuery */ diff --git a/src/cli/presence-server.ts b/src/cli/presence-server.ts index 4fafaf9..55df914 100644 --- a/src/cli/presence-server.ts +++ b/src/cli/presence-server.ts @@ -162,7 +162,7 @@ export class SplatNet3User { const schedules = await this.getSchedules(); this.fest_vote_status = !schedules.currentFest || new Date(schedules.currentFest.endTime).getTime() <= Date.now() ? null : - this.fest_vote_status?.data.fest.id === schedules.currentFest.id ? + this.fest_vote_status?.data.fest?.id === schedules.currentFest.id ? await this.splatnet.getFestVotingStatusRefetch(schedules.currentFest.id) : await this.splatnet.getFestVotingStatus(schedules.currentFest.id); }, this.update_interval_fest_voting_status ?? this.update_interval); @@ -208,7 +208,7 @@ function createApp( const result: (Friend & { splatoon3?: FriendListResult['friends']['nodes'][0] | null; - splatoon3_fest_team?: DetailVotingStatusResult['fest']['teams'][0] | null; + splatoon3_fest_team?: Exclude['teams'][0] | null; })[] = []; const users = await Promise.all(user_ids.map(async id => { @@ -305,7 +305,7 @@ function createApp( let match_splatnet3_fest_team: Exclude['teams'][0] | null | undefined = undefined; let match_splatnet3_fest_team_vote_status: - DetailVotingStatusResult['fest']['teams'][0] | null | undefined = undefined; + Exclude['teams'][0] | null | undefined = undefined; const additional_response_data: { splatoon3_vs_setting?: @@ -482,9 +482,9 @@ function createFestScheduleTeam( } function createFestVoteTeam( - team: DetailVotingStatusResult['fest']['teams'][0], + team: Exclude['teams'][0], state?: FestVoteState | null -): DetailVotingStatusResult['fest']['teams'][0] { +): Exclude['teams'][0] { return { id: team.id, teamName: team.teamName, diff --git a/src/cli/splatnet3/festival.ts b/src/cli/splatnet3/festival.ts index 5813f80..c2fae27 100644 --- a/src/cli/splatnet3/festival.ts +++ b/src/cli/splatnet3/festival.ts @@ -4,6 +4,7 @@ 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 { FestState } from '../../api/splatnet3-types.js'; const debug = createDebug('cli:splatnet3:festival'); @@ -53,40 +54,42 @@ export async function handler(argv: ArgumentsCamelCase) { } const detail = await splatnet.getFestDetail(fest.id); - const votes = await splatnet.getFestVotingStatus(fest.id); + const votes = detail.data.fest.state !== FestState.CLOSED ? await splatnet.getFestVotingStatus(fest.id) : null; if (argv.jsonPrettyPrint) { - console.log(JSON.stringify({fest: detail.data.fest, votes: votes.data.fest}, null, 4)); + console.log(JSON.stringify({fest: detail.data.fest, votes: votes?.data.fest ?? undefined}, null, 4)); return; } if (argv.json) { - console.log(JSON.stringify({fest: detail.data.fest, votes: votes.data.fest})); + console.log(JSON.stringify({fest: detail.data.fest, votes: votes?.data.fest ?? undefined})); return; } console.log('Details', detail.data.fest); - const table = new Table({ - head: [ - 'Name', - 'State', - 'Team', - ], - }); + if (votes) { + const table = new Table({ + head: [ + 'Name', + 'State', + 'Team', + ], + }); - for (const team of votes.data.fest.teams) { - for (const vote of team.votes.nodes) { - table.push([vote.playerName, 'Voted', team.teamName]); + for (const team of votes.data.fest.teams) { + for (const vote of team.votes?.nodes ?? []) { + table.push([vote.playerName, 'Voted', team.teamName]); + } + for (const vote of team.preVotes?.nodes ?? []) { + table.push([vote.playerName, 'Planning to vote', team.teamName]); + } } - for (const vote of team.preVotes.nodes) { - table.push([vote.playerName, 'Planning to vote', team.teamName]); + + for (const vote of votes.data.fest.undecidedVotes?.nodes ?? []) { + table.push([vote.playerName, 'Undecided', '-']); } - } - for (const vote of votes.data.fest.undecidedVotes.nodes) { - table.push([vote.playerName, 'Undecided', '-']); + console.log('Friends votes'); + console.log(table.toString()); } - - console.log('Friends votes'); - console.log(table.toString()); } diff --git a/src/discord/titles/nintendo/splatoon3.ts b/src/discord/titles/nintendo/splatoon3.ts index ed88e50..d822506 100644 --- a/src/discord/titles/nintendo/splatoon3.ts +++ b/src/discord/titles/nintendo/splatoon3.ts @@ -34,7 +34,7 @@ export default class SplatNet3Monitor extends EmbeddedLoop { x_schedule: StageScheduleResult['xSchedules']['nodes'][0] | null = null; coop_schedule: StageScheduleResult['coopGroupingSchedule']['regularSchedules']['nodes'][0] | null = null; fest: StageScheduleResult['currentFest'] | null = null; - fest_team_voting_status: DetailVotingStatusResult['fest']['teams'][0] | null = null; + fest_team_voting_status: Exclude['teams'][0] | null = null; fest_team: Exclude['teams'][0] | null = null; constructor( @@ -136,13 +136,13 @@ export default class SplatNet3Monitor extends EmbeddedLoop { this.fest = this.cached_schedules?.data.currentFest ?? null; // Identify the user by their icon as the vote list doesn't have friend IDs - let fest_team = this.cached_voting_status?.data.fest.teams + let fest_team = this.cached_voting_status?.data.fest?.teams .find(t => t.votes.nodes.find(f => f.userIcon.url === friend?.userIcon.url)); if (this.fest && friend && (!this.cached_voting_status || (friend?.vsMode?.mode === 'FEST' && !fest_team))) { this.cached_voting_status = await this.splatnet?.getFestVotingStatus(this.fest.id) ?? null; - fest_team = this.cached_voting_status?.data.fest.teams + fest_team = this.cached_voting_status?.data.fest?.teams .find(t => t.votes.nodes.find(f => f.userIcon.url === friend?.userIcon.url)); } @@ -221,7 +221,7 @@ export function getConfigFromAppConfig( interface PresenceUrlResponse { splatoon3?: FriendListResult['friends']['nodes'][0] | null; splatoon3_fest_team?: - (DetailVotingStatusResult['fest']['teams'][0] & + (Exclude['teams'][0] & Exclude['teams'][0]) | null; splatoon3_vs_setting?: StageScheduleResult['regularSchedules']['nodes'][0]['regularMatchSetting'] |