modertaion: add ban, kick and timeout commands

This commit is contained in:
Ayden Jahola 2024-09-08 21:58:36 +01:00
parent 088b575f20
commit 1a3599d8b0
No known key found for this signature in database
GPG key ID: 71DD90AE4AE92742
6 changed files with 518 additions and 0 deletions

148
commands/moderation/ban.js Normal file
View file

@ -0,0 +1,148 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const BannedUser = require("../../models/BannedUser");
module.exports = {
data: new SlashCommandBuilder()
.setName("ban")
.setDescription("Bans a user from the server")
.addUserOption((option) =>
option.setName("user").setDescription("The user to ban").setRequired(true)
)
.addStringOption((option) =>
option
.setName("reason")
.setDescription("Reason for banning the user")
.setRequired(true)
),
isModOnly: true,
async execute(interaction) {
let replySent = false;
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
await interaction.reply({
content: "You do not have the required role to use this command!",
ephemeral: true,
});
replySent = true;
return;
}
const user = interaction.options.getUser("user");
const reason =
interaction.options.getString("reason") || "No reason provided";
// Fetch the member object from the guild
const member = await interaction.guild.members.fetch(user.id);
if (!member) {
await interaction.reply({
content: "User not found in the guild!",
ephemeral: true,
});
replySent = true;
return;
}
// Check if the bot can ban the member
if (
member.roles.highest.position >=
interaction.guild.members.me.roles.highest.position
) {
await interaction.reply({
content:
"I cannot ban this user as they have a higher or equal role than me!",
ephemeral: true,
});
replySent = true;
return;
}
// Ban the user
await member.ban({ reason });
// Save banned user to MongoDB
await BannedUser.create({
bannedUserId: user.id,
bannedUserTag: user.tag,
bannerId: interaction.user.id,
bannerTag: interaction.user.tag,
reason: reason,
});
// Send DM to the banned user
try {
await user.send({
content: `You have been banned from the server. Reason: ${reason}`,
});
} catch (dmError) {
console.error(`Error sending DM to ${user.tag}:`, dmError);
// Only reply with this if the interaction hasn't been replied to yet
if (!replySent) {
await interaction.reply({
content: `Failed to send a DM to ${user.tag}. They might have DMs disabled.`,
ephemeral: true,
});
replySent = true;
}
}
const banEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Banned")
.setDescription(
`${user.tag} has been banned from the server.\nReason: ${reason}`
)
.setTimestamp()
.setFooter({
text: `Requested by ${interaction.user.tag}`,
iconURL: interaction.user.displayAvatarURL(),
});
// Send confirmation as ephemeral message
if (!replySent) {
await interaction.reply({
embeds: [banEmbed],
ephemeral: true,
});
replySent = true;
}
// log the ban in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId);
if (logChannel) {
const logEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Banned")
.setDescription(
`${user.tag} was banned from the server. Reason: ${reason}`
)
.setFooter({
text: `Banned by ${interaction.user.tag}`,
iconURL: interaction.user.displayAvatarURL(),
})
.setTimestamp();
await logChannel.send({ embeds: [logEmbed] });
}
} catch (error) {
console.error("Error executing ban command:", error);
// If the interaction hasn't been replied to yet, reply with an error message
if (!replySent) {
try {
await interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
} catch (replyError) {
console.error("Error replying to interaction:", replyError);
}
}
}
},
};

136
commands/moderation/kick.js Normal file
View file

