Update Bot (j'ai plus le repo sur GitHub)

Qui c'est la conne qui a delete le repo sur GitHub? C'EST MOIIIII
This commit is contained in:
2026-02-09 14:36:26 +01:00
parent eab4419e12
commit ad2014b7b2
586 changed files with 58986 additions and 25205 deletions

View File

@@ -1,121 +1,119 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
const db = require('../../functions/database/db.js'); // instance mysql2/promise
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { parseDuration, formatDuration, sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('ban')
.setDescription('Select a member and ban them.')
.setDescription('Bannir un membre du serveur.')
.addUserOption(option =>
option.setName('target')
.setDescription('The member to ban')
.setDescription('Le membre à bannir')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Reason')
.setDescription('Raison du bannissement')
.setRequired(false))
.addStringOption(option =>
option.setName('duration')
.setDescription('Ban duration (ex: 1h, 2d, leave empty for permanent)')
.setDescription('Durée du ban (ex: 1h, 2d, laisser vide pour permanent)')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
const target = interaction.options.getUser('target');
const reason = interaction.options.getString('reason') || 'No reason provided';
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
const durationInput = interaction.options.getString('duration');
await interaction.deferReply()
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: 'No user specified!' });
await interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (member) {
if (member.roles.highest.position >= interaction.member.roles.highest.position && interaction.guild.ownerId !== interaction.user.id) {
return interaction.editReply({ content: '❌ Tu ne peux pas bannir cet utilisateur car il a un rôle supérieur ou égal au tien.' });
}
if (member.id === interaction.guild.ownerId) {
return interaction.editReply({ content: '❌ Tu ne peux pas bannir le propriétaire du serveur.' });
}
if (!member.bannable) {
return interaction.editReply({ content: '❌ Je ne peux pas bannir cet utilisateur (rôle supérieur au mien ?).' });
}
}
let type = 'Permanent';
let unbanDate = null;
let durationText = 'Permanent';
if (durationInput) {
const regex = /^(\d+)(s|m|h|d|mo|y)$/;
const match = durationInput.match(regex);
if (match) {
const value = parseInt(match[1]);
const unit = match[2];
let multiplier = 1000;
if (unit === 's') multiplier *= 60 / 60;
if (unit === 'm') multiplier *= 60;
if (unit === 'h') multiplier *= 60 * 60;
if (unit === 'd') multiplier *= 60 * 60 * 24;
if (unit === 'mo') multiplier *= 60 * 60 * 24 * 30;
if (unit === 'y') multiplier *= 60 * 60 * 24 * 365;
unbanDate = Date.now() + value * multiplier;
const durationMs = parseDuration(durationInput);
if (durationMs) {
unbanDate = Date.now() + durationMs;
type = 'Temporary';
durationText = formatDuration(durationMs);
} else {
return interaction.editReply({ content: '❌ Format de durée invalide (ex: 1h, 2d).' });
}
}
// juste avant la création de l'embed
let durationText = type;
if (type === 'Temporary' && unbanDate) {
const initialDurationMs = unbanDate - Date.now() + (Date.now() - Date.now()); // ça reste l'unbanDate - timestamp original
function formatInitialDuration(ms) {
const seconds = Math.floor(ms / 1000) % 60;
const minutes = Math.floor(ms / (1000 * 60)) % 60;
const hours = Math.floor(ms / (1000 * 60 * 60)) % 24;
const days = Math.floor(ms / (1000 * 60 * 60 * 24));
const months = Math.floor(ms / (1000 * 60 * 60 * 24 * 30));
const years = Math.floor(ms / (1000 * 60 * 60 * 24 * 365));
if (years) return `${years} Année${years > 1 ? 's' : ''}`;
if (months) return `${months} Mois`;
if (days) return `${days} Jour${days > 1 ? 's' : ''}`;
if (hours) return `${hours} Heure${hours > 1 ? 's' : ''}`;
if (minutes) return `${minutes} Minute${minutes > 1 ? 's' : ''}`;
if (seconds) return `${seconds} Seconde${seconds > 1 ? 's' : ''}`;
return 'Instantané';
}
durationText = formatInitialDuration(initialDurationMs);
}
try {
// Bannissement
interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
await interaction.guild.bans.create(target.id, { reason: `Banni par ${interaction.user.tag}: ${reason}` });
// Stockage dans MySQL (bans)
await db.query(
`INSERT INTO bans (userId, reason, modId, timestamp, type, unbanDate, guildId)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE reason=VALUES(reason), modId=VALUES(modId), timestamp=VALUES(timestamp), type=VALUES(type), unbanDate=VALUES(unbanDate)`,
ON DUPLICATE KEY UPDATE reason=VALUES(reason), modId=VALUES(modId), timestamp=VALUES(timestamp), type=VALUES(type), unbanDate=VALUES(unbanDate)`,
[target.id, reason, interaction.user.id, Date.now(), type, unbanDate, interaction.guild.id]
);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Bannissement', reason, type, interaction.guild.id, Date.now()]
);
await interaction.editReply(`✅ Successfully banned ${target.tag} (${type})`);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.ban} Utilisateur Banni`)
.setColor(colors.ban)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été banni du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '⏱️ Durée', value: `\`${durationText}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true },
{ name: '🏷️ Type', value: `\`${type}\``, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
// Embed log
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
if (logChannel) {
const embed = new EmbedBuilder()
.setTitle('User Banned')
.setColor('Red')
.addFields(
{ name: 'Banned User', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: 'Banned By', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: 'Reason', value: reason, inline: false },
{ name: 'Duration', value: durationText, inline: true },
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
{ name: 'Guild', value: interaction.guild.name, inline: true }
)
.setTimestamp();
logChannel.send({ embeds: [embed] });
}
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.ban} Bannissement`)
.setColor(colors.ban)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été banni du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '⏱️ Durée', value: `\`${durationText}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error(err);
await interaction.editReply({ content: `Failed to ban ${target.tag}` });
console.error('Erreur ban:', err);
await interaction.editReply({ content: `Erreur: ${err.message}` });
}
}
};
},
};

View File

@@ -1,56 +1,69 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { colors } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('casier')
.setDescription('Voir toutes les sanctions dun membre.')
.setDescription('Voir toutes les sanctions d\'un membre.')
.addUserOption(option =>
option.setName('membre')
.setDescription('Le membre dont tu veux voir le casier')
.setRequired(true)),
async execute(interaction) {
await interaction.deferReply();
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const member = interaction.options.getUser('membre');
try {
const [rows] = await db.query(
'SELECT * FROM logs WHERE userId = ? ORDER BY timestamp DESC',
[member.id]
'SELECT * FROM logs WHERE userId = ? AND guildId = ? ORDER BY timestamp DESC',
[member.id, interaction.guild.id]
);
if (!rows.length) return interaction.editReply(`${member.tag} na aucune sanction.`);
const sanctions = rows.filter(row =>
row.action !== 'Modification de sanction' &&
row.action !== 'Révocation de sanction' &&
row.action !== 'Modification de mute'
);
if (!sanctions.length) {
return interaction.editReply({ content: `${member.tag} n'a aucune sanction sur ce serveur.` });
}
// Découpe les sanctions en pages de 25
const pages = [];
for (let i = 0; i < rows.length; i += 10) {
pages.push(rows.slice(i, i + 10));
for (let i = 0; i < sanctions.length; i += 10) {
pages.push(sanctions.slice(i, i + 10));
}
const embeds = pages.map((page, pageIndex) => {
const embed = new EmbedBuilder()
.setTitle(`Casier de ${member.tag} - Page ${pageIndex + 1}/${pages.length}`)
.setColor('Orange')
.setAuthor({ name: member.displayName, iconURL: member.displayAvatarURL({ dynamic: true }) })
.setTitle(`📋 Casier Judiciaire - Page ${pageIndex + 1}/${pages.length}`)
.setColor(colors.warning)
.setThumbnail(member.displayAvatarURL({ dynamic: true, size: 256 }))
.setDescription(`**${member.toString()}** • \`${member.tag}\``)
.setFooter({ text: `Total: ${sanctions.length} sanction(s) • ${interaction.guild.name}` })
.setTimestamp();
page.forEach((row, index) => {
const date = new Date(row.timestamp).toLocaleString();
const date = `<t:${Math.floor(row.timestamp / 1000)}:F>`;
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1);
const action = row.action || 'N/A';
const guildName = row.guildId
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Unknown Server'
: 'Unknown Server';
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Serveur inconnu'
: 'Serveur inconnu';
const displayNumber = index + 1 + pageIndex * 10;
embed.addFields({
name: `#${index + 1 + pageIndex * 10} - ${actionCapitalized}`,
value: `**Modérateur:** <@${row.modId}> (${row.modTag || 'N/A'})\n` +
`**Raison:** ${row.reason || 'N/A'}\n` +
`**Serveur:** ${guildName}\n` +
name: `#${displayNumber} (ID: \`${row.id}\`) - ${actionCapitalized}`,
value: `**Modérateur:** <@${row.modId}> (\`${row.modTag || 'N/A'}\`)\n` +
`**Raison:** ${row.reason || '`N/A`'}\n` +
`**Serveur:** \`${guildName}\`\n` +
`**Date:** ${date}\n` +
`**Type:** ${action}`,
`**Type:** \`${row.type || 'N/A'}\``,
inline: false
});
});
@@ -58,28 +71,36 @@ module.exports = {
return embed;
});
// Composants pour naviguer
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId('prev').setLabel('⬅️').setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId('next').setLabel('➡️').setStyle(ButtonStyle.Primary)
new ButtonBuilder().setCustomId('prev').setLabel('⬅️ Précédent').setStyle(ButtonStyle.Primary).setDisabled(pages.length === 1),
new ButtonBuilder().setCustomId('next').setLabel('Suivant ➡️').setStyle(ButtonStyle.Primary).setDisabled(pages.length === 1)
);
let currentPage = 0;
const message = await interaction.editReply({ embeds: [embeds[currentPage]], components: [row] });
const message = await interaction.editReply({ embeds: [embeds[currentPage]], components: pages.length > 1 ? [row] : [] });
// Collecteur de boutons
const collector = message.createMessageComponentCollector({ time: 60_000 });
collector.on('collect', i => {
if (i.user.id !== interaction.user.id) return i.reply({ content: 'Ce nest pas ton menu !', embeds: [embed] });
if (i.customId === 'next') currentPage = (currentPage + 1) % embeds.length;
if (i.customId === 'prev') currentPage = (currentPage - 1 + embeds.length) % embeds.length;
collector.on('collect', async i => {
if (i.user.id !== interaction.user.id) {
return i.reply({ content: '❌ Ce n\'est pas ton menu !', flags: MessageFlags.Ephemeral });
}
if (i.customId === 'next') {
currentPage = (currentPage + 1) % embeds.length;
}
if (i.customId === 'prev') {
currentPage = (currentPage - 1 + embeds.length) % embeds.length;
}
i.update({ embeds: [embeds[currentPage]] });
await i.update({ embeds: [embeds[currentPage]] });
});
collector.on('end', () => {
message.edit({ components: [] }).catch(() => null);
});
} catch (err) {
console.error(err);
await interaction.editReply('❌ Une erreur est survenue lors de la récupération du casier.');
console.error('Erreur casier:', err);
await interaction.editReply({ content: '❌ Une erreur est survenue lors de la récupération du casier.' });
}
},
};
};

