2022-09-21 05:05:03 +00:00
|
|
|
if (process.versions.node.split(".")[0] < 16) {
|
2022-01-07 17:44:18 +00:00
|
|
|
console.error(`You are currently running Node.js version ${process.version}.
|
2022-09-21 05:05:03 +00:00
|
|
|
esmBot requires Node.js version 16 or above.
|
2022-01-07 17:44:18 +00:00
|
|
|
Please refer to step 3 of the setup guide.`);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2022-09-21 05:05:03 +00:00
|
|
|
if (process.platform === "win32") {
|
|
|
|
console.error("\x1b[1m\x1b[31m\x1b[40m" + `WINDOWS IS NOT OFFICIALLY SUPPORTED!
|
|
|
|
Although there's a (very) slim chance of it working, multiple aspects of the bot are built with UNIX-like systems in mind and could break on Win32-based systems. If you want to run the bot on Windows, using Windows Subsystem for Linux is highly recommended.
|
|
|
|
The bot will continue to run past this message in 5 seconds, but keep in mind that it could break at any time. Continue running at your own risk; alternatively, stop the bot using Ctrl+C and install WSL.` + "\x1b[0m");
|
|
|
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
|
|
|
}
|
2020-12-03 16:30:33 +00:00
|
|
|
|
2019-11-13 00:09:06 +00:00
|
|
|
// load config from .env file
|
2022-03-12 02:28:35 +00:00
|
|
|
import { resolve, dirname } from "path";
|
|
|
|
import { fileURLToPath } from "url";
|
2021-08-19 14:19:14 +00:00
|
|
|
import { config } from "dotenv";
|
2022-03-12 02:28:35 +00:00
|
|
|
config({ path: resolve(dirname(fileURLToPath(import.meta.url)), ".env") });
|
2019-11-13 00:09:06 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
import { generateList, createPage } from "./utils/help.js";
|
|
|
|
import { reloadImageConnections } from "./utils/image.js";
|
|
|
|
|
2021-11-12 23:21:43 +00:00
|
|
|
// main services
|
2022-09-24 03:25:16 +00:00
|
|
|
import { Client } from "oceanic.js";
|
2022-09-21 05:05:03 +00:00
|
|
|
import pm2 from "pm2";
|
2021-07-05 13:14:52 +00:00
|
|
|
// some utils
|
2022-03-10 21:24:24 +00:00
|
|
|
import { promises, readFileSync } from "fs";
|
2022-09-21 05:05:03 +00:00
|
|
|
import { logger } from "./utils/logger.js";
|
2021-09-17 02:37:36 +00:00
|
|
|
import { exec as baseExec } from "child_process";
|
|
|
|
import { promisify } from "util";
|
|
|
|
const exec = promisify(baseExec);
|
2022-09-21 05:05:03 +00:00
|
|
|
// initialize command loader
|
|
|
|
import { load, send } from "./utils/handler.js";
|
|
|
|
// command collections
|
|
|
|
import { paths } from "./utils/collections.js";
|
2022-02-22 00:55:25 +00:00
|
|
|
// database stuff
|
|
|
|
import database from "./utils/database.js";
|
2022-09-21 05:05:03 +00:00
|
|
|
// lavalink stuff
|
|
|
|
import { checkStatus, connect, reload, status, connected } from "./utils/soundplayer.js";
|
|
|
|
// events
|
|
|
|
import { endBroadcast, startBroadcast, activityChanger, checkBroadcast } from "./utils/misc.js";
|
|
|
|
import { parseThreshold } from "./utils/tempimages.js";
|
Class commands, improved sharding, and many other changes (#88)
* Load commands recursively
* Sort commands
* Missed a couple of spots
* missed even more spots apparently
* Ported commands in "fun" category to new class-based format, added babel eslint plugin
* Ported general commands, removed old/unneeded stuff, replaced moment with day, many more fixes I lost track of
* Missed a spot
* Removed unnecessary abort-controller package, add deprecation warning for mongo database
* Added imagereload, clarified premature end message
* Fixed docker-compose path issue, added total bot uptime to stats, more fixes for various parts
* Converted image commands into classes, fixed reload, ignore another WS event, cleaned up command handler and image runner
* Converted music/soundboard commands to class format
* Cleanup unnecessary logs
* awful tag command class port
* I literally somehow just learned that you can leave out the constructor in classes
* Pass client directly to commands/events, cleaned up command handler
* Migrated bot to eris-sharder, fixed some error handling stuff
* Remove unused modules
* Fixed type returning
* Switched back to Eris stable
* Some fixes and cleanup
* might wanna correct this
* Implement image command ratelimiting
* Added Bot token prefix, added imagestats, added running endpoint to API
2021-04-12 16:16:12 +00:00
|
|
|
|
2022-09-01 01:00:34 +00:00
|
|
|
const { types } = JSON.parse(readFileSync(new URL("./config/commands.json", import.meta.url)));
|
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
const esmBotVersion = JSON.parse(readFileSync(new URL("./package.json", import.meta.url))).version;
|
|
|
|
exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o).then(() => {
|
2021-07-05 13:14:52 +00:00
|
|
|
console.log(`
|
2022-09-21 05:05:03 +00:00
|
|
|
,*\`$ z\`"v
|
|
|
|
F zBw\`% A ,W "W
|
|
|
|
,\` ,EBBBWp"%. ,-=~~==-,+* 4BBE T
|
|
|
|
M BBBBBBBB* ,w=####Wpw 4BBBBB# 1
|
|
|
|
F BBBBBBBMwBBBBBBBBBBBBB#wXBBBBBH E
|
|
|
|
F BBBBBBkBBBBBBBBBBBBBBBBBBBBE4BL k
|
|
|
|
# BFBBBBBBBBBBBBF" "RBBBW F
|
|
|
|
V ' 4BBBBBBBBBBM TBBL F
|
|
|
|
F BBBBBBBBBBF JBB L
|
|
|
|
F FBBBBBBBEB BBL 4
|
|
|
|
E [BB4BBBBEBL BBL 4
|
|
|
|
I #BBBBBBBEB 4BBH *w
|
|
|
|
A 4BBBBBBBBBEW, ,BBBB W [
|
2021-07-05 13:14:52 +00:00
|
|
|
.A ,k 4BBBBBBBBBBBEBW####BBBBBBM BF F
|
|
|
|
k <BBBw BBBBEBBBBBBBBBBBBBBBBBQ4BM #
|
2022-09-21 05:05:03 +00:00
|
|
|
5, REBBB4BBBBB#BBBBBBBBBBBBP5BFF ,F
|
|
|
|
*w \`*4BBW\`"FF#F##FFFF"\` , * +"
|
|
|
|
*+, " F'"'*^~~~^"^\` V+*^
|
|
|
|
\`"""
|
|
|
|
|
|
|
|
esmBot ${esmBotVersion} (${process.env.GIT_REV})
|
2021-07-05 13:14:52 +00:00
|
|
|
`);
|
2022-09-21 05:05:03 +00:00
|
|
|
});
|
2022-08-25 15:36:44 +00:00
|
|
|
|
2022-09-01 01:00:34 +00:00
|
|
|
const intents = [
|
2022-09-24 03:25:16 +00:00
|
|
|
"GUILD_VOICE_STATES",
|
|
|
|
"DIRECT_MESSAGES"
|
2022-09-01 01:00:34 +00:00
|
|
|
];
|
2022-09-09 19:55:03 +00:00
|
|
|
if (types.classic) {
|
2022-09-24 03:25:16 +00:00
|
|
|
intents.push("GUILDS");
|
|
|
|
intents.push("GUILD_MESSAGES");
|
|
|
|
intents.push("MESSAGE_CONTENT");
|
2022-09-09 19:55:03 +00:00
|
|
|
}
|
2022-09-01 01:00:34 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
// PM2-specific handling
|
|
|
|
if (process.env.PM2_USAGE) {
|
|
|
|
pm2.launchBus((err, pm2Bus) => {
|
|
|
|
if (err) {
|
|
|
|
logger.error(err);
|
|
|
|
return;
|
2021-07-05 04:15:27 +00:00
|
|
|
}
|
2021-04-21 18:35:06 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
pm2Bus.on("process:msg", async (packet) => {
|
|
|
|
switch (packet.data?.type) {
|
|
|
|
case "reload":
|
|
|
|
var path = paths.get(packet.data.message);
|
2022-09-24 03:25:16 +00:00
|
|
|
await load(client, path, await checkStatus(), true);
|
2022-09-21 05:05:03 +00:00
|
|
|
break;
|
|
|
|
case "soundreload":
|
|
|
|
var soundStatus = await checkStatus();
|
|
|
|
if (!soundStatus) {
|
|
|
|
reload();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "imagereload":
|
|
|
|
await reloadImageConnections();
|
|
|
|
break;
|
|
|
|
case "broadcastStart":
|
2022-09-24 03:25:16 +00:00
|
|
|
startBroadcast(client, packet.data.message);
|
2022-09-21 05:05:03 +00:00
|
|
|
break;
|
|
|
|
case "broadcastEnd":
|
2022-09-24 03:25:16 +00:00
|
|
|
endBroadcast(client);
|
2022-09-21 05:05:03 +00:00
|
|
|
break;
|
|
|
|
case "serverCounts":
|
|
|
|
pm2.sendDataToProcessId(0, {
|
|
|
|
id: 0,
|
|
|
|
type: "process:msg",
|
|
|
|
data: {
|
|
|
|
type: "serverCounts",
|
2022-09-24 03:25:16 +00:00
|
|
|
guilds: client.guilds.size,
|
|
|
|
shards: client.shards.size
|
2022-09-21 05:05:03 +00:00
|
|
|
},
|
|
|
|
topic: true
|
|
|
|
}, (err) => {
|
|
|
|
if (err) logger.error(err);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
2021-07-05 13:14:52 +00:00
|
|
|
});
|
2022-09-21 05:05:03 +00:00
|
|
|
}
|
2021-08-14 21:15:21 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
database.upgrade(logger).then(result => {
|
|
|
|
if (result === 1) return process.exit(1);
|
|
|
|
});
|
2021-07-06 00:20:21 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
// process the threshold into bytes early
|
|
|
|
if (process.env.TEMPDIR && process.env.THRESHOLD) {
|
|
|
|
parseThreshold();
|
|
|
|
}
|
2022-02-22 00:55:25 +00:00
|
|
|
|
2022-09-21 05:05:03 +00:00
|
|
|
if (!types.classic && !types.application) {
|
|
|
|
logger.error("Both classic and application commands are disabled! Please enable at least one command type in config/commands.json.");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2022-09-24 03:25:16 +00:00
|
|
|
const client = new Client({
|
|
|
|
auth: `Bot ${process.env.TOKEN}`,
|
2022-09-21 05:05:03 +00:00
|
|
|
allowedMentions: {
|
|
|
|
everyone: false,
|
|
|
|
roles: false,
|
|
|
|
users: true,
|
|
|
|
repliedUser: true
|
|
|
|
},
|
2022-09-24 03:25:16 +00:00
|
|
|
gateway: {
|
|
|
|
concurrency: "auto",
|
|
|
|
maxShards: "auto",
|
|
|
|
presence: {
|
|
|
|
status: "dnd",
|
|
|
|
activities: [{
|
|
|
|
type: "GAME",
|
|
|
|
name: "Starting esmBot..."
|
|
|
|
}]
|
|
|
|
},
|
|
|
|
intents
|
|
|
|
},
|
|
|
|
collectionLimits: {
|
|
|
|
messages: 50
|
|
|
|
}
|
2022-09-21 05:05:03 +00:00
|
|
|
});
|
|
|
|
|
2022-09-24 03:25:16 +00:00
|
|
|
client.once("ready", async () => {
|
2022-09-21 05:05:03 +00:00
|
|
|
// register commands and their info
|
|
|
|
const soundStatus = await checkStatus();
|
|
|
|
logger.log("info", "Attempting to load commands...");
|
|
|
|
for await (const commandFile of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./commands/"))) {
|
|
|
|
logger.log("main", `Loading command from ${commandFile}...`);
|
|
|
|
try {
|
2022-09-24 03:25:16 +00:00
|
|
|
await load(client, commandFile, soundStatus);
|
2022-09-21 05:05:03 +00:00
|
|
|
} catch (e) {
|
|
|
|
logger.error(`Failed to register command from ${commandFile}: ${e}`);
|
|
|
|
}
|
2021-04-21 18:35:06 +00:00
|
|
|
}
|
2022-09-21 05:05:03 +00:00
|
|
|
if (types.application) {
|
|
|
|
try {
|
2022-09-24 03:25:16 +00:00
|
|
|
await send(client);
|
2022-09-21 05:05:03 +00:00
|
|
|
} catch (e) {
|
|
|
|
logger.log("error", e);
|
|
|
|
logger.log("error", "Failed to send command data to Discord, slash/message commands may be unavailable.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logger.log("info", "Finished loading commands.");
|
|
|
|
|
|
|
|
if (process.env.API_TYPE === "ws") await reloadImageConnections();
|
|
|
|
await database.setup();
|
|
|
|
|
|
|
|
// register events
|
|
|
|
logger.log("info", "Attempting to load events...");
|
|
|
|
for await (const file of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./events/"))) {
|
|
|
|
logger.log("main", `Loading event from ${file}...`);
|
|
|
|
const eventArray = file.split("/");
|
|
|
|
const eventName = eventArray[eventArray.length - 1].split(".")[0];
|
|
|
|
if (eventName === "interactionCreate" && !types.application) {
|
|
|
|
logger.log("warn", `Skipped loading event from ${file} because application commands are disabled`);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const { default: event } = await import(file);
|
2022-09-24 03:25:16 +00:00
|
|
|
client.on(eventName, event.bind(null, client));
|
2022-09-21 05:05:03 +00:00
|
|
|
}
|
|
|
|
logger.log("info", "Finished loading events.");
|
|
|
|
|
|
|
|
// generate docs
|
|
|
|
if (process.env.OUTPUT && process.env.OUTPUT !== "") {
|
|
|
|
generateList();
|
|
|
|
await createPage(process.env.OUTPUT);
|
|
|
|
logger.log("info", "The help docs have been generated.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// connect to lavalink
|
2022-09-24 03:25:16 +00:00
|
|
|
if (!status && !connected) connect(client);
|
2022-03-10 21:24:24 +00:00
|
|
|
|
2022-09-24 03:25:16 +00:00
|
|
|
checkBroadcast(client);
|
|
|
|
activityChanger(client);
|
2022-09-21 05:05:03 +00:00
|
|
|
|
|
|
|
logger.log("info", "Started esmBot.");
|
|
|
|
});
|
|
|
|
|
|
|
|
async function* getFiles(dir) {
|
|
|
|
const dirents = await promises.readdir(dir, { withFileTypes: true });
|
|
|
|
for (const dirent of dirents) {
|
|
|
|
const name = dir + (dir.charAt(dir.length - 1) !== "/" ? "/" : "") + dirent.name;
|
|
|
|
if (dirent.isDirectory()) {
|
|
|
|
yield* getFiles(name);
|
|
|
|
} else if (dirent.name.endsWith(".js")) {
|
|
|
|
yield name;
|
2022-03-10 21:24:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-13 23:09:05 +00:00
|
|
|
}
|
2022-09-21 05:05:03 +00:00
|
|
|
|
2022-09-24 03:25:16 +00:00
|
|
|
client.connect();
|