bot: change moderation to be setup using discord permissions, add setup command to get rid of .env variables

This commit is contained in:
Ayden Jahola 2024-09-25 23:07:02 +01:00
parent 6fa17a5022
commit f65ec8ca0f
No known key found for this signature in database
GPG key ID: 71DD90AE4AE92742
14 changed files with 230 additions and 59 deletions

View file

@ -36,9 +36,10 @@ module.exports = {
await interaction.reply({ embeds: [embed] }); await interaction.reply({ embeds: [embed] });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
await interaction.reply( await interaction.reply({
"There was an error trying to fetch a random activity." content: "There was an error trying to fetch a random activity.",
); epemeral: true,
});
} }
}, },
}; };

View file

@ -1,4 +1,8 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); const {
SlashCommandBuilder,
EmbedBuilder,
PermissionFlagsBits,
} = require("discord.js");
const BannedUser = require("../../models/BannedUser"); const BannedUser = require("../../models/BannedUser");
module.exports = { module.exports = {
@ -20,10 +24,10 @@ module.exports = {
let replySent = false; let replySent = false;
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Ban Members permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (!interaction.member.permissions.has(PermissionFlagsBits.BanMembers)) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
replySent = true; replySent = true;
@ -101,7 +105,7 @@ module.exports = {
iconURL: interaction.user.displayAvatarURL(), iconURL: interaction.user.displayAvatarURL(),
}); });
// Send confirmation as ephemeral message // Send confirmation as an ephemeral message
if (!replySent) { if (!replySent) {
await interaction.reply({ await interaction.reply({
embeds: [banEmbed], embeds: [banEmbed],
@ -110,7 +114,7 @@ module.exports = {
replySent = true; replySent = true;
} }
// log the ban in a designated channel // Log the ban in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID; const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId); const logChannel = interaction.guild.channels.cache.get(logChannelId);

View file

@ -9,10 +9,10 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Server permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (!interaction.member.permissions.has("ManageGuild")) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
return; return;

View file

@ -21,10 +21,10 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Kick Members permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (!interaction.member.permissions.has("KickMembers")) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
return; return;
@ -77,8 +77,7 @@ module.exports = {
}); });
} catch (dmError) { } catch (dmError) {
console.error(`Error sending DM to ${user.tag}:`, dmError); console.error(`Error sending DM to ${user.tag}:`, dmError);
// If DM fails, make sure to handle it properly // Handle DM errors appropriately, if needed
// Avoid using followUp here if interaction has already been replied to
} }
const kickEmbed = new EmbedBuilder() const kickEmbed = new EmbedBuilder()
@ -99,7 +98,7 @@ module.exports = {
ephemeral: true, ephemeral: true,
}); });
// log the kick in a designated channel // Log the kick in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID; const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId); const logChannel = interaction.guild.channels.cache.get(logChannelId);

View file

@ -26,10 +26,10 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Messages permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (!interaction.member.permissions.has("ManageMessages")) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
return; return;

View file