View File

@@ -0,0 +1,149 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { parseDuration, formatDuration, sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('changemute')
.setDescription('Changer le temps ou la raison d\'un mute.')
.addUserOption(option =>
option.setName('user')
.setDescription('Le membre concerné')
.setRequired(true))
.addStringOption(option =>
option.setName('time')
.setDescription('Nouvelle durée du mute (ex: 5m, 1h, 1d)')
.setRequired(false))
.addStringOption(option =>
option.setName('reason')
.setDescription('Nouvelle raison du mute')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('user');
const newTime = interaction.options.getString('time');
const newReason = interaction.options.getString('reason');
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (!newTime && !newReason) return interaction.editReply({ content: '❌ Tu dois spécifier au moins une nouvelle durée ou une nouvelle raison.' });
const [muteRows] = await db.query(
'SELECT * FROM mutes WHERE userId = ? AND guildId = ?',
[target.id, interaction.guild.id]
);
if (!muteRows.length && (!member.communicationDisabledUntilTimestamp || member.communicationDisabledUntilTimestamp < Date.now())) {
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas muté.' });
}
const currentMute = muteRows[0];
const oldReason = currentMute?.reason || 'N/A';
const oldUnmuteDate = currentMute?.unmuteDate || null;
try {
let newUnmuteDate = oldUnmuteDate;
let newDurationMs = null;
let durationText = 'Non modifié';
if (newTime) {
newDurationMs = parseDuration(newTime);
if (!newDurationMs) return interaction.editReply({ content: '❌ Durée invalide. Utilise un format comme: 5m, 1h, 2d (max 28 jours).' });
const maxDuration = 28 * 24 * 60 * 60 * 1000;
if (newDurationMs > maxDuration) return interaction.editReply({ content: '❌ La durée maximum est de 28 jours.' });
newUnmuteDate = Date.now() + newDurationMs;
durationText = formatDuration(newDurationMs);
await member.timeout(newDurationMs, `Mute modifié par ${interaction.user.tag}: ${newReason || oldReason}`);
}
const finalReason = newReason || oldReason;
const type = 'Temporary';
if (currentMute) {
await db.query(
'UPDATE mutes SET reason = ?, modId = ?, modTag = ?, timestamp = ?, unmuteDate = ?, type = ? WHERE userId = ? AND guildId = ?',
[finalReason, interaction.user.id, interaction.user.tag, Date.now(), newUnmuteDate, type, target.id, interaction.guild.id]
);
} else {
await db.query(
`INSERT INTO mutes (userId, guildId, reason, modId, modTag, timestamp, unmuteDate, type)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, interaction.guild.id, finalReason, interaction.user.id, interaction.user.tag, Date.now(), newUnmuteDate, type]
);
}
const changeLog = [];
if (newTime) changeLog.push(`Durée: ${formatDuration(newDurationMs)}`);
if (newReason) changeLog.push(`Raison: ${oldReason}${newReason}`);
const changeReason = changeLog.join(', ');
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Modification de mute', changeReason, type, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setTitle(`${emojis.success} Mute Modifié`)
.setColor(colors.success)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '🔧 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '⏰ Nouvelle durée', value: durationText, inline: true },
{ name: '📝 Ancienne raison', value: oldReason, inline: false },
{ name: '📝 Nouvelle raison', value: finalReason, inline: false }
)
.setFooter({ text: `ID: ${target.id}` })
.setTimestamp();
if (newUnmuteDate) {
embed.addFields({ name: '📅 Démute le', value: `<t:${Math.floor(newUnmuteDate / 1000)}:F>`, inline: true });
}
await interaction.editReply({ embeds: [embed] });
try {
const dmEmbed = new EmbedBuilder()
.setTitle(`${emojis.info} Ton mute a été modifié`)
.setColor(colors.info)
.setDescription(`Ton mute sur **${interaction.guild.name}** a été modifié`)
.addFields(
{ name: '⏰ Durée', value: durationText, inline: true },
{ name: '📝 Raison', value: finalReason, inline: false }
)
.setFooter({ text: interaction.guild.name, iconURL: interaction.guild.iconURL({ dynamic: true }) })
.setTimestamp();
await target.send({ embeds: [dmEmbed] });
} catch { /* DMs fermés */ }
const logEmbed = new EmbedBuilder()
.setTitle(`${emojis.success} Modification de Mute`)
.setColor(colors.success)
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '🔧 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Modifications', value: changeReason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id} | Serveur: ${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur changemute:', err);
await interaction.editReply({ content: `❌ Échec de la modification du mute: ${err.message}` });
}
},
};

View File

@@ -0,0 +1,114 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('changesanction')
.setDescription('Changer la raison d\'une sanction.')
.addUserOption(option =>
option.setName('user')
.setDescription('L\'utilisateur concerné')
.setRequired(true))
.addIntegerOption(option =>
option.setName('sanction_id')
.setDescription('L\'ID DB de la sanction (visible dans /casier, ex: ID: 123)')
.setRequired(true))
.addStringOption(option =>
option.setName('new_reason')
.setDescription('La nouvelle raison')
.setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('user');
const sanctionId = interaction.options.getInteger('sanction_id');
const newReason = interaction.options.getString('new_reason');
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
try {
const [sanctionRows] = await db.query(
'SELECT * FROM logs WHERE id = ? AND userId = ? AND guildId = ?',
[sanctionId, target.id, interaction.guild.id]
);
if (!sanctionRows.length) {
const [allUserLogs] = await db.query(
'SELECT id, action, guildId FROM logs WHERE id = ? AND userId = ?',
[sanctionId, target.id]
);
if (allUserLogs.length > 0) {
const log = allUserLogs[0];
const guildName = interaction.client.guilds.cache.get(log.guildId)?.name || 'Serveur inconnu';
return interaction.editReply({
content: `❌ La sanction avec l'ID ${sanctionId} existe, mais elle appartient au serveur "${guildName}" (guildId: ${log.guildId}), pas à "${interaction.guild.name}" (guildId: ${interaction.guild.id}).`
});
}
return interaction.editReply({
content: `❌ Aucune sanction trouvée avec l'ID ${sanctionId} pour ${target.tag}.\n\n💡 **Astuce:** Utilise l'ID DB visible dans \`/casier\` (ex: \`#1 (ID: 123)\` → utilise \`123\`).`
});
}
const sanction = sanctionRows[0];
if (sanction.action === 'Modification de sanction' ||
sanction.action === 'Révocation de sanction' ||
sanction.action === 'Modification de mute') {
return interaction.editReply({ content: `❌ Cette entrée est une modification, pas une sanction. Utilise l'ID d'une vraie sanction.` });
}
const oldReason = sanction.reason;
await db.query(
'UPDATE logs SET reason = ?, modId = ?, modTag = ? WHERE id = ?',
[newReason, interaction.user.id, interaction.user.tag, sanction.id]
);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Modification de sanction', `Ancienne raison: ${oldReason} → Nouvelle raison: ${newReason}`, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setTitle(`${emojis.success} Sanction Modifiée`)
.setColor(colors.success)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '🔧 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Ancienne raison', value: oldReason || 'N/A', inline: false },
{ name: '📝 Nouvelle raison', value: newReason, inline: false },
{ name: '🆔 ID Sanction', value: `${sanction.id}`, inline: true },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setTitle(`${emojis.success} Modification de Sanction`)
.setColor(colors.success)
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '🔧 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Ancienne raison', value: oldReason || 'N/A', inline: false },
{ name: '📝 Nouvelle raison', value: newReason, inline: false },
{ name: '🆔 ID Sanction', value: `${sanction.id}`, inline: true },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id} | Serveur: ${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur changesanction:', err);
await interaction.editReply({ content: `❌ Échec de la modification de la sanction: ${err.message}` });
}
},
};

