Bwuh, this is a hot mess, but progress is being made, so don't hurt me lol
This commit is contained in:
parent
8d6aa4cbe7
commit
a7f79e906e
|
@ -14,11 +14,15 @@ module.exports = {
|
|||
new Discord.SlashCommandBuilder()
|
||||
.setName("ban")
|
||||
.setDescription("Ban a user.")
|
||||
.setDefaultMemberPermissions(0),
|
||||
.setDefaultMemberPermissions(0)
|
||||
.addStringOption(option => option.setName("reason").setDescription("The reason for the ban.").setRequired(true))
|
||||
.addStringOption(option => option.setName("roblox_id").setDescription("The Roblox ID of the user you're banning.").setRequired(false))
|
||||
.addStringOption(option => option.setName("discord_id").setDescription("The Discord ID of the user you're banning.").setRequired(false)),
|
||||
new Discord.SlashCommandBuilder()
|
||||
.setName("unban")
|
||||
.setDescription("Unban a user.")
|
||||
.addNumberOption(option => option.setName("roblox_id").setDescription("The Roblox ID of the user you're unbanning.").setRequired(true))
|
||||
.addNumberOption(option => option.setName("roblox_id").setDescription("The Roblox ID of the user you're unbanning.").setRequired(false))
|
||||
.addStringOption(option => option.setName("discord_id").setDescription("The Discord ID of the user you're unbanning.").setRequired(false))
|
||||
.setDefaultMemberPermissions(0),
|
||||
]
|
||||
}
|
232
index.js
232
index.js
|
@ -4,6 +4,7 @@ const path = require('path');
|
|||
const { execSync } = require('child_process');
|
||||
const flags = require("./flags.js")
|
||||
const log = require("./log");
|
||||
const uuid = require("uuid").v7
|
||||
|
||||
// Legal stuff
|
||||
log.info(`UBS Server (${execSync("git rev-parse --short HEAD").toString().trim()}) on ${execSync("git rev-parse --abbrev-ref HEAD").toString().trim()}`)
|
||||
|
@ -18,7 +19,17 @@ require("dotenv").config();
|
|||
const noblox = require("noblox.js")
|
||||
noblox.setCookie(process.env.ROBLOSECURITY)
|
||||
|
||||
// DB
|
||||
const mariadb = require('mariadb');
|
||||
|
||||
const pool = mariadb.createPool({
|
||||
host: process.env.DB_HOST, // Replace with your database host
|
||||
port: process.env.DB_PORT || 3306,
|
||||
user: process.env.DB_USER, // Replace with your database username
|
||||
password: process.env.DB_PASS, // Replace with your database password
|
||||
database: process.env.DB_DATABASE, // Replace with your database name
|
||||
connectionLimit: 5 // Adjust connection limit as needed
|
||||
});
|
||||
|
||||
// Express
|
||||
const express = require("express");
|
||||
|
@ -51,51 +62,250 @@ process.env.REASON_FLAGS = JSON.stringify(reasonFlags)
|
|||
|
||||
// Discord stuff
|
||||
const Discord = require("discord.js");
|
||||
const client = new Discord.Client({intents: ["Guilds", "GuildBans", "GuildMembers"]})
|
||||
const client = new Discord.Client({ intents: ["Guilds", "GuildBans", "GuildMembers"] })
|
||||
|
||||
client.on("ready", async () => {
|
||||
log.info(`Logged into Discord as ${client.user.displayName}`);
|
||||
const commands = require("./commands")
|
||||
// Command registration
|
||||
log.info("Registering commands...")
|
||||
await (async () => {
|
||||
try {
|
||||
const rest = new Discord.REST().setToken(client.token);
|
||||
//Global
|
||||
await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: [] })
|
||||
//await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: [] })
|
||||
log.info(`Registering global commands`);
|
||||
await rest.put(Discord.Routes.applicationCommands(client.user.id), { body: commands.global })
|
||||
rest.put(Discord.Routes.applicationCommands(client.user.id), { body: commands.global }).then(() => {
|
||||
log.info("Global commands registered")
|
||||
}).catch((error) => {
|
||||
log.error(error)
|
||||
});
|
||||
|
||||
//Admin
|
||||
log.info(`Registering admin commands`);
|
||||
await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: commands.admin })
|
||||
rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: commands.admin }).then(() => {
|
||||
log.info("Admin commands registered")
|
||||
}).catch((error) => {
|
||||
log.error(error)
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
log.error(error)
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
|
||||
// In-memory storage for the ban command (store each message id so we can track flags)
|
||||
const banMessages = {}
|
||||
|
||||
|
||||
client.on("interactionCreate", async (interaction) => {
|
||||
// Switch by type (either slash command or modal)
|
||||
switch(interaction.type) {
|
||||
switch (interaction.type) {
|
||||
// Slash Command Handler
|
||||
case Discord.InteractionType.ApplicationCommand:
|
||||
if (!interaction.isCommand()) return;
|
||||
const command = interaction.commandName;
|
||||
const args = interaction.options;
|
||||
switch(command) {
|
||||
switch (command) {
|
||||
// Report Command
|
||||
case "report":
|
||||
const robloxId = args.getNumber("roblox_id");
|
||||
const reason = args.getString("reason");
|
||||
robloxId = args.getNumber("roblox_id");
|
||||
reason = args.getString("reason");
|
||||
// TODO: Report Command
|
||||
break;
|
||||
|
||||
// Ban Command
|
||||
case "ban":
|
||||
robloxId = args.getString("roblox_id");
|
||||
discordId = args.getString("discord_id");
|
||||
reason = args.getString("reason");
|
||||
if (robloxId && !robloxId.match(/^\d+$/)) {
|
||||
return interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Invalid Roblox ID!"
|
||||
})
|
||||
}
|
||||
if (discordId && !discordId.match(/^\d+$/)) {
|
||||
return interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Invalid Discord ID!"
|
||||
})
|
||||
}
|
||||
|
||||
if (!robloxId && !discordId) {
|
||||
return interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Specify a Roblox ID and/or Discord ID!"
|
||||
})
|
||||
}
|
||||
|
||||
if (robloxId) {
|
||||
try {
|
||||
robloxUsername = await noblox.getUsernameFromId(robloxId) || "Unknown"
|
||||
} catch (e) {
|
||||
return interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Invalid Roblox ID!"
|
||||
})
|
||||
}
|
||||
} else {
|
||||
robloxUsername = null
|
||||
}
|
||||
|
||||
if (discordId) {
|
||||
discordUsername = (await client.users.fetch(discordId)).username || "Unknown"
|
||||
} else {
|
||||
discordUsername = null
|
||||
}
|
||||
|
||||
embed = {
|
||||
title: "Ban User",
|
||||
color: 0xff0000,
|
||||
fields: [
|
||||
robloxId ? { name: "Roblox", value: `${robloxUsername} (${robloxId})` } : null,
|
||||
discordId ? { name: "Discord ID", value: `${discordUsername} (${discordId})` } : null,
|
||||
{ name: "Reason", value: reason},
|
||||
{ name: "Moderator", value: interaction.user.tag }
|
||||
].filter(field => field !== null)
|
||||
}
|
||||
flagButtons = await reasonFlagTypes.map(flag => {
|
||||
return new Discord.ButtonBuilder()
|
||||
.setCustomId(flag)
|
||||
.setStyle(Discord.ButtonStyle.Danger)
|
||||
.setLabel(flag)
|
||||
})
|
||||
|
||||
submitButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("ban")
|
||||
.setStyle(Discord.ButtonStyle.Primary)
|
||||
.setLabel("Ban")
|
||||
.setEmoji("🔨")
|
||||
interaction.reply({
|
||||
ephemeral: true,
|
||||
embeds: [embed],
|
||||
components: [
|
||||
new Discord.ActionRowBuilder().addComponents(flagButtons),
|
||||
new Discord.ActionRowBuilder().addComponents(submitButton)
|
||||
]
|
||||
})
|
||||
|
||||
rep = await interaction.fetchReply()
|
||||
banMessages[rep.id] = {
|
||||
flags: 0,
|
||||
robloxId,
|
||||
discordId,
|
||||
robloxUsername,
|
||||
discordUsername,
|
||||
moderator: interaction.user.id,
|
||||
reason,
|
||||
interaction: interaction
|
||||
}
|
||||
break;
|
||||
case "unban":
|
||||
robloxId = args.getNumber("roblox_id");
|
||||
discordId = args.getString("discord_id");
|
||||
// In the db, find any instance of either robloxId or discordId and set if the expiry is null or in the future, set it to now
|
||||
const connection = await pool.getConnection();
|
||||
try {
|
||||
await connection.query('UPDATE bans SET expiresTimestamp = NOW() WHERE robloxId = ? OR discordId = ? AND (expiresTimestamp IS NULL OR expiresTimestamp > NOW())', [robloxId || uuid(), discordId || uuid()]);
|
||||
interaction.reply({
|
||||
embeds: [
|
||||
{
|
||||
title: "User Unbanned",
|
||||
color: 0x00ff00,
|
||||
fields: [
|
||||
robloxId ? { name: "Roblox", value: robloxId } : null,
|
||||
discordId ? { name: "Discord ID", value: discordId } : null,
|
||||
{ name: "Moderator", value: interaction.user.tag }
|
||||
].filter(field => field !== null)
|
||||
}
|
||||
]
|
||||
})
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
interaction.reply({
|
||||
embeds: [
|
||||
{
|
||||
title: "Error",
|
||||
color: 0xff0000,
|
||||
description: "An error occurred while unbanning the user."
|
||||
}
|
||||
]
|
||||
})
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
break;
|
||||
};
|
||||
break;
|
||||
|
||||
// Modal Handler
|
||||
case Discord.InteractionType.ModalSubmit:
|
||||
case Discord.InteractionType.MessageComponent:
|
||||
if (!interaction.isButton()) return;
|
||||
const flag = interaction.customId;
|
||||
const message = banMessages[interaction.message.id];
|
||||
if (!message) return interaction.reply({
|
||||
ephemeral: true,
|
||||
content: "Invalid message!"
|
||||
})
|
||||
|
||||
if (flag == "ban") {
|
||||
// Ban the user by adding a ban record to the database
|
||||
const connection = await pool.getConnection();
|
||||
try {
|
||||
await connection.query('INSERT INTO bans (robloxId, discordId, robloxUsername, discordUsername, reasonShort, moderator, reasonsFlag) VALUES (?, ?, ?, ?, ?, ? ,?)', [message.robloxId, message.discordId, message.robloxUsername, message.discordUsername, message.reason, message.moderator, message.flags]);
|
||||
message.interaction.editReply({
|
||||
embeds: [
|
||||
{
|
||||
title: "User Banned",
|
||||
color: 0xff0000,
|
||||
fields: [
|
||||
message.robloxId ? { name: "Roblox", value: `${message.robloxUsername} (${message.robloxId})` } : null,
|
||||
message.discordId ? { name: "Discord ID", value: `${message.discordUsername} (${message.discordId})` }: null,
|
||||
{ name: "Moderator", value: interaction.user.tag }
|
||||
].filter(field => field !== null)
|
||||
}
|
||||
],
|
||||
components: []
|
||||
})
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
message.interaction.editReply({
|
||||
embeds: [
|
||||
{
|
||||
title: "Error",
|
||||
color: 0xff0000,
|
||||
description: "An error occurred while banning the user."
|
||||
}
|
||||
],
|
||||
components: []
|
||||
})
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
} else {
|
||||
message.flags ^= reasonFlags[flag]
|
||||
interaction.deferUpdate();
|
||||
flagButtons = await reasonFlagTypes.map(flag => {
|
||||
return new Discord.ButtonBuilder()
|
||||
.setCustomId(flag)
|
||||
.setStyle(flags.hasFlag(message.flags, reasonFlags[flag]) ? Discord.ButtonStyle.Success : Discord.ButtonStyle.Danger)
|
||||
.setLabel(flag)
|
||||
})
|
||||
|
||||
submitButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("ban")
|
||||
.setStyle(Discord.ButtonStyle.Primary)
|
||||
.setLabel("Ban")
|
||||
.setEmoji("🔨")
|
||||
message.interaction.editReply({
|
||||
components: [
|
||||
new Discord.ActionRowBuilder().addComponents(flagButtons),
|
||||
new Discord.ActionRowBuilder().addComponents(submitButton)
|
||||
]
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ CREATE TABLE bans (
|
|||
reasonShort VARCHAR(255),
|
||||
reasonLong VARCHAR(2048),
|
||||
reasonsFlag INT,
|
||||
moderator VARCHAR(255),
|
||||
banTimestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expiresTimestamp TIMESTAMP DEFAULT NULL
|
||||
);
|
||||
|
|
24
package-lock.json
generated
24
package-lock.json
generated
|
@ -14,7 +14,8 @@
|
|||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.1",
|
||||
"mariadb": "^3.4.0",
|
||||
"noblox.js": "^6.0.2"
|
||||
"noblox.js": "^6.0.2",
|
||||
"uuid": "^11.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
|
@ -1671,6 +1672,15 @@
|
|||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/postman-request/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -2049,12 +2059,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz",
|
||||
"integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.1",
|
||||
"mariadb": "^3.4.0",
|
||||
"noblox.js": "^6.0.2"
|
||||
"noblox.js": "^6.0.2",
|
||||
"uuid": "^11.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ const pool = mariadb.createPool({
|
|||
connectionLimit: 5 // Adjust connection limit as needed
|
||||
});
|
||||
|
||||
|
||||
// Route to fetch all bans
|
||||
router.get('/v1/bans', async (req, res) => {
|
||||
try {
|
||||
|
@ -24,6 +25,11 @@ router.get('/v1/bans', async (req, res) => {
|
|||
try {
|
||||
// Execute the query to fetch all rows from the `bans` table
|
||||
const rows = await connection.query('SELECT * FROM bans');
|
||||
// Convert all timestamps into epoch
|
||||
rows.forEach(row => {
|
||||
row.expiresTimestamp = row.expiresTimestamp ? row.expiresTimestamp.getTime() : null
|
||||
row.banTimestamp = row.banTimestamp ? row.banTimestamp.getTime() : null
|
||||
});
|
||||
|
||||
// Send the results as a JSON response
|
||||
res.json(rows);
|
||||
|
@ -48,7 +54,11 @@ router.get("/v1/ban/roblox/:uid", async (req, res) => {
|
|||
try {
|
||||
// Execute the query to fetch all rows from the `bans` table
|
||||
const rows = await connection.query('SELECT * FROM bans WHERE robloxId = ?', [req.params.uid]);
|
||||
|
||||
// Convert all timestamps into epoch
|
||||
rows.forEach(row => {
|
||||
row.expiresTimestamp = row.expiresTimestamp ? row.expiresTimestamp.getTime() : null
|
||||
row.banTimestamp = row.banTimestamp ? row.banTimestamp.getTime() : null
|
||||
});
|
||||
// Send the results as a JSON response
|
||||
res.json(rows);
|
||||
} finally {
|
||||
|
@ -72,7 +82,11 @@ router.get("/v1/ban/discord/:uid", async (req, res) => {
|
|||
try {
|
||||
// Execute the query to fetch all rows from the `bans` table
|
||||
const rows = await connection.query('SELECT * FROM bans WHERE discordId = ?', [req.params.uid]);
|
||||
|
||||
// Convert all timestamps into epoch
|
||||
rows.forEach(row => {
|
||||
row.expiresTimestamp = row.expiresTimestamp ? row.expiresTimestamp.getTime() : null
|
||||
row.banTimestamp = row.banTimestamp ? row.banTimestamp.getTime() : null
|
||||
});
|
||||
// Send the results as a JSON response
|
||||
res.json(rows);
|
||||
} finally {
|
||||
|
|
Loading…
Reference in a new issue