@ -0,0 +1,136 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const KickedUser = require("../../models/KickedUser");
module.exports = {
data: new SlashCommandBuilder()
.setName("kick")
.setDescription("Kicks a user from the server")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to kick")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("reason")
.setDescription("Reason for kicking the user")
.setRequired(true)
),
isModOnly: true,
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
await interaction.reply({
content: "You do not have the required role to use this command!",
ephemeral: true,
});
return;
}
const user = interaction.options.getUser("user");
const reason =
interaction.options.getString("reason") || "No reason provided";
// Fetch the member object from the guild
const member = await interaction.guild.members.fetch(user.id);
if (!member) {
await interaction.reply({
content: "User not found in the guild!",
ephemeral: true,
});
return;
}
// Check if the bot can kick the member
if (
member.roles.highest.position >=
interaction.guild.members.me.roles.highest.position
) {
await interaction.reply({
content:
"I cannot kick this user as they have a higher or equal role than me!",
ephemeral: true,
});
return;
}
// Kick the user
await member.kick(reason);
// Save kicked user to MongoDB
await KickedUser.create({
kickedUserId: user.id,
kickedUserTag: user.tag,
kickerId: interaction.user.id,
kickerTag: interaction.user.tag,
reason: reason,
});
// Send DM to the kicked user
try {
await user.send({
content: `You have been kicked from the server. Reason: ${reason}`,
});
} 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
}
const kickEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Kicked")
.setDescription(
`${user.tag} has been kicked from the server.\nReason: ${reason}`
)
.setTimestamp()
.setFooter({
text: `Requested by ${interaction.user.tag}`,
iconURL: interaction.user.displayAvatarURL(),
});
// Send confirmation as ephemeral message
await interaction.reply({
embeds: [kickEmbed],
ephemeral: true,
});
// log the kick in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId);
if (logChannel) {
const logEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Kicked")
.setDescription(
`${user.tag} was kicked from the server. Reason: ${reason}`
)
.setFooter({
text: `Kicked by ${interaction.user.tag}`,
iconURL: interaction.user.displayAvatarURL(),
})
.setTimestamp();
await logChannel.send({ embeds: [logEmbed] });
}
} catch (error) {
console.error("Error executing kick command:", error);
try {
if (!interaction.replied) {
await interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
}
} catch (replyError) {
console.error("Error replying to interaction:", replyError);
}
}
},
};

View file

@ -0,0 +1,191 @@
const {
SlashCommandBuilder,
EmbedBuilder,
PermissionsBitField,
} = require("discord.js");
const TimedOutUser = require("../../models/TimedOutUser");
module.exports = {
data: new SlashCommandBuilder()
.setName("timeout")
.setDescription("Timeout a user in the server")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user to timeout")
.setRequired(true)
)
.addIntegerOption((option) =>
option
.setName("duration")
.setDescription("Duration of the timeout in minutes")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("reason")
.setDescription("Reason for the timeout")
.setRequired(true)
),
isModOnly: true,
async execute(interaction) {
let replySent = false;
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
await interaction.reply({
content: "You do not have the required role to use this command!",
ephemeral: true,
});
replySent = true;
return;
}
const user = interaction.options.getUser("user");
const duration = interaction.options.getInteger("duration");
const reason =
interaction.options.getString("reason") || "No reason provided";
// Fetch the member object from the guild
const member = await interaction.guild.members.fetch(user.id);
if (!member) {
await interaction.reply({
content: "User not found in the guild!",
ephemeral: true,
});
replySent = true;
return;
}
// Check if the bot has permission to manage roles
if (
!interaction.guild.members.me.permissions.has(
PermissionsBitField.Flags.ManageRoles
)
) {
await interaction.reply({
content: "I do not have permission to manage roles!",
ephemeral: true,
});
replySent = true;
return;
}
// Ensure the bot's role is high enough to apply the timeout
if (
member.roles.highest.position >=
interaction.guild.members.me.roles.highest.position
) {
await interaction.reply({
content:
"I cannot timeout this user as they have a higher or equal role than me!",
ephemeral: true,
});
replySent = true;
return;
}
// Create a timeout role or use an existing one
let timeoutRole = interaction.guild.roles.cache.find(
(role) => role.name === "Timed Out"
);
if (!timeoutRole) {
timeoutRole = await interaction.guild.roles.create({
name: "Timed Out",
color: "#ff0000",
permissions: [],
});
interaction.guild.channels.cache.each(async (channel) => {
await channel.permissionOverwrites.edit(timeoutRole, {
SendMessages: false,
});
});
}
// Apply the timeout
await member.roles.add(timeoutRole);
const timeoutEnd = Date.now() + duration * 60 * 1000;
// Save timed out user to MongoDB
await TimedOutUser.create({
timedOutUserId: user.id,
timedOutUserTag: user.tag,
moderatorId: interaction.user.id,
moderatorTag: interaction.user.tag,
reason: reason,
timeoutEnd: timeoutEnd,
});
// Inform the user of the timeout
try {
await user.send({
content: `Hello ${user.username},\n\nWe wanted to inform you that a timeout has been applied to your account in **${interaction.guild.name}**. This action was taken to ensure the community remains respectful and enjoyable for everyone.\n\n**Reason:** ${reason}\n**Duration:** ${duration} minutes\n\nPlease use this time to review our community guidelines, and we look forward to welcoming you back after the timeout ends.\n\nIf you have any questions or concerns, feel free to reach out to the moderation team once your timeout is over.\n\nThank you for understanding, and we appreciate your cooperation.\n\nBest regards,\n**${interaction.guild.name}** Moderation Team`,
});
} catch (dmError) {
console.error(`Error sending DM to ${user.tag}:`, dmError);
// Inform mod about the DM failure
if (!replySent) {
await interaction.reply({
content: `Failed to send a DM to ${user.tag}. They might have DMs disabled.`,
ephemeral: true,
});
replySent = true;
}
}
const timeoutEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Timed Out")
.setDescription(
`${user.tag} has been timed out in the server.\nReason: ${reason}\nDuration: ${duration} minutes.`
)
.setTimestamp();
// Send confirmation as ephemeral message
if (!replySent) {
await interaction.reply({
embeds: [timeoutEmbed],
ephemeral: true,
});
replySent = true;
}
// log the timeout in a designated channel
const logChannelId = process.env.LOG_CHANNEL_ID;
const logChannel = interaction.guild.channels.cache.get(logChannelId);
if (logChannel) {
const logEmbed = new EmbedBuilder()
.setColor("#ff0000")
.setTitle("User Timed Out")
.setDescription(
`${user.tag} was timed out in the server. Reason: ${reason}\nDuration: ${duration} minutes.`
)
.setFooter({
text: `Timed out by ${interaction.user.tag}`,
iconURL: interaction.user.displayAvatarURL(),
})
.setTimestamp();
await logChannel.send({ embeds: [logEmbed] });
}
} catch (error) {
console.error("Error executing timeout command:", error);
// If the interaction hasn't been replied to yet, reply with an error message
if (!replySent) {
try {
await interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
} catch (replyError) {
console.error("Error replying to interaction:", replyError);
}
}
}
},
};

