2026-05-13 01:12:11 +07:00
|
|
|
import "./mock-crc";
|
2026-05-13 02:30:09 +07:00
|
|
|
import "libsodium-wrappers";
|
|
|
|
|
import "@snazzah/davey";
|
2026-05-13 16:57:07 +07:00
|
|
|
import "dotenv/config";
|
2026-05-12 19:38:23 +07:00
|
|
|
import { Client } from "discord.js-selfbot-v13";
|
|
|
|
|
import { config } from "./config";
|
2026-05-14 15:02:23 +07:00
|
|
|
import { getDatabase } from "./database/adapter";
|
2026-05-13 16:57:07 +07:00
|
|
|
import { createChildLogger } from "./logger";
|
2026-05-14 15:02:23 +07:00
|
|
|
import { startPendingAIAnalysisWorker } from "./moderation/aiAnalyzer";
|
|
|
|
|
import { syncBacklogMessages } from "./moderation/backlogSync";
|
|
|
|
|
import { registerMessageCapture } from "./moderation/messageCapture";
|
2026-05-13 00:32:27 +07:00
|
|
|
import { discordPlayer } from "./player";
|
2026-05-13 18:23:20 +07:00
|
|
|
import { VoiceController } from "./voiceController";
|
2026-05-13 15:28:25 +07:00
|
|
|
import { startWebserver } from "./webserver";
|
2026-05-13 16:25:01 +07:00
|
|
|
|
|
|
|
|
const logger = createChildLogger("bot");
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-13 16:57:07 +07:00
|
|
|
const token = config.DISCORD_TOKEN;
|
2026-05-14 15:02:23 +07:00
|
|
|
logger.info(
|
|
|
|
|
{ hasToken: token.length > 0, tokenLength: token.length },
|
|
|
|
|
"Config loaded",
|
|
|
|
|
);
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-14 01:07:50 +07:00
|
|
|
logger.info("Creating Discord client");
|
2026-05-12 19:38:23 +07:00
|
|
|
const client = new Client();
|
2026-05-13 18:23:20 +07:00
|
|
|
const voiceController = new VoiceController(client);
|
2026-05-14 01:07:50 +07:00
|
|
|
|
2026-05-13 16:32:14 +07:00
|
|
|
let isShuttingDown = false;
|
2026-05-14 14:58:28 +07:00
|
|
|
let db: Awaited<ReturnType<typeof getDatabase>> | null = null;
|
2026-05-13 16:32:14 +07:00
|
|
|
|
|
|
|
|
async function gracefulShutdown(signal: string) {
|
|
|
|
|
if (isShuttingDown) {
|
|
|
|
|
logger.warn(`Already shutting down, ignoring ${signal}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isShuttingDown = true;
|
|
|
|
|
logger.info({ signal }, "Graceful shutdown initiated");
|
|
|
|
|
|
|
|
|
|
try {
|
2026-05-14 14:58:28 +07:00
|
|
|
logger.info("Closing database...");
|
|
|
|
|
if (db) {
|
|
|
|
|
await db.close();
|
|
|
|
|
logger.info("Database closed");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 18:23:20 +07:00
|
|
|
logger.info("Stopping voice connection...");
|
|
|
|
|
await voiceController.disconnect();
|
2026-05-13 16:32:14 +07:00
|
|
|
|
|
|
|
|
logger.info("Pausing player...");
|
|
|
|
|
discordPlayer.pause();
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
async function initializeApp() {
|
2026-05-14 14:58:28 +07:00
|
|
|
try {
|
|
|
|
|
logger.info("Initializing database adapter");
|
|
|
|
|
db = await getDatabase();
|
|
|
|
|
logger.info({ type: config.DATABASE_TYPE }, "Database initialized");
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error({ error: err }, "Failed to initialize database");
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
2026-05-14 14:55:21 +07:00
|
|
|
|
|
|
|
|
client.on("ready", async () => {
|
|
|
|
|
logger.info({ user: client.user?.tag }, "Bot logged in");
|
2026-05-14 14:58:28 +07:00
|
|
|
registerMessageCapture(client, db!);
|
2026-05-14 15:41:11 +07:00
|
|
|
startPendingAIAnalysisWorker();
|
2026-05-14 14:58:28 +07:00
|
|
|
syncBacklogMessages(client, db!).catch((error) => {
|
2026-05-14 14:55:21 +07:00
|
|
|
logger.warn({ error }, "Backlog sync failed");
|
|
|
|
|
});
|
|
|
|
|
await startWebserver(config.WEBSERVER_PORT, client, voiceController);
|
2026-05-13 21:04:45 +07:00
|
|
|
});
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
client.on("error", (err) => {
|
|
|
|
|
logger.error({ error: err }, "Client error");
|
|
|
|
|
});
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
process.on("SIGINT", () => {
|
|
|
|
|
gracefulShutdown("SIGINT");
|
|
|
|
|
});
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
process.on("SIGTERM", () => {
|
|
|
|
|
gracefulShutdown("SIGTERM");
|
|
|
|
|
});
|
2026-05-13 16:32:14 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
process.on("uncaughtException", (err) => {
|
|
|
|
|
logger.error({ error: err }, "Uncaught exception");
|
|
|
|
|
gracefulShutdown("uncaughtException");
|
|
|
|
|
});
|
2026-05-13 16:32:14 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
process.on("unhandledRejection", (reason, promise) => {
|
|
|
|
|
logger.error({ reason, promise }, "Unhandled rejection");
|
|
|
|
|
gracefulShutdown("unhandledRejection");
|
|
|
|
|
});
|
2026-05-12 19:38:23 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
logger.info("Calling Discord client.login");
|
2026-05-14 15:02:23 +07:00
|
|
|
client
|
|
|
|
|
.login(token)
|
|
|
|
|
.then(() => {
|
|
|
|
|
logger.info("Discord client.login resolved");
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
logger.error({ error }, "Discord client.login failed");
|
|
|
|
|
});
|
2026-05-14 14:55:21 +07:00
|
|
|
}
|
2026-05-13 19:34:15 +07:00
|
|
|
|
2026-05-14 14:55:21 +07:00
|
|
|
initializeApp().catch((error) => {
|
|
|
|
|
logger.error({ error }, "Failed to initialize app");
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|