Update coral types

This commit is contained in:
Samuel Elliott 2025-04-01 21:20:24 +01:00
parent 8c911d5bca
commit 6f989104c7
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
9 changed files with 127 additions and 32 deletions

View File

@ -108,16 +108,53 @@ export interface Friends {
friends: Friend[];
}
/** /v4/Friend/List */
export interface Friends_4 {
friends: Friend_4[];
extractFriendsIds: string[];
}
export interface Friend {
id: number;
nsaId: string;
imageUri: string;
image2Uri: string;
name: string;
isFriend: boolean;
isFavoriteFriend: boolean;
isServiceUser: boolean;
isNew: boolean;
friendCreatedAt: number;
presence: Presence;
route: FriendRoute;
presence: Presence | PresenceOnline | PresenceOffline;
}
/** /v4/Friend/Show */
export interface Friend_4 extends Friend {
isOnlineNotificationEnabled: boolean;
presence: PresenceOnline_4 | PresenceOffline;
}
export interface FriendRoute {
appName: string;
/** In-game player name */
userName: string;
shopUri: string;
imageUri: string;
// if not IN_APP all other properties are empty strings
channel: FriendRouteChannel;
}
export enum FriendRouteChannel {
/** Added from friend code lookup on a Switch console or using coral */
FRIEND_CODE = 'FRIEND_CODE',
/** Added from users you've played with */
IN_APP = 'IN_APP',
/** Added from search for local users */
NX_FACED = 'NX_FACED',
'3DS' = '3DS',
// Wii U, Facebook, Twitter suggestions?
}
export interface Presence {
@ -128,7 +165,19 @@ export interface Presence {
*/
updatedAt: number;
logoutAt: number;
game: Game | {};
game: PresenceGame | {};
}
export interface PresenceOnline extends Presence {
state: PresenceState.ONLINE | PresenceState.PLAYING;
game: PresenceGame;
}
export interface PresenceOnline_4 extends PresenceOnline {
platform: PresencePlatform;
}
export interface PresenceOffline extends Presence {
state: PresenceState.OFFLINE | PresenceState.INACTIVE;
game: {};
}
export enum PresenceState {
@ -146,6 +195,10 @@ export enum PresenceState {
PLAYING = 'PLAYING',
}
export enum PresencePlatform {
NINTENDO_SWITCH = 1,
}
export interface Game {
name: string;
imageUri: string;
@ -153,6 +206,9 @@ export interface Game {
totalPlayTime: number;
/** 0 if never played before */
firstPlayedAt: number;
}
export interface PresenceGame extends Game {
sysDescription: string;
}
@ -167,7 +223,9 @@ export interface FriendCodeUser {
id: number;
nsaId: string;
imageUri: string;
image2Uri: string;
name: string;
isBlocking: boolean;
extras: {};
}
@ -240,14 +298,16 @@ export interface User {
id: number;
nsaId: string;
imageUri: string;
image2Uri: string;
name: string;
}
/** /v3/User/ShowSelf */
/** /v4/User/ShowSelf */
export interface CurrentUser {
id: number;
nsaId: string;
imageUri: string;
image2Uri: string;
name: string;
supportId: string;
isChildRestricted: boolean;
@ -255,9 +315,7 @@ export interface CurrentUser {
links: {
nintendoAccount: {
membership: {
active: {
active: boolean;
} | boolean;
active: boolean;
};
};
friendCode: {
@ -267,11 +325,19 @@ export interface CurrentUser {
};
};
permissions: {
playLog: PlayLogPermissions;
presence: PresencePermissions;
friendRequestReception: boolean;
};
presence: Presence;
presence: PresenceOnline_4 | PresenceOffline;
}
export enum PlayLogPermissions {
EVERYONE = 'EVERYONE',
FRIENDS = 'FRIENDS',
FAVORITE_FRIENDS = 'FAVORITE_FRIENDS',
SELF = 'SELF',
}
export enum PresencePermissions {
FRIENDS = 'FRIENDS',
FAVORITE_FRIENDS = 'FAVORITE_FRIENDS',
@ -282,7 +348,9 @@ export enum PresencePermissions {
export interface CurrentUserPermissions {
etag: string;
permissions: {
playLog: PlayLogPermissions;
presence: PresencePermissions;
friendRequestReception: boolean;
};
}
@ -295,3 +363,21 @@ export interface UpdateCurrentUserPermissionsParameter {
};
etag: string;
}
/** /v4/User/PlayLog/Show */
export type UserPlayLog = Game[];
/** /v4/FriendRequest/Received/List */
export interface ReceivedFriendRequests {
friendRequests: unknown[];
}
/** /v4/FriendRequest/Sent/List */
export interface SentFriendRequests {
friendRequests: unknown[];
}
/** /v3/User/Block/List */
export interface BlockingUsers {
blockingUsers: unknown[];
}

View File

@ -4,7 +4,7 @@ import React, { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { ActivityIndicator, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { CheckBox } from 'react-native-web';
import { Friend, Game, Presence, PresencePermissions, PresenceState } from '../../../api/coral-types.js';
import { Friend, Presence, PresenceGame, PresencePermissions, PresenceState } from '../../../api/coral-types.js';
import { getTitleIdFromEcUrl, hrduration } from '../../../util/misc.js';
import { Button } from '../components/index.js';
import { DEFAULT_ACCENT_COLOUR, TEXT_COLOUR_ACTIVE, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
@ -174,7 +174,7 @@ function FriendPresence(props: {
}
function FriendPresenceGame(props: {
game: Game;
game: PresenceGame;
}) {
const theme = useColourScheme() === 'light' ? light : dark;
const accent_colour = useAccentColour();

View File

@ -3,7 +3,7 @@ import { i18n } from 'i18next';
import { App } from './index.js';
import { showErrorDialog, tryGetNativeImageFromUrl } from './util.js';
import { DiscordPresenceConfiguration, DiscordPresenceExternalMonitorsConfiguration, DiscordPresenceSource, DiscordStatus } from '../common/types.js';
import { CurrentUser, Friend, Game, CoralError } from '../../api/coral-types.js';
import { CurrentUser, Friend, CoralError, PresenceGame } from '../../api/coral-types.js';
import { ErrorResponse } from '../../api/util.js';
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
import { NotificationManager } from '../../common/notify.js';
@ -551,7 +551,7 @@ export class ElectronNotificationManager extends NotificationManager {
}
async onFriendOnline(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
new Notification({
title: friend.name,
@ -570,7 +570,7 @@ export class ElectronNotificationManager extends NotificationManager {
}
async onFriendPlayingChangeTitle(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
new Notification({
title: friend.name,
@ -581,7 +581,7 @@ export class ElectronNotificationManager extends NotificationManager {
}
async onFriendTitleStateChange(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
new Notification({
title: friend.name,

View File

@ -7,7 +7,7 @@ import { initStorage } from '../../util/storage.js';
import { getToken } from '../../common/auth/coral.js';
import { getIksmToken } from '../../common/auth/splatnet2.js';
import { EmbeddedSplatNet2Monitor, NotificationManager, ZncNotifications } from '../../common/notify.js';
import { CurrentUser, Friend, Game } from '../../api/coral-types.js';
import { CurrentUser, Friend, PresenceGame } from '../../api/coral-types.js';
const debug = createDebug('cli:nso:notify');
@ -133,7 +133,7 @@ export class TerminalNotificationManager extends NotificationManager {
}
onFriendOnline(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
this.notifier.notify({
title: friend.name,
@ -153,7 +153,7 @@ export class TerminalNotificationManager extends NotificationManager {
}
onFriendPlayingChangeTitle(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
this.notifier.notify({
title: friend.name,
@ -165,7 +165,7 @@ export class TerminalNotificationManager extends NotificationManager {
}
onFriendTitleStateChange(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
this.notifier.notify({
title: friend.name,

View File

@ -1,7 +1,7 @@
import process from 'node:process';
import { fetch } from 'undici';
import { getPresenceFromUrl } from '../../api/znc-proxy.js';
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState } from '../../api/coral-types.js';
import { ActiveEvent, CurrentUser, Friend, Presence, PresenceGame, PresenceState } from '../../api/coral-types.js';
import type { Arguments as ParentArguments } from './index.js';
import { getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js';
import { DiscordPresenceContext, DiscordPresencePlayTime } from '../../discord/types.js';
@ -122,7 +122,7 @@ async function getPresenceFromJson(json: string) {
data.state === 'INACTIVE' ? PresenceState.INACTIVE :
PresenceState.OFFLINE;
const game: Game | null = data.game && 'name' in data.game ? {
const game: PresenceGame | null = data.game && 'name' in data.game ? {
name: typeof data.game.name === 'string' ? data.game.name : 'undefined',
imageUri: typeof data.game.imageUri === 'string' ? data.game.imageUrl : null,
shopUri: typeof data.game.shopUri === 'string' ? data.game.shopUri : null,

View File

@ -119,7 +119,7 @@ export class RateLimitError extends Error implements HasErrorDescription {
export function checkMembershipActive(data: SavedToken) {
const membership = data.nsoAccount.user.links.nintendoAccount.membership;
const active = typeof membership.active === 'object' ? membership.active.active : membership.active;
const active = typeof membership.active === 'object' ? (membership.active as typeof membership).active : membership.active;
if (!active) throw new MembershipRequiredError('Nintendo Switch Online membership required');
}

View File

@ -1,6 +1,6 @@
import persist from 'node-persist';
import { CoralApiInterface } from '../api/coral.js';
import { ActiveEvent, Announcements, CurrentUser, Friend, Game, Presence, PresenceState, WebServices, CoralError, GetActiveEventResult } from '../api/coral-types.js';
import { ActiveEvent, Announcements, CurrentUser, Friend, Presence, PresenceState, WebServices, CoralError, GetActiveEventResult, FriendRouteChannel, PresenceGame } from '../api/coral-types.js';
import ZncProxyApi from '../api/znc-proxy.js';
import { ErrorResponse } from '../api/util.js';
import { SavedToken } from './auth/coral.js';
@ -71,11 +71,20 @@ export class ZncNotifications extends Loop {
id: 0,
nsaId: r.friend,
imageUri: '',
image2Uri: '',
name: '',
isFriend: true,
isFavoriteFriend: false,
isServiceUser: false,
isNew: false,
friendCreatedAt: 0,
route: {
appName: '',
userName: '',
shopUri: '',
imageUri: '',
channel: FriendRouteChannel.FRIEND_CODE,
},
presence: await nso.fetch<Presence>('/friend/' + r.friend + '/presence'),
};
@ -145,7 +154,7 @@ export class ZncNotifications extends Loop {
const monitor = this.splatnet2_monitors.get(nsa_id);
if (playing && monitor) {
const currenttitle = presence.game as Game;
const currenttitle = presence.game as PresenceGame;
const titleid = getTitleIdFromEcUrl(currenttitle.shopUri);
if (titleid && EmbeddedSplatNet2Monitor.title_ids.includes(titleid)) {
@ -233,7 +242,7 @@ export class NotificationManager {
type = PresenceEvent.STATE_CHANGE;
callback = 'onFriendOnline';
const currenttitle = friend.presence.game as Game;
const currenttitle = friend.presence.game as PresenceGame;
debugFriends('%s is now online%s%s, title %s %s - played for %s since %s', friend.name,
friend.presence.state === PresenceState.ONLINE ? '' : ' (' + friend.presence.state + ')',
@ -250,7 +259,7 @@ export class NotificationManager {
type = PresenceEvent.STATE_CHANGE;
callback = 'onFriendOffline';
const lasttitle = lastpresence.game as Game;
const lasttitle = lastpresence.game as PresenceGame;
debugFriends('%s is now offline%s, was playing title %s %s, logout time %s', friend.name,
friend.presence.state !== PresenceState.OFFLINE ? ' (console still online)' : '',
@ -262,8 +271,8 @@ export class NotificationManager {
}
} else if (wasonline && online) {
// Friend is still online
const lasttitle = lastpresence.game as Game;
const currenttitle = friend.presence.game as Game;
const lasttitle = lastpresence.game as PresenceGame;
const currenttitle = friend.presence.game as PresenceGame;
if (getTitleIdFromEcUrl(lasttitle.shopUri) !== getTitleIdFromEcUrl(currenttitle.shopUri)) {
// Friend is playing a different title

View File

@ -4,7 +4,7 @@ import { Request } from 'express';
import { CoopRule, FestVoteState, FriendOnlineState, StageScheduleResult } from 'splatnet3-types/splatnet3';
import { dir } from '../util/product.js';
import createDebug from '../util/debug.js';
import { Game, PresenceState } from '../api/coral-types.js';
import { PresenceGame, PresenceState } from '../api/coral-types.js';
import { RawValueSymbol, htmlentities } from '../util/misc.js';
import { PresenceResponse } from '../cli/presence-server.js';
@ -216,7 +216,7 @@ export function renderUserEmbedSvg(
}
function renderUserTitleEmbedPartialSvg(
game: Game, description: string | null | undefined,
game: PresenceGame, description: string | null | undefined,
colours: PresenceEmbedThemeColours, font_family: string,
) {
if (typeof description !== 'string') description = game.sysDescription;

View File

@ -1,5 +1,5 @@
import DiscordRPC from 'discord-rpc';
import { Game, PresenceState } from '../api/coral-types.js';
import { PresenceGame, PresenceState } from '../api/coral-types.js';
import { defaultTitle, titles } from './titles.js';
import createDebug from '../util/debug.js';
import { product, version } from '../util/product.js';
@ -9,7 +9,7 @@ import { DiscordPresence, DiscordPresenceContext, DiscordPresencePlayTime } from
const debug = createDebug('nxapi:discord');
export function getDiscordPresence(
state: PresenceState, game: Game, context?: DiscordPresenceContext
state: PresenceState, game: PresenceGame, context?: DiscordPresenceContext
): DiscordPresence {
const titleid = getTitleIdFromEcUrl(game.shopUri);
const title = titles.find(t => t.id === titleid) || defaultTitle;
@ -80,7 +80,7 @@ export function getDiscordPresence(
};
}
function getPlayTimeText(type: DiscordPresencePlayTime, game: Game) {
function getPlayTimeText(type: DiscordPresencePlayTime, game: PresenceGame) {
if (type === DiscordPresencePlayTime.NINTENDO) {
const days = Math.floor(Date.now() / 1000 / 86400) - Math.floor(game.firstPlayedAt / 86400);
if (days <= 10) return getFirstPlayedText(game.firstPlayedAt);
@ -154,7 +154,7 @@ export function getInactiveDiscordPresence(
};
}
export function getTitleConfiguration(game: Game, id: string) {
export function getTitleConfiguration(game: PresenceGame, id: string) {
return titles.find(title => {
if (title.id !== id) return false;