From 616c40888a4d48ce110599f47f05a6a28495a444 Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Fri, 21 Oct 2022 23:31:30 +0100 Subject: [PATCH] Update splatnet3-types --- package-lock.json | 14 +- package.json | 2 +- src/api/splatnet3.ts | 369 +++++++++++++++++++++-- src/cli/presence-server.ts | 18 +- src/cli/util/storage.ts | 2 +- src/discord/titles/nintendo/splatoon3.ts | 10 +- src/util/loop.ts | 4 +- 7 files changed, 373 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95fea7e..19a3ac0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "node-notifier": "^10.0.1", "node-persist": "^3.1.0", "read": "^1.0.7", - "splatnet3-types": "^0.1.20221020213035", + "splatnet3-types": "^0.1.20221021222422", "supports-color": "^8.1.1", "tslib": "^2.4.0", "uuid": "^8.3.2", @@ -4362,9 +4362,9 @@ "dev": true }, "node_modules/splatnet3-types": { - "version": "0.1.20221020213035", - "resolved": "https://registry.npmjs.org/splatnet3-types/-/splatnet3-types-0.1.20221020213035.tgz", - "integrity": "sha512-IjetRtZCcr4kKVdKGVsmiqAHt8b6WqyOJB+8+2pj5imDiAXBWqTKeKp9++CkdIpNPRBO4BLpWUwM5wHaYI0YGA==" + "version": "0.1.20221021222422", + "resolved": "https://registry.npmjs.org/splatnet3-types/-/splatnet3-types-0.1.20221021222422.tgz", + "integrity": "sha512-eAzmc5x4/tVeFGXs0yASlw/fCte4YTV/BmeimkJvH83Y38mOV7J9fghcaKJyDMMvRTcp0hVr3v3feOCzh90o+g==" }, "node_modules/sprintf-js": { "version": "1.1.2", @@ -8393,9 +8393,9 @@ "dev": true }, "splatnet3-types": { - "version": "0.1.20221020213035", - "resolved": "https://registry.npmjs.org/splatnet3-types/-/splatnet3-types-0.1.20221020213035.tgz", - "integrity": "sha512-IjetRtZCcr4kKVdKGVsmiqAHt8b6WqyOJB+8+2pj5imDiAXBWqTKeKp9++CkdIpNPRBO4BLpWUwM5wHaYI0YGA==" + "version": "0.1.20221021222422", + "resolved": "https://registry.npmjs.org/splatnet3-types/-/splatnet3-types-0.1.20221021222422.tgz", + "integrity": "sha512-eAzmc5x4/tVeFGXs0yASlw/fCte4YTV/BmeimkJvH83Y38mOV7J9fghcaKJyDMMvRTcp0hVr3v3feOCzh90o+g==" }, "sprintf-js": { "version": "1.1.2", diff --git a/package.json b/package.json index 239f782..eec4283 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "node-notifier": "^10.0.1", "node-persist": "^3.1.0", "read": "^1.0.7", - "splatnet3-types": "^0.1.20221020213035", + "splatnet3-types": "^0.1.20221021222422", "supports-color": "^8.1.1", "tslib": "^2.4.0", "uuid": "^8.3.2", diff --git a/src/api/splatnet3.ts b/src/api/splatnet3.ts index bd2e7e9..7fa9012 100644 --- a/src/api/splatnet3.ts +++ b/src/api/splatnet3.ts @@ -1,9 +1,9 @@ import fetch, { Response } from 'node-fetch'; import createDebug from 'debug'; -import { GraphQLRequest, GraphQLResponse, KnownRequestId, RequestId, RequestParametersFor, RequestResultFor } from 'splatnet3-types/splatnet3'; +import { GraphQLRequest, GraphQLResponse, KnownRequestId, MyOutfitInput, RequestId, ResultTypes, VariablesTypes } from 'splatnet3-types/splatnet3'; import { WebServiceToken } from './coral-types.js'; import { NintendoAccountUser } from './na.js'; -import { defineResponse, ErrorResponse, HasResponse } from './util.js'; +import { defineResponse, ErrorResponse, HasResponse, ResponseSymbol } from './util.js'; import CoralApi from './coral.js'; import { timeoutSignal } from '../util/misc.js'; import { BulletToken } from './splatnet3-types.js'; @@ -35,6 +35,8 @@ const AUTH_ERROR_CODES = { 599: 'ERROR_SERVER', } as const; +const REPLAY_CODE_REGEX = /^[A-Z0-9]{16}$/; + export default class SplatNet3Api { onTokenShouldRenew: ((remaining: number, res: Response) => Promise) | null = null; onTokenExpired: ((res: Response) => Promise) | null = null; @@ -85,7 +87,7 @@ export default class SplatNet3Api { }).finally(() => { this._renewToken = null; }); - return this.fetch(url, method, body, headers, _log, _attempt + 1); + return this.fetch(url, method, body, headers, _log, _attempt + 1); } if (response.status !== 200) { @@ -109,11 +111,18 @@ export default class SplatNet3Api { } async persistedQuery< - I extends string, - T = I extends KnownRequestId ? RequestResultFor : unknown, - V = I extends KnownRequestId ? RequestParametersFor : unknown, - >(id: I, variables: V) { - const req: GraphQLRequest = { + T = unknown, V = unknown, + + /** @private */ + _Id extends string = string, + /** @private */ + _Result extends (T extends unknown ? _Id extends KnownRequestId ? ResultTypes[_Id] : unknown : T) = + (T extends unknown ? _Id extends KnownRequestId ? ResultTypes[_Id] : unknown : T), + /** @private */ + _Variables extends (V extends unknown ? _Id extends KnownRequestId ? VariablesTypes[_Id] : unknown : V) = + (V extends unknown ? _Id extends KnownRequestId ? VariablesTypes[_Id] : unknown : V), + >(id: _Id, variables: _Variables) { + const req: GraphQLRequest<_Variables> = { variables, extensions: { persistedQuery: { @@ -123,124 +132,442 @@ export default class SplatNet3Api { }, }; - const data = await this.fetch>('/graphql', 'POST', JSON.stringify(req), undefined, + const data = await this.fetch>('/graphql', 'POST', JSON.stringify(req), undefined, 'graphql query ' + id); + if ('errors' in data) { + throw new ErrorResponse('[splatnet3] GraphQL error: ' + data.errors.map(e => e.message).join(', '), + data[ResponseSymbol], data); + } + return data; } - async getHome() { - return this.persistedQuery(RequestId.HomeQuery, {}); - } - + /** * */ async getCurrentFest() { return this.persistedQuery(RequestId.CurrentFestQuery, {}); } + /** * */ async getConfigureAnalytics() { return this.persistedQuery(RequestId.ConfigureAnalyticsQuery, {}); } + /** / */ + async getHome() { + return this.persistedQuery(RequestId.HomeQuery, {}); + } + + /** / -> /setting */ async getSettings() { return this.persistedQuery(RequestId.SettingQuery, {}); } + /** / -> /photo_album */ + async getPhotoAlbum() { + return this.persistedQuery(RequestId.PhotoAlbumQuery, {}); + } + + /** / -> /photo_album -> pull-to-refresh */ + async getPhotoAlbumRefetch() { + return this.persistedQuery(RequestId.PhotoAlbumRefetchQuery, {}); + } + + /** / -> /catalog_record */ + async getCatalog() { + return this.persistedQuery(RequestId.CatalogQuery, {}); + } + + /** / -> /catalog_record -> pull-to-refresh */ + async getCatalogRefetch() { + return this.persistedQuery(RequestId.CatalogRefetchQuery, {}); + } + + /** / -> /checkin */ + async getCheckinHistory() { + return this.persistedQuery(RequestId.CheckinQuery, {}); + } + + /** / -> /checkin */ + async checkin(id: string) { + return this.persistedQuery(RequestId.CheckinWithQRCodeMutation, { + checkinEventId: id, + }); + } + + /** / -> /friends */ + async getFriends() { + return this.persistedQuery(RequestId.FriendListQuery, {}); + } + + /** / -> /friends -> pull-to-refresh */ + async getFriendsRefetch() { + return this.persistedQuery(RequestId.FriendListRefetchQuery, {}); + } + + /** / -> /hero_record */ + async getHeroRecords() { + return this.persistedQuery(RequestId.HeroHistoryQuery, {}); + } + + /** / -> /hero_record -> pull-to-refresh */ + async getHeroRecordsRefetch() { + return this.persistedQuery(RequestId.HeroHistoryRefetchQuery, {}); + } + + /** / -> /history_record */ + async getHistoryRecords() { + return this.persistedQuery(RequestId.HistoryRecordQuery, {}); + } + + /** / -> /history_record -> pull-to-refresh */ + async getHistoryRecordsRefetch() { + return this.persistedQuery(RequestId.HistoryRecordRefetchQuery, {}); + } + + /** / -> /schedule */ + async getSchedules() { + return this.persistedQuery(RequestId.StageScheduleQuery, {}); + } + + /** / -> /stage_record */ + async getStageRecords() { + return this.persistedQuery(RequestId.StageRecordQuery, {}); + } + + /** / -> /stage_record -> pull-to-refresh */ + async getStageRecordsRefetch() { + return this.persistedQuery(RequestId.StageRecordsRefetchQuery, {}); + } + + /** / -> /weapon_record */ + async getWeaponRecords() { + return this.persistedQuery(RequestId.WeaponRecordQuery, {}); + } + + /** / -> /weapon_record -> pull-to-refresh */ + async getWeaponRecordsRefetch() { + return this.persistedQuery(RequestId.WeaponRecordsRefetchQuery, {}); + } + + // + // Wandercrust + // + + /** / -> /challenge */ + async getChallengeHome() { + return this.persistedQuery(RequestId.ChallengeQuery, {}); + } + + /** / -> /challenge -> pull-to-refresh */ + async getChallengeHomeRefetch() { + return this.persistedQuery(RequestId.ChallengeRefetchQuery, {}); + } + + /** / -> /challenge -> /challenge/{id} */ + async getChallengeJourney(id: string) { + return this.persistedQuery(RequestId.JourneyQuery, { + id, + }); + } + + /** / -> /challenge -> /challenge/{id} -> pull-to-refresh */ + async getChallengeJourneyRefetch(id: string) { + return this.persistedQuery(RequestId.JourneyRefetchQuery, { + id, + }); + } + + /** / -> /challenge -> /challenge/{id} -> /challenge/{id}/*s */ + async getChallengeJourneyChallenges(id: string) { + return this.persistedQuery(RequestId.JourneyChallengeDetailQuery, { + journeyId: id, + }); + } + + /** / -> /challenge -> /challenge/{id} -> /challenge/{id}/* -> pull-to-refresh */ + async getChallengeJourneyChallengesRefetch(id: string) { + return this.persistedQuery(RequestId.JourneyChallengeDetailRefetchQuery, { + journeyId: id, + }); + } + + /** / -> /challenge -> /challenge/{id} -> /challenge/{id}/* -> support */ + async supportChallenge(id: string) { + return this.persistedQuery(RequestId.SupportButton_SupportChallengeMutation, { + id, + }); + } + + // + // Splatfests + // + + /** / -> /fest_record */ async getFestRecords() { return this.persistedQuery(RequestId.FestRecordQuery, {}); } + /** / -> /fest_record -> pull-to-refresh */ async getFestRecordsRefetch() { return this.persistedQuery(RequestId.FestRecordRefetchQuery, {}); } + /** / -> /fest_record/{id} */ async getFestDetail(id: string) { return this.persistedQuery(RequestId.DetailFestRecordDetailQuery, { festId: id, }); } + /** / -> /fest_record -> /fest_record/{id} -> pull-to-refresh */ async getFestDetailRefetch(id: string) { return this.persistedQuery(RequestId.DetailFestRefethQuery, { festId: id, }); } + /** / -> /fest_record -> /fest_record/{id} - not closed -> /fest_record/voting_status/{id} */ async getFestVotingStatus(id: string) { return this.persistedQuery(RequestId.DetailVotingStatusQuery, { festId: id, }); } + /** / -> /fest_record -> /fest_record/{id} - not closed -> /fest_record/voting_status/{id} -> pull-to-refresh */ async getFestVotingStatusRefetch(id: string) { return this.persistedQuery(RequestId.DetailFestVotingStatusRefethQuery, { festId: id, }); } + /** / -> /fest_record -> /fest_record/{id} - not closed -> /fest_record/voting_status/{id} - not voted in game */ async updateFestPoll(id: string) { return this.persistedQuery(RequestId.VotesUpdateFestVoteMutation, { teamId: id, }); } - async getFriends() { - return this.persistedQuery(RequestId.FriendListQuery, {}); + /** / -> /fest_record -> /fest_record/{id} - closed -> /fest_record/ranking/{id} */ + async getFestRanking(id: string) { + return this.persistedQuery(RequestId.DetailRankingQuery, { + festId: id, + }); } - async getFriendsRefetch() { - return this.persistedQuery(RequestId.FriendListRefetchQuery, {}); + // + // SplatNet Shop + // + + /** / -> /gesotown */ + async getSaleGear() { + return this.persistedQuery(RequestId.GesotownQuery, {}); } - async getHistoryRecords() { - return this.persistedQuery(RequestId.HistoryRecordQuery, {}); + /** / -> /gesotown -> pull-to-refresh */ + async getSaleGearRefetch() { + return this.persistedQuery(RequestId.GesotownRefetchQuery, {}); } - async getSchedules() { - return this.persistedQuery(RequestId.StageScheduleQuery, {}); + /** / -> /gesotown -> /gesotown/{id} */ + async getSaleGearDetail(id: string) { + return this.persistedQuery(RequestId.SaleGearDetailQuery, { + saleGearId: id, + }); } + /** / -> /gesotown -> /gesotown/{id} -> order */ + async orderSaleGear(id: string, force = false) { + return this.persistedQuery(RequestId.SaleGearDetailOrderGesotownGearMutation, { + input: { + id, + isForceOrder: force, + }, + }); + } + + // + // Freshest Fits/my outfits + // + + /** / -> /my_outfits */ + async getMyOutfits() { + return this.persistedQuery(RequestId.MyOutfitsQuery, {}); + } + + /** / -> /my_outfits -> pull-to-refresh */ + async getMyOutfitsRefetch() { + return this.persistedQuery(RequestId.MyOutfitsRefetchQuery, {}); + } + + /** / -> /my_outfits -> /my_outfits/{id} */ + async getMyOutfitDetail(id: string) { + return this.persistedQuery(RequestId.MyOutfitDetailQuery, { + myOutfitId: id, + }); + } + + /** / -> /my_outfits -> /my_outfits/{id / create} */ + async getEquipmentFilters(id: string) { + return this.persistedQuery(RequestId.MyOutfitCommonDataFilteringConditionQuery, {}); + } + + /** / -> /my_outfits -> /my_outfits/{id / create} */ + async getEquipment(id: string) { + return this.persistedQuery(RequestId.MyOutfitCommonDataEquipmentsQuery, {}); + } + + /** / -> /my_outfits -> /my_outfits/{id / create} */ + async createOutfit(data: MyOutfitInput) { + return this.persistedQuery(RequestId.CreateMyOutfitMutation, { + input: { + myOutfit: data, + }, + connections: [ + 'client:root:__connection_myOutfits_connection', + ], + }); + } + + /** / -> /my_outfits -> /my_outfits/{id / create} */ + async updateOutfit(id: string, data: MyOutfitInput) { + return this.persistedQuery(RequestId.UpdateMyOutfitMutation, { + input: { + myOutfit: { + id, + ...data, + }, + }, + }); + } + + // + // Replays + // + + /** / -> /replay */ + async getReplays() { + return this.persistedQuery(RequestId.ReplayQuery, {}); + } + + /** / -> /replay -> pull-to-refetch */ + async getReplaysRefetch() { + return this.persistedQuery(RequestId.ReplayUploadedReplayListRefetchQuery, {}); + } + + /** / -> /replay -> enter code */ + async getReplaySearchResult(code: string) { + if (!REPLAY_CODE_REGEX.test(code)) throw new Error('Invalid replay code'); + + return this.persistedQuery(RequestId.DownloadSearchReplayQuery, { + code, + }); + } + + /** / -> /replay -> enter code -> download */ + async reserveReplayDownload(id: string) { + return this.persistedQuery(RequestId.ReplayModalReserveReplayDownloadMutation, { + input: { + id, + }, + }); + } + + // + // Battle history + // + + /** / -> /history */ async getBattleHistoryCurrentPlayer() { return this.persistedQuery(RequestId.BattleHistoryCurrentPlayerQuery, {}); } + /** / -> /history */ async getLatestBattleHistories() { return this.persistedQuery(RequestId.LatestBattleHistoriesQuery, {}); } + /** / -> /history */ async getRegularBattleHistories() { return this.persistedQuery(RequestId.RegularBattleHistoriesQuery, {}); } + /** / -> /history */ async getBankaraBattleHistories() { return this.persistedQuery(RequestId.BankaraBattleHistoriesQuery, {}); } + /** / -> /history */ async getPrivateBattleHistories() { return this.persistedQuery(RequestId.PrivateBattleHistoriesQuery, {}); } + /** / -> /history -> /history/detail/{id} */ async getBattleHistoryDetail(id: string) { return this.persistedQuery(RequestId.VsHistoryDetailQuery, { vsResultId: id, }); } + /** / -> /history -> /history/detail/{id} -> pull-to-refresh */ async getBattleHistoryDetailPagerRefetch(id: string) { return this.persistedQuery(RequestId.VsHistoryDetailPagerRefetchQuery, { vsResultId: id, }); } + /** / -> /history -> /history/detail/* -> latest */ + async getBattleHistoryLatest() { + return this.persistedQuery(RequestId.PagerLatestVsDetailQuery, {}); + } + + /** / -> /history -> /history/detail/* -> latest */ + async getBattleHistoryPagerUpdateByVsMode() { + return this.persistedQuery(RequestId.PagerUpdateBattleHistoriesByVsModeQuery, { + isBankara: false, + isLeague: false, + isPrivate: false, + isRegular: false, + isXBattle: false, + }); + } + + // + // Salmon Run + // + + /** / -> /coop */ async getCoopHistory() { return this.persistedQuery(RequestId.CoopHistoryQuery, {}); } + /** / -> /coop */ + async getCoopHistoryRefetch() { + return this.persistedQuery(RequestId.RefetchableCoopHistory_CoopResultQuery, {}); + } + + /** / -> /coop -> /coop/{id} */ async getCoopHistoryDetail(id: string) { return this.persistedQuery(RequestId.CoopHistoryDetailQuery, { coopHistoryDetailId: id, }); } + /** / -> /coop -> /coop/{id} -> pull-to-refresh */ + async getCoopHistoryDetailRefetch(id: string) { + return this.persistedQuery(RequestId.CoopHistoryDetailRefetchQuery, { + id, + }); + } + + /** / -> /coop -> /coop/* -> latest */ + async getCoopHistoryLatest() { + return this.persistedQuery(RequestId.CoopPagerLatestCoopQuery, {}); + } + + // + // + async renewTokenWithCoral(nso: CoralApi, user: NintendoAccountUser) { const data = await SplatNet3Api.loginWithCoral(nso, user); this.setTokenWithSavedToken(data); diff --git a/src/cli/presence-server.ts b/src/cli/presence-server.ts index 3313114..48077ce 100644 --- a/src/cli/presence-server.ts +++ b/src/cli/presence-server.ts @@ -2,7 +2,7 @@ import * as net from 'node:net'; import createDebug from 'debug'; import express from 'express'; import * as persist from 'node-persist'; -import { BankaraMatchMode, BankaraMatchSetting, CoopSetting, DetailVotingStatusResult, FestMatchSetting, FestState, FestTeam_schedule, FestTeam_votingStatus, FestVoteState, Fest_schedule, Friend as SplatNetFriend, FriendListResult, FriendOnlineState, GraphQLResponse, LeagueMatchSetting, RegularMatchSetting, StageScheduleResult, XMatchSetting } from 'splatnet3-types/splatnet3'; +import { BankaraMatchMode, BankaraMatchSetting, CoopSetting, DetailVotingStatusResult, FestMatchSetting, FestState, FestTeam_schedule, FestTeam_votingStatus, FestVoteState, Fest_schedule, Friend as SplatNetFriend, FriendListResult, FriendOnlineState, GraphQLSuccessResponse, LeagueMatchSetting, RegularMatchSetting, StageScheduleResult, XMatchSetting } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../cli.js'; import { ArgumentsCamelCase, Argv, YargsArguments } from '../util/yargs.js'; import { initStorage } from '../util/storage.js'; @@ -92,8 +92,8 @@ export class SplatNet3User { created_at = Date.now(); expires_at = Infinity; - schedules: GraphQLResponse | null = null; - fest_vote_status: GraphQLResponse | null = null; + schedules: GraphQLSuccessResponse | null = null; + fest_vote_status: GraphQLSuccessResponse | null = null; promise = new Map>(); @@ -109,7 +109,7 @@ export class SplatNet3User { constructor( public splatnet: SplatNet3Api, public data: SavedBulletToken, - public friends: GraphQLResponse, + public friends: GraphQLSuccessResponse, ) {} private async update(key: keyof SplatNet3User['updated'], callback: () => Promise, ttl: number) { @@ -261,19 +261,19 @@ function createApp( for (const player of team.votes.nodes) { if (player.userIcon.url !== friend.userIcon.url) continue; - + match.splatoon3_fest_team = createFestVoteTeam(team, FestVoteState.VOTED); break; } - + for (const player of team.preVotes.nodes) { if (player.userIcon.url !== friend.userIcon.url) continue; - + match.splatoon3_fest_team = createFestVoteTeam(team, FestVoteState.PRE_VOTED); break; } } - + if (!match.splatoon3_fest_team && fest_vote_status.undecidedVotes) { match.splatoon3_fest_team = null; } @@ -310,7 +310,7 @@ function createApp( splatoon3_vs_setting?: RegularMatchSetting | BankaraMatchSetting | FestMatchSetting | LeagueMatchSetting | XMatchSetting | null; - splatoon3_coop_setting?: CoopSetting | null; + splatoon3_coop_setting?: Pick | null; splatoon3_fest?: Fest_schedule | null; } = {}; diff --git a/src/cli/util/storage.ts b/src/cli/util/storage.ts index 57e61b1..b78d1a2 100644 --- a/src/cli/util/storage.ts +++ b/src/cli/util/storage.ts @@ -23,7 +23,7 @@ export function builder(yargs: Argv) { ], colWidths: [10, 42, 80], }); - + for await (const data of iterateLocalStorage(storage)) { const value = util.inspect(data.value, { compact: true, diff --git a/src/discord/titles/nintendo/splatoon3.ts b/src/discord/titles/nintendo/splatoon3.ts index e7c2e8a..39a8767 100644 --- a/src/discord/titles/nintendo/splatoon3.ts +++ b/src/discord/titles/nintendo/splatoon3.ts @@ -1,7 +1,7 @@ import createDebug from 'debug'; import persist from 'node-persist'; import DiscordRPC from 'discord-rpc'; -import { BankaraMatchMode, BankaraMatchSetting, CoopSchedule, CoopSetting, DetailVotingStatusResult, FestMatchSetting, FestState, FestTeamRole, FestTeam_schedule, FestTeam_votingStatus, Fest_schedule, FriendListResult, FriendOnlineState, GraphQLResponse, LeagueMatchSetting, RegularMatchSetting, StageScheduleResult, VsSchedule_bankara, VsSchedule_fest, VsSchedule_league, VsSchedule_regular, VsSchedule_xMatch, XMatchSetting } from 'splatnet3-types/splatnet3'; +import { BankaraMatchMode, BankaraMatchSetting, CoopSchedule, CoopSetting, DetailVotingStatusResult, FestMatchSetting, FestState, FestTeamRole, FestTeam_schedule, FestTeam_votingStatus, Fest_schedule, FriendListResult, FriendOnlineState, GraphQLSuccessResponse, LeagueMatchSetting, RegularMatchSetting, StageScheduleResult, VsSchedule_bankara, VsSchedule_fest, VsSchedule_league, VsSchedule_regular, VsSchedule_xMatch, XMatchSetting } from 'splatnet3-types/splatnet3'; import { Game } from '../../../api/coral-types.js'; import SplatNet3Api from '../../../api/splatnet3.js'; import { DiscordPresenceExternalMonitorsConfiguration } from '../../../app/common/types.js'; @@ -21,9 +21,9 @@ export default class SplatNet3Monitor extends EmbeddedLoop { splatnet: SplatNet3Api | null = null; data: SavedBulletToken | null = null; - cached_friends: GraphQLResponse | null = null; - cached_schedules: GraphQLResponse | null = null; - cached_voting_status: GraphQLResponse | null = null; + cached_friends: GraphQLSuccessResponse | null = null; + cached_schedules: GraphQLSuccessResponse | null = null; + cached_voting_status: GraphQLSuccessResponse | null = null; friend: FriendListResult['friends']['nodes'][0] | null = null; @@ -32,7 +32,7 @@ export default class SplatNet3Monitor extends EmbeddedLoop { fest_schedule: VsSchedule_fest | null = null; league_schedule: VsSchedule_league | null = null; x_schedule: VsSchedule_xMatch | null = null; - coop_schedule: CoopSchedule | null = null; + coop_schedule: Pick | null = null; fest: Fest_schedule | null = null; fest_team_voting_status: FestTeam_votingStatus | null = null; fest_team: FestTeam_schedule | null = null; diff --git a/src/util/loop.ts b/src/util/loop.ts index 7c526ca..adfc9bc 100644 --- a/src/util/loop.ts +++ b/src/util/loop.ts @@ -95,7 +95,7 @@ export abstract class EmbeddedLoop extends Loop { try { const result = await this.loop(true); if (result === LoopResult.STOP) return; - + while (i === this._running) { const result = await this.loop(); @@ -104,7 +104,7 @@ export abstract class EmbeddedLoop extends Loop { return; } } - + if (this._running === 0 && !this.onStop) { // Run one more time after the loop ends const result = await this.loopRun();