sl-banbot/index.js
2024-07-22 18:41:04 -06:00

359 lines
9.3 KiB
JavaScript

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: `<t:${new Date(ipBans[value].banned_timestamp) / 1000}:f> <t:${new Date(ipBans[value].banned_timestamp) / 1000}:R>`,
inline: true
},
{
name: "Expires",
value: `<t:${new Date(ipBans[value].expires) / 1000}:f> <t:${new Date(ipBans[value].expires) / 1000}:R>`,
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: `<t:${new Date(idBans[value].banned_timestamp) / 1000}:f> <t:${new Date(idBans[value].banned_timestamp) / 1000}:R>`,
inline: true
},
{
name: "Expires",
value: `<t:${new Date(idBans[value].expires) / 1000}:f> <t:${new Date(idBans[value].expires) / 1000}:R>`,
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: `<t:${new Date(thisBan.banned_timestamp) / 1000}:f> <t:${new Date(thisBan.banned_timestamp) / 1000}:R>`,
inline: true
},
{
name: "Expires",
value: `<t:${new Date(thisBan.expires) / 1000}:f> <t:${new Date(thisBan.expires) / 1000}:R>`,
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: `<t:${new Date(thisBan.banned_timestamp) / 1000}:f> <t:${new Date(thisBan.banned_timestamp) / 1000}:R>`,
inline: true
},
{
name: "Expires",
value: `<t:${new Date(thisBan.expires) / 1000}:f> <t:${new Date(thisBan.expires) / 1000}:R>`,
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)