View File

@@ -0,0 +1,74 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('clear')
.setDescription('Supprimer plusieurs messages d\'un coup.')
.addIntegerOption(option =>
option.setName('amount')
.setDescription('Nombre de messages à supprimer (1-100)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100))
.addUserOption(option =>
option.setName('target')
.setDescription('Supprimer uniquement les messages de cet utilisateur')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
async execute(interaction) {
const amount = interaction.options.getInteger('amount');
const target = interaction.options.getUser('target');
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!interaction.channel.isTextBased()) {
return interaction.editReply({ content: '❌ Cette commande ne peut être utilisée que dans un canal texte.' });
}
try {
const messages = await interaction.channel.messages.fetch({ limit: amount });
let messagesToDelete = messages;
if (target) {
messagesToDelete = messages.filter(msg => msg.author.id === target.id);
}
const filteredMessages = messagesToDelete.filter(msg => {
const msgAge = Date.now() - msg.createdTimestamp;
return msgAge < 14 * 24 * 60 * 60 * 1000;
});
if (filteredMessages.size === 0) {
return interaction.editReply({ content: '❌ Aucun message à supprimer (les messages de plus de 14 jours ne peuvent pas être supprimés).' });
}
await interaction.channel.bulkDelete(filteredMessages, true);
const embed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.success} Messages Supprimés`)
.setColor(colors.success)
.setDescription(`**${filteredMessages.size}** message(s) supprimé(s)${target ? ` de ${target.toString()}` : ''}.`)
.addFields(
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📊 Messages supprimés', value: `\`${filteredMessages.size}\``, inline: true },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `${interaction.guild.name}${interaction.channel.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
} catch (err) {
console.error('Erreur clear:', err);
if (err.code === 50034) {
await interaction.editReply({ content: '❌ Les messages de plus de 14 jours ne peuvent pas être supprimés.' });
} else {
await interaction.editReply({ content: `❌ Échec de la suppression: ${err.message}` });
}
}
},
};

View File

@@ -0,0 +1,89 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('kick')
.setDescription('Expulser un membre du serveur.')
.addUserOption(option =>
option.setName('target')
.setDescription('Le membre à expulser')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison de l\'expulsion')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
async execute(interaction) {
const target = interaction.options.getUser('target');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (member.id === interaction.guild.ownerId) return interaction.editReply({ content: '❌ Tu ne peux pas expulser le propriétaire du serveur.' });
if (member.id === interaction.user.id) return interaction.editReply({ content: '❌ Tu ne peux pas t\'expulser toi-même.' });
if (member.roles.highest.position >= interaction.member.roles.highest.position && interaction.guild.ownerId !== interaction.user.id) {
return interaction.editReply({ content: '❌ Tu ne peux pas expulser cet utilisateur car il a un rôle supérieur ou égal au tien.' });
}
if (!member.kickable) {
return interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour expulser cet utilisateur.' });
}
try {
await member.kick(`Expulsé par ${interaction.user.tag}: ${reason}`);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Expulsion', reason, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.kick} Utilisateur Expulsé`)
.setColor(colors.kick)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été expulsé du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.kick} Expulsion`)
.setColor(colors.kick)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été expulsé du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur kick:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
},
};

113
commands/moderation/mute.js Normal file
View File

@@ -0,0 +1,113 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { parseDuration, formatDuration, sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('mute')
.setDescription('Muter un membre du serveur.')
.addUserOption(option =>
option.setName('user')
.setDescription('Le membre à muter')
.setRequired(true))
.addStringOption(option =>
option.setName('duration')
.setDescription('Durée du mute (ex: 5m, 1h, 1d, max 28j)')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison du mute')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('user');
const durationInput = interaction.options.getString('duration');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (member.id === interaction.guild.ownerId) return interaction.editReply({ content: '❌ Tu ne peux pas muter le propriétaire du serveur.' });
if (member.id === interaction.user.id) return interaction.editReply({ content: '❌ Tu ne peux pas te muter toi-même.' });
if (member.roles.highest.position >= interaction.member.roles.highest.position && interaction.guild.ownerId !== interaction.user.id) {
return interaction.editReply({ content: '❌ Tu ne peux pas muter cet utilisateur car il a un rôle supérieur ou égal au tien.' });
}
if (!member.moderatable) {
return interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour muter cet utilisateur.' });
}
const durationMs = parseDuration(durationInput);
if (!durationMs) return interaction.editReply({ content: '❌ Durée invalide. Utilise un format comme: 5m, 1h, 2d (max 28 jours).' });
const maxDuration = 28 * 24 * 60 * 60 * 1000;
if (durationMs > maxDuration) return interaction.editReply({ content: '❌ La durée maximum est de 28 jours.' });
const unmuteDate = Date.now() + durationMs;
const type = 'Temporary'; // Timeout est toujours temporaire
try {
await member.timeout(durationMs, `Muté par ${interaction.user.tag}: ${reason}`);
await db.query(
`INSERT INTO mutes (userId, guildId, reason, modId, modTag, timestamp, unmuteDate, type)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE reason=VALUES(reason), modId=VALUES(modId), modTag=VALUES(modTag), timestamp=VALUES(timestamp), unmuteDate=VALUES(unmuteDate), type=VALUES(type)`,
[target.id, interaction.guild.id, reason, interaction.user.id, interaction.user.tag, Date.now(), unmuteDate, type]
);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Mute', reason, type, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.mute} Utilisateur Muté`)
.setColor(colors.mute)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été muté.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '⏰ Durée', value: `\`${formatDuration(durationMs)}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Jusqu\'à', value: `<t:${Math.floor(unmuteDate / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.mute} Mute`)
.setColor(colors.mute)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été muté.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '⏰ Durée', value: `\`${formatDuration(durationMs)}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Jusqu\'à', value: `<t:${Math.floor(unmuteDate / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur mute:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
},
};

View File

@@ -0,0 +1,97 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { colors } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('mysanctions')
.setDescription('Voir tes propres sanctions sur ce serveur.'),
async execute(interaction) {
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const userId = interaction.user.id;
try {
const [rows] = await db.query(
'SELECT * FROM logs WHERE userId = ? AND guildId = ? ORDER BY timestamp DESC',
[userId, interaction.guild.id]
);
const sanctions = rows.filter(row =>
row.action !== 'Modification de sanction' &&
row.action !== 'Révocation de sanction' &&
row.action !== 'Modification de mute'
);
if (!sanctions.length) {
return interaction.editReply({ content: '✅ Tu n\'as aucune sanction sur ce serveur.' });
}
const pages = [];
for (let i = 0; i < sanctions.length; i += 10) {
pages.push(sanctions.slice(i, i + 10));
}
const embeds = pages.map((page, pageIndex) => {
const embed = new EmbedBuilder()
.setTitle(`📋 Mes Sanctions - Page ${pageIndex + 1}/${pages.length}`)
.setColor(colors.warning)
.setThumbnail(interaction.user.displayAvatarURL({ dynamic: true }))
.setFooter({ text: `Total: ${sanctions.length} sanction(s)` })
.setTimestamp();
page.forEach((row, index) => {
const date = `<t:${Math.floor(row.timestamp / 1000)}:F>`;
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1);
embed.addFields({
name: `#${index + 1 + pageIndex * 10} - ${actionCapitalized}`,
value: `**Modérateur:** <@${row.modId}> (${row.modTag || 'N/A'})\n` +
`**Raison:** ${row.reason || 'N/A'}\n` +
`**Date:** ${date}\n` +
`**Type:** ${row.type || 'N/A'}`,
inline: false
});
});
return embed;
});
if (pages.length === 1) {
return interaction.editReply({ embeds: [embeds[0]] });
}
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId('prev').setLabel('⬅️ Précédent').setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId('next').setLabel('Suivant ➡️').setStyle(ButtonStyle.Primary)
);
let currentPage = 0;
const message = await interaction.editReply({ embeds: [embeds[currentPage]], components: [row] });
const collector = message.createMessageComponentCollector({ time: 60_000 });
collector.on('collect', async i => {
if (i.user.id !== interaction.user.id) {
return i.reply({ content: '❌ Ce n\'est pas ton menu !', flags: MessageFlags.Ephemeral });
}
if (i.customId === 'next') {
currentPage = (currentPage + 1) % embeds.length;
}
if (i.customId === 'prev') {
currentPage = (currentPage - 1 + embeds.length) % embeds.length;
}
await i.update({ embeds: [embeds[currentPage]] });
});
collector.on('end', () => {
message.edit({ components: [] }).catch(() => null);
});
} catch (err) {
console.error('Erreur mysanctions:', err);
await interaction.editReply({ content: '❌ Une erreur est survenue lors de la récupération de tes sanctions.' });
}
},
};

