From 0839ff520b7cf2dd2f7c32828ee90bf65f52dfec Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Tue, 22 Mar 2022 20:07:21 +0000 Subject: [PATCH] Show if playing online even for games that don't set a description --- src/cli/nso/presence.ts | 7 +-- src/{ => discord}/titles.ts | 4 ++ src/discord/util.ts | 93 +++++++++++++++++++++++++++++++++++++ src/util.ts | 79 ++++--------------------------- 4 files changed, 110 insertions(+), 73 deletions(-) rename src/{ => discord}/titles.ts (95%) create mode 100644 src/discord/util.ts diff --git a/src/cli/nso/presence.ts b/src/cli/nso/presence.ts index a51a75b..a08d799 100644 --- a/src/cli/nso/presence.ts +++ b/src/cli/nso/presence.ts @@ -5,7 +5,8 @@ import fetch from 'node-fetch'; import { CurrentUser, Friend, Presence, PresenceState } from '../../api/znc-types.js'; import ZncApi from '../../api/znc.js'; import type { Arguments as ParentArguments } from '../nso.js'; -import { ArgumentsCamelCase, Argv, getDiscordPresence, getInactiveDiscordPresence, getToken, initStorage, SavedToken, YargsArguments } from '../../util.js'; +import { ArgumentsCamelCase, Argv, getToken, initStorage, SavedToken, YargsArguments } from '../../util.js'; +import { getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js'; import { ZncNotifications } from './notify.js'; import ZncProxyApi from '../../api/znc-proxy.js'; @@ -188,8 +189,8 @@ class ZncDiscordPresence extends ZncNotifications { const fc = this.argv.friendCode === '' || this.argv.friendCode === '-' ? friendcode : this.forceFriendCode; const discordpresence = 'name' in presence.game ? - getDiscordPresence(presence.game, fc) : - getInactiveDiscordPresence(fc); + getDiscordPresence(presence.state, presence.game, fc) : + getInactiveDiscordPresence(presence.state, presence.logoutAt, fc); if (this.rpc && this.rpc.id !== discordpresence.id) { const client = this.rpc.client; diff --git a/src/titles.ts b/src/discord/titles.ts similarity index 95% rename from src/titles.ts rename to src/discord/titles.ts index 6e914a3..4db9420 100644 --- a/src/titles.ts +++ b/src/discord/titles.ts @@ -4,6 +4,7 @@ export const defaultTitle: Title = { id: '0000000000000000', client: '950883021165330493', titleName: true, + showPlayingOnline: true, }; const titles: Title[] = [ @@ -12,18 +13,21 @@ const titles: Title[] = [ id: '0100f8f0000a2000', client: '950886725398429726', largeImageKey: '0100f8f0000a2000', + showPlayingOnline: true, }, { // Splatoon 2 [The Americas] id: '01003bc0000a0000', client: '950886725398429726', largeImageKey: '0100f8f0000a2000', + showPlayingOnline: true, }, { // Splatoon 2 [Japan] id: '01003c700009c000', client: '950886725398429726', largeImageKey: '01003c700009c000', + showPlayingOnline: true, }, { diff --git a/src/discord/util.ts b/src/discord/util.ts new file mode 100644 index 0000000..6caace2 --- /dev/null +++ b/src/discord/util.ts @@ -0,0 +1,93 @@ +import DiscordRPC from 'discord-rpc'; +import { CurrentUser, Game, PresenceState } from '../api/znc-types.js'; +import titles, { defaultTitle } from './titles.js'; +import { getTitleIdFromEcUrl, hrduration } from '../util.js'; + +export function getDiscordPresence( + state: PresenceState, game: Game, + friendcode?: CurrentUser['links']['friendCode'] +): DiscordPresence { + const titleid = getTitleIdFromEcUrl(game.shopUri); + const title = titles.find(t => t.id === titleid) || defaultTitle; + + const text = []; + + if (title.titleName === true) text.push(game.name); + else if (title.titleName) text.push(title.titleName); + + if (game.sysDescription) text.push(game.sysDescription); + else if (state === PresenceState.PLAYING && title.showPlayingOnline === true) text.push('Playing online'); + else if (state === PresenceState.PLAYING && title.showPlayingOnline) text.push(title.showPlayingOnline as string); + + if (game.totalPlayTime >= 60) { + text.push('Played for ' + hrduration(game.totalPlayTime) + + ' since ' + (game.firstPlayedAt ? new Date(game.firstPlayedAt * 1000).toLocaleDateString('en-GB') : 'now')); + } + + const activity: DiscordRPC.Presence = { + details: text[0], + state: text[1], + largeImageKey: title.largeImageKey ?? game.imageUri, + largeImageText: friendcode ? 'SW-' + friendcode.id : undefined, + smallImageKey: title.smallImageKey, + buttons: game.shopUri ? [ + { + label: 'Nintendo eShop', + url: game.shopUri, + }, + ] : [], + }; + + title.callback?.call(null, activity, game); + + return { + id: title.client || defaultTitle.client, + title: titleid, + activity, + showTimestamp: title.showTimestamp, + }; +} + +export function getInactiveDiscordPresence( + state: PresenceState, logoutAt: number, + friendcode?: CurrentUser['links']['friendCode'] +): DiscordPresence { + return { + id: defaultTitle.client, + title: null, + activity: { + state: 'Not playing', + largeImageKey: 'nintendoswitch', + largeImageText: friendcode ? 'SW-' + friendcode.id : undefined, + }, + }; +} + +export interface DiscordPresence { + id: string; + title: string | null; + activity: DiscordRPC.Presence; + showTimestamp?: boolean; +} + +export function getTitleConfiguration(game: Game, id: string) { + return titles.find(title => { + if (title.id !== id) return false; + + return true; + }); +} + +export interface Title { + /** Lowercase hexadecimal title ID */ + id: string; + /** Discord client ID */ + client: string; + + titleName?: string | boolean; + largeImageKey?: string; + smallImageKey?: string; + showTimestamp?: boolean; + /** Show "Playing online" if playing online and the game doesn't set activity details */ + showPlayingOnline?: string | boolean; +} diff --git a/src/util.ts b/src/util.ts index 198b6da..724402b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,14 +2,12 @@ import * as path from 'path'; import * as yargs from 'yargs'; import type * as yargstypes from '../node_modules/@types/yargs/index.js'; import createDebug from 'debug'; -import DiscordRPC from 'discord-rpc'; import persist from 'node-persist'; import getPaths from 'env-paths'; import { FlapgApiResponse } from './api/f.js'; import { NintendoAccountToken, NintendoAccountUser } from './api/na.js'; -import { AccountLogin, CurrentUser, Game } from './api/znc-types.js'; +import { AccountLogin } from './api/znc-types.js'; import ZncApi from './api/znc.js'; -import titles, { defaultTitle } from './titles.js'; import ZncProxyApi from './api/znc-proxy.js'; import MoonApi from './api/moon.js'; @@ -50,6 +48,14 @@ export async function initStorage(dir: string) { return storage; } +export async function getToken(storage: persist.LocalStorage, token: string, proxy_url: string): Promise<{ + nso: ZncProxyApi; + data: SavedToken; +}> +export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string): Promise<{ + nso: ZncApi; + data: SavedToken; +}> export async function getToken(storage: persist.LocalStorage, token: string, proxy_url?: string) { if (!token) { console.error('No token set. Set a Nintendo Account session token using the `--token` option or by running `nxapi nso token`.'); @@ -127,73 +133,6 @@ export function getTitleIdFromEcUrl(url: string) { return match?.[1] ?? null; } -export function getDiscordPresence(game: Game, friendcode?: CurrentUser['links']['friendCode']): DiscordPresence { - const titleid = getTitleIdFromEcUrl(game.shopUri); - const title = titles.find(t => t.id === titleid) || defaultTitle; - - const text = []; - - if (title.titleName === true) text.push(game.name); - else if (title.titleName) text.push(title.titleName); - - if (game.sysDescription) text.push(game.sysDescription); - - if (game.totalPlayTime >= 60) { - text.push('Played for ' + hrduration(game.totalPlayTime) + - ' since ' + (game.firstPlayedAt ? new Date(game.firstPlayedAt * 1000).toLocaleDateString('en-GB') : 'now')); - } - - return { - id: title.client || defaultTitle.client, - title: titleid, - activity: { - details: text[0], - state: text[1], - largeImageKey: title.largeImageKey ?? game.imageUri, - largeImageText: friendcode ? 'SW-' + friendcode.id : undefined, - smallImageKey: title.smallImageKey, - buttons: game.shopUri ? [ - { - label: 'Nintendo eShop', - url: game.shopUri, - }, - ] : [], - }, - showTimestamp: title.showTimestamp, - }; -} - -export function getInactiveDiscordPresence(friendcode?: CurrentUser['links']['friendCode']): DiscordPresence { - return { - id: defaultTitle.client, - title: null, - activity: { - state: 'Not playing', - largeImageKey: 'nintendoswitch', - largeImageText: friendcode ? 'SW-' + friendcode.id : undefined, - }, - }; -} - -export interface DiscordPresence { - id: string; - title: string | null; - activity: DiscordRPC.Presence; - showTimestamp?: boolean; -} - -export interface Title { - /** Lowercase hexadecimal title ID */ - id: string; - /** Discord client ID */ - client: string; - - titleName?: string | true; - largeImageKey?: string; - smallImageKey?: string; - showTimestamp?: boolean; -} - export function hrduration(duration: number, short = false) { const hours = Math.floor(duration / 60); const minutes = duration - (hours * 60);