From 11582d50345cae9fb645a65ca4e621596de6a408 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Thu, 17 Oct 2024 23:20:02 +0700 Subject: [PATCH] fix(bots/discord): fix reload not working --- bots/discord/src/commands/admin/reload.ts | 11 ++----- bots/discord/src/context.ts | 40 +++++++++++++++++++---- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/bots/discord/src/commands/admin/reload.ts b/bots/discord/src/commands/admin/reload.ts index 6fcc71d..d7e66e9 100644 --- a/bots/discord/src/commands/admin/reload.ts +++ b/bots/discord/src/commands/admin/reload.ts @@ -1,8 +1,5 @@ -import { dirname, join } from 'path' import { AdminCommand } from '$/classes/Command' -import type { Config } from 'config.schema' - export default new AdminCommand({ name: 'reload', description: 'Reload configuration', @@ -10,10 +7,8 @@ export default new AdminCommand({ const { api, logger, discord } = context logger.info(`Reload triggered by ${context.executor.tag} (${context.executor.id})`) - // Apparently the query strings only work with non-Windows "URLs", otherwise it'd just infinitely hang - const path = `${Bun.pathToFileURL(join(dirname(Bun.main), '..', 'config.js')).toString()}?cache=${Date.now()}` - logger.debug(`Reloading configuration from: ${path}`) - context.config = ((await import(path)) as { default: Config }).default + logger.debug('Invalidating previous config...') + context.config.invalidate() if ('deferReply' in trigger) await trigger.deferReply({ ephemeral: true }) @@ -28,7 +23,7 @@ export default new AdminCommand({ api.client.disconnect(true) api.disconnectCount = 0 api.intentionallyDisconnecting = false - await api.client.connect() + api.client.connect() logger.info('Reinitializing Discord client to reload configuration...') await discord.client.destroy() diff --git a/bots/discord/src/context.ts b/bots/discord/src/context.ts index 97229ba..30bed63 100644 --- a/bots/discord/src/context.ts +++ b/bots/discord/src/context.ts @@ -1,23 +1,49 @@ import { Database } from 'bun:sqlite' import { existsSync, readFileSync, readdirSync } from 'fs' -import { join } from 'path' +import { dirname, join } from 'path' import { Client as APIClient } from '@revanced/bot-api' import { createLogger } from '@revanced/bot-shared' import { Client as DiscordClient, type Message, Partials } from 'discord.js' import { drizzle } from 'drizzle-orm/bun-sqlite' +import type { default as Command, CommandOptionsOptions, CommandType } from './classes/Command' +import * as schemas from './database/schemas' // Export some things first, as commands require them -import config from '../config.js' -export { config } +import _firstConfig from '../config.js' + +let currentConfig = _firstConfig + +// Other parts of the code will access properties of this proxy, they don't care what the target looks like +export const config = new Proxy( + { + INSPECTION_WARNING: 'Run `context.__getConfig()` to inspect the latest config.', + } as unknown as typeof currentConfig, + { + get(_, p, receiver) { + if (p === 'invalidate') + return async () => { + const path = join(dirname(Bun.main), '..', 'config.js') + Loader.registry.delete(path) + currentConfig = (await import(path)).default + logger.debug('New config set') + } + + return Reflect.get(currentConfig, p, receiver) + }, + set(_, p, newValue, receiver) { + return Reflect.set(currentConfig, p, newValue, receiver) + }, + }, +) as typeof _firstConfig & { invalidate(): void } + +export const __getConfig = () => currentConfig export const logger = createLogger({ level: config.logLevel === 'none' ? Number.MAX_SAFE_INTEGER : config.logLevel, }) -import * as commands from './commands' -import * as schemas from './database/schemas' - -import type { default as Command, CommandOptionsOptions, CommandType } from './classes/Command' +// Importing later because config needs to be exported before +const commands = await import('./commands') export const api = { client: new APIClient({