teto_ai/bot.js

148 lines
4 KiB
JavaScript

import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import { Client as DiscordClient } from "discord.js-selfbot-v13";
import express from "express";
import session from "express-session";
import path from "path";
import { fileURLToPath, pathToFileURL } from "url";
import { WebSocketServer } from "ws";
import authRouter from "./src/routes/authRoutes.js";
import fs from "fs";
// --- CONFIGURATION ---
const {
USER_TOKEN,
BOT_CLIENT_ID,
BOT_CLIENT_SECRET,
BOT_REDIRECT_URI,
COOKIE_SECRET,
} = process.env;
const WEB_PORT = 3000;
const ADMIN_USER_IDS = ["339753362297847810"];
const MAX_LOG_SIZE = 20;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// --- IN-MEMORY STATE ---
const activityLog = [];
const actionsLog = [];
// --- WEB SERVER SETUP ---
const app = express();
// Make config and data available to routers/controllers via app.locals
app.locals.config = {
BOT_CLIENT_ID,
BOT_CLIENT_SECRET,
BOT_REDIRECT_URI,
ADMIN_USER_IDS,
};
app.locals.data = {
activityLog,
actionsLog,
};
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.set("trust proxy", 1); // Required for secure cookies if behind a proxy
app.use(
session({
secret: COOKIE_SECRET || "a-very-secret-string-for-teto-is-required",
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === "production",
httpOnly: true,
sameSite: "lax",
},
}),
);
// --- STATIC ASSETS & ROUTES ---
app.use(express.static(path.join(__dirname, "public")));
app.use("/", authRouter);
// --- DISCORD BOT & SERVER INITIALIZATION ---
(async () => {
// Puppeteer setup (currently optional)
puppeteer.use(StealthPlugin());
/*
const browser = await puppeteer.launch({ headless: false });
// ... more puppeteer logic if needed
*/
// Start the web server AND capture the HTTP server instance it creates
const server = app.listen(WEB_PORT, () => {
console.log(`Dashboard server running on http://localhost:${WEB_PORT}`);
});
// Create the WebSocket server and attach it to our existing HTTP server
const wss = new WebSocketServer({ server });
// Function to broadcast data to all connected WebSocket clients
const broadcast = (data) => {
// The 'wss.clients' property is a Set of all connected clients
wss.clients.forEach((client) => {
// Check if the connection is still open before sending
if (client.readyState === client.OPEN) {
// WebSocket only sends strings, so we stringify our object
client.send(JSON.stringify(data));
}
});
};
const logItem = (logArray, item) => {
logArray.unshift(item);
if (logArray.length > MAX_LOG_SIZE) logArray.pop();
if (logArray === activityLog) {
broadcast({ type: "activityLogUpdate", payload: item });
}
};
wss.on("connection", (ws) => {
console.log("Dashboard client connected via WebSocket.");
ws.on("close", () => {
console.log("Dashboard client disconnected.");
});
});
const client = new DiscordClient({ checkUpdate: false });
// --- DYNAMIC EVENT HANDLER LOADING ---
const eventsPath = path.join(__dirname, "src", "events");
const eventFiles = fs
.readdirSync(eventsPath)
.filter((file) => file.endsWith(".js"));
const context = {
client,
logItem,
activityLog,
actionsLog,
MAX_LOG_SIZE,
};
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = (await import(pathToFileURL(filePath))).default;
if (event.name && event.execute) {
if (event.once) {
client.once(event.name, (...args) => event.execute(context, ...args));
} else {
client.on(event.name, (...args) => event.execute(context, ...args));
}
}
}
// End dynamic event handler loading
try {
await client.login(USER_TOKEN);
} catch (error) {
console.error("Failed to login to Discord. Check your USER_TOKEN.", error);
process.exit(1);
}
})();