14
models/BannedUser.js Normal file
View file

@ -0,0 +1,14 @@
const mongoose = require("mongoose");
const bannedUserSchema = new mongoose.Schema({
bannedUserId: { type: String, required: true },
bannedUserTag: { type: String, required: true },
bannerId: { type: String, required: true },
bannerTag: { type: String, required: true },
reason: { type: String, default: "No reason provided" },
bannedAt: { type: Date, default: Date.now },
});
const BannedUser = mongoose.model("BannedUser", bannedUserSchema);
module.exports = BannedUser;

14
models/KickedUser.js Normal file
View file

@ -0,0 +1,14 @@
const mongoose = require("mongoose");
const kickedUserSchema = new mongoose.Schema({
kickedUserId: { type: String, required: true },
kickedUserTag: { type: String, required: true },
kickerId: { type: String, required: true },
kickerTag: { type: String, required: true },
reason: { type: String, default: "No reason provided" },
kickedAt: { type: Date, default: Date.now },
});
const KickedUser = mongoose.model("KickedUser", kickedUserSchema);
module.exports = KickedUser;

15
models/TimedOutUser.js Normal file
View file

@ -0,0 +1,15 @@
const mongoose = require("mongoose");
const timedOutUserSchema = new mongoose.Schema({
timedOutUserId: { type: String, required: true },
timedOutUserTag: { type: String, required: true },
moderatorId: { type: String, required: true },
moderatorTag: { type: String, required: true },
reason: { type: String, default: "No reason provided" },
timeoutEnd: { type: Date, required: true },
timedOutAt: { type: Date, default: Date.now },
});
const TimedOutUser = mongoose.model("TimedOutUser", timedOutUserSchema);
module.exports = TimedOutUser;