@ -0,0 +1,92 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js");
const ServerSettings = require("../../models/ServerSettings");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup")
.setDescription("Configure server settings for verification.")
.addChannelOption((option) =>
option
.setName("logchannel")
.setDescription("Select the log channel for logging actions.")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("generalchannel")
.setDescription("Select the general channel for join messages.")
.setRequired(true)
)
.addChannelOption((option) =>
option
.setName("verificationchannel")
.setDescription("Select the verification channel where users verify.")
.setRequired(true)
)
.addRoleOption((option) =>
option
.setName("verifiedrole")
.setDescription("Select the Verified role for verified users.")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("emaildomains")
.setDescription("Comma-separated list of allowed email domains.")
.setRequired(true)
),
async execute(interaction) {
// Check if the user has admin permissions
if (
!interaction.member.permissions.has(PermissionFlagsBits.Administrator)
) {
return interaction.reply({
content: "You do not have permission to use this command.",
ephemeral: true,
});
}
const logChannel = interaction.options.getChannel("logchannel");
const generalChannel = interaction.options.getChannel("generalchannel");
const verificationChannel = interaction.options.getChannel(
"verificationchannel"
);
const verifiedRole = interaction.options.getRole("verifiedrole");
const emailDomains = interaction.options
.getString("emaildomains")
.split(",");
try {
// Store the channel IDs instead of names
await ServerSettings.findOneAndUpdate(
{ guildId: interaction.guild.id },
{
guildId: interaction.guild.id,
logChannelId: logChannel.id, // Store log channel ID
verifiedRoleName: verifiedRole.name,
verificationChannelId: verificationChannel.id, // Store verification channel ID
generalChannelId: generalChannel.id, // Store general channel ID
emailDomains: emailDomains,
},
{ upsert: true, new: true }
);
interaction.reply({
content: `Server settings have been updated successfully!\n
**Log Channel**: <#${logChannel.id}>\n
**General Channel**: <#${generalChannel.id}>\n
**Verification Channel**: <#${verificationChannel.id}>\n
**Verified Role**: ${verifiedRole.name}\n
**Allowed Email Domains**: ${emailDomains.join(", ")}`,
ephemeral: true,
});
} catch (error) {
console.error("Error updating server settings:", error);
interaction.reply({
content: "There was an error updating the server settings.",
ephemeral: true,
});
}
},
};

View file