View File

@@ -0,0 +1,103 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('purge')
.setDescription('Supprimer les messages d\'un utilisateur dans un canal.')
.addUserOption(option =>
option.setName('target')
.setDescription('L\'utilisateur dont supprimer les messages')
.setRequired(true))
.addIntegerOption(option =>
option.setName('amount')
.setDescription('Nombre de messages à vérifier (1-100)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison de la purge')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
async execute(interaction) {
const target = interaction.options.getUser('target');
const amount = interaction.options.getInteger('amount');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!interaction.channel.isTextBased()) {
return interaction.editReply({ content: '❌ Cette commande ne peut être utilisée que dans un canal texte.' });
}
try {
const messages = await interaction.channel.messages.fetch({ limit: amount });
const messagesToDelete = messages.filter(msg => msg.author.id === target.id);
const filteredMessages = messagesToDelete.filter(msg => {
const msgAge = Date.now() - msg.createdTimestamp;
return msgAge < 14 * 24 * 60 * 60 * 1000;
});
if (filteredMessages.size === 0) {
return interaction.editReply({ content: `❌ Aucun message à supprimer de ${target.tag} (les messages de plus de 14 jours ne peuvent pas être supprimés).` });
}
await interaction.channel.bulkDelete(filteredMessages, true);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Purge', reason, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.success} Messages Supprimés`)
.setColor(colors.success)
.setDescription(`**${filteredMessages.size}** message(s) de ${target.toString()} supprimé(s).`)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📊 Messages supprimés', value: `\`${filteredMessages.size}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.success} Purge`)
.setColor(colors.success)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`**${filteredMessages.size}** message(s) de ${target.toString()} supprimé(s).`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📊 Messages supprimés', value: `\`${filteredMessages.size}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur purge:', err);
if (err.code === 50034) {
await interaction.editReply({ content: '❌ Les messages de plus de 14 jours ne peuvent pas être supprimés.' });
} else {
await interaction.editReply({ content: `❌ Échec de la purge: ${err.message}` });
}
}
},
};

