feat: implement graceful shutdown process for bot
- Added graceful shutdown functionality to handle SIGINT, SIGTERM, uncaught exceptions, and unhandled promise rejections. - Integrated stopRecording and pause functionality during shutdown. - Enhanced logging for shutdown steps and error handling. - Updated package.json to include pino-pretty for improved logging output.
This commit is contained in:
1532
package-lock.json
generated
1532
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@
|
|||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
"@types/fluent-ffmpeg": "^2.1.28",
|
"@types/fluent-ffmpeg": "^2.1.28",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
|
"pino-pretty": "^10.3.1",
|
||||||
"vitest": "latest"
|
"vitest": "latest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/index.ts
81
src/index.ts
@@ -5,10 +5,9 @@ import { getVoiceConnection } from "@discordjs/voice";
|
|||||||
import { Client } from "discord.js-selfbot-v13";
|
import { Client } from "discord.js-selfbot-v13";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
import { discordPlayer } from "./player";
|
import { discordPlayer } from "./player";
|
||||||
import { startRecording } from "./recorder";
|
import { startRecording, stopRecording } from "./recorder";
|
||||||
import { startWebserver } from "./webserver";
|
import { startWebserver } from "./webserver";
|
||||||
import { createChildLogger } from "./logger";
|
import { createChildLogger } from "./logger";
|
||||||
import { retryWithBackoff } from "./retry";
|
|
||||||
|
|
||||||
const logger = createChildLogger("bot");
|
const logger = createChildLogger("bot");
|
||||||
|
|
||||||
@@ -24,6 +23,58 @@ if (!guildId) throw new Error("Missing GUILD_ID in .env");
|
|||||||
// Inisialisasi selfbot client
|
// Inisialisasi selfbot client
|
||||||
const client = new Client();
|
const client = new Client();
|
||||||
|
|
||||||
|
// Track shutdown state
|
||||||
|
let isShuttingDown = false;
|
||||||
|
|
||||||
|
async function gracefulShutdown(signal: string) {
|
||||||
|
if (isShuttingDown) {
|
||||||
|
logger.warn(`Already shutting down, ignoring ${signal}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isShuttingDown = true;
|
||||||
|
logger.info({ signal }, "Graceful shutdown initiated");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Step 1: Stop recording
|
||||||
|
if (guildId) {
|
||||||
|
logger.info("Stopping recording...");
|
||||||
|
stopRecording(guildId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Pause player
|
||||||
|
logger.info("Pausing player...");
|
||||||
|
discordPlayer.pause();
|
||||||
|
|
||||||
|
// Step 3: Destroy voice connection
|
||||||
|
if (guildId) {
|
||||||
|
const connection = getVoiceConnection(guildId);
|
||||||
|
if (connection) {
|
||||||
|
logger.info("Destroying voice connection...");
|
||||||
|
try {
|
||||||
|
connection.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn({ error: err }, "Error destroying voice connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Destroy client
|
||||||
|
logger.info("Destroying Discord client...");
|
||||||
|
try {
|
||||||
|
client.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn({ error: err }, "Error destroying client");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Graceful shutdown completed");
|
||||||
|
process.exit(0);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error({ error: err }, "Error during graceful shutdown");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.on("ready", async () => {
|
client.on("ready", async () => {
|
||||||
if (config.verbose) {
|
if (config.verbose) {
|
||||||
logger.info({ user: client.user?.tag }, "Bot logged in");
|
logger.info({ user: client.user?.tag }, "Bot logged in");
|
||||||
@@ -70,21 +121,25 @@ client.on("error", (err) => {
|
|||||||
logger.error({ error: err }, "Client error");
|
logger.error({ error: err }, "Client error");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Graceful shutdown
|
// Graceful shutdown handlers
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
if (config.verbose) {
|
gracefulShutdown("SIGINT");
|
||||||
logger.info("Shutting down gracefully...");
|
|
||||||
}
|
|
||||||
client.destroy();
|
|
||||||
process.exit(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGTERM", () => {
|
process.on("SIGTERM", () => {
|
||||||
if (config.verbose) {
|
gracefulShutdown("SIGTERM");
|
||||||
logger.info("Terminating...");
|
});
|
||||||
}
|
|
||||||
client.destroy();
|
// Handle uncaught exceptions
|
||||||
process.exit(0);
|
process.on("uncaughtException", (err) => {
|
||||||
|
logger.error({ error: err }, "Uncaught exception");
|
||||||
|
gracefulShutdown("uncaughtException");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle unhandled promise rejections
|
||||||
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
|
logger.error({ reason, promise }, "Unhandled rejection");
|
||||||
|
gracefulShutdown("unhandledRejection");
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(token);
|
client.login(token);
|
||||||
|
|||||||
Reference in New Issue
Block a user