Add separate Discord client for Nintendo Switch/Nintendo Switch 2

This commit is contained in:
Samuel Elliott 2025-04-20 20:25:41 +01:00
parent 8a05e4c6ef
commit 7666191600
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
6 changed files with 72 additions and 42 deletions

View File

@ -209,6 +209,7 @@ function getActivityFromPresence(
proxy_response,
nsaid: argv.friendNsaid ?? user?.nsaId,
user,
// platform: presence.platform,
};
const discordpresence = 'name' in presence.game ?

View File

@ -119,6 +119,7 @@ class ZncDiscordPresenceClient {
monitors: [...this.monitors.values()],
nsaid: this.m.presence_user!,
user,
// platform: presence.platform,
};
const discord_presence = 'name' in presence.game ?

View File

@ -1,5 +1,4 @@
import persist from 'node-persist';
import DiscordRPC from 'discord-rpc';
import { BankaraMatchMode, CoopRule, CoopSetting_schedule, DetailVotingStatusResult, FestMatchMode, FestTeam_schedule, FestTeam_votingStatus, Fest_schedule, FriendListResult, FriendOnlineState, GraphQLSuccessResponse, StageScheduleResult, VsMode, VsSchedule_regular } from 'splatnet3-types/splatnet3';
import { Game } from '../../api/coral-types.js';
import SplatNet3Api, { SplatNet3GraphQLErrorResponse } from '../../api/splatnet3.js';
@ -10,8 +9,8 @@ import { ExternalMonitorPresenceInterface } from '../../common/presence.js';
import createDebug from '../../util/debug.js';
import { EmbeddedLoop, LoopResult } from '../../util/loop.js';
import { ArgumentsCamelCase } from '../../util/yargs.js';
import { product } from '../../util/product.js';
import { DiscordPresenceContext, ErrorResult } from '../types.js';
import { DiscordActivity } from '../util.js';
const debug = createDebug('nxapi:discord:splatnet3');
@ -299,7 +298,7 @@ interface PresenceUrlResponse {
splatoon3_fest?: Fest_schedule | null;
}
export function callback(activity: DiscordRPC.Presence, game: Game, context?: DiscordPresenceContext) {
export function callback(activity: DiscordActivity, game: Game, context?: DiscordPresenceContext) {
const monitor = context?.monitors?.find(m => m instanceof SplatNet3Monitor) as SplatNet3Monitor | undefined;
const presence_proxy_data = context?.proxy_response ? context.proxy_response as PresenceUrlResponse : null;
@ -351,9 +350,7 @@ export function callback(activity: DiscordRPC.Presence, game: Game, context?: Di
null;
if (proxy_stage_image) {
activity.largeImageKey = proxy_stage_image;
activity.largeImageText = fest.tricolorStage.name +
' | ' + product;
activity.setLargeImage(proxy_stage_image, fest.tricolorStage.name);
}
}
@ -366,15 +363,12 @@ export function callback(activity: DiscordRPC.Presence, game: Game, context?: Di
// (friend.vsMode.id === 'VnNNb2RlLTg=')
// );
activity.largeImageKey = 'https://fancy.org.uk/api/nxapi/s3/image?' + new URLSearchParams({
activity.setLargeImage('https://fancy.org.uk/api/nxapi/s3/image?' + new URLSearchParams({
a: setting.vsStages[0].id,
b: setting.vsStages[1].id,
// ...(possibly_tricolour ? {t: fest?.tricolorStage.id} : {}),
v: '2022092400',
}).toString();
activity.largeImageText = setting.vsStages.map(s => s.name).join('/') +
// (possibly_tricolour ? '/' + fest?.tricolorStage.name : '') +
' | ' + product;
}).toString(), setting.vsStages.map(s => s.name).join('/'));
}
// REGULAR, BANKARA, X_MATCH, LEAGUE, PRIVATE, FEST
@ -391,8 +385,9 @@ export function callback(activity: DiscordRPC.Presence, game: Game, context?: Di
friend.vsMode.mode === 'PRIVATE' ? 'mode-vs-private-1' :
undefined;
activity.smallImageKey = mode_image;
activity.smallImageText = mode_name ?? friend.vsMode.name;
if (mode_image) {
activity.setSmallImage(mode_image, mode_name ?? friend.vsMode.name);
}
}
if (friend.onlineState === FriendOnlineState.COOP_MODE_MATCHING ||
@ -415,23 +410,18 @@ export function callback(activity: DiscordRPC.Presence, game: Game, context?: Di
null;
if (proxy_stage_image) {
activity.largeImageKey = proxy_stage_image;
activity.largeImageText = setting.coopStage.name +
' | ' + product;
activity.setLargeImage(proxy_stage_image, setting.coopStage.name);
}
}
if (friend.coopRule === CoopRule.REGULAR) {
activity.smallImageKey = 'mode-coop-regular-1';
activity.smallImageText = 'Salmon Run';
activity.setSmallImage('mode-coop-regular-1', 'Salmon Run');
}
if (friend.coopRule === CoopRule.BIG_RUN) {
activity.smallImageKey = 'mode-coop-bigrun-1';
activity.smallImageText = 'Big Run';
activity.setSmallImage('mode-coop-bigrun-1', 'Big Run');
}
if (friend.coopRule === CoopRule.TEAM_CONTEST) {
activity.smallImageKey = 'mode-coop-teamcontest-1';
activity.smallImageText = 'Eggstra Work';
activity.setSmallImage('mode-coop-teamcontest-1', 'Eggstra Work');
}
}

View File

@ -1,5 +1,6 @@
import { Title } from './types.js';
import * as publishers from './titles/index.js';
import { PresencePlatform } from '../api/coral-types.js';
export const defaultTitle: Title = {
id: '0000000000000000',
@ -9,6 +10,11 @@ export const defaultTitle: Title = {
showActiveEvent: true,
};
export const platform_clients: Record<PresencePlatform, string> = {
[PresencePlatform.NINTENDO_SWITCH]: '950883021165330493',
// [PresencePlatform.NINTENDO_SWITCH_2]: '1358060657957928970',
};
export const titles: Title[] = [];
for (const [publisher, m] of Object.entries(publishers)) {

View File

@ -1,7 +1,8 @@
import DiscordRPC from 'discord-rpc';
import { ActiveEvent, CurrentUser, Friend, Game } from '../api/coral-types.js';
import { ActiveEvent, CurrentUser, Friend, Game, PresencePlatform } from '../api/coral-types.js';
import { ExternalMonitorPresenceInterface, ZncDiscordPresence, ZncProxyDiscordPresence } from '../common/presence.js';
import { EmbeddedLoop } from '../util/loop.js';
import { DiscordActivity } from './util.js';
export interface DiscordPresenceContext {
friendcode?: CurrentUser['links']['friendCode'];
@ -12,6 +13,7 @@ export interface DiscordPresenceContext {
monitors?: ExternalMonitor[];
nsaid?: string;
user?: CurrentUser | Friend;
platform?: PresencePlatform;
}
export interface DiscordPresence {
@ -104,7 +106,7 @@ export interface Title<M extends ExternalMonitor = ExternalMonitor> {
/**
* A function to call to customise the Discord activity.
*/
callback?: (activity: DiscordRPC.Presence, game: Game, context?: DiscordPresenceContext, monitor?: M) => void;
callback?: (activity: DiscordActivity, game: Game, context?: DiscordPresenceContext, monitor?: M) => void;
}
export enum DiscordPresencePlayTime {

View File

@ -1,6 +1,6 @@
import DiscordRPC from 'discord-rpc';
import { PresenceGame, PresenceState } from '../api/coral-types.js';
import { defaultTitle, titles } from './titles.js';
import { defaultTitle, platform_clients, titles } from './titles.js';
import createDebug from '../util/debug.js';
import { product, version } from '../util/product.js';
import { getTitleIdFromEcUrl, hrduration } from '../util/misc.js';
@ -42,24 +42,28 @@ export function getDiscordPresence(
const nintendo_eshop_redirect_url = titleid ?
'https://fancy.org.uk/api/nxapi/title/' + titleid + '/redirect?source=nxapi-' + version + '-discord' : null;
const activity: DiscordRPC.Presence = {
details: text[0],
state: text[1],
largeImageKey: title.largeImageKey ?? game.imageUri,
largeImageText: title.largeImageText ? title.largeImageText + ' | ' + product : product,
smallImageKey: title.smallImageKey || (context?.friendcode ? context?.user?.imageUri : undefined),
smallImageText: title.smallImageKey ? title.smallImageText :
context?.friendcode && context?.user?.imageUri ? 'SW-' + context.friendcode.id : undefined,
buttons: game.shopUri ? [
{
label: 'Nintendo eShop',
url: nintendo_eshop_redirect_url ?? game.shopUri,
},
] : [],
};
const activity = new DiscordActivity();
activity.details = text[0];
activity.state = text[1];
activity.setLargeImage(title.largeImageKey ?? game.imageUri, title.largeImageText);
if (title.smallImageKey) {
activity.setSmallImage(title.smallImageKey, title.smallImageText);
} else if (context?.friendcode && context.user?.imageUri) {
activity.setSmallImage(context.user.imageUri, 'SW-' + context.friendcode.id);
}
if (game.shopUri) {
activity.buttons.push({
label: 'Nintendo eShop',
url: nintendo_eshop_redirect_url ?? game.shopUri,
});
}
if (online && title.showActiveEvent) {
activity.buttons!.push({
activity.buttons.push({
label: context?.activeevent?.shareUri ? 'Join' : 'Join via Nintendo Switch',
url: context?.activeevent?.shareUri ?? 'https://lounge.nintendo.com',
});
@ -72,7 +76,9 @@ export function getDiscordPresence(
}
return {
id: title.client || defaultTitle.client,
id: title.client ||
(typeof context?.platform !== 'undefined' && platform_clients[context.platform]) ||
defaultTitle.client,
title: titleid,
config: title,
activity,
@ -80,6 +86,30 @@ export function getDiscordPresence(
};
}
export class DiscordActivity implements DiscordRPC.Presence {
details?: string = undefined;
state?: string = undefined;
largeImageKey?: string = undefined;
largeImageText?: string = undefined;
smallImageKey?: string = undefined;
smallImageText?: string = undefined;
buttons: { label: string; url: string; }[] = [];
constructor() {
//
}
setLargeImage(key: string, text?: string) {
this.largeImageKey = key;
this.largeImageText = text ? text + ' | ' + product : product;
}
setSmallImage(key: string, text?: string) {
this.smallImageKey = key;
this.smallImageText = text;
}
}
function getPlayTimeText(type: DiscordPresencePlayTime, game: PresenceGame) {
if (type === DiscordPresencePlayTime.NINTENDO) {
const days = Math.floor(Date.now() / 1000 / 86400) - Math.floor(game.firstPlayedAt / 86400);