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] });
} catch (error) {
console.error(error);
await interaction.reply(
"There was an error trying to fetch a random activity."
);
await interaction.reply({
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");
module.exports = {
@ -20,10 +24,10 @@ module.exports = {
let replySent = false;
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Ban Members permission
if (!interaction.member.permissions.has(PermissionFlagsBits.BanMembers)) {
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,
});
replySent = true;
@ -101,7 +105,7 @@ module.exports = {
iconURL: interaction.user.displayAvatarURL(),
});
// Send confirmation as ephemeral message
// Send confirmation as an ephemeral message
if (!replySent) {
await interaction.reply({
embeds: [banEmbed],
@ -110,7 +114,7 @@ module.exports = {
replySent = true;
}
// log the ban in a designated channel
// Log the ban in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId);

View file

@ -9,10 +9,10 @@ module.exports = {
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Manage Server permission
if (!interaction.member.permissions.has("ManageGuild")) {
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,
});
return;

View file

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

View file

@ -26,10 +26,10 @@ module.exports = {
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Manage Messages permission
if (!interaction.member.permissions.has("ManageMessages")) {
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,
});
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;
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Manage Roles permission
if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
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,
});
replySent = true;
@ -98,6 +102,8 @@ module.exports = {
color: "#ff0000",
permissions: [],
});
// Disable send messages permission for the timeout role in all channels
interaction.guild.channels.cache.each(async (channel) => {
await channel.permissionOverwrites.edit(timeoutRole, {
SendMessages: false,
@ -153,7 +159,7 @@ module.exports = {
replySent = true;
}
// log the timeout in a designated channel
// Log the timeout in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID;
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 = {
data: new SlashCommandBuilder()
@ -14,10 +18,14 @@ module.exports = {
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Manage Roles permission
if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
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,
});
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");
module.exports = {
@ -21,10 +25,14 @@ module.exports = {
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
// Check if the user has the Manage Roles permission
if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
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,
});
return;

View file

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

View file

@ -1,5 +1,6 @@
const { SlashCommandBuilder } = require("discord.js");
const VerificationCode = require("../../models/VerificationCode");
const ServerSettings = require("../../models/ServerSettings");
module.exports = {
data: new SlashCommandBuilder()
@ -13,11 +14,24 @@ module.exports = {
),
async execute(interaction, client) {
// Ensure command is only used in the specified verification channel
const verificationChannelName = process.env.VERIFICATION_CHANNEL_NAME;
if (interaction.channel.name !== verificationChannelName) {
// Fetch server settings from the database
const serverSettings = await ServerSettings.findOne({
guildId: interaction.guild.id,
});
if (!serverSettings) {
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,
});
}
@ -33,6 +47,7 @@ module.exports = {
}
try {
// Find the verification code in the database
const verificationEntry = await VerificationCode.findOne({
userId: interaction.user.id,
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) {
console.error("Guild not found.");
@ -66,13 +81,13 @@ module.exports = {
}
const role = guild.roles.cache.find(
(r) => r.name === process.env.VERIFIED_ROLE_NAME
(r) => r.name === serverSettings.verifiedRoleName
);
if (!role) {
console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`);
console.error(`Role "${serverSettings.verifiedRoleName}" not found.`);
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,
});
}
@ -89,9 +104,9 @@ module.exports = {
// Delete the verification code entry
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(
process.env.LOG_CHANNEL_ID
serverSettings.logChannelId
);
if (adminLogChannel) {
await adminLogChannel.send({
@ -103,7 +118,7 @@ module.exports = {
// Get the general channel and send a welcome message
const generalChannel = client.channels.cache.get(
process.env.GENERAL_CHANNEL_ID
serverSettings.generalChannelId
);
if (generalChannel) {
await generalChannel.send({

View file

@ -1,11 +1,12 @@
const { SlashCommandBuilder } = require("discord.js");
const nodemailer = require("nodemailer");
const VerificationCode = require("../../models/VerificationCode");
const ServerSettings = require("../../models/ServerSettings");
const transporter = nodemailer.createTransport({
service: "Gmail",
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,
},
});
@ -22,27 +23,41 @@ module.exports = {
),
async execute(interaction, client) {
// Ensure command is only used in the specified verification channel
const verificationChannelName = process.env.VERIFICATION_CHANNEL_NAME;
if (interaction.channel.name !== verificationChannelName) {
// Fetch the server settings from the database using guild ID
const serverSettings = await ServerSettings.findOne({
guildId: interaction.guild.id,
});
if (!serverSettings) {
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,
});
}
const email = interaction.options.getString("email");
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({
content: "You must use a valid DCU email address.",
ephemeral: true,
});
}
const guild = client.guilds.cache.get(process.env.GUILD_ID);
const guild = client.guilds.cache.get(interaction.guild.id);
if (!guild) {
console.error("Guild not found.");
@ -63,13 +78,13 @@ module.exports = {
}
const role = guild.roles.cache.find(
(r) => r.name === process.env.VERIFIED_ROLE_NAME
(r) => r.name === serverSettings.verifiedRoleName
);
if (!role) {
console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`);
console.error(`Role "${serverSettings.verifiedRoleName}" not found.`);
return interaction.reply({
content: `Role "${process.env.VERIFIED_ROLE_NAME}" not found.`,
content: `Role "${serverSettings.verifiedRoleName}" not found.`,
ephemeral: true,
});
}
@ -81,6 +96,7 @@ module.exports = {
});
}
// Generate a 6-digit verification code
const verificationCode = Math.floor(
100000 + Math.random() * 900000
).toString();
@ -102,6 +118,7 @@ module.exports = {
`;
try {
// Send the verification email
await transporter.sendMail({
from: `"${process.env.EMAIL_NAME}" <${process.env.EMAIL_USER}>`,
to: email,
@ -109,6 +126,7 @@ module.exports = {
html: emailHtml,
});
// Save the verification code and email in the database
await VerificationCode.create({
userId: interaction.user.id,
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",
"mongoose": "^8.6.0",
"nodemailer": "^6.9.14",
"owoify-js": "^2.0.0"
"owoify-js": "^2.0.0",
"puppeteer": "^23.4.1"
}
}