mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-04-21 06:27:36 -05:00
Add functions to use a specific Discord user in the app
This commit is contained in:
parent
241ce19ec8
commit
ca35545f98
|
|
@ -15,6 +15,12 @@ export interface WindowConfiguration<T extends WindowType = WindowType> {
|
|||
props: WindowProps[T];
|
||||
}
|
||||
|
||||
export interface DiscordPresenceConfiguration {
|
||||
source: DiscordPresenceSource;
|
||||
/** Discord user ID */
|
||||
user?: string;
|
||||
}
|
||||
|
||||
export type DiscordPresenceSource = DiscordPresenceSourceZnc | DiscordPresenceSourceUrl;
|
||||
export interface DiscordPresenceSourceZnc {
|
||||
na_id: string;
|
||||
|
|
|
|||
|
|
@ -8,15 +8,13 @@ import dotenv from 'dotenv';
|
|||
import dotenvExpand from 'dotenv-expand';
|
||||
import MenuApp from './menu.js';
|
||||
import { handleOpenWebServiceUri } from './webservices.js';
|
||||
import { EmbeddedPresenceMonitor, EmbeddedProxyPresenceMonitor, PresenceMonitorManager } from './monitor.js';
|
||||
import { EmbeddedPresenceMonitor, PresenceMonitorManager } from './monitor.js';
|
||||
import { createWindow } from './windows.js';
|
||||
import { DiscordPresenceSource, WindowType } from '../common/types.js';
|
||||
import { DiscordPresenceConfiguration, WindowType } from '../common/types.js';
|
||||
import { initStorage, paths } from '../../util/storage.js';
|
||||
import { checkUpdates, UpdateCacheData } from '../../common/update.js';
|
||||
import Users, { CoralUser } from '../../common/users.js';
|
||||
import { setupIpc } from './ipc.js';
|
||||
import { version } from '../../util/product.js';
|
||||
import { GITLAB_URL } from '../../common/constants.js';
|
||||
|
||||
const debug = createDebug('app:main');
|
||||
|
||||
|
|
@ -75,6 +73,9 @@ app.whenReady().then(async () => {
|
|||
|
||||
setupIpc(appinstance, ipcMain);
|
||||
|
||||
// @ts-expect-error
|
||||
globalThis.app = appinstance;
|
||||
|
||||
await store.restoreMonitorState(appinstance.monitors);
|
||||
|
||||
const menu = new MenuApp(appinstance);
|
||||
|
|
@ -95,9 +96,6 @@ app.whenReady().then(async () => {
|
|||
debug('App started');
|
||||
|
||||
appinstance.showMainWindow();
|
||||
|
||||
// @ts-expect-error
|
||||
globalThis.app = appinstance;
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
|
|
@ -129,7 +127,7 @@ interface SavedMonitorState {
|
|||
user_notifications: boolean;
|
||||
friend_notifications: boolean;
|
||||
}[];
|
||||
discord_presence: DiscordPresenceSource | null;
|
||||
discord_presence: DiscordPresenceConfiguration | null;
|
||||
}
|
||||
|
||||
export class Store extends EventEmitter {
|
||||
|
|
@ -160,18 +158,10 @@ export class Store extends EventEmitter {
|
|||
friend_notifications: monitor.friend_notifications,
|
||||
});
|
||||
}
|
||||
|
||||
if (monitor.presence_enabled && !state.discord_presence) {
|
||||
state.discord_presence = monitor instanceof EmbeddedProxyPresenceMonitor ? {
|
||||
url: monitor.presence_url,
|
||||
} : {
|
||||
na_id: monitor.data.user.id,
|
||||
friend_nsa_id: monitor.presence_user === monitor.data.nsoAccount.user.nsaId ? undefined :
|
||||
monitor.presence_user ?? undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
state.discord_presence = monitors.getDiscordPresenceConfiguration();
|
||||
|
||||
debug('Saving monitor state', state);
|
||||
await this.storage.setItem('AppMonitors', state);
|
||||
}
|
||||
|
|
@ -182,8 +172,8 @@ export class Store extends EventEmitter {
|
|||
if (!state) return;
|
||||
|
||||
for (const user of state.users) {
|
||||
const discord_presence_active = state.discord_presence && 'na_id' in state.discord_presence &&
|
||||
state.discord_presence.na_id === user.id;
|
||||
const discord_presence_active = state.discord_presence && 'na_id' in state.discord_presence.source &&
|
||||
state.discord_presence.source.na_id === user.id;
|
||||
|
||||
if (!discord_presence_active &&
|
||||
!user.user_notifications &&
|
||||
|
|
@ -192,13 +182,15 @@ export class Store extends EventEmitter {
|
|||
|
||||
try {
|
||||
await monitors.start(user.id, monitor => {
|
||||
monitor.presence_user = state.discord_presence && 'na_id' in state.discord_presence &&
|
||||
state.discord_presence.na_id === user.id ?
|
||||
state.discord_presence.friend_nsa_id ?? monitor.data.nsoAccount.user.nsaId : null;
|
||||
monitor.presence_user = state.discord_presence && 'na_id' in state.discord_presence.source &&
|
||||
state.discord_presence.source.na_id === user.id ?
|
||||
state.discord_presence.source.friend_nsa_id ?? monitor.data.nsoAccount.user.nsaId : null;
|
||||
monitor.user_notifications = user.user_notifications;
|
||||
monitor.friend_notifications = user.friend_notifications;
|
||||
|
||||
if (monitor.presence_user) {
|
||||
monitor.discord_client_filter = state.discord_presence?.user ?
|
||||
monitors.createDiscordClientFilter(state.discord_presence.user) : undefined;
|
||||
this.emit('update-discord-presence-source', monitors.getDiscordPresenceSource());
|
||||
}
|
||||
});
|
||||
|
|
@ -208,11 +200,13 @@ export class Store extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
if (state.discord_presence && 'url' in state.discord_presence) {
|
||||
if (state.discord_presence && 'url' in state.discord_presence.source) {
|
||||
try {
|
||||
await monitors.startUrl(state.discord_presence.url);
|
||||
const monitor = await monitors.startUrl(state.discord_presence.source.url);
|
||||
monitor.discord_client_filter = state.discord_presence?.user ?
|
||||
monitors.createDiscordClientFilter(state.discord_presence.user) : undefined;
|
||||
} catch (err) {
|
||||
dialog.showErrorBox('Error restoring monitor for presence URL ' + state.discord_presence.url,
|
||||
dialog.showErrorBox('Error restoring monitor for presence URL ' + state.discord_presence.source.url,
|
||||
err instanceof Error ? err.stack ?? err.message : err as any);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
import { BrowserWindow, clipboard, dialog, IpcMain, Menu, MenuItem, ShareMenu, SharingItem, shell, systemPreferences } from './electron.js';
|
||||
import * as util from 'node:util';
|
||||
import createDebug from 'debug';
|
||||
import { User } from 'discord-rpc';
|
||||
import openWebService, { WebServiceIpc } from './webservices.js';
|
||||
import { createWindow, getWindowConfiguration } from './windows.js';
|
||||
import { DiscordPresenceSource, WindowType } from '../common/types.js';
|
||||
import { DiscordPresenceConfiguration, DiscordPresenceSource, WindowType } from '../common/types.js';
|
||||
import { CurrentUser, Friend, Game, PresenceState, WebService } from '../../api/znc-types.js';
|
||||
import { addNsoAccount, addPctlAccount } from './na-auth.js';
|
||||
import { App } from './index.js';
|
||||
import { DiscordPresence } from '../../discord/util.js';
|
||||
import { User } from 'discord-rpc';
|
||||
import { NintendoAccountUser } from '../../api/na.js';
|
||||
import { hrduration } from '../../util/misc.js';
|
||||
import { DiscordPresence } from '../../discord/util.js';
|
||||
import { getDiscordRpcClients } from '../../discord/rpc.js';
|
||||
import { defaultTitle } from '../../discord/titles.js';
|
||||
|
||||
const debug = createDebug('app:main:ipc');
|
||||
|
||||
|
|
@ -112,10 +114,22 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) {
|
|||
window.show();
|
||||
});
|
||||
|
||||
ipcMain.handle('nxapi:discord:config', () => appinstance.monitors.getDiscordPresenceConfiguration());
|
||||
ipcMain.handle('nxapi:discord:setconfig', (e, config: DiscordPresenceConfiguration | null) => appinstance.monitors.setDiscordPresenceConfiguration(config));
|
||||
ipcMain.handle('nxapi:discord:source', () => appinstance.monitors.getDiscordPresenceSource());
|
||||
ipcMain.handle('nxapi:discord:setsource', (e, source: DiscordPresenceSource | null) => appinstance.monitors.setDiscordPresenceSource(source));
|
||||
ipcMain.handle('nxapi:discord:presence', () => appinstance.monitors.getDiscordPresence());
|
||||
ipcMain.handle('nxapi:discord:user', () => appinstance.monitors.getActiveDiscordPresenceMonitor()?.discord.rpc?.client.user ?? null);
|
||||
ipcMain.handle('nxapi:discord:users', async () => {
|
||||
const users: User[] = [];
|
||||
|
||||
for (const client of await getDiscordRpcClients()) {
|
||||
await client.connect(defaultTitle.client);
|
||||
if (client.user && !users.find(u => u.id === client.user!.id)) users.push(client.user);
|
||||
}
|
||||
|
||||
return users;
|
||||
});
|
||||
|
||||
ipcMain.handle('nxapi:moon:gettoken', (e, id: string) => storage.getItem('NintendoAccountToken-pctl.' + id));
|
||||
ipcMain.handle('nxapi:moon:getcachedtoken', (e, token: string) => storage.getItem('MoonToken.' + token));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { NotificationManager } from '../../common/notify.js';
|
|||
import { LoopResult } from '../../util/loop.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
import { App } from './index.js';
|
||||
import { DiscordPresenceSource } from '../common/types.js';
|
||||
import { DiscordPresenceConfiguration, DiscordPresenceSource } from '../common/types.js';
|
||||
import { DiscordPresence } from '../../discord/util.js';
|
||||
import { DiscordRpcClient } from '../../discord/rpc.js';
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ export class PresenceMonitorManager {
|
|||
const existing = this.monitors.find(m => m instanceof EmbeddedPresenceMonitor && m.data.user.id === user.data.user.id);
|
||||
if (existing) {
|
||||
callback?.call(null, existing as EmbeddedPresenceMonitor, false);
|
||||
return;
|
||||
return existing;
|
||||
}
|
||||
|
||||
const i = new EmbeddedPresenceMonitor(this.app.store.storage, token, user.nso, user.data, user);
|
||||
|
|
@ -54,13 +54,15 @@ export class PresenceMonitorManager {
|
|||
callback?.call(null, i, true);
|
||||
|
||||
i.enable();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
async startUrl(presence_url: string) {
|
||||
debug('Starting monitor', presence_url);
|
||||
|
||||
const existing = this.monitors.find(m => m instanceof EmbeddedProxyPresenceMonitor && m.presence_url === presence_url);
|
||||
if (existing) return;
|
||||
if (existing) return existing;
|
||||
|
||||
const i = new EmbeddedProxyPresenceMonitor(presence_url);
|
||||
|
||||
|
|
@ -76,6 +78,8 @@ export class PresenceMonitorManager {
|
|||
this.monitors.push(i);
|
||||
|
||||
i.enable();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
async stop(id: string) {
|
||||
|
|
@ -102,6 +106,45 @@ export class PresenceMonitorManager {
|
|||
return this.getActiveDiscordPresenceMonitor()?.discord.last_activity ?? null;
|
||||
}
|
||||
|
||||
getDiscordPresenceConfiguration(): DiscordPresenceConfiguration | null {
|
||||
const monitor = this.getActiveDiscordPresenceMonitor();
|
||||
const source = this.getDiscordPresenceSource();
|
||||
|
||||
if (!monitor || !source) return null;
|
||||
|
||||
return {
|
||||
source,
|
||||
user: this.getDiscordClientFilterConfiguration(monitor.discord_client_filter),
|
||||
};
|
||||
}
|
||||
|
||||
async setDiscordPresenceConfiguration(config: DiscordPresenceConfiguration | null) {
|
||||
if (!config) return this.setDiscordPresenceSource(null);
|
||||
|
||||
await this.setDiscordPresenceSource(config.source, monitor => {
|
||||
monitor.discord_client_filter = config.user ? this.createDiscordClientFilter(config.user) : undefined;
|
||||
monitor.skipIntervalInCurrentLoop();
|
||||
});
|
||||
|
||||
await this.app.store.saveMonitorState(this);
|
||||
}
|
||||
|
||||
private discord_client_filter_config = new WeakMap<
|
||||
Exclude<ZncDiscordPresence['discord_client_filter'], undefined>,
|
||||
/** Discord user ID */
|
||||
string
|
||||
>();
|
||||
|
||||
createDiscordClientFilter(user: string) {
|
||||
const filter: ZncDiscordPresence['discord_client_filter'] = (client, id) => client.user?.id === user;
|
||||
this.discord_client_filter_config.set(filter, user);
|
||||
return filter;
|
||||
}
|
||||
|
||||
getDiscordClientFilterConfiguration(filter: ZncDiscordPresence['discord_client_filter']) {
|
||||
return filter ? this.discord_client_filter_config.get(filter) : undefined;
|
||||
}
|
||||
|
||||
getDiscordPresenceSource(): DiscordPresenceSource | null {
|
||||
const monitor = this.getActiveDiscordPresenceMonitor();
|
||||
if (!monitor) return null;
|
||||
|
|
@ -115,16 +158,21 @@ export class PresenceMonitorManager {
|
|||
};
|
||||
}
|
||||
|
||||
async setDiscordPresenceSource(source: DiscordPresenceSource | null) {
|
||||
const monitor = this.getActiveDiscordPresenceMonitor();
|
||||
async setDiscordPresenceSource(
|
||||
source: DiscordPresenceSource | null,
|
||||
callback?: (monitor: EmbeddedPresenceMonitor | EmbeddedProxyPresenceMonitor) => void
|
||||
) {
|
||||
const existing = this.getActiveDiscordPresenceMonitor();
|
||||
|
||||
if (source && 'na_id' in source &&
|
||||
monitor && monitor instanceof EmbeddedPresenceMonitor &&
|
||||
monitor.data.user.id === source.na_id &&
|
||||
monitor.presence_user !== (source.friend_nsa_id ?? monitor.data.nsoAccount.user.nsaId)
|
||||
existing && existing instanceof EmbeddedPresenceMonitor &&
|
||||
existing.data.user.id === source.na_id &&
|
||||
existing.presence_user !== (source.friend_nsa_id ?? existing.data.nsoAccount.user.nsaId)
|
||||
) {
|
||||
await this.start(source.na_id, monitor => {
|
||||
monitor.presence_user = source.friend_nsa_id ?? monitor.data.nsoAccount.user.nsaId;
|
||||
monitor.discord_client_filter = existing.discord_client_filter;
|
||||
callback?.call(null, monitor);
|
||||
monitor.skipIntervalInCurrentLoop();
|
||||
});
|
||||
this.app.store.saveMonitorState(this);
|
||||
|
|
@ -132,35 +180,44 @@ export class PresenceMonitorManager {
|
|||
return;
|
||||
}
|
||||
|
||||
if (monitor) {
|
||||
if (source && 'na_id' in source && monitor instanceof EmbeddedPresenceMonitor && monitor.data.user.id === source.na_id) return;
|
||||
if (source && 'url' in source && monitor instanceof EmbeddedProxyPresenceMonitor && monitor.presence_url === source.url) return;
|
||||
if (existing) {
|
||||
if (source && (
|
||||
('na_id' in source && existing instanceof EmbeddedPresenceMonitor && existing.data.user.id === source.na_id) ||
|
||||
('url' in source && existing instanceof EmbeddedProxyPresenceMonitor && existing.presence_url === source.url)
|
||||
)) {
|
||||
callback?.call(null, existing);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.discord.updatePresenceForDiscord(null);
|
||||
existing.discord.updatePresenceForDiscord(null);
|
||||
|
||||
if (monitor instanceof EmbeddedPresenceMonitor) {
|
||||
monitor.presence_user = null;
|
||||
if (existing instanceof EmbeddedPresenceMonitor) {
|
||||
existing.presence_user = null;
|
||||
|
||||
if (!monitor.user_notifications && !monitor.friend_notifications) {
|
||||
this.stop(monitor.data.user.id);
|
||||
if (!existing.user_notifications && !existing.friend_notifications) {
|
||||
this.stop(existing.data.user.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor instanceof EmbeddedProxyPresenceMonitor) {
|
||||
this.stop(monitor.presence_url);
|
||||
if (existing instanceof EmbeddedProxyPresenceMonitor) {
|
||||
this.stop(existing.presence_url);
|
||||
}
|
||||
}
|
||||
|
||||
if (source && 'na_id' in source) {
|
||||
await this.start(source.na_id, monitor => {
|
||||
monitor.presence_user = source.friend_nsa_id ?? monitor.data.nsoAccount.user.nsaId;
|
||||
monitor.discord_client_filter = existing?.discord_client_filter;
|
||||
callback?.call(null, monitor);
|
||||
monitor.skipIntervalInCurrentLoop();
|
||||
});
|
||||
} else if (source && 'url' in source) {
|
||||
await this.startUrl(source.url);
|
||||
const monitor = await this.startUrl(source.url);
|
||||
monitor.discord_client_filter = existing?.discord_client_filter;
|
||||
callback?.call(null, monitor);
|
||||
}
|
||||
|
||||
if (monitor || source) {
|
||||
if (existing || source) {
|
||||
this.app.store.saveMonitorState(this);
|
||||
this.app.menu?.updateMenu();
|
||||
this.app.store.emit('update-discord-presence-source', source);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { contextBridge, ipcRenderer } from 'electron';
|
|||
import * as process from 'node:process';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import createDebug from 'debug';
|
||||
import type { DiscordPresenceSource, WindowConfiguration } from '../common/types.js';
|
||||
import type { DiscordPresenceConfiguration, DiscordPresenceSource, WindowConfiguration } from '../common/types.js';
|
||||
import type { SavedToken } from '../../common/auth/nso.js';
|
||||
import type { SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import type { UpdateCacheData } from '../../common/update.js';
|
||||
|
|
@ -40,10 +40,13 @@ const ipc = {
|
|||
openWebService: (webservice: WebService, token: string, qs?: string) => inv<number>('nso:openwebservice', webservice, token, qs),
|
||||
getNsoActiveEvent: (token: string) => inv<GetActiveEventResult>('nso:activeevent', token),
|
||||
|
||||
getDiscordPresenceConfig: () => inv<DiscordPresenceConfiguration | null>('discord:config'),
|
||||
setDiscordPresenceConfig: (config: DiscordPresenceConfiguration | null) => inv<void>('discord:setconfig', config),
|
||||
getDiscordPresenceSource: () => inv<DiscordPresenceSource | null>('discord:source'),
|
||||
setDiscordPresenceSource: (source: DiscordPresenceSource | null) => inv<void>('discord:setsource', source),
|
||||
getDiscordPresence: () => inv<DiscordPresence | null>('discord:presence'),
|
||||
getDiscordUser: () => inv<User | null>('discord:user'),
|
||||
getDiscordUsers: () => inv<User[]>('discord:users'),
|
||||
|
||||
getNintendoAccountMoonToken: (id: string) => inv<string | undefined>('moon:gettoken', id),
|
||||
getSavedMoonToken: (token: string) => inv<SavedMoonToken | undefined>('moon:getcachedtoken', token),
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user