console.log("Starting up...") require("dotenv").config() const funcs = require("./helpers.js"); const fs = require("fs"); const path = require("path"); const colors = require("colors"); const Discord = require("discord.js"); const client = new Discord.Client({ intents: ["Guilds"] }); const { REST, Routes } = require('discord.js'); const { title } = require("process"); const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); ipBans = {} idBans = {} ipRaw = ""; idRaw = ""; // Setup // if process.env.CONFIG_PATH is not set or it doesnt exist, exit 1 if (!process.env.CONFIG_PATH || !fs.existsSync(process.env.CONFIG_PATH)) { console.error("CONFIG_PATH is not set or does not exist. Exiting.") process.exit(1); } // if ./errors/ doesnt exist, create it if (!fs.existsSync("errors")) { fs.mkdirSync("errors") } const jsonfix = (json) => JSON.parse(JSON.stringify(json)); client.on('ready', async () => { console.log(`${colors.cyan("[INFO]")} Logged in as ${colors.green(client.user.tag)}`); const commands = [ { name: "ban", description: "Interact with SL server bans", type: 1, options: [ { name: "lookup", description: "Look up a ban", type: 1, options: [ { name: "type", description: "The type of ban to look up", type: 3, required: true, choices: [ { name: "IP", value: "ip" }, { name: "ID", value: "id" } ] }, { name: "value", description: "The value to look up (IP or ID)", type: 3, required: true } ] }, { name: "pardon" } ] } ] await(async () => { try { console.log(`${colors.cyan("[INFO]")} Registering Commands...`) //Global await rest.put(Routes.applicationCommands(client.user.id), { body: commands }) console.log(`${colors.cyan("[INFO]")} Successfully registered commands. Took ${colors.green((Date.now() - startTime) / 1000)} seconds.`); } catch (error) { console.error(error); } })(); watchFiles(); }); client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) return; switch (interaction.commandName) { case "ban": switch (interaction.options.getSubcommand()) { case "lookup": type = interaction.options.getString("type"); value = interaction.options.getString("value"); if (type == "ip") { if (ipBans[value]) { embed = { fields: [ { name: "Banned Username", value: ipBans[value].banned_username, inline: true }, { name: "Banned ID", value: ipBans[value].banned_id, inline: true }, { name: "Banned", value: ` `, inline: true }, { name: "Expires", value: ` `, inline: true }, { name: "Reason", value: ipBans[value].reason, inline: true }, { name: "Moderator", value: ipBans[value].moderator, inline: true } ] } embed.title = "IP Ban Lookup" embed.color = 0xffff00 interaction.reply({ embeds: [embed] }) } else { interaction.reply("No ban found for that IP") } } else if (type == "id") { if (idBans[value]) { embed = { fields: [ { name: "Banned Username", value: idBans[value].banned_username, inline: true }, { name: "Banned ID", value: idBans[value].banned_id, inline: true }, { name: "Banned", value: ` `, inline: true }, { name: "Expires", value: ` `, inline: true }, { name: "Reason", value: idBans[value].reason, inline: true }, { name: "Moderator", value: idBans[value].moderator, inline: true } ] } embed.title = "ID Ban Lookup" embed.color = 0xffff00 interaction.reply({ embeds: [embed] }) } else { interaction.reply("No ban found for that ID") } } break; } break; } }); const sendMessages = async (data) => { const channel = await client.channels.fetch(process.env.DISCORD_BANLOG_CHANNEL_ID); for (const ban in data.added) { thisBan = data.added[ban] embed = { fields: [ { name: "Banned Username", value: thisBan.banned_username, inline: true }, { name: "Banned ID", value: thisBan.banned_id, inline: true }, { name: "Banned", value: ` `, inline: true }, { name: "Expires", value: ` `, inline: true }, { name: "Reason", value: thisBan.reason, inline: true }, { name: "Moderator", value: thisBan.moderator, inline: true } ] } embed.title = "Ban Added" embed.color = 0xff0000 channel.send({ embeds: [embed] }) } for (const ban in data.removed) { thisBan = data.removed[ban] embed = { fields: [ { name: "Banned Username", value: thisBan.banned_username, inline: true }, { name: "Banned ID", value: thisBan.banned_id, inline: true }, { name: "Banned", value: ` `, inline: true }, { name: "Expires", value: ` `, inline: true }, { name: "Reason", value: thisBan.reason, inline: true }, { name: "Moderator", value: thisBan.moderator, inline: true } ] } embed.title = "Ban Removed" embed.color = 0x00ff00 channel.send({ embeds: [embed] }) }; } fs.readdirSync(process.env.CONFIG_PATH).forEach(file => { if (file.startsWith(".")) return; // Ignore hidden files contents = fs.readFileSync(path.join(process.env.CONFIG_PATH, file)).toString() switch (file) { case "IpBans.txt": ipRaw = String(contents); lines = contents.split("\n"); ipBans = funcs.makeBansTable(contents) break; case "UserIdBans.txt": idRaw = String(contents); idBans = funcs.makeBansTable(contents) break; } }) const watchFiles = () => { fs.watch(process.env.CONFIG_PATH, (event, file) => { if (file.startsWith(".")) return; // Ignore hidden files setTimeout(() => { tmp = {} newContents = fs.readFileSync(path.join(process.env.CONFIG_PATH, file)).toString() switch (file) { case "IpBans.txt": if (newContents == ipRaw) return; // File didnt change, ignore tmp = funcs.makeBansTable(newContents) // Do stuff console.log(`IP Bans changed\nOld Length: ${Object.keys(ipBans).length}\nNew Length: ${Object.keys(tmp).length}`) // if the number of adds or removes is over 40 ignore it if (Object.keys(funcs.diff(ipBans, tmp).added).length > 40 || Object.keys(funcs.diff(ipBans, tmp).removed).length > 40) { console.log("Too many changes, logging and ignoring") fs.writeFileSync(`errors/changes-${Date.now()}.log`, JSON.stringify(funcs.diff(idBans, tmp), null, 2)); break; } sendMessages(funcs.diff(ipBans, tmp)) ipBans = jsonfix(tmp) ipRaw = String(newContents) break; case "UserIdBans.txt": if (newContents == idRaw) return; // File didnt change, ignore tmp = funcs.makeBansTable(newContents) // Do stuff console.log(`ID Bans changed\nOld Length: ${Object.keys(idBans).length}\nNew Length: ${Object.keys(tmp).length}`) // if the number of adds or removes is over 40 ignore it if (Object.keys(funcs.diff(idBans, tmp).added).length > 40 || Object.keys(funcs.diff(idBans, tmp).removed).length > 40) { console.log("Too many changes, logging and ignoring") fs.writeFileSync(`errors/changes-${Date.now()}.log`, JSON.stringify(funcs.diff(idBans, tmp), null, 2)); break; } sendMessages(funcs.diff(idBans, tmp)) idBans = jsonfix(tmp) idRaw = String(newContents) break; } }) }) } // Catch unhandled promise rejections and unhandled exceptions process.on('unhandledRejection', (error) => { console.error('Unhandled promise rejection:', error); // save full stack to file in errors/timestamp.log fs.writeFileSync(`errors/rejection-${Date.now()}.log`, error.stack); }); process.on('uncaughtException', (error) => { console.error('Uncaught exception:', error); // save full stack to file in errors/timestamp.log fs.writeFileSync(`errors/exception-${Date.now()}.log`, error.stack); }); const startTime = new Date(); client.login(process.env.DISCORD_TOKEN)