View File

@@ -0,0 +1,101 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('revokesanction')
.setDescription('Révoquer une sanction (Haut gradés, Co-Fonda/Fonda uniquement).')
.addUserOption(option =>
option.setName('user')
.setDescription('L\'utilisateur concerné')
.setRequired(true))
.addIntegerOption(option =>
option.setName('sanction_id')
.setDescription('L\'ID DB de la sanction à révoquer (visible dans /casier, ex: ID: 123)')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison de la révocation')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
async execute(interaction) {
const target = interaction.options.getUser('user');
const sanctionId = interaction.options.getInteger('sanction_id');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator) && interaction.user.id !== interaction.guild.ownerId) {
return interaction.editReply({ content: '❌ Seuls les administrateurs et le propriétaire du serveur peuvent révoquer une sanction.' });
}
try {
const [sanctionRows] = await db.query(
'SELECT * FROM logs WHERE id = ? AND userId = ? AND guildId = ?',
[sanctionId, target.id, interaction.guild.id]
);
if (!sanctionRows.length) {
return interaction.editReply({
content: `❌ Aucune sanction trouvée avec l'ID ${sanctionId} pour ${target.tag}.\n\n💡 **Astuce:** Utilise l'ID DB visible dans \`/casier\` (ex: \`#1 (ID: 123)\` → utilise \`123\`).`
});
}
const sanction = sanctionRows[0];
if (sanction.action === 'Modification de sanction' ||
sanction.action === 'Révocation de sanction' ||
sanction.action === 'Modification de mute') {
return interaction.editReply({ content: `❌ Cette entrée est une modification, pas une sanction. Utilise l'ID d'une vraie sanction.` });
}
await db.query(
'UPDATE logs SET reason = CONCAT(reason, " [RÉVOQUÉE]") WHERE id = ?',
[sanction.id]
);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Révocation de sanction', `Sanction #${sanction.id} révoquée. Raison: ${reason}`, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setTitle(`${emojis.success} Sanction Révoquée`)
.setColor(colors.success)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '👑 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Raison de révocation', value: reason, inline: false },
{ name: '🆔 ID Sanction révoquée', value: `${sanction.id}`, inline: true },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setTitle(`${emojis.success} Révocation de Sanction`)
.setColor(colors.success)
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '👑 Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Raison de révocation', value: reason, inline: false },
{ name: '🆔 ID Sanction révoquée', value: `${sanction.id}`, inline: true },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id} | Serveur: ${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur revokesanction:', err);
await interaction.editReply({ content: `❌ Échec de la révocation de la sanction: ${err.message}` });
}
},
};

