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:
syntaxbullet
2025-12-15 22:29:03 +01:00
parent 9333d6ac6c
commit 3acb5304f5
5 changed files with 108 additions and 33 deletions

View 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")] });
}
}
});

View File

@@ -0,0 +1,75 @@
import { createCommand } from "@/lib/utils";
import { SlashCommandBuilder, PermissionFlagsBits, TextChannel, NewsChannel, VoiceChannel, MessageFlags } from "discord.js";
import { createErrorEmbed } from "@/lib/embeds";
export const webhook = createCommand({
data: new SlashCommandBuilder()
.setName("webhook")
.setDescription("Send a message via webhook using a JSON payload")
.setDefaultMemberPermissions(PermissionFlagsBits.ManageWebhooks)
.addStringOption(option =>
option.setName("payload")
.setDescription("The JSON payload for the webhook message")
.setRequired(true)
),
execute: async (interaction) => {
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const payloadString = interaction.options.getString("payload", true);
let payload;
try {
payload = JSON.parse(payloadString);
} catch (error) {
await interaction.editReply({
embeds: [createErrorEmbed("The provided payload is not valid JSON.", "Invalid JSON")]
});
return;
}
const channel = interaction.channel;
if (!channel || !('createWebhook' in channel)) {
await interaction.editReply({
embeds: [createErrorEmbed("This channel does not support webhooks.", "Unsupported Channel")]
});
return;
}
let webhook;
try {
webhook = await channel.createWebhook({
name: `${interaction.client.user.username} - Proxy`,
avatar: interaction.client.user.displayAvatarURL(),
reason: `Proxy message requested by ${interaction.user.tag}`
});
// Support snake_case keys for raw API compatibility
if (payload.avatar_url && !payload.avatarURL) {
payload.avatarURL = payload.avatar_url;
delete payload.avatar_url;
}
await webhook.send(payload);
await webhook.delete("Proxy message sent");
await interaction.editReply({ content: "Message sent successfully!" });
} catch (error) {
console.error("Webhook error:", error);
// Attempt cleanup if webhook was created but sending failed
if (webhook) {
try {
await webhook.delete("Cleanup after failure");
} catch (cleanupError) {
console.error("Failed to delete webhook during cleanup:", cleanupError);
}
}
await interaction.editReply({
embeds: [createErrorEmbed("Failed to send message via webhook. Ensure the bot has 'Manage Webhooks' permission and the payload is valid.", "Delivery Failed")]
});
}
}
});