mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-10-23 01:54:10 -05:00
Use detailed presence information from SplatNet 3
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import createDebug from 'debug';
|
||||
import { DiscordRpcClient, findDiscordRpcClient } from '../discord/rpc.js';
|
||||
import { getDiscordPresence, getInactiveDiscordPresence } from '../discord/util.js';
|
||||
import { DiscordPresencePlayTime, DiscordPresenceContext, DiscordPresence } from '../discord/types.js';
|
||||
import { DiscordPresencePlayTime, DiscordPresenceContext, DiscordPresence, ExternalMonitorConstructor, ExternalMonitor, ErrorResult } from '../discord/types.js';
|
||||
import { EmbeddedSplatNet2Monitor, ZncNotifications } from './notify.js';
|
||||
import { getPresenceFromUrl } from '../api/znc-proxy.js';
|
||||
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState, CoralErrorResponse } from '../api/coral-types.js';
|
||||
@@ -25,6 +25,7 @@ interface SavedPresence {
|
||||
class ZncDiscordPresenceClient {
|
||||
rpc: {client: DiscordRpcClient, id: string} | null = null;
|
||||
title: {id: string; since: number} | null = null;
|
||||
monitors = new Map<ExternalMonitorConstructor<any>, ExternalMonitor>();
|
||||
protected i = 0;
|
||||
|
||||
last_presence: Presence | null = null;
|
||||
@@ -35,6 +36,9 @@ class ZncDiscordPresenceClient {
|
||||
last_activity: DiscordPresence | null = null;
|
||||
onUpdateActivity: ((activity: DiscordPresence | null) => void) | null = null;
|
||||
onUpdateClient: ((client: DiscordRpcClient | null) => void) | null = null;
|
||||
onWillStartMonitor: (<T>(monitor: ExternalMonitorConstructor<T>) => any | T | null | Promise<T | null>) | null = null;
|
||||
onMonitorError: ((monitor: ExternalMonitorConstructor, instance: ExternalMonitor, error: Error) =>
|
||||
ErrorResult | Promise<ErrorResult>) | null = null;
|
||||
|
||||
update_presence_errors = 0;
|
||||
|
||||
@@ -60,6 +64,12 @@ class ZncDiscordPresenceClient {
|
||||
(this.m.show_console_online && presence?.state === PresenceState.INACTIVE);
|
||||
|
||||
if (!presence || !show_presence) {
|
||||
for (const [monitor, instance] of this.monitors.entries()) {
|
||||
debug('Stopping monitor %s', monitor.name);
|
||||
instance.disable();
|
||||
this.monitors.delete(monitor);
|
||||
}
|
||||
|
||||
if (this.m.presence_enabled && this.m.discord_preconnect && !this.rpc) {
|
||||
debugDiscord('No presence but Discord preconnect enabled - connecting');
|
||||
const discordpresence = getInactiveDiscordPresence(PresenceState.OFFLINE, 0);
|
||||
@@ -83,6 +93,7 @@ class ZncDiscordPresenceClient {
|
||||
activeevent: this.m.show_active_event ? activeevent : undefined,
|
||||
show_play_time: this.m.show_play_time,
|
||||
znc_discord_presence: this.m,
|
||||
monitors: [...this.monitors.values()],
|
||||
nsaid: this.m.presence_user!,
|
||||
user,
|
||||
};
|
||||
@@ -91,6 +102,8 @@ class ZncDiscordPresenceClient {
|
||||
getDiscordPresence(presence.state, presence.game, presence_context) :
|
||||
getInactiveDiscordPresence(presence.state, presence.logoutAt, presence_context);
|
||||
|
||||
const prev_title = this.title;
|
||||
|
||||
if (discord_presence.title) {
|
||||
if (discord_presence.title !== this.title?.id) {
|
||||
// Use the timestamp the user's presence was last updated according to Nintendo
|
||||
@@ -109,9 +122,85 @@ class ZncDiscordPresenceClient {
|
||||
this.title = null;
|
||||
}
|
||||
|
||||
const monitors = discord_presence.config?.monitor ? [discord_presence.config.monitor] : [];
|
||||
this.updateExternalMonitors(monitors, 'name' in presence.game ? presence.game : undefined,
|
||||
prev_title?.id, this.title?.id);
|
||||
|
||||
this.setActivity(discord_presence);
|
||||
}
|
||||
|
||||
async updateExternalMonitors(monitors: ExternalMonitorConstructor[], game?: Game, prev_title?: string, new_title?: string) {
|
||||
for (const monitor of monitors) {
|
||||
const instance = this.monitors.get(monitor);
|
||||
|
||||
if (instance) {
|
||||
if (prev_title !== new_title) {
|
||||
instance.onChangeTitle?.(game);
|
||||
}
|
||||
} else {
|
||||
const config = await this.getExternalMonitorConfig(monitor);
|
||||
debug('Starting monitor %s', monitor.name, config);
|
||||
|
||||
const i = new ExternalMonitorPresenceInterface(monitor, this.m);
|
||||
const instance = new monitor(i, config, game);
|
||||
Object.assign(i, {instance});
|
||||
this.monitors.set(monitor, instance);
|
||||
instance.enable();
|
||||
}
|
||||
}
|
||||
|
||||
for (const [monitor, instance] of this.monitors.entries()) {
|
||||
if (monitors.includes(monitor)) continue;
|
||||
|
||||
debug('Stopping monitor %s', monitor.name);
|
||||
instance.disable();
|
||||
this.monitors.delete(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
async getExternalMonitorConfig(monitor: ExternalMonitorConstructor) {
|
||||
return this.onWillStartMonitor?.call(null, monitor) ?? null;
|
||||
}
|
||||
|
||||
async refreshExternalMonitorsConfig() {
|
||||
for (const [monitor, instance] of this.monitors.entries()) {
|
||||
const config = await this.getExternalMonitorConfig(monitor);
|
||||
await this.updateExternalMonitorConfig(monitor, config);
|
||||
}
|
||||
}
|
||||
|
||||
async updateExternalMonitorConfig<T>(monitor: ExternalMonitorConstructor<T>, config: T) {
|
||||
const instance = this.monitors.get(monitor);
|
||||
if (!instance) return;
|
||||
|
||||
debug('Updating monitor %s config', monitor.name, config);
|
||||
|
||||
try {
|
||||
if (await instance.onUpdateConfig?.(config)) {
|
||||
debug('Updated monitor %s config', monitor.name);
|
||||
} else {
|
||||
await this.forceRestartMonitor(monitor, config /* , game */);
|
||||
}
|
||||
} catch (err) {
|
||||
await this.forceRestartMonitor(monitor, config /* , game */);
|
||||
}
|
||||
}
|
||||
|
||||
async forceRestartMonitor<T>(monitor: ExternalMonitorConstructor<T>, config: T, game?: Game) {
|
||||
const existing = this.monitors.get(monitor);
|
||||
if (!existing) return;
|
||||
|
||||
debug('Restarting monitor %s', monitor.name);
|
||||
|
||||
existing.disable();
|
||||
|
||||
const i = new ExternalMonitorPresenceInterface(monitor, this.m);
|
||||
const instance = new monitor(i, config, game);
|
||||
Object.assign(i, {instance});
|
||||
this.monitors.set(monitor, instance);
|
||||
instance.enable();
|
||||
}
|
||||
|
||||
async setActivity(activity: DiscordPresence | string | null) {
|
||||
this.onUpdateActivity?.call(null, typeof activity === 'string' ? null : activity);
|
||||
this.last_activity = typeof activity === 'string' ? null : activity;
|
||||
@@ -239,6 +328,38 @@ class ZncDiscordPresenceClient {
|
||||
this.title = null;
|
||||
}
|
||||
}
|
||||
|
||||
refreshPresence() {
|
||||
this.updatePresenceForDiscord(
|
||||
this.last_presence,
|
||||
this.last_user,
|
||||
this.last_friendcode,
|
||||
this.last_event,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExternalMonitorPresenceInterface {
|
||||
readonly instance!: ExternalMonitor;
|
||||
|
||||
constructor(
|
||||
readonly monitor: ExternalMonitorConstructor<any>,
|
||||
readonly znc_discord_presence: ZncDiscordPresence | ZncProxyDiscordPresence,
|
||||
) {}
|
||||
|
||||
refreshPresence() {
|
||||
this.znc_discord_presence.discord.refreshPresence();
|
||||
}
|
||||
|
||||
async handleError(err: Error) {
|
||||
debug('Error in external monitor %s', this.monitor.name, err);
|
||||
|
||||
if (this.znc_discord_presence.discord.onMonitorError) {
|
||||
return await this.znc_discord_presence.discord.onMonitorError.call(null, this.monitor, this.instance, err);
|
||||
} else {
|
||||
return ErrorResult.STOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ZncDiscordPresence extends ZncNotifications {
|
||||
|
||||
Reference in New Issue
Block a user