View File

@@ -1,19 +1,21 @@
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('unban')
.setDescription('Unban a user by their ID or tag.')
.setDescription('bannir un utilisateur par son ID ou son tag.')
.addStringOption(option =>
option.setName('user')
.setDescription('The ID or tag (Username#1234) of the user to unban')
.setDescription('L\'ID ou le tag (Username#1234) de l\'utilisateur à débannir')
.setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
await interaction.deferReply();
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const input = interaction.options.getString('user').trim();
@@ -22,44 +24,57 @@ module.exports = {
const bannedUser = bannedUsers.find(b =>
b.user.id === input || b.user.tag.toLowerCase() === input.toLowerCase()
);
if (!bannedUser) return interaction.editReply('❌ This user is not banned.');
// Récupération depuis MySQL
const [rows] = await db.query('SELECT * FROM bans WHERE userId = ?', [bannedUser.user.id]);
const banRecord = rows[0];
if (!bannedUser) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas banni.' });
const reason = `Unbanned by ${interaction.user.tag}`;
const reason = `banni par ${interaction.user.tag}`;
await interaction.guild.members.unban(bannedUser.user.id, reason);
if (banRecord) await db.query('DELETE FROM bans WHERE userId = ?', [bannedUser.user.id]);
await db.query('DELETE FROM bans WHERE userId = ? AND guildId = ?', [bannedUser.user.id, interaction.guild.id]);
await interaction.editReply(`✅ Successfully unbanned ${bannedUser.user.tag}`);
const embed = new EmbedBuilder()
.setAuthor({ name: bannedUser.user.displayName, iconURL: bannedUser.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.unban} Utilisateur Débanni`)
.setColor(colors.unban)
.setThumbnail(bannedUser.user.displayAvatarURL({ dynamic: true }))
.setDescription(`${bannedUser.user.toString()} a été débanni du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${bannedUser.user.toString()}\n\`${bannedUser.user.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${bannedUser.user.id}${interaction.guild.name}` })
.setTimestamp();
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
if (logChannel) {
const embed = new EmbedBuilder()
.setTitle('User Unbanned')
.setColor('Green')
.addFields(
{ name: 'Membre Banni', value: `<@${bannedUser.user.id}> (${bannedUser.user.tag})`, inline: true },
{ name: 'Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
{ name: 'Guild', value: interaction.guild.name, inline: true }
)
.setTimestamp();
logChannel.send({ embeds: [embed] });
}
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.unban} Débannissement`)
.setColor(colors.unban)
.setThumbnail(bannedUser.user.displayAvatarURL({ dynamic: true }))
.setDescription(`${bannedUser.user.toString()} a été débanni du serveur.`)
.addFields(
{ name: '👤 Utilisateur', value: `${bannedUser.user.toString()}\n\`${bannedUser.user.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${bannedUser.user.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
// Log dans la DB avec guildId
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[bannedUser.user.id, bannedUser.user.tag, interaction.user.id, interaction.user.tag, 'unban', reason, null, interaction.guild.id, Date.now()]
[bannedUser.user.id, bannedUser.user.tag, interaction.user.id, interaction.user.tag, 'bannissement', reason, null, interaction.guild.id, Date.now()]
);
} catch (err) {
console.error(err);
await interaction.editReply('❌ Failed to unban the user.');
console.error('Erreur unban:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
}
};
},
};

View File

@@ -0,0 +1,86 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('unmute')
.setDescription('Démuter un membre du serveur.')
.addUserOption(option =>
option.setName('user')
.setDescription('Le membre à démuter')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison du démute')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('user');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (!member.communicationDisabledUntilTimestamp || member.communicationDisabledUntilTimestamp < Date.now()) {
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas muté.' });
}
try {
await member.timeout(null, `Démuté par ${interaction.user.tag}: ${reason}`);
await db.query('DELETE FROM mutes WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Unmute', reason, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.unmute} Utilisateur Démuté`)
.setColor(colors.unmute)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été démuté.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.unmute} Unmute`)
.setColor(colors.unmute)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a été démuté.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur unmute:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
},
};

View File

@@ -0,0 +1,78 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('untimeout')
.setDescription('Retirer le timeout d\'un membre.')
.addUserOption(option =>
option.setName('target')
.setDescription('Le membre à dé-timeout')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison de la levée du timeout')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('target');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (!member.communicationDisabledUntilTimestamp || member.communicationDisabledUntilTimestamp < Date.now()) {
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas en timeout.' });
}
try {
await member.timeout(null, `Timeout retiré par ${interaction.user.tag}: ${reason}`);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Timeout retiré', reason, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setTitle(`${emojis.success} Timeout Retiré`)
.setColor(colors.success)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '✅ Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
const logEmbed = new EmbedBuilder()
.setTitle(`${emojis.success} Timeout Retiré`)
.setColor(colors.success)
.addFields(
{ name: '👤 Utilisateur', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: '✅ Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id} | Serveur: ${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur untimeout:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
},
};

101
commands/moderation/warn.js Normal file
View File

@@ -0,0 +1,101 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
const db = require('../../functions/database/db.js');
const { sendLog } = require('../../utils/helpers');
const { colors, emojis } = require('../../utils/constants');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('warn')
.setDescription('Avertir un membre.')
.addUserOption(option =>
option.setName('target')
.setDescription('Le membre à avertir')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Raison de l\'avertissement')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
async execute(interaction) {
const target = interaction.options.getUser('target');
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur le serveur.' });
if (member.id === interaction.user.id) return interaction.editReply({ content: '❌ Tu ne peux pas t\'avertir toi-même.' });
try {
const [warnRows] = await db.query(
'SELECT COUNT(*) as count FROM logs WHERE userId = ? AND guildId = ? AND action = ?',
[target.id, interaction.guild.id, 'Avertissement']
);
const warnCount = warnRows[0].count + 1;
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Avertissement', reason, null, interaction.guild.id, Date.now()]
);
const embed = new EmbedBuilder()
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.warn} Utilisateur Averti`)
.setColor(colors.warn)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a reçu un avertissement.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '🔢 Nombre d\'avertissements', value: `\`${warnCount}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await interaction.editReply({ embeds: [embed] });
try {
const dmEmbed = new EmbedBuilder()
.setTitle(`${emojis.warn} Tu as reçu un avertissement`)
.setColor(colors.warn)
.setDescription(`Tu as été averti sur **${interaction.guild.name}**`)
.addFields(
{ name: '📝 Raison', value: reason, inline: false },
{ name: '🔢 Nombre d\'avertissements', value: `${warnCount}`, inline: true }
)
.setFooter({ text: interaction.guild.name, iconURL: interaction.guild.iconURL({ dynamic: true }) })
.setTimestamp();
await target.send({ embeds: [dmEmbed] });
} catch { /* DMs fermés */ }
const logEmbed = new EmbedBuilder()
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setTitle(`${emojis.warn} Avertissement`)
.setColor(colors.warn)
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
.setDescription(`${target.toString()} a reçu un avertissement.`)
.addFields(
{ name: '👤 Utilisateur', value: `${target.toString()}\n\`${target.tag}\``, inline: true },
{ name: '👮 Modérateur', value: `${interaction.user.toString()}\n\`${interaction.user.tag}\``, inline: true },
{ name: '🔢 Nombre d\'avertissements', value: `\`${warnCount}\``, inline: true },
{ name: '📝 Raison', value: reason, inline: false },
{ name: '📅 Date', value: `<t:${Math.floor(Date.now() / 1000)}:F>`, inline: true }
)
.setFooter({ text: `ID: ${target.id}${interaction.guild.name}` })
.setTimestamp();
await sendLog(interaction.guild, { embeds: [logEmbed] });
} catch (err) {
console.error('Erreur warn:', err);
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
}
},
};