diff --git a/src/app/main/index.ts b/src/app/main/index.ts index fb10785..8367ea6 100644 --- a/src/app/main/index.ts +++ b/src/app/main/index.ts @@ -180,6 +180,7 @@ export class PresenceMonitorManager { friendCode: undefined, showInactivePresence: false, showEvent: false, + showPlayTime: 'detailed-since', friendNsaid: undefined, discordPreconnect: false, }, this.store.storage, token, nso, data); diff --git a/src/cli/nso/presence.ts b/src/cli/nso/presence.ts index 6f18b36..3c30b12 100644 --- a/src/cli/nso/presence.ts +++ b/src/cli/nso/presence.ts @@ -1,12 +1,11 @@ import createDebug from 'debug'; import persist from 'node-persist'; import DiscordRPC from 'discord-rpc'; -import fetch from 'node-fetch'; 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'; -import { DiscordPresenceContext, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js'; +import { DiscordPresencePlayTime, DiscordPresenceContext, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js'; import { handleEnableSplatNet2Monitoring, ZncNotifications } from './notify.js'; import { ErrorResponse } from '../../index.js'; import { getPresenceFromUrl } from '../../api/znc-proxy.js'; @@ -33,6 +32,10 @@ export function builder(yargs: Argv) { describe: 'Show event (Online Lounge/voice chat) details - this shows the number of players in game (experimental)', type: 'boolean', default: false, + }).option('show-play-time', { + describe: 'Play time format ("hidden", "nintendo", "approximate", "approximate-since", "detailed", "detailed-since")', + type: 'string', + default: 'detailed-since', }).option('friend-nsaid', { alias: ['friend-naid'], describe: 'Friend\'s Nintendo Switch account ID', @@ -193,12 +196,13 @@ export class ZncDiscordPresence extends ZncNotifications { force_friend_code: CurrentUser['links']['friendCode'] | undefined = undefined; show_console_online = false; show_active_event = true; + show_play_time = DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE; constructor( argv: Pick, 'userNotifications' | 'friendNotifications' | 'updateInterval' | 'friendCode' | 'showInactivePresence' | 'showEvent' | 'friendNsaid' | - 'discordPreconnect' + 'showPlayTime' | 'discordPreconnect' >, storage: persist.LocalStorage, token: string, @@ -215,6 +219,14 @@ export class ZncDiscordPresence extends ZncNotifications { this.show_console_online = argv.showInactivePresence; this.show_active_event = argv.showEvent; + this.show_play_time = argv.showPlayTime.toLowerCase() === 'hidden' ? DiscordPresencePlayTime.HIDDEN : + argv.showPlayTime.toLowerCase() === 'nintendo' ? DiscordPresencePlayTime.NINTENDO : + argv.showPlayTime.toLowerCase() === 'approximate' ? DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME : + argv.showPlayTime.toLowerCase() === 'approximate-since' ? DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME_SINCE : + argv.showPlayTime.toLowerCase() === 'detailed' ? DiscordPresencePlayTime.DETAILED_PLAY_TIME : + argv.showPlayTime.toLowerCase() === 'detailed-since' ? DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE : + DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE; + this.presence_user = argv.friendNsaid ?? data?.nsoAccount.user.nsaId; this.discord_preconnect = argv.discordPreconnect; } @@ -305,7 +317,8 @@ export class ZncDiscordPresence extends ZncNotifications { const presencecontext: DiscordPresenceContext = { friendcode: this.show_friend_code ? this.force_friend_code ?? friendcode : undefined, - activeevent, + activeevent: this.show_active_event ? activeevent : undefined, + show_play_time: this.show_play_time, znc_discord_presence: this, nsaid: this.presence_user!, user, diff --git a/src/cli/util/discord-activity.ts b/src/cli/util/discord-activity.ts index 10fe3e6..fa93edb 100644 --- a/src/cli/util/discord-activity.ts +++ b/src/cli/util/discord-activity.ts @@ -3,7 +3,7 @@ import fetch from 'node-fetch'; import { getPresenceFromUrl } from '../../api/znc-proxy.js'; import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState } from '../../api/znc-types.js'; import type { Arguments as ParentArguments } from '../../cli.js'; -import { DiscordPresenceContext, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js'; +import { DiscordPresenceContext, DiscordPresencePlayTime, getDiscordPresence, getInactiveDiscordPresence } from '../../discord/util.js'; import { ArgumentsCamelCase, Argv, getToken, initStorage, YargsArguments } from '../../util.js'; const debug = createDebug('cli:util:discord-activity'); @@ -36,6 +36,10 @@ export function builder(yargs: Argv) { describe: 'Show event (Online Lounge/voice chat) details - this shows the number of players in game (experimental)', type: 'boolean', default: false, + }).option('show-play-time', { + describe: 'Play time format ("hidden", "nintendo", "approximate", "approximate-since", "detailed", "detailed-since")', + type: 'string', + default: 'detailed-since', }).option('friend-nsaid', { describe: 'Friend\'s Nintendo Switch account ID', type: 'string', @@ -179,9 +183,18 @@ function getActivityFromPresence( {id: match[2] + '-' + match[3] + '-' + match[4], regenerable: false, regenerableAt: 0} : undefined; const show_friend_code = !!force_friend_code || argv.friendCode === '' || argv.friendCode === '-'; + const show_play_time = argv.showPlayTime.toLowerCase() === 'hidden' ? DiscordPresencePlayTime.HIDDEN : + argv.showPlayTime.toLowerCase() === 'nintendo' ? DiscordPresencePlayTime.NINTENDO : + argv.showPlayTime.toLowerCase() === 'approximate' ? DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME : + argv.showPlayTime.toLowerCase() === 'approximate-since' ? DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME_SINCE : + argv.showPlayTime.toLowerCase() === 'detailed' ? DiscordPresencePlayTime.DETAILED_PLAY_TIME : + argv.showPlayTime.toLowerCase() === 'detailed-since' ? DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE : + DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE; + const presencecontext: DiscordPresenceContext = { friendcode: show_friend_code ? force_friend_code ?? friendcode : undefined, activeevent, + show_play_time, // znc_discord_presence: this, nsaid: argv.friendNsaid ?? user?.nsaId, user, diff --git a/src/discord/util.ts b/src/discord/util.ts index 796b85a..52ae7a3 100644 --- a/src/discord/util.ts +++ b/src/discord/util.ts @@ -29,8 +29,9 @@ export function getDiscordPresence( else if (online && title.showPlayingOnline) text.push(title.showPlayingOnline as string + event_text); 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')); + const play_time_text = getPlayTimeText(context?.show_play_time ?? + DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE, game); + if (play_time_text) text.push(play_time_text); } const activity: DiscordRPC.Presence = { @@ -66,6 +67,60 @@ export function getDiscordPresence( }; } +function getPlayTimeText(type: DiscordPresencePlayTime, game: Game) { + if (type === DiscordPresencePlayTime.NINTENDO) { + const days = Math.floor(Date.now() / 1000 / 86400) - Math.floor(game.firstPlayedAt / 86400); + if (days <= 10) return getFirstPlayedText(game.firstPlayedAt); + if (game.totalPlayTime < 60) return 'Played for a little while'; + return 'Played for ' + hrduration(getApproximatePlayTime(game.totalPlayTime)) + ' or more'; + } + + if (type === DiscordPresencePlayTime.HIDDEN || game.totalPlayTime < 0) return null; + + const since = game.firstPlayedAt ? new Date(game.firstPlayedAt * 1000).toLocaleDateString('en-GB') : 'now'; + + switch (type) { + case DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME: + if (game.totalPlayTime < 60) return null; + return 'Played for ' + hrduration(getApproximatePlayTime(game.totalPlayTime)) + ' or more'; + case DiscordPresencePlayTime.APPROXIMATE_PLAY_TIME_SINCE: + if (game.totalPlayTime < 60) return null; + return 'Played for ' + hrduration(getApproximatePlayTime(game.totalPlayTime)) + ' or more since ' + since; + case DiscordPresencePlayTime.DETAILED_PLAY_TIME: + return 'Played for ' + hrduration(game.totalPlayTime); + case DiscordPresencePlayTime.DETAILED_PLAY_TIME_SINCE: + return 'Played for ' + hrduration(game.totalPlayTime) + ' since ' + since; + } + + return null; +} + +function getFirstPlayedText(first_played_at: number) { + const minutes = Math.floor(Date.now() / 1000 / 60) - Math.floor(first_played_at / 60); + if (minutes <= 0) return null; + + if (minutes <= 60) { + return 'First played ' + minutes + ' minute' + (minutes === 1 ? '' : 's') + ' ago'; + } + + const hours = Math.floor(Date.now() / 1000 / 3600) - Math.floor(first_played_at / 3600); + if (hours <= 24) { + return 'First played ' + hours + ' hour' + (hours === 1 ? '' : 's') + ' ago'; + } + + const days = Math.floor(Date.now() / 1000 / 86400) - Math.floor(first_played_at / 86400); + return 'First played ' + days + ' day' + (days === 1 ? '' : 's') + ' ago'; +} + +function getApproximatePlayTime(minutes: number) { + if (minutes < 300) { + // Less than 5 hours + return Math.floor(minutes / 60) * 60; + } else { + return Math.floor(minutes / 300) * 300; + } +} + export function getInactiveDiscordPresence( state: PresenceState, logoutAt: number, context?: DiscordPresenceContext ): DiscordPresence { @@ -85,6 +140,7 @@ export function getInactiveDiscordPresence( export interface DiscordPresenceContext { friendcode?: CurrentUser['links']['friendCode']; activeevent?: ActiveEvent; + show_play_time?: DiscordPresencePlayTime; znc_discord_presence?: ZncDiscordPresence; nsaid?: string; user?: CurrentUser | Friend; @@ -178,3 +234,18 @@ export interface Title { */ callback?: (activity: DiscordRPC.Presence, game: Game, context?: DiscordPresenceContext) => void; } + +export enum DiscordPresencePlayTime { + /** Don't show play time */ + HIDDEN, + /** "First played x minutes/hours/days ago" or "Played for [x5] hours or more" */ + NINTENDO, + /** "Played for [x5] hours or more" */ + APPROXIMATE_PLAY_TIME, + /** "Played for [x5] hours or more since dd/mm/yyyy" */ + APPROXIMATE_PLAY_TIME_SINCE, + /** "Played for x hours and x minutes" */ + DETAILED_PLAY_TIME, + /** "Played for x hours and x minutes since dd/mm/yyyy" */ + DETAILED_PLAY_TIME_SINCE, +}