@ -33,10 +33,14 @@ module.exports = {
let replySent = false; let replySent = false;
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Roles permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
replySent = true; replySent = true;
@ -98,6 +102,8 @@ module.exports = {
color: "#ff0000", color: "#ff0000",
permissions: [], permissions: [],
}); });
// Disable send messages permission for the timeout role in all channels
interaction.guild.channels.cache.each(async (channel) => { interaction.guild.channels.cache.each(async (channel) => {
await channel.permissionOverwrites.edit(timeoutRole, { await channel.permissionOverwrites.edit(timeoutRole, {
SendMessages: false, SendMessages: false,
@ -153,7 +159,7 @@ module.exports = {
replySent = true; replySent = true;
} }
// log the timeout in a designated channel // Log the timeout in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID; const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId); const logChannel = interaction.guild.channels.cache.get(logChannelId);

View file

@ -1,4 +1,8 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); const {
SlashCommandBuilder,
EmbedBuilder,
PermissionsBitField,
} = require("discord.js");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@ -14,10 +18,14 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Roles permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
return; return;

View file

@ -1,4 +1,8 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); const {
SlashCommandBuilder,
EmbedBuilder,
PermissionsBitField,
} = require("discord.js");
const Warning = require("../../models/warning"); const Warning = require("../../models/warning");
module.exports = { module.exports = {
@ -21,10 +25,14 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
try { try {
const requiredRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Roles permission
if (!interaction.member.roles.cache.has(requiredRoleId)) { if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
await interaction.reply({ await interaction.reply({
content: "You do not have the required role to use this command!", content: "You do not have permission to use this command!",
ephemeral: true, ephemeral: true,
}); });
return; return;

View file

@ -1,4 +1,8 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); const {
SlashCommandBuilder,
EmbedBuilder,
PermissionsBitField,
} = require("discord.js");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@ -7,8 +11,10 @@ module.exports = {
async execute(interaction, client) { async execute(interaction, client) {
try { try {
const modRoleId = process.env.MOD_ROLE_ID; // Check if the user has the Manage Roles permission
const isMod = interaction.member.roles.cache.has(modRoleId); const isMod = interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
);
const serverName = interaction.guild.name; const serverName = interaction.guild.name;

View file

@ -1,5 +1,6 @@
const { SlashCommandBuilder } = require("discord.js"); const { SlashCommandBuilder } = require("discord.js");
const VerificationCode = require("../../models/VerificationCode"); const VerificationCode = require("../../models/VerificationCode");
const ServerSettings = require("../../models/ServerSettings");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@ -13,11 +14,24 @@ module.exports = {
), ),
async execute(interaction, client) { async execute(interaction, client) {
// Ensure command is only used in the specified verification channel // Fetch server settings from the database
const verificationChannelName = process.env.VERIFICATION_CHANNEL_NAME; const serverSettings = await ServerSettings.findOne({
if (interaction.channel.name !== verificationChannelName) { guildId: interaction.guild.id,
});
if (!serverSettings) {
return interaction.reply({ return interaction.reply({
content: `This command can only be used in the #${verificationChannelName} channel.`, content:
"Server settings have not been configured yet. Please contact an administrator.",
ephemeral: true,
});
}
// Ensure command is only used in the specified verification channel
const verificationChannelId = serverSettings.verificationChannelId;
if (interaction.channel.id !== verificationChannelId) {
return interaction.reply({
content: `This command can only be used in <#${verificationChannelId}> channel.`,
ephemeral: true, ephemeral: true,
}); });
} }
@ -33,6 +47,7 @@ module.exports = {
} }
try { try {
// Find the verification code in the database
const verificationEntry = await VerificationCode.findOne({ const verificationEntry = await VerificationCode.findOne({
userId: interaction.user.id, userId: interaction.user.id,
code, code,
@ -45,7 +60,7 @@ module.exports = {
}); });
} }
const guild = client.guilds.cache.get(process.env.GUILD_ID); const guild = client.guilds.cache.get(interaction.guild.id);
if (!guild) { if (!guild) {
console.error("Guild not found."); console.error("Guild not found.");
@ -66,13 +81,13 @@ module.exports = {
} }
const role = guild.roles.cache.find( const role = guild.roles.cache.find(
(r) => r.name === process.env.VERIFIED_ROLE_NAME (r) => r.name === serverSettings.verifiedRoleName
); );
if (!role) { if (!role) {
console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`); console.error(`Role "${serverSettings.verifiedRoleName}" not found.`);
return interaction.reply({ return interaction.reply({
content: `The role "${process.env.VERIFIED_ROLE_NAME}" could not be found.`, content: `The role "${serverSettings.verifiedRoleName}" could not be found.`,
ephemeral: true, ephemeral: true,
}); });
} }
@ -89,9 +104,9 @@ module.exports = {
// Delete the verification code entry // Delete the verification code entry
await VerificationCode.deleteOne({ userId: interaction.user.id, code }); await VerificationCode.deleteOne({ userId: interaction.user.id, code });
// Get the admin log channel and send a log message // Get the log channel and send a log message
const adminLogChannel = client.channels.cache.get( const adminLogChannel = client.channels.cache.get(
process.env.LOG_CHANNEL_ID serverSettings.logChannelId
); );
if (adminLogChannel) { if (adminLogChannel) {
await adminLogChannel.send({ await adminLogChannel.send({
@ -103,7 +118,7 @@ module.exports = {
// Get the general channel and send a welcome message // Get the general channel and send a welcome message
const generalChannel = client.channels.cache.get( const generalChannel = client.channels.cache.get(
process.env.GENERAL_CHANNEL_ID serverSettings.generalChannelId
); );
if (generalChannel) { if (generalChannel) {
await generalChannel.send({ await generalChannel.send({

View file

@ -1,11 +1,12 @@
const { SlashCommandBuilder } = require("discord.js"); const { SlashCommandBuilder } = require("discord.js");
const nodemailer = require("nodemailer"); const nodemailer = require("nodemailer");
const VerificationCode = require("../../models/VerificationCode"); const VerificationCode = require("../../models/VerificationCode");
const ServerSettings = require("../../models/ServerSettings");
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
service: "Gmail", service: "Gmail",
auth: { auth: {
user: process.env.EMAIL_USER, user: process.env.EMAIL_USER, // Email user and pass still from .env (for now)
pass: process.env.EMAIL_PASS, pass: process.env.EMAIL_PASS,
}, },
}); });
@ -22,27 +23,41 @@ module.exports = {
), ),
async execute(interaction, client) { async execute(interaction, client) {
// Ensure command is only used in the specified verification channel // Fetch the server settings from the database using guild ID
const verificationChannelName = process.env.VERIFICATION_CHANNEL_NAME; const serverSettings = await ServerSettings.findOne({
if (interaction.channel.name !== verificationChannelName) { guildId: interaction.guild.id,
});
if (!serverSettings) {
return interaction.reply({ return interaction.reply({
content: `This command can only be used in the #${verificationChannelName} channel.`, content:
"Server settings have not been configured yet. Please contact an administrator.",
ephemeral: true,
});
}
// Ensure command is only used in the specified verification channel
const verificationChannelId = serverSettings.verificationChannelId;
if (interaction.channel.id !== verificationChannelId) {
return interaction.reply({
content: `This command can only be used in <#${verificationChannelId}> channel.`,
ephemeral: true, ephemeral: true,
}); });
} }
const email = interaction.options.getString("email"); const email = interaction.options.getString("email");
const emailDomain = email.split("@")[1]; const emailDomain = email.split("@")[1];
const EMAIL_DOMAINS = process.env.EMAIL_DOMAINS.split(","); const allowedEmailDomains = serverSettings.emailDomains;
if (!EMAIL_DOMAINS.includes(emailDomain)) { // Check if the email domain is allowed
if (!allowedEmailDomains.includes(emailDomain)) {
return interaction.reply({ return interaction.reply({
content: "You must use a valid DCU email address.", content: "You must use a valid DCU email address.",
ephemeral: true, ephemeral: true,
}); });
} }
const guild = client.guilds.cache.get(process.env.GUILD_ID); const guild = client.guilds.cache.get(interaction.guild.id);
if (!guild) { if (!guild) {
console.error("Guild not found."); console.error("Guild not found.");
@ -63,13 +78,13 @@ module.exports = {
} }
const role = guild.roles.cache.find( const role = guild.roles.cache.find(
(r) => r.name === process.env.VERIFIED_ROLE_NAME (r) => r.name === serverSettings.verifiedRoleName
); );
if (!role) { if (!role) {
console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`); console.error(`Role "${serverSettings.verifiedRoleName}" not found.`);
return interaction.reply({ return interaction.reply({
content: `Role "${process.env.VERIFIED_ROLE_NAME}" not found.`, content: `Role "${serverSettings.verifiedRoleName}" not found.`,
ephemeral: true, ephemeral: true,
}); });
} }
@ -81,6 +96,7 @@ module.exports = {
}); });
} }
// Generate a 6-digit verification code
const verificationCode = Math.floor( const verificationCode = Math.floor(
100000 + Math.random() * 900000 100000 + Math.random() * 900000
).toString(); ).toString();
@ -102,6 +118,7 @@ module.exports = {
`; `;
try { try {
// Send the verification email
await transporter.sendMail({ await transporter.sendMail({
from: `"${process.env.EMAIL_NAME}" <${process.env.EMAIL_USER}>`, from: `"${process.env.EMAIL_NAME}" <${process.env.EMAIL_USER}>`,
to: email, to: email,
@ -109,6 +126,7 @@ module.exports = {
html: emailHtml, html: emailHtml,
}); });
// Save the verification code and email in the database
await VerificationCode.create({ await VerificationCode.create({
userId: interaction.user.id, userId: interaction.user.id,
email: email, email: email,

13
models/ServerSettings.js Normal file
View file

@ -0,0 +1,13 @@
const mongoose = require("mongoose");
const ServerSettingsSchema = new mongoose.Schema({
guildId: { type: String, required: true, unique: true },
logChannelId: { type: String, required: true },
verifiedRoleName: { type: String, required: true },
verificationChannelId: { type: String, required: true },
generalChannelId: { type: String, required: true },
emailDomains: { type: [String], required: true },
});
const ServerSettings = mongoose.model("ServerSettings", ServerSettingsSchema);
module.exports = ServerSettings;

View file

@ -18,6 +18,7 @@
"html-entities": "^2.5.2", "html-entities": "^2.5.2",
"mongoose": "^8.6.0", "mongoose": "^8.6.0",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"owoify-js": "^2.0.0" "owoify-js": "^2.0.0",
"puppeteer": "^23.4.1"
} }
} }