forked from syntaxbullet/AuroraBot-discord
feat: Introduce admin webhook and enhanced reload commands with redeploy functionality, implement post-restart notifications, and update Docker container names from Kyoko to Aurora.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:17-alpine
|
image: postgres:17-alpine
|
||||||
container_name: kyoko_db
|
container_name: aurora_db
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=${DB_USER}
|
- POSTGRES_USER=${DB_USER}
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||||
@@ -12,7 +12,7 @@ services:
|
|||||||
- ./src/db/data:/var/lib/postgresql/data
|
- ./src/db/data:/var/lib/postgresql/data
|
||||||
- ./src/db/log:/var/log/postgresql
|
- ./src/db/log:/var/log/postgresql
|
||||||
app:
|
app:
|
||||||
container_name: kyoko_app
|
container_name: aurora_app
|
||||||
image: kyoko-app
|
image: kyoko-app
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -38,7 +38,7 @@ services:
|
|||||||
command: bun run dev
|
command: bun run dev
|
||||||
|
|
||||||
studio:
|
studio:
|
||||||
container_name: kyoko_studio
|
container_name: aurora_studio
|
||||||
image: kyoko-app
|
image: kyoko-app
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
82
src/commands/admin/reload.ts
Normal file
82
src/commands/admin/reload.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { createCommand } from "@lib/utils";
|
||||||
|
import { KyokoClient } from "@/lib/BotClient";
|
||||||
|
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||||
|
import { createErrorEmbed, createSuccessEmbed, createWarningEmbed } from "@lib/embeds";
|
||||||
|
|
||||||
|
export const reload = createCommand({
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("reload")
|
||||||
|
.setDescription("Reloads all commands")
|
||||||
|
.addBooleanOption(option =>
|
||||||
|
option
|
||||||
|
.setName("redeploy")
|
||||||
|
.setDescription("Pull latest changes from git and restart the bot")
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
|
execute: async (interaction) => {
|
||||||
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||||
|
|
||||||
|
const redeploy = interaction.options.getBoolean("redeploy") ?? false;
|
||||||
|
|
||||||
|
if (redeploy) {
|
||||||
|
const { exec } = await import("child_process");
|
||||||
|
const { promisify } = await import("util");
|
||||||
|
const { writeFile, utimes } = await import("fs/promises");
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [createWarningEmbed("Pulling latest changes and restarting...", "Redeploy Initiated")]
|
||||||
|
});
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execAsync("git pull");
|
||||||
|
|
||||||
|
if (stderr && !stdout) {
|
||||||
|
throw new Error(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [createSuccessEmbed(`Git Pull Output:\n\`\`\`\n${stdout}\n\`\`\`\nRestarting process...`, "Update Successful")]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write context for post-restart notification
|
||||||
|
await writeFile(".restart_context.json", JSON.stringify({
|
||||||
|
channelId: interaction.channelId,
|
||||||
|
userId: interaction.user.id,
|
||||||
|
timestamp: Date.now()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Trigger restart by touching entry point
|
||||||
|
const now = new Date();
|
||||||
|
await utimes("src/index.ts", now, now);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.editReply({
|
||||||
|
embeds: [createErrorEmbed(`Failed to redeploy:\n\`\`\`\n${error instanceof Error ? error.message : String(error)}\n\`\`\``, "Redeploy Failed")]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const start = Date.now();
|
||||||
|
await KyokoClient.loadCommands(true);
|
||||||
|
const duration = Date.now() - start;
|
||||||
|
|
||||||
|
// Deploy commands
|
||||||
|
await KyokoClient.deployCommands();
|
||||||
|
|
||||||
|
const embed = createSuccessEmbed(
|
||||||
|
`Successfully reloaded ${KyokoClient.commands.size} commands in ${duration}ms.`,
|
||||||
|
"System Reloaded"
|
||||||
|
);
|
||||||
|
|
||||||
|
await interaction.editReply({ embeds: [embed] });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.editReply({ embeds: [createErrorEmbed("An error occurred while reloading commands. Check console for details.", "Reload Failed")] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { createCommand } from "@lib/utils";
|
|
||||||
import { KyokoClient } from "@/lib/BotClient";
|
|
||||||
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
|
||||||
import { createErrorEmbed } from "@lib/embeds";
|
|
||||||
|
|
||||||
export const reload = createCommand({
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("reload")
|
|
||||||
.setDescription("Reloads all commands")
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
|
||||||
execute: async (interaction) => {
|
|
||||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
||||||
|
|
||||||
try {
|
|
||||||
await KyokoClient.loadCommands(true);
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle("✅ System Reloaded")
|
|
||||||
.setDescription(`Successfully reloaded ${KyokoClient.commands.size} commands.`)
|
|
||||||
.setColor("Green");
|
|
||||||
|
|
||||||
// Deploy commands
|
|
||||||
await KyokoClient.deployCommands();
|
|
||||||
|
|
||||||
await interaction.editReply({ embeds: [embed] });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
await interaction.editReply({ embeds: [createErrorEmbed("An error occurred while reloading commands. Check console for details.", "Reload Failed")] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -8,6 +8,29 @@ const event: Event<Events.ClientReady> = {
|
|||||||
execute: async (c) => {
|
execute: async (c) => {
|
||||||
console.log(`Ready! Logged in as ${c.user.tag}`);
|
console.log(`Ready! Logged in as ${c.user.tag}`);
|
||||||
schedulerService.start();
|
schedulerService.start();
|
||||||
|
|
||||||
|
// Check for restart context
|
||||||
|
const { readFile, unlink } = await import("fs/promises");
|
||||||
|
const { createSuccessEmbed } = await import("@lib/embeds");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const contextData = await readFile(".restart_context.json", "utf-8");
|
||||||
|
const context = JSON.parse(contextData);
|
||||||
|
|
||||||
|
// Validate context freshness (e.g., ignore if older than 5 minutes)
|
||||||
|
if (Date.now() - context.timestamp < 5 * 60 * 1000) {
|
||||||
|
const channel = await c.channels.fetch(context.channelId);
|
||||||
|
if (channel && channel.isSendable()) {
|
||||||
|
await channel.send({
|
||||||
|
embeds: [createSuccessEmbed("Bot is back online! Redeploy successful.", "System Online")]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await unlink(".restart_context.json");
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore errors (file not found, etc.)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user