diff --git a/src/cli/nso/presence.ts b/src/cli/nso/presence.ts index bc16123..d031e5a 100644 --- a/src/cli/nso/presence.ts +++ b/src/cli/nso/presence.ts @@ -3,7 +3,7 @@ import createDebug from 'debug'; import persist from 'node-persist'; import DiscordRPC from 'discord-rpc'; import fetch from 'node-fetch'; -import { ActiveEvent, CurrentUser, Presence, PresenceState, ZncErrorResponse } from '../../api/znc-types.js'; +import { ActiveEvent, CurrentUser, Friend, Presence, PresenceState, ZncErrorResponse } from '../../api/znc-types.js'; import ZncApi from '../../api/znc.js'; import type { Arguments as ParentArguments } from '../nso.js'; import { ArgumentsCamelCase, Argv, getToken, initStorage, LoopResult, SavedToken, YargsArguments } from '../../util.js'; @@ -232,9 +232,9 @@ export class ZncDiscordPresence extends ZncNotifications { throw new Error('User "' + this.presence_user + '" is not friends with this user'); } - await this.updatePresenceForDiscord(friend.presence); + await this.updatePresenceForDiscord(friend.presence, user); } else { - await this.updatePresenceForDiscord(user!.presence, user!.links.friendCode, activeevent); + await this.updatePresenceForDiscord(user!.presence, user, user!.links.friendCode, activeevent); } } @@ -249,15 +249,19 @@ export class ZncDiscordPresence extends ZncNotifications { i = 0; last_presence: Presence | null = null; - friendcode: CurrentUser['links']['friendCode'] | undefined = undefined; + last_user: CurrentUser | Friend | undefined = undefined; + last_friendcode: CurrentUser['links']['friendCode'] | undefined = undefined; last_event: ActiveEvent | undefined = undefined; async updatePresenceForDiscord( - presence: Presence | null, friendcode?: CurrentUser['links']['friendCode'], + presence: Presence | null, + user?: CurrentUser | Friend, + friendcode?: CurrentUser['links']['friendCode'], activeevent?: ActiveEvent ) { this.last_presence = presence; - this.friendcode = friendcode; + this.last_user = user; + this.last_friendcode = friendcode; this.last_event = activeevent; const online = presence?.state === PresenceState.ONLINE || presence?.state === PresenceState.PLAYING; @@ -282,6 +286,7 @@ export class ZncDiscordPresence extends ZncNotifications { activeevent, znc_discord_presence: this, nsaid: this.presence_user!, + user, }; const discordpresence = 'name' in presence.game ? getDiscordPresence(presence.state, presence.game, presencecontext) : @@ -382,10 +387,10 @@ export class ZncDiscordPresence extends ZncNotifications { // Is the authenticated user no longer friends with this user? await this.updatePresenceForDiscord(null); } else { - await this.updatePresenceForDiscord(friend.presence); + await this.updatePresenceForDiscord(friend.presence, friend); } } else { - await this.updatePresenceForDiscord(user!.presence, user!.links.friendCode, activeevent); + await this.updatePresenceForDiscord(user!.presence, user, user!.links.friendCode, activeevent); } } diff --git a/src/discord/titles/mojang.ts b/src/discord/titles/mojang.ts index 19dc105..838652d 100644 --- a/src/discord/titles/mojang.ts +++ b/src/discord/titles/mojang.ts @@ -5,12 +5,14 @@ export const titles: Title[] = [ // Minecraft id: '0100d71004694000', client: '950906152391168020', - largeImageKey: '0100d71004694000', + showPlayingOnline: true, + showActiveEvent: true, }, { // Minecraft: Nintendo Switch Edition id: '01006bd001e06000', client: '950906152391168020', - largeImageKey: '01006bd001e06000', + showPlayingOnline: true, + showActiveEvent: true, }, ]; diff --git a/src/discord/titles/nintendo.ts b/src/discord/titles/nintendo.ts index 9b2d4f1..4ba581c 100644 --- a/src/discord/titles/nintendo.ts +++ b/src/discord/titles/nintendo.ts @@ -5,7 +5,6 @@ export const titles: Title[] = [ // Splatoon 2 [Europe] id: '0100f8f0000a2000', client: '950886725398429726', - largeImageKey: '0100f8f0000a2000', showPlayingOnline: true, showActiveEvent: true, }, @@ -13,7 +12,6 @@ export const titles: Title[] = [ // Splatoon 2 [The Americas] id: '01003bc0000a0000', client: '950886725398429726', - largeImageKey: '0100f8f0000a2000', showPlayingOnline: true, showActiveEvent: true, }, @@ -21,7 +19,6 @@ export const titles: Title[] = [ // Splatoon 2 [Japan] id: '01003c700009c000', client: '950886725398429726', - largeImageKey: '01003c700009c000', showPlayingOnline: true, showActiveEvent: true, }, @@ -29,49 +26,49 @@ export const titles: Title[] = [ // Splatoon 2: Splatfest World Premiere [Europe, The Americas] id: '01003870040fa000', client: '950886725398429726', - titleName: 'Splatfest World Premiere', + largeImageText: 'Splatfest World Premiere', showPlayingOnline: true, }, { // Splatoon 2: Special Demo [Europe] id: '01007e200d45c000', client: '950886725398429726', - titleName: 'Special Demo', + largeImageText: 'Special Demo', showPlayingOnline: true, }, { // Splatoon 2 Special Demo [The Americas] id: '01006bb00d45a000', client: '950886725398429726', - titleName: 'Special Demo', + largeImageText: 'Special Demo', showPlayingOnline: true, }, { // Splatoon 2 Special Demo [Japan] id: '01009c900d458000', client: '950886725398429726', - titleName: 'Special Demo', + largeImageText: 'Special Demo', showPlayingOnline: true, }, { // Splatoon 2: Special Demo 2020 [Europe] id: '01009240116cc000', client: '950886725398429726', - titleName: 'Special Demo 2020', + largeImageText: 'Special Demo 2020', showPlayingOnline: true, }, { // Splatoon 2 Special Demo 2020 [The Americas] id: '01002120116c4000', client: '950886725398429726', - titleName: 'Special Demo 2020', + largeImageText: 'Special Demo 2020', showPlayingOnline: true, }, { // Splatoon 2 Special Demo 2020 [Japan] id: '0100998011330000', client: '950886725398429726', - titleName: 'Special Demo 2020', + largeImageText: 'Special Demo 2020', showPlayingOnline: true, }, @@ -79,7 +76,6 @@ export const titles: Title[] = [ // Super Smash Bros. Ultimate id: '01006a800016e000', client: '950894516104212490', - largeImageKey: '01006a800016e000', showActiveEvent: true, }, @@ -87,7 +83,6 @@ export const titles: Title[] = [ // Mario Kart 8 Deluxe id: '0100152000022000', client: '950905573149409280', - largeImageKey: '0100152000022000', showActiveEvent: true, }, @@ -95,43 +90,37 @@ export const titles: Title[] = [ // Super Mario Odyssey id: '0100000000010000', client: '950905939899351050', - largeImageKey: '0100000000010000', }, { // Nintendo Entertainment System - Nintendo Switch Online id: '0100d870045b6000', client: '950907272438104064', - titleName: 'Nintendo Entertainment System', - largeImageKey: '0100d870045b6000', + largeImageText: 'Nintendo Entertainment System', }, { // Super Nintendo Entertainment System - Nintendo Switch Online id: '01008d300c50c000', client: '950907272438104064', - titleName: 'Super Nintendo Entertainment System', - largeImageKey: '01008d300c50c000', + largeImageText: 'Super Nintendo Entertainment System', }, { // Nintendo 64 - Nintendo Switch Online id: '0100c9a00ece6000', client: '950907272438104064', - titleName: 'Nintendo 64', - largeImageKey: '0100c9a00ece6000', + largeImageText: 'Nintendo 64', }, { // SEGA Mega Drive - Nintendo Switch Online id: '0100b3c014bda000', client: '950907272438104064', - titleName: 'SEGA Mega Drive', - largeImageKey: '0100b3c014bda000', + largeImageText: 'SEGA Mega Drive', }, { // Animal Crossing: New Horizons id: '01006f8002326000', client: '950908097235415079', - largeImageKey: '01006f8002326000', showPlayingOnline: true, showActiveEvent: true, }, @@ -140,7 +129,6 @@ export const titles: Title[] = [ id: '0100f38011cfe000', client: '950908097235415079', titleName: 'Island Transfer Tool', - largeImageKey: '0100f38011cfe000', }, { @@ -170,7 +158,7 @@ export const titles: Title[] = [ // Snipperclips - Cut it out, together! Demo id: '0100d87002ee0000', client: '966102704525025301', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -186,7 +174,7 @@ export const titles: Title[] = [ // ARMS Sparring Demo id: '0100a5400ac86000', client: '966102773642960917', - titleName: 'Sparring Demo', + largeImageText: 'Sparring Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -209,7 +197,7 @@ export const titles: Title[] = [ // Pokémon: Let's Go, Pikachu! & Pokémon: Let's Go, Eevee! Demo id: '0100c1800d7ae000', client: '966103072407437332', - titleName: 'Pokémon: Let\'s Go, Pikachu! & Pokémon: Let\'s Go, Eevee! Demo', + largeImageText: 'Pokémon: Let\'s Go, Pikachu! & Pokémon: Let\'s Go, Eevee! Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -241,7 +229,7 @@ export const titles: Title[] = [ // Yoshi's Crafted World Demo id: '0100ae800c9c6000', client: '966103360589672488', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -257,7 +245,7 @@ export const titles: Title[] = [ // Pokkén Tournament DX Demo id: '010030d005ae6000', client: '966103482702647326', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -273,7 +261,7 @@ export const titles: Title[] = [ // OCTOPATH TRAVELER: Prologue Demo id: '010096000b3ea000', client: '966147770006241340', - titleName: 'Prologue Demo', + largeImageText: 'Prologue Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -297,7 +285,7 @@ export const titles: Title[] = [ // Captain Toad: Treasure Tracker Demo id: '01002c400b6b6000', client: '966148067134939186', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -313,7 +301,7 @@ export const titles: Title[] = [ // // Sushi Striker: The Way of Sushido Demo // id: '', // TODO // client: '966148228154265661', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -329,7 +317,7 @@ export const titles: Title[] = [ // Mario Tennis Aces: Special Demo [Europe] id: '0100f9600da78000', client: '966148353727561738', - titleName: 'Special Demo', + largeImageText: 'Special Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -337,7 +325,7 @@ export const titles: Title[] = [ // Mario Tennis Aces Special Online Demo [The Americas] id: '0100a4200da76000', client: '966148353727561738', - titleName: 'Special Online Demo', + largeImageText: 'Special Online Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -368,7 +356,7 @@ export const titles: Title[] = [ // Pikmin 3 Deluxe Demo id: '01001cb0106f8000', client: '966148753365041173', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -384,7 +372,7 @@ export const titles: Title[] = [ // Dragon Quest Builders Demo id: '0100c360070f6000', client: '966148914933792838', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -432,7 +420,7 @@ export const titles: Title[] = [ // Kirby Star Allies Demo id: '01005a70096fa000', client: '966330020391952384', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -472,7 +460,7 @@ export const titles: Title[] = [ // // Dragon Quest Builders 2 Demo // id: '', // TODO // client: '966331488322871346', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -480,7 +468,7 @@ export const titles: Title[] = [ // // Dragon Quest Builders 2 Jumbo Demo // id: '', // TODO // client: '966331488322871346', - // titleName: 'Jumbo Demo', + // largeImageText: 'Jumbo Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -504,7 +492,7 @@ export const titles: Title[] = [ // Fitness Boxing Demo id: '010050200d0da000', client: '966387481375285300', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -544,7 +532,7 @@ export const titles: Title[] = [ // // Pokémon Mystery Dungeon: Rescue Team DX Demo // id: '', // TODO // client: '966387876520685668', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -568,7 +556,7 @@ export const titles: Title[] = [ // DAEMON X MACHINA: Prototype Missions id: '0100bf600d83a000', client: '966388060428320789', - titleName: 'Prototype Missions', + largeImageText: 'Prototype Missions', showPlayingOnline: true, showActiveEvent: true, }, @@ -576,7 +564,7 @@ export const titles: Title[] = [ // // DAEMON X MACHINA Demo // id: '', // TODO // client: '966388060428320789', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -608,7 +596,7 @@ export const titles: Title[] = [ // BOXBOY! + BOXGIRL! Demo id: '0100b7200e02e000', client: '966388339127234592', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -640,7 +628,7 @@ export const titles: Title[] = [ // // Cadence of Hyrule - Crypt of the NecroDancer Featuring The Legend of Zelda Demo // id: '', // TODO // client: '966441763973763162', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -656,7 +644,7 @@ export const titles: Title[] = [ // // Dragon Quest XI S: Echoes of an Elusive Age - Definitive Edition Demo // id: '', // TODO // client: '966441876267864084', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -696,7 +684,7 @@ export const titles: Title[] = [ // Bravely Default II Demo id: '0100b6801137e000', client: '966442378095382578', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -704,7 +692,7 @@ export const titles: Title[] = [ // // Bravely Default II Demo // id: '', // TODO - there's two demos apparently // client: '966442378095382578', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -728,7 +716,7 @@ export const titles: Title[] = [ // 51 Worldwide Games: Local Multiplayer Guest Edition id: '0100cd9011c04000', client: '966442617938280526', - titleName: 'Local Multiplayer Guest Edition', + largeImageText: 'Local Multiplayer Guest Edition', showPlayingOnline: true, showActiveEvent: true, }, @@ -784,7 +772,7 @@ export const titles: Title[] = [ // // Kirby Fighters 2 Demo // id: '', // TODO // client: '966479069287960656', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -800,7 +788,7 @@ export const titles: Title[] = [ // // Hyrule Warriors: Age of Calamity Demo // id: '', // TODO // client: '966479236762325013', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -816,7 +804,7 @@ export const titles: Title[] = [ // Fitness Boxing 2: Rhythm & Exercise Demo id: '01000f50130a2000', client: '966479353510789150', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -856,7 +844,7 @@ export const titles: Title[] = [ // Miitopia Demo id: '01007da0140e8000', client: '966487135194591282', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -903,7 +891,7 @@ export const titles: Title[] = [ // Game Builder Garage Demo id: '01003b101497c000', client: '966487590721163264', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -919,7 +907,7 @@ export const titles: Title[] = [ // // Metroid Dread Demo // id: '', // TODO // client: '966487674162642974', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -935,7 +923,7 @@ export const titles: Title[] = [ // WarioWare: Get It Together! Demo id: '0100ee9015b5e000', client: '966487765447483412', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -975,7 +963,7 @@ export const titles: Title[] = [ // Big Brain Academy: Brain vs. Brain Demo id: '0100219016ab4000', client: '966533636969082921', - titleName: 'Demo', + largeImageText: 'Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -991,7 +979,7 @@ export const titles: Title[] = [ // Project Triangle Strategy Debut Demo id: '01002980140f6000', client: '966533747023437904', - titleName: 'Project Triangle Strategy Debut Demo', + largeImageText: 'Project Triangle Strategy Debut Demo', showPlayingOnline: true, showActiveEvent: true, }, @@ -999,7 +987,7 @@ export const titles: Title[] = [ // // Project Triangle Strategy Prologue demo // id: '', // TODO // client: '966533747023437904', - // titleName: 'Prologue demo', + // largeImageText: 'Prologue demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -1015,7 +1003,7 @@ export const titles: Title[] = [ // // Kirby and the Forgotten Land Demo // id: '', // TODO // client: '966534055116021821', - // titleName: 'Demo', + // largeImageText: 'Demo', // showPlayingOnline: true, // showActiveEvent: true, // }, @@ -1031,7 +1019,7 @@ export const titles: Title[] = [ // Nintendo Switch Sports Online Play Test id: '01000ee017182000', client: '966534181783998474', - titleName: 'Online Play Test', + largeImageText: 'Online Play Test', showPlayingOnline: true, showActiveEvent: true, }, diff --git a/src/discord/util.ts b/src/discord/util.ts index 73b1c31..08f8bf2 100644 --- a/src/discord/util.ts +++ b/src/discord/util.ts @@ -1,7 +1,7 @@ import DiscordRPC from 'discord-rpc'; -import { ActiveEvent, CurrentUser, Game, PresenceState } from '../api/znc-types.js'; +import { ActiveEvent, CurrentUser, Friend, Game, PresenceState } from '../api/znc-types.js'; import { defaultTitle, titles } from './titles.js'; -import { getTitleIdFromEcUrl, hrduration } from '../util.js'; +import { getTitleIdFromEcUrl, hrduration, version } from '../util.js'; import { ZncDiscordPresence } from '../cli/nso/presence.js'; export function getDiscordPresence( @@ -21,11 +21,11 @@ export function getDiscordPresence( ' (' + members?.length + ' player' + (members?.length === 1 ? '' : 's') + ')' : ''; - if (game.sysDescription) text.push(game.sysDescription + event_text); + if ((title.showDescription ?? true) && game.sysDescription) text.push(game.sysDescription + event_text); else if (online && title.showPlayingOnline === true) text.push('Playing online' + event_text); else if (online && title.showPlayingOnline) text.push(title.showPlayingOnline as string + event_text); - if (game.totalPlayTime >= 60) { + if ((title.showPlayTime ?? true) && game.totalPlayTime >= 60) { text.push('Played for ' + hrduration(game.totalPlayTime) + ' since ' + (game.firstPlayedAt ? new Date(game.firstPlayedAt * 1000).toLocaleDateString('en-GB') : 'now')); } @@ -34,8 +34,10 @@ export function getDiscordPresence( details: text[0], state: text[1], largeImageKey: title.largeImageKey ?? game.imageUri, - largeImageText: context?.friendcode ? 'SW-' + context.friendcode.id : undefined, - smallImageKey: title.smallImageKey, + largeImageText: title.largeImageText ?? 'nxapi ' + version, + 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', @@ -44,15 +46,10 @@ export function getDiscordPresence( ] : [], }; - if (online && title.showActiveEvent && context?.activeevent?.shareUri) { - activity.buttons?.push({ - label: 'Join', - url: context.activeevent.shareUri, - }); - } else if (online && title.showActiveEvent) { - activity.buttons?.push({ - label: 'Join via Nintendo Switch', - url: 'https://lounge.nintendo.com', + if (online && title.showActiveEvent) { + activity.buttons!.push({ + label: context?.activeevent?.shareUri ? 'Join' : 'Join via Nintendo Switch', + url: context?.activeevent?.shareUri ?? 'https://lounge.nintendo.com', }); } @@ -75,7 +72,9 @@ export function getInactiveDiscordPresence( activity: { state: 'Not playing', largeImageKey: 'nintendoswitch', - largeImageText: context?.friendcode ? 'SW-' + context.friendcode.id : undefined, + largeImageText: 'nxapi ' + version, + smallImageKey: context?.friendcode ? context?.user?.imageUri : undefined, + smallImageText: context?.friendcode && context?.user?.imageUri ? 'SW-' + context.friendcode.id : undefined, }, }; } @@ -85,6 +84,7 @@ export interface DiscordPresenceContext { activeevent?: ActiveEvent; znc_discord_presence?: ZncDiscordPresence; nsaid?: string; + user?: CurrentUser | Friend; } export interface DiscordPresence { @@ -128,16 +128,24 @@ export interface Title { * By default the title's icon from znc will be used. (No icons need to be uploaded to Discord.) */ largeImageKey?: string; + largeImageText?: string; /** * By default this will not be set. */ smallImageKey?: string; + smallImageText?: string; /** * Whether to show the timestamp the user started playing the title in Discord. Discord shows this as the number of minutes and seconds since the timestamp; this will be inaccurate as it may take up to a minute (by default) to detect the user's presence, so this is disabled by default. * * @default false */ showTimestamp?: boolean; + /** + * Show the activity description set by the title. + * + * @default true + */ + showDescription?: boolean; /** * Show "Playing online" if playing online and the game doesn't set activity details. * @@ -150,6 +158,12 @@ export interface Title { * @default false */ showActiveEvent?: boolean; + /** + * Whether to show "Played for ... since ..." in Discord. + * + * @default true + */ + showPlayTime?: boolean; /** * A function to call to customise the Discord activity.