Files
SupportBot/Structures/BotReload.js
T
2026-05-16 15:10:27 +01:00

151 lines
3.9 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const configStore = require("./ConfigStore.js");
const { getPrimaryGuild } = require("./GuildManager.js");
const {
applyPresence,
runBotReady,
logSlashCommandsRegistered,
} = require("./BotStartup.js");
function walk(dir, ext = ".js", fileList = []) {
if (!fs.existsSync(dir)) return fileList;
for (const file of fs.readdirSync(dir)) {
const full = path.join(dir, file);
if (fs.statSync(full).isDirectory()) {
walk(full, ext, fileList);
} else if (file.endsWith(ext)) {
fileList.push(full.replace(/\\/g, "/"));
}
}
return fileList;
}
async function refreshSlashCommands(client) {
if (!client?.application) return { count: 0 };
const cmdconfig = configStore.commands;
const commandFiles = walk("./Commands");
client.commands.clear();
for (const file of commandFiles) {
const resolved = path.resolve(file);
if (require.cache[resolved]) {
delete require.cache[resolved];
}
}
let count = 0;
for (const file of commandFiles) {
const command = require(path.resolve(file));
const matchingConfig = Object.values(cmdconfig).find(
(entry) =>
entry &&
typeof entry === "object" &&
entry.Command &&
String(entry.Command).toLowerCase() ===
String(command.name).toLowerCase(),
);
if (matchingConfig && matchingConfig.Enabled === false) {
continue;
}
client.commands.set(command.name, command);
count++;
}
const payload = client.commands.map((cmd) => cmd.data ?? cmd);
for (const guild of client.guilds.cache.values()) {
await guild.commands.set(payload);
}
return { count };
}
async function reloadBot(client, options = {}) {
const { fileKey, refreshCommands = true } = options;
let reloaded;
if (fileKey) {
configStore.reloadFile(fileKey);
reloaded = [fileKey];
} else {
reloaded = configStore.reloadBotConfigs();
}
applyPresence(client);
let commands = null;
if (refreshCommands && (!fileKey || fileKey === "commands")) {
commands = await refreshSlashCommands(client);
}
return {
reloaded,
commands,
};
}
/**
* Reload configs from disk, reconnect the Discord session, and refresh presence/commands.
* API and dashboard keep running on the same process.
*/
async function restartDiscordBot(client) {
if (!client) {
const err = new Error("Bot client not available");
err.code = "NO_CLIENT";
throw err;
}
const reloaded = configStore.reloadBotConfigs();
const token = configStore.supportbot?.General?.Token;
if (!token || String(token).trim() === "" || token === "BOT_TOKEN") {
const err = new Error("Invalid or missing bot token in supportbot.yml");
err.code = "INVALID_TOKEN";
throw err;
}
console.log("[BotReload] Restarting Discord bot after config change…");
client.__restarting = true;
if (client.user) {
try {
await client.destroy();
} catch (destroyErr) {
console.warn("[BotReload] destroy:", destroyErr.message);
}
}
client.__explicitStartup = true;
try {
try {
await client.login(token);
} catch (loginErr) {
console.error("[BotReload] login failed:", loginErr.message);
const err = new Error(`Could not start bot: ${loginErr.message}`);
err.code = "LOGIN_FAILED";
throw err;
}
// clientReady does not fire again after destroy()+login() — run startup explicitly
await runBotReady(client, { clearConsole: true, logCommands: false });
const commands = await refreshSlashCommands(client);
logSlashCommandsRegistered(client);
console.log("[BotReload] Bot restarted successfully");
return { restarted: true, reloaded, commands };
} finally {
client.__explicitStartup = false;
client.__restarting = false;
}
}
module.exports = {
applyPresence,
refreshSlashCommands,
reloadBot,
restartDiscordBot,
};