mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-10-16 14:36:09 -05:00
Add CoralApiInterface
This commit is contained in:
107
src/api/coral.ts
107
src/api/coral.ts
@@ -1,13 +1,14 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import { f, FResult, HashMethod } from './f.js';
|
||||
import { AccountLogin, AccountToken, Announcements, CurrentUser, CurrentUserPermissions, Event, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, CoralErrorResponse, CoralResponse, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl, AccountTokenParameter, AccountLoginParameter, WebServiceTokenParameter } from './coral-types.js';
|
||||
import { generateAuthData, getNintendoAccountToken, getNintendoAccountUser, NintendoAccountSessionAuthorisation, NintendoAccountToken, NintendoAccountUser } from './na.js';
|
||||
import { ErrorResponse, ResponseSymbol } from './util.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { JwtPayload } from '../util/jwt.js';
|
||||
import { getAdditionalUserAgents } from '../util/useragent.js';
|
||||
import { timeoutSignal } from '../util/misc.js';
|
||||
import { getAdditionalUserAgents } from '../util/useragent.js';
|
||||
import type { CoralRemoteConfig } from '../common/remote-config.js';
|
||||
import { AccountLogin, AccountLoginParameter, AccountToken, AccountTokenParameter, Announcements, CoralErrorResponse, CoralResponse, CoralStatus, CoralSuccessResponse, CurrentUser, CurrentUserPermissions, Event, FriendCodeUrl, FriendCodeUser, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, WebServiceTokenParameter } from './coral-types.js';
|
||||
import { f, FResult, HashMethod } from './f.js';
|
||||
import { generateAuthData, getNintendoAccountToken, getNintendoAccountUser, NintendoAccountSessionAuthorisation, NintendoAccountToken, NintendoAccountUser } from './na.js';
|
||||
import { ErrorResponse, ResponseSymbol } from './util.js';
|
||||
|
||||
const debug = createDebug('nxapi:api:coral');
|
||||
|
||||
@@ -40,7 +41,39 @@ export interface ResultData<T> {
|
||||
correlationId: string;
|
||||
}
|
||||
|
||||
export default class CoralApi {
|
||||
export interface CoralApiInterface {
|
||||
getAnnouncements(): Promise<Result<Announcements>>;
|
||||
getFriendList(): Promise<Result<Friends>>;
|
||||
addFavouriteFriend(nsa_id: string): Promise<Result<{}>>;
|
||||
removeFavouriteFriend(nsa_id: string): Promise<Result<{}>>;
|
||||
getWebServices(): Promise<Result<WebServices>>;
|
||||
getActiveEvent(): Promise<Result<GetActiveEventResult>>;
|
||||
getEvent(id: number): Promise<Result<Event>>;
|
||||
getUser(id: number): Promise<Result<User>>;
|
||||
getUserByFriendCode(friend_code: string, hash?: string): Promise<Result<FriendCodeUser>>;
|
||||
getCurrentUser(): Promise<Result<CurrentUser>>;
|
||||
getFriendCodeUrl(): Promise<Result<FriendCodeUrl>>;
|
||||
getCurrentUserPermissions(): Promise<Result<CurrentUserPermissions>>;
|
||||
getWebServiceToken(id: number): Promise<Result<WebServiceToken>>;
|
||||
}
|
||||
|
||||
export interface ClientInfo {
|
||||
platform: string;
|
||||
version: string;
|
||||
useragent: string;
|
||||
}
|
||||
|
||||
const RemoteConfigSymbol = Symbol('RemoteConfigSymbol');
|
||||
const ClientInfoSymbol = Symbol('CoralClientInfo');
|
||||
const CoralUserIdSymbol = Symbol('CoralUserId');
|
||||
const NintendoAccountIdSymbol = Symbol('NintendoAccountId');
|
||||
|
||||
export default class CoralApi implements CoralApiInterface {
|
||||
[RemoteConfigSymbol]!: CoralRemoteConfig | null;
|
||||
[ClientInfoSymbol]: ClientInfo;
|
||||
[CoralUserIdSymbol]: string;
|
||||
[NintendoAccountIdSymbol]: string;
|
||||
|
||||
onTokenExpired: ((data?: CoralErrorResponse, res?: Response) => Promise<CoralAuthData | void>) | null = null;
|
||||
/** @internal */
|
||||
_renewToken: Promise<void> | null = null;
|
||||
@@ -50,11 +83,30 @@ export default class CoralApi {
|
||||
protected constructor(
|
||||
public token: string,
|
||||
public useragent: string | null = getAdditionalUserAgents(),
|
||||
public coral_user_id: string,
|
||||
public na_id: string,
|
||||
readonly znca_version = ZNCA_VERSION,
|
||||
readonly znca_useragent = ZNCA_USER_AGENT,
|
||||
) {}
|
||||
coral_user_id: string,
|
||||
na_id: string,
|
||||
znca_version = ZNCA_VERSION,
|
||||
znca_useragent = ZNCA_USER_AGENT,
|
||||
config?: CoralRemoteConfig,
|
||||
) {
|
||||
this[ClientInfoSymbol] = {platform: ZNCA_PLATFORM, version: znca_version, useragent: znca_useragent};
|
||||
this[CoralUserIdSymbol] = coral_user_id;
|
||||
this[NintendoAccountIdSymbol] = na_id;
|
||||
|
||||
Object.defineProperty(this, RemoteConfigSymbol, {enumerable: false, value: config ?? null});
|
||||
Object.defineProperty(this, 'token', {enumerable: false, value: this.token});
|
||||
Object.defineProperty(this, '_renewToken', {enumerable: false, value: this._renewToken});
|
||||
Object.defineProperty(this, '_token_expired', {enumerable: false, value: this._token_expired});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
get znca_version() {
|
||||
return this[ClientInfoSymbol].version;
|
||||
}
|
||||
/** @internal */
|
||||
get znca_useragent() {
|
||||
return this[ClientInfoSymbol].useragent;
|
||||
}
|
||||
|
||||
async fetch<T = unknown>(
|
||||
url: string, method = 'GET', body?: string, headers?: object,
|
||||
@@ -79,11 +131,11 @@ export default class CoralApi {
|
||||
const response = await fetch(ZNC_URL + url, {
|
||||
method,
|
||||
headers: Object.assign({
|
||||
'X-Platform': ZNCA_PLATFORM,
|
||||
'X-ProductVersion': this.znca_version,
|
||||
'X-Platform': this[ClientInfoSymbol].platform,
|
||||
'X-ProductVersion': this[ClientInfoSymbol].version,
|
||||
'Authorization': 'Bearer ' + this.token,
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'User-Agent': this.znca_useragent,
|
||||
'User-Agent': this[ClientInfoSymbol].useragent,
|
||||
}, headers),
|
||||
body,
|
||||
signal,
|
||||
@@ -148,15 +200,15 @@ export default class CoralApi {
|
||||
return this.call<Friends>('/v3/Friend/List');
|
||||
}
|
||||
|
||||
async addFavouriteFriend(nsaid: string) {
|
||||
async addFavouriteFriend(nsa_id: string) {
|
||||
return this.call<{}>('/v3/Friend/Favorite/Create', {
|
||||
nsaId: nsaid,
|
||||
nsaId: nsa_id,
|
||||
});
|
||||
}
|
||||
|
||||
async removeFavouriteFriend(nsaid: string) {
|
||||
async removeFavouriteFriend(nsa_id: string) {
|
||||
return this.call<{}>('/v3/Friend/Favorite/Delete', {
|
||||
nsaId: nsaid,
|
||||
nsaId: nsa_id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,10 +278,10 @@ export default class CoralApi {
|
||||
await this._renewToken;
|
||||
|
||||
const data = await f(this.token, HashMethod.WEB_SERVICE, {
|
||||
platform: ZNCA_PLATFORM,
|
||||
version: this.znca_version,
|
||||
platform: this[ClientInfoSymbol].platform,
|
||||
version: this[ClientInfoSymbol].version,
|
||||
useragent: this.useragent ?? getAdditionalUserAgents(),
|
||||
user: {na_id: this.na_id, coral_user_id: this.coral_user_id},
|
||||
user: {na_id: this[NintendoAccountIdSymbol], coral_user_id: this[CoralUserIdSymbol]},
|
||||
});
|
||||
|
||||
const req: WebServiceTokenParameter = {
|
||||
@@ -268,10 +320,10 @@ export default class CoralApi {
|
||||
nintendoAccountToken: NintendoAccountToken, user: NintendoAccountUser,
|
||||
): Promise<PartialCoralAuthData> {
|
||||
const fdata = await f(nintendoAccountToken.id_token, HashMethod.CORAL, {
|
||||
platform: ZNCA_PLATFORM,
|
||||
version: this.znca_version,
|
||||
platform: this[ClientInfoSymbol].platform,
|
||||
version: this[ClientInfoSymbol].version,
|
||||
useragent: this.useragent ?? getAdditionalUserAgents(),
|
||||
user: {na_id: user.id, coral_user_id: this.coral_user_id},
|
||||
user: {na_id: user.id, coral_user_id: this[CoralUserIdSymbol]},
|
||||
});
|
||||
|
||||
const req: AccountTokenParameter = {
|
||||
@@ -305,11 +357,10 @@ export default class CoralApi {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
setTokenWithSavedToken(data: CoralAuthData | PartialCoralAuthData) {
|
||||
protected setTokenWithSavedToken(data: CoralAuthData | PartialCoralAuthData) {
|
||||
this.token = data.credential.accessToken;
|
||||
this.coral_user_id = '' + data.nsoAccount.user.id;
|
||||
if ('user' in data) this.na_id = data.user.id;
|
||||
this[CoralUserIdSymbol] = '' + data.nsoAccount.user.id;
|
||||
if ('user' in data) this[NintendoAccountIdSymbol] = data.user.id;
|
||||
this._token_expired = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -295,10 +295,12 @@ export enum NintendoAccountScope {
|
||||
}
|
||||
export enum NintendoAccountJwtScope {
|
||||
'openid' = 0,
|
||||
'offline' = 1,
|
||||
'user' = 8,
|
||||
'user.birthday' = 9,
|
||||
'user.mii' = 17,
|
||||
'user.screenName' = 23,
|
||||
'user.links.nintendoNetwork.id' = 31,
|
||||
'moonUser:administration' = 320,
|
||||
'moonDevice:create' = 321,
|
||||
'moonOwnedDevice:administration' = 325,
|
||||
@@ -323,10 +325,6 @@ export enum NintendoAccountJwtScope {
|
||||
// 'pointWallet' = -1,
|
||||
// 'userNotificationMessage:anyClients' = -1,
|
||||
// 'userNotificationMessage:anyClients:write' = -1,
|
||||
|
||||
// 1, 31
|
||||
// 'offline' = -1,
|
||||
// 'user.links.nintendoNetwork.id' = -1,
|
||||
}
|
||||
|
||||
export interface NintendoAccountUser {
|
||||
|
||||
@@ -2,7 +2,7 @@ import fetch, { Response } from 'node-fetch';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
import { defineResponse, ErrorResponse, HasResponse } from './util.js';
|
||||
import CoralApi from './coral.js';
|
||||
import { CoralApiInterface } from './coral.js';
|
||||
import { WebServiceError, Users, AuthToken, UserProfile, Newspapers, Newspaper, Emoticons, Reaction, IslandProfile } from './nooklink-types.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { timeoutSignal } from '../util/misc.js';
|
||||
@@ -107,8 +107,8 @@ export default class NooklinkApi {
|
||||
return NooklinkUserApi._createWithNooklinkApi(this, user_id);
|
||||
}
|
||||
|
||||
async renewTokenWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const data = await NooklinkApi.loginWithCoral(nso, user);
|
||||
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const data = await NooklinkApi.loginWithCoral(coral, user);
|
||||
this.setTokenWithSavedToken(data);
|
||||
return data;
|
||||
}
|
||||
@@ -124,8 +124,8 @@ export default class NooklinkApi {
|
||||
this._token_expired = false;
|
||||
}
|
||||
|
||||
static async createWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(nso, user);
|
||||
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(coral, user);
|
||||
return {nooklink: this.createWithSavedToken(data), data};
|
||||
}
|
||||
|
||||
@@ -133,11 +133,11 @@ export default class NooklinkApi {
|
||||
return new this(data.gtoken, data.useragent);
|
||||
}
|
||||
|
||||
static async loginWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const { default: { coral_gws_nooklink: config } } = await import('../common/remote-config.js');
|
||||
if (!config) throw new Error('Remote configuration prevents NookLink authentication');
|
||||
|
||||
const webserviceToken = await nso.getWebServiceToken(NOOKLINK_WEBSERVICE_ID);
|
||||
const webserviceToken = await coral.getWebServiceToken(NOOKLINK_WEBSERVICE_ID);
|
||||
|
||||
return this.loginWithWebServiceToken(webserviceToken, user);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { v4 as uuidgen } from 'uuid';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
import { defineResponse, ErrorResponse } from './util.js';
|
||||
import CoralApi from './coral.js';
|
||||
import { CoralApiInterface } from './coral.js';
|
||||
import { ActiveFestivals, CoopResult, CoopResults, CoopSchedules, HeroRecords, LeagueMatchRankings, NicknameAndIcons, PastFestivals, Records, Result, Results, Schedules, ShareResponse, ShopMerchandises, Stages, Timeline, WebServiceError, XPowerRankingRecords, XPowerRankingSummary } from './splatnet2-types.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { timeoutSignal } from '../util/misc.js';
|
||||
@@ -240,8 +240,8 @@ ${colour}
|
||||
});
|
||||
}
|
||||
|
||||
static async createWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(nso, user);
|
||||
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(coral, user);
|
||||
return {splatnet: this.createWithSavedToken(data), data};
|
||||
}
|
||||
|
||||
@@ -269,8 +269,8 @@ ${colour}
|
||||
);
|
||||
}
|
||||
|
||||
static async loginWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const webserviceToken = await nso.getWebServiceToken(SPLATNET2_WEBSERVICE_ID);
|
||||
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const webserviceToken = await coral.getWebServiceToken(SPLATNET2_WEBSERVICE_ID);
|
||||
|
||||
return this.loginWithWebServiceToken(webserviceToken, user);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { BankaraBattleHistoriesRefetchResult, BankaraBattleHistoriesRefetchVariables, GraphQLRequest, GraphQLResponse, GraphQLSuccessResponse, KnownRequestId, LatestBattleHistoriesRefetchResult, LatestBattleHistoriesRefetchVariables, MyOutfitInput, PagerUpdateBattleHistoriesByVsModeResult, PagerUpdateBattleHistoriesByVsModeVariables, PrivateBattleHistoriesRefetchResult, PrivateBattleHistoriesRefetchVariables, RegularBattleHistoriesRefetchResult, RegularBattleHistoriesRefetchVariables, RequestId, ResultTypes, VariablesTypes, XBattleHistoriesRefetchResult, XBattleHistoriesRefetchVariables } from 'splatnet3-types/splatnet3';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import CoralApi from './coral.js';
|
||||
import { CoralApiInterface } from './coral.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
import { BulletToken } from './splatnet3-types.js';
|
||||
import { defineResponse, ErrorResponse, HasResponse, ResponseSymbol } from './util.js';
|
||||
@@ -928,8 +928,8 @@ export default class SplatNet3Api {
|
||||
//
|
||||
//
|
||||
|
||||
async renewTokenWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const data = await SplatNet3Api.loginWithCoral(nso, user);
|
||||
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const data = await SplatNet3Api.loginWithCoral(coral, user);
|
||||
this.setTokenWithSavedToken(data);
|
||||
return data;
|
||||
}
|
||||
@@ -948,8 +948,8 @@ export default class SplatNet3Api {
|
||||
this._token_expired = false;
|
||||
}
|
||||
|
||||
static async createWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(nso, user);
|
||||
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const data = await this.loginWithCoral(coral, user);
|
||||
return {splatnet: this.createWithSavedToken(data), data};
|
||||
}
|
||||
|
||||
@@ -977,11 +977,11 @@ export default class SplatNet3Api {
|
||||
);
|
||||
}
|
||||
|
||||
static async loginWithCoral(nso: CoralApi, user: NintendoAccountUser) {
|
||||
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
|
||||
const { default: { coral_gws_splatnet3: config } } = await import('../common/remote-config.js');
|
||||
if (!config) throw new Error('Remote configuration prevents SplatNet 3 authentication');
|
||||
|
||||
const webserviceToken = await nso.getWebServiceToken(SPLATNET3_WEBSERVICE_ID);
|
||||
const webserviceToken = await coral.getWebServiceToken(SPLATNET3_WEBSERVICE_ID);
|
||||
|
||||
return this.loginWithWebServiceToken(webserviceToken, user);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { ActiveEvent, Announcements, CurrentUser, Event, Friend, Presence, PresencePermissions, User, WebService, WebServiceToken, CoralErrorResponse, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl } from './coral-types.js';
|
||||
import { ActiveEvent, Announcements, CurrentUser, Event, Friend, Presence, PresencePermissions, User, WebService, WebServiceToken, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl } from './coral-types.js';
|
||||
import { defineResponse, ErrorResponse, ResponseSymbol } from './util.js';
|
||||
import CoralApi, { CoralAuthData, CorrelationIdSymbol, PartialCoralAuthData, ResponseDataSymbol, Result } from './coral.js';
|
||||
import { CoralApiInterface, CoralAuthData, CorrelationIdSymbol, PartialCoralAuthData, ResponseDataSymbol, Result } from './coral.js';
|
||||
import { NintendoAccountToken, NintendoAccountUser } from './na.js';
|
||||
import { SavedToken } from '../common/auth/coral.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
@@ -10,21 +10,7 @@ import { getAdditionalUserAgents, getUserAgent } from '../util/useragent.js';
|
||||
|
||||
const debug = createDebug('nxapi:api:znc-proxy');
|
||||
|
||||
export default class ZncProxyApi implements CoralApi {
|
||||
// Not used by ZncProxyApi
|
||||
onTokenExpired: ((data?: CoralErrorResponse, res?: Response) => Promise<CoralAuthData | void>) | null = null;
|
||||
/** @internal */
|
||||
_renewToken: Promise<void> | null = null;
|
||||
|
||||
/** @internal */
|
||||
_token_expired = false;
|
||||
/** @internal */
|
||||
na_id = '';
|
||||
/** @internal */
|
||||
coral_user_id = '';
|
||||
readonly znca_version = '';
|
||||
readonly znca_useragent = '';
|
||||
|
||||
export default class ZncProxyApi implements CoralApiInterface {
|
||||
constructor(
|
||||
private url: string,
|
||||
// ZncApi uses the NSO token (valid for a few hours)
|
||||
@@ -70,15 +56,15 @@ export default class ZncProxyApi implements CoralApi {
|
||||
return createResult(result, result);
|
||||
}
|
||||
|
||||
async addFavouriteFriend(nsaid: string) {
|
||||
const result = await this.fetch('/friend/' + nsaid, 'POST', JSON.stringify({
|
||||
async addFavouriteFriend(nsa_id: string) {
|
||||
const result = await this.fetch('/friend/' + nsa_id, 'POST', JSON.stringify({
|
||||
isFavoriteFriend: true,
|
||||
}));
|
||||
return createResult(result, {});
|
||||
}
|
||||
|
||||
async removeFavouriteFriend(nsaid: string) {
|
||||
const result = await this.fetch('/friend/' + nsaid, 'POST', JSON.stringify({
|
||||
async removeFavouriteFriend(nsa_id: string) {
|
||||
const result = await this.fetch('/friend/' + nsa_id, 'POST', JSON.stringify({
|
||||
isFavoriteFriend: false,
|
||||
}));
|
||||
return createResult(result, {});
|
||||
@@ -143,7 +129,7 @@ export default class ZncProxyApi implements CoralApi {
|
||||
return createResult(result, result.token);
|
||||
}
|
||||
|
||||
async getToken(token: string, user: NintendoAccountUser): ReturnType<CoralApi['getToken']> {
|
||||
async getToken(token: string, user: NintendoAccountUser): Promise<PartialCoralAuthData> {
|
||||
throw new Error('Not supported in ZncProxyApi');
|
||||
}
|
||||
|
||||
@@ -165,8 +151,7 @@ export default class ZncProxyApi implements CoralApi {
|
||||
throw new Error('Not supported in ZncProxyApi');
|
||||
}
|
||||
|
||||
/** @private */
|
||||
setTokenWithSavedToken(data: CoralAuthData | PartialCoralAuthData) {
|
||||
protected setTokenWithSavedToken(data: CoralAuthData | PartialCoralAuthData) {
|
||||
throw new Error('Not supported in ZncProxyApi');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user