From f4aed98a1d26006bfbaf9db7628b92a301db40f4 Mon Sep 17 00:00:00 2001 From: Ellie Date: Fri, 20 Mar 2026 01:42:11 +0000 Subject: [PATCH] Coral 3.3.0 --- src/api/coral-types.ts | 1 + src/api/coral.ts | 15 ++++++++++++--- src/api/znc-proxy.ts | 9 +++++++++ src/cli/nso/http-server.ts | 28 ++++++++++++++++++++++++++-- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/api/coral-types.ts b/src/api/coral-types.ts index 666fa10..abb3052 100644 --- a/src/api/coral-types.ts +++ b/src/api/coral-types.ts @@ -181,6 +181,7 @@ export interface Friend { export interface Friend_4 extends Friend { isOnlineNotificationEnabled: boolean; presence: PresenceOnline_4 | PresenceOffline; + note: string; } export interface FriendRoute { diff --git a/src/api/coral.ts b/src/api/coral.ts index 9cd24cd..bd9d42d 100644 --- a/src/api/coral.ts +++ b/src/api/coral.ts @@ -15,10 +15,10 @@ const debug = createDebug('nxapi:api:coral'); const ZNCA_PLATFORM = 'Android'; const ZNCA_PLATFORM_VERSION = '12'; -export const ZNCA_VERSION = '3.2.0'; // TODO: update to 3.1.0 +export const ZNCA_VERSION = '3.3.0'; const ZNCA_USER_AGENT = `com.nintendo.znca/${ZNCA_VERSION}(${ZNCA_PLATFORM}/${ZNCA_PLATFORM_VERSION})`; -export const ZNCA_API_COMPATIBILITY_VERSION = 'hio87-mJks_e9GNF'; +export const ZNCA_API_COMPATIBILITY_VERSION = 'w8zSLBsxR7rVoGJA'; const ZNC_URL = 'https://api-lp1.znc.srv.nintendo.net'; export const ZNCA_CLIENT_ID = '71b963c1b7b6d119'; @@ -53,6 +53,7 @@ export interface ResponseEncryption { export interface CoralApiInterface { getAnnouncements(): Promise>; getFriendList(): Promise>; + updateFriendNote(nsa_id: string, note: string): Promise>; addFavouriteFriend(nsa_id: string): Promise>; removeFavouriteFriend(nsa_id: string): Promise>; deleteFriendIsNew(nsa_id: string): Promise>; @@ -149,13 +150,21 @@ export abstract class AbstractCoralApi { }); } - /** @deprecated unused */ async getFriend(nsa_id: string) { return this.call('/v4/Friend/Show', { nsaId: nsa_id, }); } + async updateFriendNote(nsa_id: string, note: string) { + if (note.length > 20) throw new TypeError('Friend note cannot be longer than 20 characters'); + + return this.call<{}, {friendNsaId: string; note: string}>('/v4/Friend/Note/Update', { + friendNsaId: nsa_id, + note, + }); + } + async getPlayLog(nsa_id: string) { return this.call('/v4/User/PlayLog/Show', { nsaId: nsa_id, diff --git a/src/api/znc-proxy.ts b/src/api/znc-proxy.ts index b7493c8..1eeae55 100644 --- a/src/api/znc-proxy.ts +++ b/src/api/znc-proxy.ts @@ -114,6 +114,15 @@ export default class ZncProxyApi extends AbstractCoralApi implements CoralApiInt return createResult(result, result.friend); } + async updateFriendNote(nsa_id: string, note: string) { + if (note.length > 20) throw new TypeError('Friend note cannot be longer than 20 characters'); + + const result = await this.fetchProxyApi('friend/' + nsa_id, 'PATCH', JSON.stringify({ + note, + })); + return createResult(result, {}); + } + async addFavouriteFriend(nsa_id: string) { const result = await this.fetchProxyApi('friend/' + nsa_id, 'PATCH', JSON.stringify({ isFavoriteFriend: true, diff --git a/src/cli/nso/http-server.ts b/src/cli/nso/http-server.ts index 0a258db..6d1a1e1 100644 --- a/src/cli/nso/http-server.ts +++ b/src/cli/nso/http-server.ts @@ -645,11 +645,17 @@ class Server extends HttpServer { throw new ResponseError(400, 'invalid_request', 'Invalid value for isFavoriteFriend.'); } - if ('isNew' in req.body && (typeof req.body.isNew !== 'boolean' || + if ('isNew' in req.body && ( + typeof req.body.isNew !== 'boolean' || // Cannot set friend as isNew (!friend.isNew && req.body.isNew) )) { throw new ResponseError(400, 'invalid_request', 'Invalid value for isNew.'); + } else if ('isNew' in req.body && req.body.isNew && ( + 'isFavoriteFriend' in req.body || + 'note' in req.body + )) { + throw new ResponseError(400, 'invalid_request', 'Invalid value for isNew.'); } if ('isOnlineNotificationEnabled' in req.body && @@ -658,6 +664,13 @@ class Server extends HttpServer { throw new ResponseError(400, 'invalid_request', 'Invalid value for isOnlineNotificationEnabled.'); } + if ('note' in req.body && ( + typeof req.body.note !== 'string' || + req.body.note.length > 20 + )) { + throw new ResponseError(400, 'invalid_request', 'Invalid value for note.'); + } + if ('isNew' in req.body) { if (friend.isNew !== req.body.isNew) { if (!req.body.isNew) await user.nso.deleteFriendIsNew(friend.nsaId); @@ -668,7 +681,7 @@ class Server extends HttpServer { // No change } } else { - if (friend.isNew && 'isFavoriteFriend' in req.body) { + if (friend.isNew && ('isFavoriteFriend' in req.body || 'note' in req.body)) { // If updating the friend they should have been marked as not new // It *is* possible to update the friend online notification setting // without doing this though @@ -703,6 +716,17 @@ class Server extends HttpServer { } } + if ('note' in req.body) { + if (friend.note !== req.body.note) { + await user.nso.updateFriendNote(friend.nsaId, req.body.note); + + // Update cached data + friend.note = req.body.note; + } else { + // No change + } + } + if (post) { res.statusCode = 204; return;