Update
This commit is contained in:
@@ -2,59 +2,53 @@ const os = require('os');
|
||||
const fs = require('fs');
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { formatUptime } = require('../../utils/helpers');
|
||||
const { colors } = require('../../utils/constants');
|
||||
|
||||
function getOS() {
|
||||
const platform = os.platform();
|
||||
const release = os.release();
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return `Windows ${release}`;
|
||||
case 'darwin':
|
||||
return `macOS ${release}`;
|
||||
case 'linux':
|
||||
try {
|
||||
const data = fs.readFileSync('/etc/os-release', 'utf8');
|
||||
const match = data.match(/PRETTY_NAME="(.+)"/);
|
||||
if (match) return `${match[1]} (kernel ${release})`;
|
||||
} catch {
|
||||
return `Linux ${release}`;
|
||||
}
|
||||
default:
|
||||
return `${platform} ${release}`;
|
||||
}
|
||||
const platform = os.platform();
|
||||
const release = os.release();
|
||||
switch (platform) {
|
||||
case 'win32': return `Windows ${release}`;
|
||||
case 'darwin': return `macOS ${release}`;
|
||||
case 'linux':
|
||||
try {
|
||||
const data = fs.readFileSync('/etc/os-release', 'utf8');
|
||||
const match = data.match(/PRETTY_NAME="(.+)"/);
|
||||
if (match) return `${match[1]} (kernel ${release})`;
|
||||
} catch { return `Linux ${release}`; }
|
||||
default: return `${platform} ${release}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
category: 'dev',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('infra')
|
||||
.setDescription('Voir l\'infrastructure du bot')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||
async execute(interaction) {
|
||||
const cpuInfo = os.cpus()[0].model;
|
||||
const cpuCores = os.cpus().length;
|
||||
const totalRAM = (os.totalmem() / (1024 ** 3)).toFixed(2);
|
||||
const freeRAM = (os.freemem() / (1024 ** 3)).toFixed(2);
|
||||
const usedRAM = (totalRAM - freeRAM).toFixed(2);
|
||||
const ramUsagePercent = ((usedRAM / totalRAM) * 100).toFixed(1);
|
||||
const uptime = os.uptime();
|
||||
category: 'dev',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('infra')
|
||||
.setDescription('Voir l\'infrastructure du bot')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||
async execute(interaction) {
|
||||
const cpuInfo = os.cpus()[0].model;
|
||||
const cpuCores = os.cpus().length;
|
||||
const totalRAM = (os.totalmem() / (1024 ** 3)).toFixed(2);
|
||||
const freeRAM = (os.freemem() / (1024 ** 3)).toFixed(2);
|
||||
const usedRAM = (totalRAM - freeRAM).toFixed(2);
|
||||
const ramUsagePercent = ((usedRAM / totalRAM) * 100).toFixed(1);
|
||||
const uptime = os.uptime();
|
||||
|
||||
const { colors } = require('../../utils/constants');
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('🖥️ Infrastructure du Bot')
|
||||
.setColor(colors.info)
|
||||
.addFields(
|
||||
{ name: '💻 CPU', value: `${cpuInfo}\n${cpuCores} cœurs`, inline: true },
|
||||
{ name: '💾 RAM', value: `${usedRAM} Go / ${totalRAM} Go\n${ramUsagePercent}% utilisée`, inline: true },
|
||||
{ name: '🖥️ OS', value: getOS(), inline: true },
|
||||
{ name: '⏱️ Uptime Système', value: formatUptime(uptime), inline: true },
|
||||
{ name: '📊 Node.js', value: process.version, inline: true },
|
||||
{ name: '📦 Architecture', value: os.arch(), inline: true }
|
||||
)
|
||||
.setFooter({ text: interaction.client.user.username, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('🖥️ Infrastructure du Bot')
|
||||
.setColor(colors.info)
|
||||
.addFields(
|
||||
{ name: '💻 CPU', value: `${cpuInfo}\n${cpuCores} cœurs`, inline: true },
|
||||
{ name: '💾 RAM', value: `${usedRAM} Go / ${totalRAM} Go\n${ramUsagePercent}% utilisée`, inline: true },
|
||||
{ name: '🖥️ OS', value: getOS(), inline: true },
|
||||
{ name: '⏱️ Uptime Système', value: formatUptime(uptime), inline: true },
|
||||
{ name: '📊 Node.js', value: process.version, inline: true },
|
||||
{ name: '📦 Architecture', value: os.arch(), inline: true }
|
||||
)
|
||||
.setFooter({ text: interaction.client.user.username, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,36 +1,40 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
category: 'dev',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('reload')
|
||||
.setDescription('Recharge une commande.')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addStringOption(option =>
|
||||
option.setName('command')
|
||||
.setDescription('La commande à recharger.')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.reply({ content: '❌ Seuls les administrateurs peuvent utiliser cette commande.', ephemeral: true });
|
||||
}
|
||||
category: 'dev',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('reload')
|
||||
.setDescription('Recharge une commande.')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addStringOption(option =>
|
||||
option.setName('command')
|
||||
.setDescription('La commande à recharger.')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.reply({ content: '❌ Seuls les administrateurs peuvent utiliser cette commande.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const commandName = interaction.options.getString('command', true).toLowerCase();
|
||||
const command = interaction.client.commands.get(commandName);
|
||||
const commandName = interaction.options.getString('command', true).toLowerCase();
|
||||
const command = interaction.client.commands.get(commandName);
|
||||
|
||||
if (!command) {
|
||||
return interaction.reply({ content: `❌ Aucune commande nommée \`${commandName}\` n'a été trouvée !`, ephemeral: true });
|
||||
}
|
||||
if (!command) {
|
||||
return interaction.reply({ content: `❌ Aucune commande nommée \`${commandName}\` n'a été trouvée !`, flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
delete require.cache[require.resolve(`../${command.category}/${command.data.name}.js`)];
|
||||
try {
|
||||
// Supprimer du cache
|
||||
const commandPath = `../${command.category}/${command.data.name}.js`;
|
||||
delete require.cache[require.resolve(commandPath)];
|
||||
|
||||
try {
|
||||
const newCommand = require(`../${command.category}/${command.data.name}.js`);
|
||||
interaction.client.commands.set(newCommand.data.name, newCommand);
|
||||
await interaction.reply({ content: `✅ Commande \`${newCommand.data.name}\` rechargée avec succès !`, ephemeral: true });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({ content: `❌ Erreur lors du rechargement de \`${command.data.name}\`:\n\`${error.message}\``, ephemeral: true });
|
||||
}
|
||||
},
|
||||
};
|
||||
// Recharger
|
||||
const newCommand = require(commandPath);
|
||||
interaction.client.commands.set(newCommand.data.name, newCommand);
|
||||
|
||||
await interaction.reply({ content: `✅ Commande \`${newCommand.data.name}\` rechargée avec succès !`, flags: MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({ content: `❌ Erreur lors du rechargement de \`${command.data.name}\`:\n\`${error.message}\``, flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,133 +1,119 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('adminprofil')
|
||||
.setDescription('Administrer les profils utilisateurs (Admin uniquement)')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('reset')
|
||||
.setDescription('Réinitialiser le profil d\'un utilisateur')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux réinitialiser le profil')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('type')
|
||||
.setDescription('Ce qui doit être réinitialisé')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Signature', value: 'signature' },
|
||||
{ name: 'Tout le profil', value: 'all' }
|
||||
))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison de la réinitialisation')
|
||||
.setRequired(false))),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
// Vérifier les permissions (Admin uniquement)
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas la permission d\'utiliser cette commande. (Administrateur requis)'
|
||||
});
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
if (subcommand === 'reset') {
|
||||
const target = interaction.options.getUser('user');
|
||||
const resetType = interaction.options.getString('type');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun utilisateur spécifié !'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Récupérer le profil actuel
|
||||
const [profiles] = await db.query(
|
||||
'SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (profiles.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: `❌ ${target.tag} n'a pas de profil à réinitialiser.`
|
||||
});
|
||||
}
|
||||
|
||||
const profile = profiles[0];
|
||||
|
||||
if (resetType === 'signature') {
|
||||
// Réinitialiser uniquement la signature
|
||||
await db.query(
|
||||
'UPDATE user_profiles SET signature = NULL, updatedAt = ? WHERE userId = ? AND guildId = ?',
|
||||
[Date.now(), target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Signature Réinitialisée')
|
||||
.setColor(colors.success)
|
||||
.setDescription(`La signature de ${target.toString()} a été réinitialisée.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (resetType === 'all') {
|
||||
// Réinitialiser tout le profil
|
||||
await db.query(
|
||||
'DELETE FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Profil Réinitialisé')
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le profil de ${target.toString()} a été complètement réinitialisé.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
// Logger l'action
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setTitle('🔄 Profil Réinitialisé (Admin)')
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le profil de ${target.toString()} a été réinitialisé.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag} (${target.id})`, inline: true },
|
||||
{ name: '⚙️ Admin', value: `${interaction.user.tag} (${interaction.user.id})`, inline: true },
|
||||
{ name: '🔄 Type', value: resetType === 'signature' ? 'Signature' : 'Tout le profil', inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'France Femboy Bot • Administration' })
|
||||
.setTimestamp();
|
||||
|
||||
await sendLog(interaction.guild, logEmbed);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la réinitialisation du profil:', err);
|
||||
await interaction.editReply({
|
||||
content: `❌ Erreur lors de la réinitialisation du profil: ${err.message}`
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('adminprofil')
|
||||
.setDescription('Administrer les profils utilisateurs (Admin uniquement)')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('reset')
|
||||
.setDescription('Réinitialiser le profil d\'un utilisateur')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux réinitialiser le profil')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('type')
|
||||
.setDescription('Ce qui doit être réinitialisé')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Signature', value: 'signature' },
|
||||
{ name: 'Tout le profil', value: 'all' }
|
||||
))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison de la réinitialisation')
|
||||
.setRequired(false))),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas la permission d\'utiliser cette commande. (Administrateur requis)'
|
||||
});
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
if (subcommand === 'reset') {
|
||||
const target = interaction.options.getUser('user');
|
||||
const resetType = interaction.options.getString('type');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
|
||||
try {
|
||||
const [profiles] = await db.query(
|
||||
'SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (profiles.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: `❌ ${target.tag} n'a pas de profil à réinitialiser.`
|
||||
});
|
||||
}
|
||||
|
||||
if (resetType === 'signature') {
|
||||
await db.query(
|
||||
'UPDATE user_profiles SET signature = NULL, updatedAt = ? WHERE userId = ? AND guildId = ?',
|
||||
[Date.now(), target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Signature Réinitialisée')
|
||||
.setColor(colors.success)
|
||||
.setDescription(`La signature de ${target.toString()} a été réinitialisée.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (resetType === 'all') {
|
||||
await db.query(
|
||||
'DELETE FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Profil Réinitialisé')
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le profil de ${target.toString()} a été complètement réinitialisé.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setTitle('🔄 Profil Réinitialisé (Admin)')
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le profil de ${target.toString()} a été réinitialisé.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${target.tag} (${target.id})`, inline: true },
|
||||
{ name: '⚙️ Admin', value: `${interaction.user.tag} (${interaction.user.id})`, inline: true },
|
||||
{ name: '🔄 Type', value: resetType === 'signature' ? 'Signature' : 'Tout le profil', inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Femboy Croissant Bot • Administration' })
|
||||
.setTimestamp();
|
||||
|
||||
await sendLog(interaction.guild, { embeds: [logEmbed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur adminprofil:', err);
|
||||
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,45 +1,43 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('embed')
|
||||
.setDescription('Créer un embed personnalisé.')
|
||||
.addStringOption(option =>
|
||||
option.setName('title')
|
||||
.setDescription('Titre de l\'embed')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('description')
|
||||
.setDescription('Description de l\'embed')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('color')
|
||||
.setDescription('Couleur de l\'embed (hex, ex: #FF0000)')
|
||||
.setRequired(false))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
|
||||
async execute(interaction) {
|
||||
const title = interaction.options.getString('title');
|
||||
const description = interaction.options.getString('description');
|
||||
const colorInput = interaction.options.getString('color');
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('embed')
|
||||
.setDescription('Créer un embed personnalisé.')
|
||||
.addStringOption(option =>
|
||||
option.setName('title')
|
||||
.setDescription('Titre de l\'embed')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('description')
|
||||
.setDescription('Description de l\'embed')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('color')
|
||||
.setDescription('Couleur de l\'embed (hex, ex: #FF0000)')
|
||||
.setRequired(false))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
|
||||
async execute(interaction) {
|
||||
const title = interaction.options.getString('title');
|
||||
const description = interaction.options.getString('description');
|
||||
const colorInput = interaction.options.getString('color');
|
||||
|
||||
let color = 0x5865F2; // Couleur par défaut (bleu Discord)
|
||||
if (colorInput) {
|
||||
// Parser la couleur hex
|
||||
const hexMatch = colorInput.match(/^#?([0-9A-Fa-f]{6})$/);
|
||||
if (hexMatch) {
|
||||
color = parseInt(hexMatch[1], 16);
|
||||
}
|
||||
}
|
||||
let color = 0x5865F2;
|
||||
if (colorInput) {
|
||||
const hexMatch = colorInput.match(/^#?([0-9A-Fa-f]{6})$/);
|
||||
if (hexMatch) {
|
||||
color = parseInt(hexMatch[1], 16);
|
||||
}
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setColor(color)
|
||||
.setFooter({ text: `Créé par ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setColor(color)
|
||||
.setFooter({ text: `Créé par ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,65 +1,62 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { formatUptime } = require('../../utils/helpers');
|
||||
const { colors } = require('../../utils/constants');
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('info')
|
||||
.setDescription('Obtenir les informations du bot'),
|
||||
async execute(interaction) {
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
const uptime = formatUptime(process.uptime());
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${interaction.client.user.username}`,
|
||||
iconURL: interaction.client.user.displayAvatarURL()
|
||||
})
|
||||
.setTitle('📝 Informations du Bot')
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(interaction.client.user.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.addFields(
|
||||
{
|
||||
name: '👩💻 Développeuse Principale',
|
||||
value: '<@361526553940721684>',
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🧠 Langages et environnement',
|
||||
value: [
|
||||
'• **Langage :** <:JavaScript:1425179797692092506> JavaScript (ECMAScript 2024)',
|
||||
'• **Backend :** <:NodeJS:1425179878252089435> Node.js v22.21.0',
|
||||
'• **Librairies :** <:DiscordJS:1425179852536938670> Discord.js v14.22.1',
|
||||
'• **IDE :** <:WebStorm:1429190717066055841> JetBrains WebStorm 2025.2.3',
|
||||
].join('\n'),
|
||||
inline: false
|
||||
},
|
||||
{
|
||||
name: '💡 Date de création',
|
||||
value: '20 Août 2025',
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '⏱️ Uptime',
|
||||
value: uptime,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '📊 Statistiques',
|
||||
value: [
|
||||
`• **Serveurs :** ${interaction.client.guilds.cache.size}`,
|
||||
`• **Utilisateurs :** ${interaction.client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)}`,
|
||||
`• **Commandes :** ${interaction.client.commands.size}`,
|
||||
].join('\n'),
|
||||
inline: false
|
||||
}
|
||||
)
|
||||
.setFooter({
|
||||
text: `${interaction.guild.name} • Made with love by Syxpi 💞`,
|
||||
iconURL: interaction.client.user.displayAvatarURL()
|
||||
})
|
||||
.setTimestamp();
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('info')
|
||||
.setDescription('Obtenir les informations du bot'),
|
||||
async execute(interaction) {
|
||||
const uptime = formatUptime(process.uptime());
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `Système d'Information • ${interaction.client.user.username}`,
|
||||
iconURL: interaction.client.user.displayAvatarURL()
|
||||
})
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(interaction.client.user.displayAvatarURL({dynamic: true, size: 256}))
|
||||
.setDescription('Voici les détails techniques et statistiques concernant mon fonctionnement actuel.')
|
||||
.addFields(
|
||||
{
|
||||
name: '👑 Propriétaire',
|
||||
value: `> <@361526553940721684> (Lumi)`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '⌛ En ligne depuis',
|
||||
value: `> \`${uptime}\``,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🛠️ Stack Technique',
|
||||
value: [
|
||||
'```yml',
|
||||
'Langage: JavaScript (ES2024)',
|
||||
'Runtime: Node.js v25.2.1',
|
||||
'Library: Discord.js v14.22.1',
|
||||
'IDE: JetBrains WebStorm',
|
||||
'```'
|
||||
].join('\n'),
|
||||
inline: false
|
||||
},
|
||||
{
|
||||
name: '📈 Statistiques Globales',
|
||||
value: [
|
||||
`• **Guildes:** \`${interaction.client.guilds.cache.size}\``,
|
||||
`• **Membres:** \`${interaction.client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)}\``,
|
||||
`• **Commandes:** \`${interaction.client.commands.size}\``,
|
||||
].join('\n'),
|
||||
inline: true
|
||||
}
|
||||
)
|
||||
.setFooter({
|
||||
text: `Femboy Croissant Bot • Développé avec passion par Lumi 💞`,
|
||||
iconURL: interaction.client.user.displayAvatarURL()
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({embeds: [embed]});
|
||||
},
|
||||
};
|
||||
@@ -1,579 +1,334 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { getUserXP } = require('../../functions/xp/xp.js');
|
||||
const { getUserXP, getUserRank } = require('../../functions/xp/xp.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
|
||||
// Mapping des emojis personnalisés pour les badges Discord
|
||||
// Format: <:nom_emoji:ID>
|
||||
const BADGE_EMOJIS = {
|
||||
// Badges généraux (utiliser des emojis Unicode par défaut si pas d'emoji personnalisé fourni)
|
||||
'staff': '👨💼', // Discord Staff (pas d'emoji fourni dans la liste)
|
||||
'partner': '<:discord_partener:1437046788916904097>', // Partner
|
||||
'hypesquad': '<:hypesquad_balance:1437046792922464337>', // HypeSquad (générique - utiliser Balance)
|
||||
'hypesquad_bravery': '<:hypesquad_bravery:1437046793899872467>', // HypeSquad Bravery
|
||||
'hypesquad_brilliance': '<:hypesquad_brilliance:1437046794868756550>', // HypeSquad Brilliance
|
||||
'hypesquad_balance': '<:hypesquad_balance:1437046792922464337>', // HypeSquad Balance
|
||||
'hypesquad_events': '<:hypesquad_events:1437046795778920448>', // HypeSquad Events
|
||||
'early_supporter': '<:early_supporter:1437046790087250071>', // Early Supporter
|
||||
'bug_hunter_level_1': '🐛', // Bug Hunter (pas d'emoji fourni)
|
||||
'bug_hunter_level_2': '🐛', // Bug Hunter Gold (pas d'emoji fourni)
|
||||
'verified_developer': '✅', // Verified Bot Developer (pas d'emoji fourni)
|
||||
'active_developer': '<:active_developer:1437049369936531587>', // Active Developer
|
||||
'certified_moderator': '🛡️', // Certified Moderator (pas d'emoji fourni)
|
||||
'bot_http_interactions': '🤖', // Bot HTTP Interactions (pas d'emoji fourni)
|
||||
|
||||
// Badges Quests
|
||||
'quests': '<:quests:1437046809464934492>', // Quests
|
||||
'slash_commands': '<:slash_commands:1437046810458984553>', // Slash Commands
|
||||
|
||||
// Badges Orbs
|
||||
'orbs': '<:orbs:1437046808185540628>', // Orbs
|
||||
|
||||
// Badges Nitro (par durée)
|
||||
'nitro': '<:nitro:1437046796810715136>', // Nitro (base)
|
||||
'nitro_1_month': '<:nitro_1_month:1437046798685442241>', // Nitro 1 mois
|
||||
'nitro_3_months': '<:nitro_3_months:1437046801550278757>', // Nitro 3 mois
|
||||
'nitro_6_months': '<:nitro_6_months:1437046805102723143>', // Nitro 6 mois
|
||||
'nitro_1_year': '<:nitro_1_year:1437046799515783198>', // Nitro 1 an
|
||||
'nitro_2_years': '<:nitro_2_years:1437046800522416138>', // Nitro 2 ans
|
||||
'nitro_3_years': '<:nitro_3_years:1437046802519035924>', // Nitro 3 ans (Émeraude)
|
||||
'nitro_5_years': '<:nitro_5_years:1437046803747967016>', // Nitro 5 ans
|
||||
'nitro_6_years': '<:nitro_6_years:1437046806747021514>', // Nitro 6 ans
|
||||
|
||||
// Badges Boost (par durée)
|
||||
'boost': '<:boost:1437046778083016745>', // Boost (base)
|
||||
'boost_2_months': '<:boost_2_months:1437046780322779266>', // Boost 2 mois
|
||||
'boost_3_months': '<:boost_3_months:1437046781375414383>', // Boost 3 mois
|
||||
'boost_6_months': '<:boost_6_months:1437046782247829616>', // Boost 6 mois
|
||||
'boost_9_months': '<:boost_9_months:1437046783070044200>', // Boost 9 mois
|
||||
'boost_12_months': '<:boost_12_months:1437046784445644930>', // Boost 12 mois
|
||||
'boost_15_months': '<:boost_15_months:1437046785402081281>', // Boost 15 mois
|
||||
'boost_18_months': '<:boost_18_months:1437046786643722342>', // Boost 18 mois
|
||||
'boost_24_months': '<:boost_24_months:1437046787667136612>', // Boost 24 mois
|
||||
'staff': '👨💼',
|
||||
'partner': '<:discord_partener:1437046788916904097>',
|
||||
'hypesquad': '<:hypesquad_balance:1437046792922464337>',
|
||||
'hypesquad_bravery': '<:hypesquad_bravery:1437046793899872467>',
|
||||
'hypesquad_brilliance': '<:hypesquad_brilliance:1437046794868756550>',
|
||||
'hypesquad_balance': '<:hypesquad_balance:1437046792922464337>',
|
||||
'hypesquad_events': '<:hypesquad_events:1437046795778920448>',
|
||||
'early_supporter': '<:early_supporter:1437046790087250071>',
|
||||
'bug_hunter_level_1': '🐛',
|
||||
'bug_hunter_level_2': '🐛',
|
||||
'verified_developer': '✅',
|
||||
'active_developer': '<:active_developer:1437049369936531587>',
|
||||
'certified_moderator': '🛡️',
|
||||
'bot_http_interactions': '🤖',
|
||||
'quests': '<:quests:1437046809464934492>',
|
||||
'slash_commands': '<:slash_commands:1437046810458984553>',
|
||||
'orbs': '<:orbs:1437046808185540628>',
|
||||
'nitro': '<:nitro:1437046796810715136>',
|
||||
'nitro_1_month': '<:nitro_1_month:1437046798685442241>',
|
||||
'nitro_3_months': '<:nitro_3_months:1437046801550278757>',
|
||||
'nitro_6_months': '<:nitro_6_months:1437046805102723143>',
|
||||
'nitro_1_year': '<:nitro_1_year:1437046799515783198>',
|
||||
'nitro_2_years': '<:nitro_2_years:1437046800522416138>',
|
||||
'nitro_3_years': '<:nitro_3_years:1437046802519035924>',
|
||||
'nitro_5_years': '<:nitro_5_years:1437046803747967016>',
|
||||
'nitro_6_years': '<:nitro_6_years:1437046806747021514>',
|
||||
'boost': '<:boost:1437046778083016745>',
|
||||
'boost_2_months': '<:boost_2_months:1437046780322779266>',
|
||||
'boost_3_months': '<:boost_3_months:1437046781375414383>',
|
||||
'boost_6_months': '<:boost_6_months:1437046782247829616>',
|
||||
'boost_9_months': '<:boost_9_months:1437046783070044200>',
|
||||
'boost_12_months': '<:boost_12_months:1437046784445644930>',
|
||||
'boost_15_months': '<:boost_15_months:1437046785402081281>',
|
||||
'boost_18_months': '<:boost_18_months:1437046786643722342>',
|
||||
'boost_24_months': '<:boost_24_months:1437046787667136612>',
|
||||
};
|
||||
|
||||
// Fonction pour obtenir les badges Discord d'un utilisateur (emojis personnalisés)
|
||||
function getDiscordBadges(user, member) {
|
||||
const badgeEmojis = [];
|
||||
|
||||
// Badges de compte - utiliser toArray() qui retourne les noms des flags
|
||||
if (user.flags) {
|
||||
try {
|
||||
const flagsArray = user.flags.toArray();
|
||||
|
||||
// Mapping des flags Discord.js vers les noms de badges
|
||||
const flagToBadgeMap = {
|
||||
'Staff': 'staff',
|
||||
'Partner': 'partner',
|
||||
'HypeSquad': 'hypesquad',
|
||||
'HypeSquadOnlineHouse1': 'hypesquad_bravery',
|
||||
'HypeSquadOnlineHouse2': 'hypesquad_brilliance',
|
||||
'HypeSquadOnlineHouse3': 'hypesquad_balance',
|
||||
'HypeSquadEvents': 'hypesquad_events',
|
||||
'BugHunterLevel1': 'bug_hunter_level_1',
|
||||
'BugHunterLevel2': 'bug_hunter_level_2',
|
||||
'PremiumEarlySupporter': 'early_supporter',
|
||||
'VerifiedDeveloper': 'verified_developer',
|
||||
'ActiveDeveloper': 'active_developer',
|
||||
'CertifiedModerator': 'certified_moderator',
|
||||
'BotHTTPInteractions': 'bot_http_interactions',
|
||||
'Quests': 'quests',
|
||||
'QuestsEarly': 'quests',
|
||||
'SlashCommands': 'slash_commands',
|
||||
};
|
||||
|
||||
// Note: Les logs de debug sont commentés pour réduire le bruit dans les logs
|
||||
// Décommenter si besoin de debugger les flags
|
||||
|
||||
// Vérifier chaque flag présent et ajouter l'emoji
|
||||
for (const flag of flagsArray) {
|
||||
const flagLower = flag.toLowerCase();
|
||||
const badgeKey = flagToBadgeMap[flag];
|
||||
|
||||
// Si le flag est dans le mapping direct, l'utiliser
|
||||
if (badgeKey && BADGE_EMOJIS[badgeKey]) {
|
||||
badgeEmojis.push(BADGE_EMOJIS[badgeKey]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recherche flexible pour les badges avec durées
|
||||
// Badges Nitro avec durées (les noms peuvent varier dans Discord.js)
|
||||
if (flagLower.includes('nitro')) {
|
||||
// Essayer de détecter la durée exacte
|
||||
if (flagLower.match(/6.*year|year.*6|emerald/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_6_years);
|
||||
} else if (flagLower.match(/5.*year|year.*5/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_5_years);
|
||||
} else if (flagLower.match(/3.*year|year.*3/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_3_years);
|
||||
} else if (flagLower.match(/2.*year|year.*2/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_2_years);
|
||||
} else if (flagLower.match(/1.*year|year.*1/) && !flagLower.includes('month')) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_1_year);
|
||||
} else if (flagLower.match(/6.*month|month.*6/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_6_months);
|
||||
} else if (flagLower.match(/3.*month|month.*3/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_3_months);
|
||||
} else if (flagLower.match(/1.*month|month.*1/)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro_1_month);
|
||||
} else {
|
||||
// Badge Nitro de base (sans durée spécifique)
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro);
|
||||
}
|
||||
}
|
||||
// Badges Boost avec durées (les badges Boost personnels, pas le boost serveur)
|
||||
// Les badges Boost sont basés sur la durée totale de boost sur TOUS les serveurs
|
||||
else if (flagLower.includes('boost') && !flagLower.includes('server')) {
|
||||
// Détecter la durée exacte en vérifiant les nombres dans le flag
|
||||
// Les flags peuvent être: PremiumGuildSubscription, PremiumGuildSubscriptionTier1, etc.
|
||||
// ou des flags spécifiques comme "PremiumGuildSubscription24Months"
|
||||
|
||||
// Vérifier d'abord les durées les plus longues (pour éviter les fausses détections)
|
||||
if (flagLower.match(/24|two.*four|twenty.*four/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_24_months);
|
||||
} else if (flagLower.match(/18|eighteen/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_18_months);
|
||||
} else if (flagLower.match(/15|fifteen/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_15_months);
|
||||
} else if (flagLower.match(/12|twelve|one.*year|1.*year/) &&
|
||||
!flagLower.match(/18|15|24|128|112/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_12_months);
|
||||
} else if (flagLower.match(/9|nine/) && !flagLower.match(/19|29|90|99/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_9_months);
|
||||
} else if (flagLower.match(/6|six/) &&
|
||||
!flagLower.match(/16|26|60|66|68|69/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_6_months);
|
||||
} else if (flagLower.match(/3|three/) &&
|
||||
!flagLower.match(/13|23|30|33|34|35|36|37|38|39/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_3_months);
|
||||
} else if (flagLower.match(/2|two/) &&
|
||||
!flagLower.match(/12|20|21|22|23|24|25|26|27|28|29/i)) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost_2_months);
|
||||
} else {
|
||||
// Badge Boost de base (sans durée spécifique)
|
||||
badgeEmojis.push(BADGE_EMOJIS.boost);
|
||||
}
|
||||
}
|
||||
// Autres badges
|
||||
else if (flagLower.includes('orb')) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.orbs);
|
||||
}
|
||||
else if (flagLower.includes('quest')) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.quests);
|
||||
}
|
||||
else if (flagLower.includes('slash')) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.slash_commands);
|
||||
}
|
||||
// Log pour debugger les flags non mappés (pour les ajouter plus tard)
|
||||
else if (!flagToBadgeMap[flag]) {
|
||||
console.log(`[PROFIL] Flag non mappé détecté: "${flag}" (pour l'utilisateur ${user.id})`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Si toArray() échoue, ignorer silencieusement
|
||||
console.warn('Erreur lors de la récupération des flags:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Badge Nitro de base (si l'utilisateur a Nitro mais pas les badges spéciaux dans les flags)
|
||||
// Vérifier si un badge Nitro n'a pas déjà été ajouté via les flags
|
||||
const hasNitroBadge = badgeEmojis.some(e =>
|
||||
typeof e === 'string' && (
|
||||
e.includes('nitro') ||
|
||||
e === BADGE_EMOJIS.nitro ||
|
||||
e === BADGE_EMOJIS.nitro_1_month ||
|
||||
e === BADGE_EMOJIS.nitro_3_months ||
|
||||
e === BADGE_EMOJIS.nitro_6_months ||
|
||||
e === BADGE_EMOJIS.nitro_1_year ||
|
||||
e === BADGE_EMOJIS.nitro_2_years ||
|
||||
e === BADGE_EMOJIS.nitro_3_years ||
|
||||
e === BADGE_EMOJIS.nitro_5_years ||
|
||||
e === BADGE_EMOJIS.nitro_6_years
|
||||
)
|
||||
);
|
||||
|
||||
// IMPORTANT: Les badges Nitro avec durée (Émeraude 3 ans, Boost 18 mois, etc.) ne sont PAS
|
||||
// accessibles via l'API Discord.js dans les flags utilisateur standard.
|
||||
// Ces badges sont visibles côté client Discord mais ne sont pas exposés via l'API du bot.
|
||||
//
|
||||
// LIMITATION DE L'API DISCORD.JS :
|
||||
// - premiumType : Méthode la plus fiable, mais souvent undefined même pour les utilisateurs Nitro
|
||||
// - banner : Nécessite généralement Nitro, mais peut être présent sans Nitro dans certains cas
|
||||
// - accentColor : Nécessite généralement Nitro, mais peut être présent sans Nitro dans certains cas
|
||||
// - avatar animé : Nécessite généralement Nitro, mais peut être présent sans Nitro dans certains cas
|
||||
//
|
||||
// SOLUTION : Détection multi-niveaux pour équilibrer précision et détection
|
||||
// Niveau 1 (preuve définitive) : premiumType défini ET != 0 → Nitro confirmé
|
||||
// - premiumType 1 = NitroClassic
|
||||
// - premiumType 2 = Nitro
|
||||
// - premiumType 3 = NitroBasic
|
||||
// - premiumType 0 = None (pas de Nitro)
|
||||
// Niveau 2 (très probable) : banner présent → très probablement Nitro
|
||||
// Niveau 3 (probable) : avatar animé ET accentColor → probablement Nitro (mais moins fiable)
|
||||
//
|
||||
// NOTE IMPORTANTE : accentColor seul n'est PAS utilisé car il peut être présent sans Nitro
|
||||
// Mais accentColor + avatar animé ensemble = probablement Nitro (moins fiable que banner)
|
||||
//
|
||||
// Référence: https://discord-api-types.dev/api/discord-api-types-v10/enum/UserPremiumType
|
||||
|
||||
// Vérifier les indicateurs
|
||||
// premiumType peut être 0 (None), 1 (NitroClassic), 2 (Nitro), ou 3 (NitroBasic)
|
||||
// On vérifie que premiumType est défini ET différent de 0
|
||||
const hasPremiumType = user.premiumType !== null &&
|
||||
user.premiumType !== undefined &&
|
||||
user.premiumType !== 0;
|
||||
const hasBanner = user.banner !== null && user.banner !== undefined;
|
||||
const hasAccentColor = user.accentColor !== null && user.accentColor !== undefined;
|
||||
const hasAnimatedAvatar = user.avatar && user.avatar.startsWith('a_');
|
||||
|
||||
// Détecter Nitro avec priorité (du plus fiable au moins fiable) :
|
||||
// 1. premiumType défini ET != 0 (preuve définitive) - score 100%
|
||||
// 2. banner présent (très fiable ~99%) - score 99%
|
||||
// 3. avatar animé ET accentColor (probable ~80%) - score 80% (moins fiable mais acceptable)
|
||||
const hasNitro = hasPremiumType ||
|
||||
hasBanner ||
|
||||
(hasAnimatedAvatar && hasAccentColor);
|
||||
|
||||
// Ajouter le badge Nitro de base si on détecte Nitro et qu'il n'y a pas déjà un badge Nitro spécifique
|
||||
if (!hasNitroBadge && hasNitro) {
|
||||
badgeEmojis.push(BADGE_EMOJIS.nitro);
|
||||
}
|
||||
|
||||
// Badge Booster (si le membre boost le serveur)
|
||||
// Note: Les badges Boost avec durée sont basés sur la durée TOTALE de boost sur TOUS les serveurs
|
||||
// et sont détectés via les flags utilisateur. Si aucun badge Boost n'est détecté dans les flags
|
||||
// mais que l'utilisateur boost le serveur actuel, on peut calculer la durée depuis premiumSince
|
||||
// pour afficher le badge approprié (mais ce n'est que pour le serveur actuel, pas la durée totale)
|
||||
if (member && member.premiumSince) {
|
||||
const hasBoostBadge = badgeEmojis.some(e =>
|
||||
typeof e === 'string' && (
|
||||
e.includes('boost') ||
|
||||
e === BADGE_EMOJIS.boost ||
|
||||
e === BADGE_EMOJIS.boost_2_months ||
|
||||
e === BADGE_EMOJIS.boost_3_months ||
|
||||
e === BADGE_EMOJIS.boost_6_months ||
|
||||
e === BADGE_EMOJIS.boost_9_months ||
|
||||
e === BADGE_EMOJIS.boost_12_months ||
|
||||
e === BADGE_EMOJIS.boost_15_months ||
|
||||
e === BADGE_EMOJIS.boost_18_months ||
|
||||
e === BADGE_EMOJIS.boost_24_months
|
||||
)
|
||||
);
|
||||
|
||||
if (!hasBoostBadge) {
|
||||
// Si le membre boost le serveur mais n'a pas de badge Boost dans les flags,
|
||||
// on calcule la durée depuis premiumSince pour afficher le badge approprié
|
||||
// NOTE: Ce n'est que pour le serveur actuel, pas la durée totale sur tous les serveurs
|
||||
const boostDurationMs = Date.now() - member.premiumSince.getTime();
|
||||
const boostDurationMonths = Math.floor(boostDurationMs / (1000 * 60 * 60 * 24 * 30));
|
||||
|
||||
// Déterminer le badge Boost en fonction de la durée (sur ce serveur)
|
||||
let boostBadge = BADGE_EMOJIS.boost; // Badge de base par défaut
|
||||
|
||||
if (boostDurationMonths >= 24) {
|
||||
boostBadge = BADGE_EMOJIS.boost_24_months;
|
||||
} else if (boostDurationMonths >= 18) {
|
||||
boostBadge = BADGE_EMOJIS.boost_18_months;
|
||||
} else if (boostDurationMonths >= 15) {
|
||||
boostBadge = BADGE_EMOJIS.boost_15_months;
|
||||
} else if (boostDurationMonths >= 12) {
|
||||
boostBadge = BADGE_EMOJIS.boost_12_months;
|
||||
} else if (boostDurationMonths >= 9) {
|
||||
boostBadge = BADGE_EMOJIS.boost_9_months;
|
||||
} else if (boostDurationMonths >= 6) {
|
||||
boostBadge = BADGE_EMOJIS.boost_6_months;
|
||||
} else if (boostDurationMonths >= 3) {
|
||||
boostBadge = BADGE_EMOJIS.boost_3_months;
|
||||
} else if (boostDurationMonths >= 2) {
|
||||
boostBadge = BADGE_EMOJIS.boost_2_months;
|
||||
}
|
||||
|
||||
badgeEmojis.push(boostBadge);
|
||||
}
|
||||
}
|
||||
|
||||
// Dédupliquer les emojis avant de retourner
|
||||
return [...new Set(badgeEmojis)];
|
||||
const badgeEmojis = [];
|
||||
|
||||
if (user.flags) {
|
||||
try {
|
||||
const flagsArray = user.flags.toArray();
|
||||
const flagToBadgeMap = {
|
||||
'Staff': 'staff', 'Partner': 'partner', 'HypeSquad': 'hypesquad',
|
||||
'HypeSquadOnlineHouse1': 'hypesquad_bravery', 'HypeSquadOnlineHouse2': 'hypesquad_brilliance', 'HypeSquadOnlineHouse3': 'hypesquad_balance',
|
||||
'HypeSquadEvents': 'hypesquad_events', 'BugHunterLevel1': 'bug_hunter_level_1', 'BugHunterLevel2': 'bug_hunter_level_2',
|
||||
'PremiumEarlySupporter': 'early_supporter', 'VerifiedDeveloper': 'verified_developer', 'ActiveDeveloper': 'active_developer',
|
||||
'CertifiedModerator': 'certified_moderator', 'BotHTTPInteractions': 'bot_http_interactions',
|
||||
'Quests': 'quests', 'QuestsEarly': 'quests', 'SlashCommands': 'slash_commands',
|
||||
};
|
||||
|
||||
for (const flag of flagsArray) {
|
||||
const flagLower = flag.toLowerCase();
|
||||
const badgeKey = flagToBadgeMap[flag];
|
||||
|
||||
if (badgeKey && BADGE_EMOJIS[badgeKey]) {
|
||||
badgeEmojis.push(BADGE_EMOJIS[badgeKey]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flagLower.includes('nitro')) {
|
||||
if (flagLower.match(/6.*year|year.*6|emerald/)) badgeEmojis.push(BADGE_EMOJIS.nitro_6_years);
|
||||
else if (flagLower.match(/5.*year|year.*5/)) badgeEmojis.push(BADGE_EMOJIS.nitro_5_years);
|
||||
else if (flagLower.match(/3.*year|year.*3/)) badgeEmojis.push(BADGE_EMOJIS.nitro_3_years);
|
||||
else if (flagLower.match(/2.*year|year.*2/)) badgeEmojis.push(BADGE_EMOJIS.nitro_2_years);
|
||||
else if (flagLower.match(/1.*year|year.*1/) && !flagLower.includes('month')) badgeEmojis.push(BADGE_EMOJIS.nitro_1_year);
|
||||
else if (flagLower.match(/6.*month|month.*6/)) badgeEmojis.push(BADGE_EMOJIS.nitro_6_months);
|
||||
else if (flagLower.match(/3.*month|month.*3/)) badgeEmojis.push(BADGE_EMOJIS.nitro_3_months);
|
||||
else if (flagLower.match(/1.*month|month.*1/)) badgeEmojis.push(BADGE_EMOJIS.nitro_1_month);
|
||||
else badgeEmojis.push(BADGE_EMOJIS.nitro);
|
||||
}
|
||||
else if (flagLower.includes('boost') && !flagLower.includes('server')) {
|
||||
if (flagLower.match(/24|two.*four|twenty.*four/i)) badgeEmojis.push(BADGE_EMOJIS.boost_24_months);
|
||||
else if (flagLower.match(/18|eighteen/i)) badgeEmojis.push(BADGE_EMOJIS.boost_18_months);
|
||||
else if (flagLower.match(/15|fifteen/i)) badgeEmojis.push(BADGE_EMOJIS.boost_15_months);
|
||||
else if (flagLower.match(/12|twelve|one.*year|1.*year/) && !flagLower.match(/18|15|24|128|112/i)) badgeEmojis.push(BADGE_EMOJIS.boost_12_months);
|
||||
else if (flagLower.match(/9|nine/) && !flagLower.match(/19|29|90|99/i)) badgeEmojis.push(BADGE_EMOJIS.boost_9_months);
|
||||
else if (flagLower.match(/6|six/) && !flagLower.match(/16|26|60|66|68|69/i)) badgeEmojis.push(BADGE_EMOJIS.boost_6_months);
|
||||
else if (flagLower.match(/3|three/) && !flagLower.match(/13|23|30|33|34|35|36|37|38|39/i)) badgeEmojis.push(BADGE_EMOJIS.boost_3_months);
|
||||
else if (flagLower.match(/2|two/) && !flagLower.match(/12|20|21|22|23|24|25|26|27|28|29/i)) badgeEmojis.push(BADGE_EMOJIS.boost_2_months);
|
||||
else badgeEmojis.push(BADGE_EMOJIS.boost);
|
||||
}
|
||||
else if (flagLower.includes('orb')) badgeEmojis.push(BADGE_EMOJIS.orbs);
|
||||
else if (flagLower.includes('quest')) badgeEmojis.push(BADGE_EMOJIS.quests);
|
||||
else if (flagLower.includes('slash')) badgeEmojis.push(BADGE_EMOJIS.slash_commands);
|
||||
}
|
||||
} catch (err) { console.warn('Erreur flags:', err.message); }
|
||||
}
|
||||
|
||||
const hasNitroBadge = badgeEmojis.some(e => typeof e === 'string' && e.includes('nitro'));
|
||||
const hasPremiumType = user.premiumType && user.premiumType !== 0;
|
||||
const hasBanner = user.banner;
|
||||
const hasAccentColor = user.accentColor;
|
||||
const hasAnimatedAvatar = user.avatar && user.avatar.startsWith('a_');
|
||||
const hasNitro = hasPremiumType || hasBanner || (hasAnimatedAvatar && hasAccentColor);
|
||||
|
||||
if (!hasNitroBadge && hasNitro) badgeEmojis.push(BADGE_EMOJIS.nitro);
|
||||
|
||||
if (member && member.premiumSince) {
|
||||
const hasBoostBadge = badgeEmojis.some(e => typeof e === 'string' && e.includes('boost'));
|
||||
if (!hasBoostBadge) {
|
||||
const boostDurationMs = Date.now() - member.premiumSince.getTime();
|
||||
const boostDurationMonths = Math.floor(boostDurationMs / (1000 * 60 * 60 * 24 * 30));
|
||||
|
||||
let boostBadge = BADGE_EMOJIS.boost;
|
||||
if (boostDurationMonths >= 24) boostBadge = BADGE_EMOJIS.boost_24_months;
|
||||
else if (boostDurationMonths >= 18) boostBadge = BADGE_EMOJIS.boost_18_months;
|
||||
else if (boostDurationMonths >= 15) boostBadge = BADGE_EMOJIS.boost_15_months;
|
||||
else if (boostDurationMonths >= 12) boostBadge = BADGE_EMOJIS.boost_12_months;
|
||||
else if (boostDurationMonths >= 9) boostBadge = BADGE_EMOJIS.boost_9_months;
|
||||
else if (boostDurationMonths >= 6) boostBadge = BADGE_EMOJIS.boost_6_months;
|
||||
else if (boostDurationMonths >= 3) boostBadge = BADGE_EMOJIS.boost_3_months;
|
||||
else if (boostDurationMonths >= 2) boostBadge = BADGE_EMOJIS.boost_2_months;
|
||||
|
||||
badgeEmojis.push(boostBadge);
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(badgeEmojis)];
|
||||
}
|
||||
|
||||
// Fonction pour obtenir le statut Discord en français
|
||||
function getStatusFrench(presence) {
|
||||
if (!presence || !presence.status) return { text: 'Hors ligne', emoji: '⚫' };
|
||||
|
||||
switch (presence.status) {
|
||||
case 'online':
|
||||
return { text: 'En ligne', emoji: '🟢' };
|
||||
case 'idle':
|
||||
return { text: 'Absent', emoji: '🟡' };
|
||||
case 'dnd':
|
||||
return { text: 'Ne pas déranger', emoji: '🔴' };
|
||||
case 'invisible':
|
||||
case 'offline':
|
||||
default:
|
||||
return { text: 'Hors ligne', emoji: '⚫' };
|
||||
}
|
||||
if (!presence || !presence.status) return { text: 'Hors ligne', emoji: '⚫' };
|
||||
switch (presence.status) {
|
||||
case 'online': return { text: 'En ligne', emoji: '🟢' };
|
||||
case 'idle': return { text: 'Absent', emoji: '🟡' };
|
||||
case 'dnd': return { text: 'Ne pas déranger', emoji: '🔴' };
|
||||
default: return { text: 'Hors ligne', emoji: '⚫' };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('profil')
|
||||
.setDescription('Afficher le profil d\'un utilisateur')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('view')
|
||||
.setDescription('Voir le profil d\'un utilisateur')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux voir le profil')
|
||||
.setRequired(false)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('set')
|
||||
.setDescription('Modifier ton profil')
|
||||
.addStringOption(option =>
|
||||
option.setName('signature')
|
||||
.setDescription('Ta signature personnalisée (max 200 caractères)')
|
||||
.setRequired(false)
|
||||
.setMaxLength(200)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('reset')
|
||||
.setDescription('Réinitialiser ta signature')),
|
||||
async execute(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
if (subcommand === 'view') {
|
||||
await interaction.deferReply();
|
||||
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
// Fetch l'utilisateur avec toutes les données pour avoir accès à premiumType, banner, accentColor, etc.
|
||||
// Utiliser { force: true } pour forcer le fetch même si l'utilisateur est dans le cache
|
||||
const fetchedUser = await interaction.client.users.fetch(target.id, {
|
||||
force: true,
|
||||
cache: false // Ne pas mettre en cache pour avoir les données les plus récentes
|
||||
}).catch(() => {
|
||||
// Si le fetch échoue, utiliser l'utilisateur du cache
|
||||
console.warn(`[PROFIL] Impossible de fetcher l'utilisateur ${target.id}, utilisation du cache`);
|
||||
return target;
|
||||
});
|
||||
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
|
||||
if (!member) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Cet utilisateur n\'est pas sur ce serveur.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Récupérer les données XP
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
|
||||
// Récupérer le profil utilisateur
|
||||
const [profiles] = await db.query(
|
||||
'SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
const profile = profiles[0] || null;
|
||||
|
||||
// Obtenir les badges (emojis seulement)
|
||||
// Utiliser fetchedUser pour avoir accès à premiumType
|
||||
const badgeEmojis = getDiscordBadges(fetchedUser, member);
|
||||
const badgesText = badgeEmojis.length > 0
|
||||
? badgeEmojis.join(' ')
|
||||
: 'Aucun badge';
|
||||
|
||||
// Obtenir le statut (préserver la présence)
|
||||
const presence = member.presence;
|
||||
const status = getStatusFrench(presence);
|
||||
|
||||
// Calculer l'âge du compte avec date
|
||||
const accountAge = Math.floor((Date.now() - target.createdTimestamp) / (1000 * 60 * 60 * 24));
|
||||
const accountAgeYears = Math.floor(accountAge / 365);
|
||||
const accountAgeMonths = Math.floor((accountAge % 365) / 30);
|
||||
const accountAgeDays = accountAge % 30;
|
||||
|
||||
let accountAgeText = '';
|
||||
if (accountAgeYears > 0) {
|
||||
accountAgeText = `${accountAgeYears} an${accountAgeYears > 1 ? 's' : ''}`;
|
||||
if (accountAgeMonths > 0) {
|
||||
accountAgeText += ` ${accountAgeMonths} mois`;
|
||||
}
|
||||
} else if (accountAgeMonths > 0) {
|
||||
accountAgeText = `${accountAgeMonths} mois`;
|
||||
if (accountAgeDays > 0) {
|
||||
accountAgeText += ` ${accountAgeDays} jour${accountAgeDays > 1 ? 's' : ''}`;
|
||||
}
|
||||
} else {
|
||||
accountAgeText = `${accountAgeDays} jour${accountAgeDays > 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
// Date de création formatée
|
||||
const creationDate = `<t:${Math.floor(target.createdTimestamp / 1000)}:D>`;
|
||||
const accountAgeWithDate = `${accountAgeText} (${creationDate})`;
|
||||
|
||||
// Calculer le temps vocal (en minutes)
|
||||
const voiceTimeMinutes = userXP?.totalVoiceTime || 0;
|
||||
const voiceTimeHours = Math.floor(voiceTimeMinutes / 60);
|
||||
const voiceTimeMins = voiceTimeMinutes % 60;
|
||||
const voiceTimeText = voiceTimeHours > 0
|
||||
? `${voiceTimeHours}h ${voiceTimeMins}min`
|
||||
: `${voiceTimeMins}min`;
|
||||
|
||||
// Calculer le rang (nécessite un await séparé)
|
||||
const { getUserRank } = require('../../functions/xp/xp.js');
|
||||
const rank = userXP ? await getUserRank(target.id, interaction.guild.id) : 0;
|
||||
|
||||
// Statistiques supplémentaires
|
||||
// Nombre de bumps
|
||||
const [bumps] = await db.query(
|
||||
'SELECT COUNT(*) as count FROM bumps WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
const bumpsCount = bumps[0]?.count || 0;
|
||||
|
||||
// Nombre de bienvenues (depuis les logs XP)
|
||||
const [welcomeLogs] = await db.query(
|
||||
'SELECT COUNT(*) as count FROM xp_logs WHERE userId = ? AND guildId = ? AND source = ?',
|
||||
[target.id, interaction.guild.id, 'welcome']
|
||||
);
|
||||
const welcomeCount = welcomeLogs[0]?.count || 0;
|
||||
|
||||
// Messages totaux (tous les serveurs où le bot est présent)
|
||||
// On récupère la somme de tous les messages de l'utilisateur sur tous les serveurs
|
||||
const [totalMessages] = await db.query(
|
||||
'SELECT SUM(totalMessages) as total FROM user_xp WHERE userId = ?',
|
||||
[target.id]
|
||||
);
|
||||
const totalMessagesCount = totalMessages[0]?.total || userXP?.totalMessages || 0;
|
||||
|
||||
// Créer l'embed
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`📋 Profil de ${target.username}`)
|
||||
.setColor(colors.primary)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setDescription(profile?.signature || '*Aucune signature*')
|
||||
.addFields(
|
||||
{ name: '🆔 Identifiant', value: `${target.tag} (${target.id})`, inline: false },
|
||||
{ name: '🏆 Badges', value: badgesText, inline: false },
|
||||
{ name: `${status.emoji} Statut`, value: status.text, inline: true },
|
||||
{ name: '📅 Âge du compte', value: accountAgeWithDate, inline: true },
|
||||
{ name: '⭐ Niveau', value: `${userXP?.level || 0}`, inline: true },
|
||||
{ name: '📊 XP Total', value: `${userXP?.xp || 0} XP`, inline: true },
|
||||
{ name: '💬 Messages', value: `${totalMessagesCount}`, inline: true },
|
||||
{ name: '🎤 Temps Vocal', value: voiceTimeText, inline: true },
|
||||
{ name: '📈 Rang', value: `#${rank}`, inline: true },
|
||||
{ name: '🚀 Bumps', value: `${bumpsCount}`, inline: true },
|
||||
{ name: '👋 Bienvenues', value: `${welcomeCount}`, inline: true }
|
||||
)
|
||||
.setFooter({ text: interaction.guild.name, iconURL: interaction.guild.iconURL({ dynamic: true }) })
|
||||
.setTimestamp();
|
||||
|
||||
// Ajouter l'âge si disponible (système d'anniversaire)
|
||||
if (profile?.birthday) {
|
||||
const birthday = new Date(profile.birthday);
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - birthday.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthday.getMonth();
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthday.getDate())) {
|
||||
age--;
|
||||
}
|
||||
embed.addFields({ name: '🎂 Âge', value: `${age} ans`, inline: true });
|
||||
}
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de l\'affichage du profil:', err);
|
||||
await interaction.editReply({
|
||||
content: '❌ Erreur lors de l\'affichage du profil.'
|
||||
});
|
||||
}
|
||||
|
||||
} else if (subcommand === 'set') {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const signature = interaction.options.getString('signature');
|
||||
|
||||
if (!signature) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu dois fournir une signature.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Vérifier ou créer le profil
|
||||
const [profiles] = await db.query(
|
||||
'SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[interaction.user.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (profiles.length > 0) {
|
||||
// Mettre à jour la signature
|
||||
await db.query(
|
||||
'UPDATE user_profiles SET signature = ?, updatedAt = ? WHERE userId = ? AND guildId = ?',
|
||||
[signature, Date.now(), interaction.user.id, interaction.guild.id]
|
||||
);
|
||||
} else {
|
||||
// Créer le profil
|
||||
await db.query(
|
||||
'INSERT INTO user_profiles (userId, guildId, signature, updatedAt) VALUES (?, ?, ?, ?)',
|
||||
[interaction.user.id, interaction.guild.id, signature, Date.now()]
|
||||
);
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Signature Modifiée')
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Ta signature a été mise à jour :\n\n${signature}`)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la modification de la signature:', err);
|
||||
await interaction.editReply({
|
||||
content: '❌ Erreur lors de la modification de la signature.'
|
||||
});
|
||||
}
|
||||
|
||||
} else if (subcommand === 'reset') {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
// Vérifier si le profil existe
|
||||
const [profiles] = await db.query(
|
||||
'SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?',
|
||||
[interaction.user.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (profiles.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas de signature à réinitialiser.'
|
||||
});
|
||||
}
|
||||
|
||||
// Réinitialiser la signature
|
||||
await db.query(
|
||||
'UPDATE user_profiles SET signature = NULL, updatedAt = ? WHERE userId = ? AND guildId = ?',
|
||||
[Date.now(), interaction.user.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Signature Réinitialisée')
|
||||
.setColor(colors.success)
|
||||
.setDescription('Ta signature a été réinitialisée.')
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la réinitialisation de la signature:', err);
|
||||
await interaction.editReply({
|
||||
content: '❌ Erreur lors de la réinitialisation de la signature.'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('profil')
|
||||
.setDescription('Afficher le profil d\'un utilisateur')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand.setName('view').setDescription('Voir le profil d\'un utilisateur')
|
||||
.addUserOption(option => option.setName('user').setDescription('L\'utilisateur dont tu veux voir le profil').setRequired(false)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand.setName('set').setDescription('Modifier ton profil')
|
||||
.addStringOption(option => option.setName('signature').setDescription('Ta signature personnalisée (max 200 caractères)').setRequired(false).setMaxLength(200))
|
||||
.addStringOption(option => option.setName('birthday').setDescription('Ta date d\'anniversaire (JJ/MM/AAAA)').setRequired(false))
|
||||
.addStringOption(option => option.setName('color').setDescription('Couleur de ton profil (Hex: #RRGGBB)').setRequired(false))
|
||||
.addStringOption(option => option.setName('gender').setDescription('Ton genre/pronoms').setRequired(false).setMaxLength(50))
|
||||
.addStringOption(option => option.setName('location').setDescription('Ta localisation').setRequired(false).setMaxLength(100)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand.setName('reset').setDescription('Réinitialiser ton profil')),
|
||||
async execute(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
if (subcommand === 'view') {
|
||||
await interaction.deferReply();
|
||||
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
const fetchedUser = await interaction.client.users.fetch(target.id, { force: true, cache: false }).catch(() => target);
|
||||
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
|
||||
if (!member) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas sur ce serveur.' });
|
||||
|
||||
try {
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
const [profiles] = await db.query('SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
|
||||
const profile = profiles[0] || null;
|
||||
|
||||
const badgeEmojis = getDiscordBadges(fetchedUser, member);
|
||||
const badgesText = badgeEmojis.length > 0 ? badgeEmojis.join(' ') : 'Aucun badge';
|
||||
const status = getStatusFrench(member.presence);
|
||||
|
||||
const accountAge = Math.floor((Date.now() - target.createdTimestamp) / (1000 * 60 * 60 * 24));
|
||||
const accountAgeYears = Math.floor(accountAge / 365);
|
||||
const accountAgeMonths = Math.floor((accountAge % 365) / 30);
|
||||
const accountAgeDays = accountAge % 30;
|
||||
|
||||
let accountAgeText = accountAgeYears > 0 ? `${accountAgeYears} an${accountAgeYears > 1 ? 's' : ''}` : '';
|
||||
if (accountAgeMonths > 0) accountAgeText += (accountAgeText ? ' ' : '') + `${accountAgeMonths} mois`;
|
||||
if (!accountAgeText) accountAgeText = `${accountAgeDays} jour${accountAgeDays > 1 ? 's' : ''}`;
|
||||
|
||||
const creationDate = `<t:${Math.floor(target.createdTimestamp / 1000)}:D>`;
|
||||
const voiceTimeMinutes = userXP?.totalVoiceTime || 0;
|
||||
const voiceTimeText = `${Math.floor(voiceTimeMinutes / 60)}h ${voiceTimeMinutes % 60}min`;
|
||||
|
||||
const rank = userXP ? await getUserRank(target.id, interaction.guild.id) : 0;
|
||||
|
||||
const [bumps] = await db.query('SELECT COUNT(*) as count FROM bumps WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
|
||||
const [welcomeLogs] = await db.query('SELECT COUNT(*) as count FROM xp_logs WHERE userId = ? AND guildId = ? AND source = ?', [target.id, interaction.guild.id, 'welcome']);
|
||||
const [totalMessages] = await db.query('SELECT SUM(totalMessages) as total FROM user_xp WHERE userId = ?', [target.id]);
|
||||
|
||||
const embedColor = profile?.color && /^#[0-9A-F]{6}$/i.test(profile.color) ? profile.color : colors.primary;
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({ name: `Profil de ${target.username}`, iconURL: target.displayAvatarURL({ dynamic: true }) })
|
||||
.setColor(embedColor)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setDescription(profile?.signature ? `\`\`\`${profile.signature}\`\`\`` : '*Aucune signature définie.*')
|
||||
.addFields(
|
||||
{ name: '👤 Informations', value: `• **Identifiant:** \`${target.id}\`\n• **Badges:** ${badgesText}\n• **Âge Compte:** \`${accountAgeText}\`\n• **Statut:** ${status.emoji} \`${status.text}\``, inline: false },
|
||||
{ name: '📊 Statistiques XP', value: `• **Niveau:** \`${userXP?.level || 0}\`\n• **XP Total:** \`${userXP?.xp || 0}\`\n• **Rang:** \`#${rank}\``, inline: true },
|
||||
{ name: '🎙️ Activité Vocal', value: `• **Temps:** \`${voiceTimeText}\`\n• **Bumps:** \`${bumps[0]?.count || 0}\`\n• **Messages:** \`${totalMessages[0]?.total || userXP?.totalMessages || 0}\``, inline: true },
|
||||
{ name: '🤝 Engagement', value: `• **Bienvenues:** \`${welcomeLogs[0]?.count || 0}\`\n• **Création:** ${creationDate}`, inline: false }
|
||||
)
|
||||
.setFooter({ text: `Demandé par ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
const personalInfos = [];
|
||||
if (profile?.gender) personalInfos.push(`• **Genre/Pronoms:** ${profile.gender}`);
|
||||
if (profile?.location) personalInfos.push(`• **Localisation:** ${profile.location}`);
|
||||
if (profile?.birthday) {
|
||||
const birthday = new Date(profile.birthday);
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - birthday.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthday.getMonth();
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthday.getDate())) age--;
|
||||
personalInfos.push(`• **Anniversaire:** ${birthday.toLocaleDateString('fr-FR')} (${age} ans)`);
|
||||
}
|
||||
|
||||
if (personalInfos.length > 0) {
|
||||
embed.addFields({ name: '📝 Infos Personnelles', value: personalInfos.join('\n'), inline: false });
|
||||
}
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur profil:', err);
|
||||
await interaction.editReply({ content: '❌ Erreur lors de l\'affichage du profil.' });
|
||||
}
|
||||
|
||||
} else if (subcommand === 'set') {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
const signature = interaction.options.getString('signature');
|
||||
const birthdayInput = interaction.options.getString('birthday');
|
||||
const color = interaction.options.getString('color');
|
||||
const gender = interaction.options.getString('gender');
|
||||
const location = interaction.options.getString('location');
|
||||
|
||||
if (!signature && !birthdayInput && !color && !gender && !location) {
|
||||
return interaction.editReply({ content: '❌ Tu dois modifier au moins un élément.' });
|
||||
}
|
||||
|
||||
try {
|
||||
let birthdayDate = null;
|
||||
if (birthdayInput) {
|
||||
const parts = birthdayInput.split('/');
|
||||
if (parts.length === 3) {
|
||||
const day = parseInt(parts[0], 10);
|
||||
const month = parseInt(parts[1], 10) - 1;
|
||||
const year = parseInt(parts[2], 10);
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
if (year < 1900 || year > currentYear) {
|
||||
return interaction.editReply({ content: '❌ Année invalide. Doit être entre 1900 et aujourd\'hui.' });
|
||||
}
|
||||
|
||||
const date = new Date(year, month, day);
|
||||
if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {
|
||||
// Vérifier si la date est dans le futur
|
||||
if (date > new Date()) {
|
||||
return interaction.editReply({ content: '❌ Tu ne peux pas être né dans le futur !' });
|
||||
}
|
||||
birthdayDate = `${year}-${month + 1}-${day}`;
|
||||
} else {
|
||||
return interaction.editReply({ content: '❌ Date invalide. Format: JJ/MM/AAAA' });
|
||||
}
|
||||
} else {
|
||||
return interaction.editReply({ content: '❌ Format de date invalide. Utilise JJ/MM/AAAA' });
|
||||
}
|
||||
}
|
||||
|
||||
if (color && !/^#[0-9A-F]{6}$/i.test(color)) {
|
||||
return interaction.editReply({ content: '❌ Couleur invalide. Utilise le format Hex (ex: #FF0000).' });
|
||||
}
|
||||
|
||||
const [profiles] = await db.query('SELECT * FROM user_profiles WHERE userId = ? AND guildId = ?', [interaction.user.id, interaction.guild.id]);
|
||||
|
||||
if (profiles.length > 0) {
|
||||
const updates = [];
|
||||
const params = [];
|
||||
|
||||
if (signature) { updates.push('signature = ?'); params.push(signature); }
|
||||
if (birthdayDate) { updates.push('birthday = ?'); params.push(birthdayDate); }
|
||||
if (color) { updates.push('color = ?'); params.push(color); }
|
||||
if (gender) { updates.push('gender = ?'); params.push(gender); }
|
||||
if (location) { updates.push('location = ?'); params.push(location); }
|
||||
|
||||
updates.push('updatedAt = ?');
|
||||
params.push(Date.now());
|
||||
params.push(interaction.user.id);
|
||||
params.push(interaction.guild.id);
|
||||
|
||||
await db.query(`UPDATE user_profiles SET ${updates.join(', ')} WHERE userId = ? AND guildId = ?`, params);
|
||||
} else {
|
||||
await db.query(
|
||||
'INSERT INTO user_profiles (userId, guildId, signature, birthday, color, gender, location, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
interaction.user.id,
|
||||
interaction.guild.id,
|
||||
signature || null,
|
||||
birthdayDate || null,
|
||||
color || null,
|
||||
gender || null,
|
||||
location || null,
|
||||
Date.now()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder().setTitle('✅ Profil Modifié').setColor(colors.success).setDescription('Ton profil a été mis à jour avec succès !').setTimestamp();
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (err) {
|
||||
console.error('Erreur set profil:', err);
|
||||
await interaction.editReply({ content: '❌ Erreur lors de la modification du profil.' });
|
||||
}
|
||||
|
||||
} else if (subcommand === 'reset') {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
try {
|
||||
await db.query('DELETE FROM user_profiles WHERE userId = ? AND guildId = ?', [interaction.user.id, interaction.guild.id]);
|
||||
const embed = new EmbedBuilder().setTitle('✅ Profil Réinitialisé').setColor(colors.success).setDescription('Ton profil a été entièrement réinitialisé.').setTimestamp();
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (err) {
|
||||
console.error('Erreur reset profil:', err);
|
||||
await interaction.editReply({ content: '❌ Erreur lors de la réinitialisation du profil.' });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,47 +1,53 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, GuildVerificationLevel } = require('discord.js');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('serverinfo')
|
||||
.setDescription('Affiche les informations du serveur.'),
|
||||
async execute(interaction) {
|
||||
const guild = interaction.guild;
|
||||
const owner = await guild.fetchOwner();
|
||||
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${guild.name}`,
|
||||
iconURL: guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle(`${emojis.server} Informations du Serveur`)
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(guild.iconURL({ dynamic: true, size: 256 }) || null)
|
||||
.addFields(
|
||||
{ name: '👑 Propriétaire', value: `${owner.user.toString()}\n\`${owner.user.tag}\``, inline: true },
|
||||
{ name: `${emojis.id} ID`, value: `\`${guild.id}\``, inline: true },
|
||||
{ name: `${emojis.calendar} Créé le`, value: `<t:${Math.floor(guild.createdTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '👥 Membres', value: `\`${guild.memberCount.toLocaleString()}\``, inline: true },
|
||||
{ name: '💬 Canaux', value: `\`${guild.channels.cache.size}\``, inline: true },
|
||||
{ name: '😀 Emojis', value: `\`${guild.emojis.cache.size}\``, inline: true },
|
||||
{ name: '🎭 Rôles', value: `\`${guild.roles.cache.size}\``, inline: true },
|
||||
{ name: '✅ Vérification', value: `\`${guild.verificationLevel}\``, inline: true },
|
||||
{ name: '🔒 Sécurité', value: guild.mfaLevel === 1 ? '`Élevé (2FA)`' : '`Normal`', inline: true }
|
||||
)
|
||||
.setFooter({ text: `${guild.name} • ${interaction.client.user.username}`, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
if (guild.description) {
|
||||
embed.setDescription(guild.description);
|
||||
}
|
||||
|
||||
if (guild.banner) {
|
||||
embed.setImage(guild.bannerURL({ dynamic: true, size: 1024 }));
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
const verificationLevels = {
|
||||
[GuildVerificationLevel.None]: 'Aucune',
|
||||
[GuildVerificationLevel.Low]: 'Faible',
|
||||
[GuildVerificationLevel.Medium]: 'Moyenne',
|
||||
[GuildVerificationLevel.High]: 'Élevée',
|
||||
[GuildVerificationLevel.VeryHigh]: 'Très Élevée'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('serverinfo')
|
||||
.setDescription('Affiche les informations du serveur.'),
|
||||
async execute(interaction) {
|
||||
const guild = interaction.guild;
|
||||
const owner = await guild.fetchOwner();
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: guild.name,
|
||||
iconURL: guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle(`${emojis.server} Informations du Serveur`)
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(guild.iconURL({ dynamic: true, size: 256 }) || null)
|
||||
.addFields(
|
||||
{ name: '👑 Propriétaire', value: `${owner.user.toString()}\n\`${owner.user.tag}\``, inline: true },
|
||||
{ name: `${emojis.id} ID`, value: `\`${guild.id}\``, inline: true },
|
||||
{ name: `${emojis.calendar} Créé le`, value: `<t:${Math.floor(guild.createdTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '👥 Membres', value: `\`${guild.memberCount.toLocaleString()}\``, inline: true },
|
||||
{ name: '💬 Canaux', value: `\`${guild.channels.cache.size}\``, inline: true },
|
||||
{ name: '😀 Emojis', value: `\`${guild.emojis.cache.size}\``, inline: true },
|
||||
{ name: '🎭 Rôles', value: `\`${guild.roles.cache.size}\``, inline: true },
|
||||
{ name: '✅ Vérification', value: `\`${verificationLevels[guild.verificationLevel] || 'Inconnue'}\``, inline: true },
|
||||
{ name: '🔒 Sécurité', value: guild.mfaLevel === 1 ? '`Élevé (2FA)`' : '`Normal`', inline: true }
|
||||
)
|
||||
.setFooter({ text: `${guild.name} • ${interaction.client.user.username}`, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
if (guild.description) {
|
||||
embed.setDescription(guild.description);
|
||||
}
|
||||
|
||||
if (guild.banner) {
|
||||
embed.setImage(guild.bannerURL({ dynamic: true, size: 1024 }));
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,91 +1,70 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
function getStatusFrench(presence) {
|
||||
if (!presence || !presence.status) return { text: 'Hors ligne', emoji: '⚫' };
|
||||
switch (presence.status) {
|
||||
case 'online': return { text: 'En ligne', emoji: '🟢' };
|
||||
case 'idle': return { text: 'Absent', emoji: '🟡' };
|
||||
case 'dnd': return { text: 'Ne pas déranger', emoji: '🔴' };
|
||||
default: return { text: 'Hors ligne', emoji: '⚫' };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('userinfo')
|
||||
.setDescription('Affiche les informations d\'un utilisateur.')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux voir les informations')
|
||||
.setRequired(false)),
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
category: 'info',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('userinfo')
|
||||
.setDescription('Affiche les informations d\'un utilisateur.')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux voir les informations')
|
||||
.setRequired(false)),
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
const member = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${target.displayName}`,
|
||||
iconURL: target.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.user} Informations Utilisateur`)
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.addFields(
|
||||
{ name: `${emojis.id} ID`, value: `\`${target.id}\``, inline: true },
|
||||
{ name: `${emojis.calendar} Compte créé`, value: `<t:${Math.floor(target.createdTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '🤖 Bot', value: target.bot ? '`Oui`' : '`Non`', inline: true }
|
||||
)
|
||||
.setFooter({ text: `${interaction.guild.name} • ${interaction.client.user.username}`, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: target.displayName,
|
||||
iconURL: target.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.user} Informations Utilisateur`)
|
||||
.setColor(colors.info)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.addFields(
|
||||
{ name: `${emojis.id} ID`, value: `\`${target.id}\``, inline: true },
|
||||
{ name: `${emojis.calendar} Compte créé`, value: `<t:${Math.floor(target.createdTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '🤖 Bot', value: target.bot ? '`Oui`' : '`Non`', inline: true }
|
||||
)
|
||||
.setFooter({ text: `${interaction.guild.name} • ${interaction.client.user.username}`, iconURL: interaction.client.user.displayAvatarURL() })
|
||||
.setTimestamp();
|
||||
|
||||
if (member) {
|
||||
// Récupérer le statut avec gestion des différents cas
|
||||
let status = 'Hors ligne';
|
||||
let statusEmoji = '⚫';
|
||||
|
||||
if (member.presence) {
|
||||
const presenceStatus = member.presence.status;
|
||||
switch (presenceStatus) {
|
||||
case 'online':
|
||||
status = 'En ligne';
|
||||
statusEmoji = '🟢';
|
||||
break;
|
||||
case 'idle':
|
||||
status = 'Inactif';
|
||||
statusEmoji = '🟡';
|
||||
break;
|
||||
case 'dnd':
|
||||
status = 'Ne pas déranger';
|
||||
statusEmoji = '🔴';
|
||||
break;
|
||||
case 'invisible':
|
||||
status = 'Invisible';
|
||||
statusEmoji = '⚫';
|
||||
break;
|
||||
case 'offline':
|
||||
status = 'Hors ligne';
|
||||
statusEmoji = '⚫';
|
||||
break;
|
||||
default:
|
||||
status = 'Inconnu';
|
||||
statusEmoji = '⚪';
|
||||
}
|
||||
}
|
||||
if (member) {
|
||||
const status = getStatusFrench(member.presence);
|
||||
const roles = member.roles.cache.filter(r => r.id !== interaction.guild.id);
|
||||
const rolesDisplay = roles.size > 0 ? roles.sort((a, b) => b.position - a.position).map(r => r.toString()).slice(0, 10).join(', ') : '`Aucun`';
|
||||
|
||||
embed.addFields(
|
||||
{ name: '📥 A rejoint le', value: `<t:${Math.floor(member.joinedTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '⏰ Statut', value: `${statusEmoji} \`${status}\``, inline: true },
|
||||
{ name: '👑 Rôle le plus élevé', value: member.roles.highest.toString(), inline: true },
|
||||
{ name: '🎭 Rôles', value: member.roles.cache.size > 1 ? member.roles.cache.filter(r => r.id !== interaction.guild.id).map(r => r.toString()).slice(0, 10).join(', ') || '`Aucun`' : '`Aucun`', inline: false }
|
||||
);
|
||||
embed.addFields(
|
||||
{ name: '📥 A rejoint le', value: `<t:${Math.floor(member.joinedTimestamp / 1000)}:F>`, inline: true },
|
||||
{ name: '⏰ Statut', value: `${status.emoji} \`${status.text}\``, inline: true },
|
||||
{ name: '👑 Rôle le plus élevé', value: member.roles.highest.toString(), inline: true },
|
||||
{ name: '🎭 Rôles', value: rolesDisplay, inline: false }
|
||||
);
|
||||
|
||||
if (member.nickname) {
|
||||
embed.addFields({ name: '📝 Surnom', value: `\`${member.nickname}\``, inline: true });
|
||||
}
|
||||
if (member.nickname) {
|
||||
embed.addFields({ name: '📝 Surnom', value: `\`${member.nickname}\``, inline: true });
|
||||
}
|
||||
|
||||
if (member.roles.cache.size > 11) {
|
||||
embed.addFields({ name: '📊 Total de rôles', value: `\`${member.roles.cache.size - 1}\``, inline: true });
|
||||
}
|
||||
}
|
||||
if (roles.size > 10) {
|
||||
embed.addFields({ name: '📊 Total de rôles', value: `\`${roles.size}\``, inline: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (target.banner) {
|
||||
embed.setImage(target.bannerURL({ dynamic: true, size: 1024 }));
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
if (target.banner) {
|
||||
embed.setImage(target.bannerURL({ dynamic: true, size: 1024 }));
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,134 +1,119 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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('Bannir un membre du serveur.')
|
||||
.addUserOption(option =>
|
||||
option.setName('target')
|
||||
.setDescription('Le membre à bannir')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison du bannissement')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option.setName('duration')
|
||||
.setDescription('Durée du ban (ex: 1h, 2d, laisser vide pour permanent)')
|
||||
.setRequired(false))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||
category: 'moderation',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Bannir un membre du serveur.')
|
||||
.addUserOption(option =>
|
||||
option.setName('target')
|
||||
.setDescription('Le membre à bannir')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison du bannissement')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option.setName('duration')
|
||||
.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') || 'Aucune raison fournie';
|
||||
const durationInput = interaction.options.getString('duration');
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
const durationInput = interaction.options.getString('duration');
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
|
||||
// Vérifier que l'utilisateur peut être banni
|
||||
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.' });
|
||||
}
|
||||
}
|
||||
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';
|
||||
let type = 'Permanent';
|
||||
let unbanDate = null;
|
||||
let durationText = 'Permanent';
|
||||
|
||||
if (durationInput) {
|
||||
const { parseDuration, formatDuration } = require('../../utils/helpers');
|
||||
const durationMs = parseDuration(durationInput);
|
||||
if (durationMs) {
|
||||
unbanDate = Date.now() + durationMs;
|
||||
type = 'Temporary';
|
||||
durationText = formatDuration(durationMs);
|
||||
}
|
||||
}
|
||||
if (durationInput) {
|
||||
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).' });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Bannissement
|
||||
await interaction.guild.bans.create(target.id, { reason: `Banni par ${interaction.user.tag}: ${reason}` });
|
||||
try {
|
||||
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)`,
|
||||
[target.id, reason, interaction.user.id, Date.now(), type, unbanDate, interaction.guild.id]
|
||||
);
|
||||
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)`,
|
||||
[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 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()]
|
||||
);
|
||||
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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();
|
||||
await interaction.editReply({ 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] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 },
|
||||
{ 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 lors du bannissement:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour bannir cet utilisateur.', ephemeral: true });
|
||||
} else if (err.code === 50035) {
|
||||
await interaction.editReply({ content: '❌ Cet utilisateur est déjà banni.', ephemeral: true });
|
||||
} else {
|
||||
await interaction.editReply({ content: `❌ Échec du bannissement de ${target.tag}: ${err.message}`, ephemeral: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('Erreur ban:', err);
|
||||
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,115 +1,106 @@
|
||||
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 d\'un membre.')
|
||||
.addUserOption(option =>
|
||||
option.setName('membre')
|
||||
.setDescription('Le membre dont tu veux voir le casier')
|
||||
.setRequired(true)),
|
||||
category: 'moderation',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('casier')
|
||||
.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();
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
const member = interaction.options.getUser('membre');
|
||||
const member = interaction.options.getUser('membre');
|
||||
|
||||
try {
|
||||
const [rows] = await db.query(
|
||||
'SELECT * FROM logs WHERE userId = ? AND guildId = ? ORDER BY timestamp DESC',
|
||||
[member.id, interaction.guild.id]
|
||||
);
|
||||
try {
|
||||
const [rows] = await db.query(
|
||||
'SELECT * FROM logs WHERE userId = ? AND guildId = ? ORDER BY timestamp DESC',
|
||||
[member.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
// Filtrer les modifications et révocations (ce ne sont pas des sanctions à afficher)
|
||||
const sanctions = rows.filter(row =>
|
||||
row.action !== 'Modification de sanction' &&
|
||||
row.action !== 'Révocation de sanction' &&
|
||||
row.action !== 'Modification de mute'
|
||||
);
|
||||
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.` });
|
||||
}
|
||||
if (!sanctions.length) {
|
||||
return interaction.editReply({ content: `✅ ${member.tag} n'a aucune sanction sur ce serveur.` });
|
||||
}
|
||||
|
||||
// Découpe les sanctions en pages de 10
|
||||
const pages = [];
|
||||
for (let i = 0; i < sanctions.length; i += 10) {
|
||||
pages.push(sanctions.slice(i, i + 10));
|
||||
}
|
||||
const pages = [];
|
||||
for (let i = 0; i < sanctions.length; i += 10) {
|
||||
pages.push(sanctions.slice(i, i + 10));
|
||||
}
|
||||
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
const embeds = pages.map((page, pageIndex) => {
|
||||
const embed = new EmbedBuilder()
|
||||
.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();
|
||||
const embeds = pages.map((page, pageIndex) => {
|
||||
const embed = new EmbedBuilder()
|
||||
.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 = `<t:${Math.floor(row.timestamp / 1000)}:F>`;
|
||||
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1);
|
||||
const guildName = row.guildId
|
||||
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Serveur inconnu'
|
||||
: 'Serveur inconnu';
|
||||
|
||||
// Numéro d'affichage (position dans la liste filtrée, à partir de 1)
|
||||
const displayNumber = index + 1 + pageIndex * 10;
|
||||
page.forEach((row, index) => {
|
||||
const date = `<t:${Math.floor(row.timestamp / 1000)}:F>`;
|
||||
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1);
|
||||
const guildName = row.guildId
|
||||
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Serveur inconnu'
|
||||
: 'Serveur inconnu';
|
||||
|
||||
const displayNumber = index + 1 + pageIndex * 10;
|
||||
|
||||
embed.addFields({
|
||||
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:** \`${row.type || 'N/A'}\``,
|
||||
inline: false
|
||||
});
|
||||
});
|
||||
embed.addFields({
|
||||
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:** \`${row.type || 'N/A'}\``,
|
||||
inline: false
|
||||
});
|
||||
});
|
||||
|
||||
return embed;
|
||||
});
|
||||
return embed;
|
||||
});
|
||||
|
||||
// Composants pour naviguer
|
||||
const row = new ActionRowBuilder().addComponents(
|
||||
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)
|
||||
);
|
||||
const row = new ActionRowBuilder().addComponents(
|
||||
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: pages.length > 1 ? [row] : [] });
|
||||
let currentPage = 0;
|
||||
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', async i => {
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
return i.reply({ content: '❌ Ce n\'est pas ton menu !', ephemeral: true });
|
||||
}
|
||||
if (i.customId === 'next') {
|
||||
currentPage = (currentPage + 1) % embeds.length;
|
||||
}
|
||||
if (i.customId === 'prev') {
|
||||
currentPage = (currentPage - 1 + embeds.length) % embeds.length;
|
||||
}
|
||||
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]] });
|
||||
});
|
||||
await i.update({ embeds: [embeds[currentPage]] });
|
||||
});
|
||||
|
||||
collector.on('end', () => {
|
||||
message.edit({ components: [] }).catch(() => null);
|
||||
});
|
||||
collector.on('end', () => {
|
||||
message.edit({ components: [] }).catch(() => null);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la récupération du casier:', err);
|
||||
await interaction.editReply({ content: '❌ Une erreur est survenue lors de la récupération du casier.' });
|
||||
}
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('Erreur casier:', err);
|
||||
await interaction.editReply({ content: '❌ Une erreur est survenue lors de la récupération du casier.' });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,172 +1,149 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { parseDuration, formatDuration } = require('../../utils/helpers');
|
||||
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),
|
||||
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');
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('user');
|
||||
const newTime = interaction.options.getString('time');
|
||||
const newReason = interaction.options.getString('reason');
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
if (!newTime && !newReason) return interaction.editReply({ content: '❌ Tu dois spécifier au moins une nouvelle durée ou une nouvelle raison.' });
|
||||
|
||||
// Récupérer le mute actuel depuis la DB
|
||||
const [muteRows] = await db.query(
|
||||
'SELECT * FROM mutes WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
const [muteRows] = await db.query(
|
||||
'SELECT * FROM mutes WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (!muteRows.length && !member.communicationDisabledUntil) {
|
||||
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas muté.' });
|
||||
}
|
||||
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;
|
||||
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é';
|
||||
try {
|
||||
let newUnmuteDate = oldUnmuteDate;
|
||||
let newDurationMs = null;
|
||||
let durationText = 'Non modifié';
|
||||
|
||||
// Si nouvelle durée spécifiée
|
||||
if (newTime) {
|
||||
newDurationMs = parseDuration(newTime);
|
||||
if (!newDurationMs) {
|
||||
return interaction.editReply({ content: '❌ Durée invalide. Utilise un format comme: 5m, 1h, 2d (max 28 jours).' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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);
|
||||
newUnmuteDate = Date.now() + newDurationMs;
|
||||
durationText = formatDuration(newDurationMs);
|
||||
|
||||
// Mettre à jour le timeout
|
||||
const durationSeconds = Math.floor(newDurationMs / 1000);
|
||||
await member.timeout(durationSeconds * 1000, `Mute modifié par ${interaction.user.tag}: ${newReason || oldReason}`);
|
||||
}
|
||||
await member.timeout(newDurationMs, `Mute modifié par ${interaction.user.tag}: ${newReason || oldReason}`);
|
||||
}
|
||||
|
||||
// Mettre à jour la DB
|
||||
const finalReason = newReason || oldReason;
|
||||
const type = newUnmuteDate && newUnmuteDate > Date.now() ? 'Temporary' : 'Permanent';
|
||||
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]
|
||||
);
|
||||
}
|
||||
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]
|
||||
);
|
||||
}
|
||||
|
||||
// Log dans la DB
|
||||
const changeLog = [];
|
||||
if (newTime) changeLog.push(`Durée: ${formatDuration(newDurationMs)}`);
|
||||
if (newReason) changeLog.push(`Raison: ${oldReason} → ${newReason}`);
|
||||
const changeReason = changeLog.join(', ');
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
if (newUnmuteDate) {
|
||||
embed.addFields({ name: '📅 Démute le', value: `<t:${Math.floor(newUnmuteDate / 1000)}:F>`, inline: true });
|
||||
}
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
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 */ }
|
||||
|
||||
// Envoyer un message privé à l'utilisateur
|
||||
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 {
|
||||
// L'utilisateur a les DMs fermés, on continue
|
||||
}
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de la modification du mute:', err);
|
||||
await interaction.editReply({ content: `❌ Échec de la modification du mute: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,125 +1,114 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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');
|
||||
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),
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('user');
|
||||
const sanctionId = interaction.options.getInteger('sanction_id');
|
||||
const newReason = interaction.options.getString('new_reason');
|
||||
|
||||
try {
|
||||
console.log(`[changesanction] Recherche sanction ID: ${sanctionId}, userId: ${target.id}, guildId: ${interaction.guild.id}`);
|
||||
|
||||
// Récupérer directement la sanction par son ID DB (plus stable)
|
||||
const [sanctionRows] = await db.query(
|
||||
'SELECT * FROM logs WHERE id = ? AND userId = ? AND guildId = ?',
|
||||
[sanctionId, target.id, interaction.guild.id]
|
||||
);
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
console.log(`[changesanction] Résultats trouvés: ${sanctionRows.length}`);
|
||||
try {
|
||||
const [sanctionRows] = await db.query(
|
||||
'SELECT * FROM logs WHERE id = ? AND userId = ? AND guildId = ?',
|
||||
[sanctionId, target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (!sanctionRows.length) {
|
||||
// Vérifier si l'ID existe pour cet utilisateur (peut-être mauvais serveur?)
|
||||
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\`).`
|
||||
});
|
||||
}
|
||||
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];
|
||||
const sanction = sanctionRows[0];
|
||||
|
||||
// Vérifier que ce n'est pas une modification/révocation
|
||||
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;
|
||||
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;
|
||||
|
||||
// Mettre à jour la raison
|
||||
await db.query(
|
||||
'UPDATE logs SET reason = ?, modId = ?, modTag = ? WHERE id = ?',
|
||||
[newReason, interaction.user.id, interaction.user.tag, sanction.id]
|
||||
);
|
||||
await db.query(
|
||||
'UPDATE logs SET reason = ?, modId = ?, modTag = ? WHERE id = ?',
|
||||
[newReason, interaction.user.id, interaction.user.tag, sanction.id]
|
||||
);
|
||||
|
||||
// Log de la modification
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de la modification de la sanction:', err);
|
||||
await interaction.editReply({ content: `❌ Échec de la modification de la sanction: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,85 +1,74 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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');
|
||||
async execute(interaction) {
|
||||
const amount = interaction.options.getInteger('amount');
|
||||
const target = interaction.options.getUser('target');
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
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.' });
|
||||
}
|
||||
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;
|
||||
try {
|
||||
const messages = await interaction.channel.messages.fetch({ limit: amount });
|
||||
let messagesToDelete = messages;
|
||||
|
||||
if (target) {
|
||||
messagesToDelete = messages.filter(msg => msg.author.id === target.id);
|
||||
}
|
||||
if (target) {
|
||||
messagesToDelete = messages.filter(msg => msg.author.id === target.id);
|
||||
}
|
||||
|
||||
// Ne pas supprimer les messages de plus de 14 jours
|
||||
const filteredMessages = messagesToDelete.filter(msg => {
|
||||
const msgAge = Date.now() - msg.createdTimestamp;
|
||||
return msgAge < 14 * 24 * 60 * 60 * 1000;
|
||||
});
|
||||
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).' });
|
||||
}
|
||||
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);
|
||||
await interaction.channel.bulkDelete(filteredMessages, true);
|
||||
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
// Supprimer le message de confirmation après 5 secondes
|
||||
setTimeout(() => {
|
||||
interaction.deleteReply().catch(() => null);
|
||||
}, 5000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la suppression des messages:', 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 des messages: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,111 +1,89 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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';
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
await interaction.deferReply();
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.' });
|
||||
|
||||
// Vérifications de permissions
|
||||
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.' });
|
||||
}
|
||||
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}`);
|
||||
try {
|
||||
await member.kick(`Expulsé par ${interaction.user.tag}: ${reason}`);
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 },
|
||||
{ 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 lors de l\'expulsion:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour expulser cet utilisateur.' });
|
||||
} else {
|
||||
await interaction.editReply({ content: `❌ Échec de l'expulsion de ${target.tag}: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,144 +1,113 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { parseDuration, formatDuration } = require('../../utils/helpers');
|
||||
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),
|
||||
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';
|
||||
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({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.' });
|
||||
|
||||
// Vérifications
|
||||
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.' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
|
||||
// Parser la durée
|
||||
const durationMs = parseDuration(durationInput);
|
||||
if (!durationMs) {
|
||||
return interaction.editReply({ content: '❌ Durée invalide. Utilise un format comme: 5m, 1h, 2d (max 28 jours).' });
|
||||
}
|
||||
const durationMs = parseDuration(durationInput);
|
||||
if (!durationMs) return interaction.editReply({ content: '❌ Durée invalide. Utilise un format comme: 5m, 1h, 2d (max 28 jours).' });
|
||||
|
||||
// Maximum 28 jours (Discord limit)
|
||||
const maxDuration = 28 * 24 * 60 * 60 * 1000;
|
||||
if (durationMs > maxDuration) {
|
||||
return interaction.editReply({ content: '❌ La durée maximum est de 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 durationSeconds = Math.floor(durationMs / 1000);
|
||||
const unmuteDate = Date.now() + durationMs;
|
||||
const type = durationMs > 0 ? 'Temporary' : 'Permanent';
|
||||
const unmuteDate = Date.now() + durationMs;
|
||||
const type = 'Temporary'; // Timeout est toujours temporaire
|
||||
|
||||
try {
|
||||
// Muter avec timeout (Discord)
|
||||
await member.timeout(durationSeconds * 1000, `Muté par ${interaction.user.tag}: ${reason}`);
|
||||
try {
|
||||
await member.timeout(durationMs, `Muté par ${interaction.user.tag}: ${reason}`);
|
||||
|
||||
// Stocker dans la DB
|
||||
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 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]
|
||||
);
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors du mute:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour muter cet utilisateur.' });
|
||||
} else {
|
||||
await interaction.editReply({ content: `❌ Échec du mute de ${target.tag}: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,98 +1,97 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = 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('mysanctions')
|
||||
.setDescription('Voir tes propres sanctions sur ce serveur.'),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
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;
|
||||
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]
|
||||
);
|
||||
try {
|
||||
const [rows] = await db.query(
|
||||
'SELECT * FROM logs WHERE userId = ? AND guildId = ? ORDER BY timestamp DESC',
|
||||
[userId, interaction.guild.id]
|
||||
);
|
||||
|
||||
if (!rows.length) {
|
||||
return interaction.editReply({ content: '✅ Tu n\'as aucune sanction sur ce serveur.' });
|
||||
}
|
||||
const sanctions = rows.filter(row =>
|
||||
row.action !== 'Modification de sanction' &&
|
||||
row.action !== 'Révocation de sanction' &&
|
||||
row.action !== 'Modification de mute'
|
||||
);
|
||||
|
||||
const { colors } = require('../../utils/constants');
|
||||
if (!sanctions.length) {
|
||||
return interaction.editReply({ content: '✅ Tu n\'as aucune sanction sur ce serveur.' });
|
||||
}
|
||||
|
||||
// Découpe les sanctions en pages de 10
|
||||
const pages = [];
|
||||
for (let i = 0; i < rows.length; i += 10) {
|
||||
pages.push(rows.slice(i, i + 10));
|
||||
}
|
||||
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: ${rows.length} sanction(s)` })
|
||||
.setTimestamp();
|
||||
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);
|
||||
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
|
||||
});
|
||||
});
|
||||
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;
|
||||
});
|
||||
return embed;
|
||||
});
|
||||
|
||||
// Si une seule page, pas besoin de boutons
|
||||
if (pages.length === 1) {
|
||||
return interaction.editReply({ embeds: [embeds[0]] });
|
||||
}
|
||||
if (pages.length === 1) {
|
||||
return interaction.editReply({ embeds: [embeds[0]] });
|
||||
}
|
||||
|
||||
// Sinon, ajouter les boutons de navigation
|
||||
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
|
||||
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)
|
||||
);
|
||||
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] });
|
||||
let currentPage = 0;
|
||||
const message = await interaction.editReply({ embeds: [embeds[currentPage]], components: [row] });
|
||||
|
||||
// Collecteur de boutons
|
||||
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 !', ephemeral: true });
|
||||
}
|
||||
if (i.customId === 'next') {
|
||||
currentPage = (currentPage + 1) % embeds.length;
|
||||
}
|
||||
if (i.customId === 'prev') {
|
||||
currentPage = (currentPage - 1 + embeds.length) % embeds.length;
|
||||
}
|
||||
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]] });
|
||||
});
|
||||
await i.update({ embeds: [embeds[currentPage]] });
|
||||
});
|
||||
|
||||
collector.on('end', () => {
|
||||
message.edit({ components: [] }).catch(() => null);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la récupération des sanctions:', err);
|
||||
await interaction.editReply({ content: '❌ Une erreur est survenue lors de la récupération de tes sanctions.' });
|
||||
}
|
||||
},
|
||||
};
|
||||
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.' });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,114 +1,103 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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';
|
||||
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({ ephemeral: true });
|
||||
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.' });
|
||||
}
|
||||
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);
|
||||
try {
|
||||
const messages = await interaction.channel.messages.fetch({ limit: amount });
|
||||
const messagesToDelete = messages.filter(msg => msg.author.id === target.id);
|
||||
|
||||
// Ne pas supprimer les messages de plus de 14 jours
|
||||
const filteredMessages = messagesToDelete.filter(msg => {
|
||||
const msgAge = Date.now() - msg.createdTimestamp;
|
||||
return msgAge < 14 * 24 * 60 * 60 * 1000;
|
||||
});
|
||||
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).` });
|
||||
}
|
||||
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 interaction.channel.bulkDelete(filteredMessages, true);
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de la 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}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,109 +1,101 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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';
|
||||
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({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Vérifier les permissions (Administrateur ou propriétaire)
|
||||
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.' });
|
||||
}
|
||||
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 {
|
||||
// Récupérer directement la sanction par son ID DB (plus stable)
|
||||
const [sanctionRows] = await db.query(
|
||||
'SELECT * FROM logs WHERE id = ? AND userId = ? AND guildId = ?',
|
||||
[sanctionId, target.id, interaction.guild.id]
|
||||
);
|
||||
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\`).`
|
||||
});
|
||||
}
|
||||
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];
|
||||
const sanction = sanctionRows[0];
|
||||
|
||||
// Vérifier que ce n'est pas une modification/révocation
|
||||
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.` });
|
||||
}
|
||||
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.` });
|
||||
}
|
||||
|
||||
// Marquer la sanction comme révoquée (on ne la supprime pas, on la marque)
|
||||
await db.query(
|
||||
'UPDATE logs SET reason = CONCAT(reason, " [RÉVOQUÉE]") WHERE id = ?',
|
||||
[sanction.id]
|
||||
);
|
||||
await db.query(
|
||||
'UPDATE logs SET reason = CONCAT(reason, " [RÉVOQUÉE]") WHERE id = ?',
|
||||
[sanction.id]
|
||||
);
|
||||
|
||||
// Log de la révocation
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de la révocation de la sanction:', err);
|
||||
await interaction.editReply({ content: `❌ Échec de la révocation de la sanction: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,100 +1,80 @@
|
||||
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('Débannir un utilisateur par son ID ou son tag.')
|
||||
.addStringOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'ID ou le tag (Username#1234) de l\'utilisateur à débannir')
|
||||
.setRequired(true))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||
category: 'moderation',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('unban')
|
||||
.setDescription('Débannir un utilisateur par son ID ou son tag.')
|
||||
.addStringOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'ID ou le tag (Username#1234) de l\'utilisateur à débannir')
|
||||
.setRequired(true))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
const input = interaction.options.getString('user').trim();
|
||||
const input = interaction.options.getString('user').trim();
|
||||
|
||||
try {
|
||||
const bannedUsers = await interaction.guild.bans.fetch();
|
||||
const bannedUser = bannedUsers.find(b =>
|
||||
b.user.id === input || b.user.tag.toLowerCase() === input.toLowerCase()
|
||||
);
|
||||
try {
|
||||
const bannedUsers = await interaction.guild.bans.fetch();
|
||||
const bannedUser = bannedUsers.find(b =>
|
||||
b.user.id === input || b.user.tag.toLowerCase() === input.toLowerCase()
|
||||
);
|
||||
|
||||
if (!bannedUser) {
|
||||
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas banni.' });
|
||||
}
|
||||
if (!bannedUser) return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas banni.' });
|
||||
|
||||
// Récupération depuis MySQL
|
||||
const [rows] = await db.query('SELECT * FROM bans WHERE userId = ? AND guildId = ?', [bannedUser.user.id, interaction.guild.id]);
|
||||
const banRecord = rows[0];
|
||||
const reason = `Débanni par ${interaction.user.tag}`;
|
||||
await interaction.guild.members.unban(bannedUser.user.id, reason);
|
||||
|
||||
const reason = `Débanni par ${interaction.user.tag}`;
|
||||
await interaction.guild.members.unban(bannedUser.user.id, reason);
|
||||
await db.query('DELETE FROM bans WHERE userId = ? AND guildId = ?', [bannedUser.user.id, interaction.guild.id]);
|
||||
|
||||
if (banRecord) {
|
||||
await db.query('DELETE FROM bans WHERE userId = ? AND guildId = ?', [bannedUser.user.id, interaction.guild.id]);
|
||||
}
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
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 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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
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, 'Débannissement', reason, null, interaction.guild.id, Date.now()]
|
||||
);
|
||||
|
||||
// Log dans le canal de logs
|
||||
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, 'Débannissement', reason, null, interaction.guild.id, Date.now()]
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors du débannissement:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour débannir cet utilisateur.' });
|
||||
} else {
|
||||
await interaction.editReply({ content: '❌ Échec du débannissement de l\'utilisateur.' });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('Erreur unban:', err);
|
||||
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,105 +1,86 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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';
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('user');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.communicationDisabledUntil) {
|
||||
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas muté.' });
|
||||
}
|
||||
if (!member.communicationDisabledUntilTimestamp || member.communicationDisabledUntilTimestamp < Date.now()) {
|
||||
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas muté.' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Démuter
|
||||
await member.timeout(null, `Démuté par ${interaction.user.tag}: ${reason}`);
|
||||
try {
|
||||
await member.timeout(null, `Démuté par ${interaction.user.tag}: ${reason}`);
|
||||
|
||||
// Supprimer de la DB
|
||||
await db.query('DELETE FROM mutes WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
|
||||
await db.query('DELETE FROM mutes WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de l\'unmute:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour démuter cet utilisateur.' });
|
||||
} else {
|
||||
await interaction.editReply({ content: `❌ Échec de l'unmute de ${target.tag}: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,90 +1,78 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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';
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.communicationDisabledUntil) {
|
||||
return interaction.editReply({ content: '❌ Cet utilisateur n\'est pas en timeout.' });
|
||||
}
|
||||
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}`);
|
||||
try {
|
||||
await member.timeout(null, `Timeout retiré par ${interaction.user.tag}: ${reason}`);
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de la levée du timeout:', err);
|
||||
if (err.code === 50013) {
|
||||
await interaction.editReply({ content: '❌ Je n\'ai pas les permissions nécessaires pour retirer le timeout de cet utilisateur.' });
|
||||
} else {
|
||||
await interaction.editReply({ content: `❌ Échec de la levée du timeout de ${target.tag}: ${err.message}` });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,121 +1,101 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||
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),
|
||||
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';
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
await interaction.deferReply();
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
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.' });
|
||||
}
|
||||
if (member.id === interaction.user.id) return interaction.editReply({ content: '❌ Tu ne peux pas t\'avertir toi-même.' });
|
||||
|
||||
try {
|
||||
// Compter les avertissements existants
|
||||
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;
|
||||
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;
|
||||
|
||||
// Log dans la DB
|
||||
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()]
|
||||
);
|
||||
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 { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
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();
|
||||
|
||||
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] });
|
||||
|
||||
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 */ }
|
||||
|
||||
// Envoyer un message privé à l'utilisateur
|
||||
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 {
|
||||
// L'utilisateur a les DMs fermés, on continue
|
||||
}
|
||||
|
||||
// Log dans le canal de logs
|
||||
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 lors de l\'avertissement:', err);
|
||||
await interaction.editReply({ content: `❌ Échec de l'avertissement de ${target.tag}: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
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}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@ module.exports = {
|
||||
.setName('ping')
|
||||
.setDescription('Affiche la latence du bot.'),
|
||||
async execute(interaction) {
|
||||
const sent = await interaction.deferReply({ fetchReply: true });
|
||||
await interaction.deferReply();
|
||||
const sent = await interaction.fetchReply();
|
||||
|
||||
const apiLatency = Math.round(interaction.client.ws.ping);
|
||||
const roundTripLatency = sent.createdTimestamp - interaction.createdTimestamp;
|
||||
@@ -29,4 +30,4 @@ module.exports = {
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,86 +1,79 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { colors } = require('../../utils/constants.js');
|
||||
const { colors, emojis } = require('../../utils/constants.js');
|
||||
|
||||
module.exports = {
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('bumptime')
|
||||
.setDescription('Vérifier le dernier bump et le temps restant avant le prochain'),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
// Récupérer le dernier bump pour ce serveur
|
||||
const [bumps] = await db.query(
|
||||
'SELECT * FROM bumps WHERE guildId = ? ORDER BY bumpTime DESC LIMIT 1',
|
||||
[interaction.guild.id]
|
||||
);
|
||||
|
||||
if (bumps.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun bump enregistré pour ce serveur.'
|
||||
});
|
||||
}
|
||||
|
||||
const lastBump = bumps[0];
|
||||
const bumpTime = lastBump.bumpTime;
|
||||
const now = Date.now();
|
||||
const timeSinceBump = now - bumpTime;
|
||||
const twoHours = 2 * 60 * 60 * 1000;
|
||||
const timeUntilNextBump = twoHours - timeSinceBump;
|
||||
|
||||
// Récupérer l'utilisateur qui a fait le bump
|
||||
const user = await interaction.client.users.fetch(lastBump.userId).catch(() => null);
|
||||
const userTag = user ? user.tag : `ID: ${lastBump.userId}`;
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${interaction.guild.name}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
if (timeUntilNextBump > 0) {
|
||||
// Le prochain bump n'est pas encore disponible
|
||||
const hours = Math.floor(timeUntilNextBump / (60 * 60 * 1000));
|
||||
const minutes = Math.floor((timeUntilNextBump % (60 * 60 * 1000)) / (60 * 1000));
|
||||
const seconds = Math.floor((timeUntilNextBump % (60 * 1000)) / 1000);
|
||||
|
||||
embed.setTitle('⏳ Bump en Cooldown')
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le dernier bump a été effectué il y a moins de 2 heures.`)
|
||||
.addFields(
|
||||
{ name: '👤 Dernier bump par', value: `\`${userTag}\``, inline: true },
|
||||
{ name: '⏰ Il y a', value: `\`${Math.floor(timeSinceBump / (60 * 60 * 1000))}h ${Math.floor((timeSinceBump % (60 * 60 * 1000)) / (60 * 1000))}m\``, inline: true },
|
||||
{ name: '🕐 Temps restant', value: `\`${hours}h ${minutes}m ${seconds}s\``, inline: true },
|
||||
{ name: '📅 Prochain bump disponible', value: `<t:${Math.floor((bumpTime + twoHours) / 1000)}:R>`, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Utilise /bump (Disboard) pour bump le serveur' });
|
||||
} else {
|
||||
// Le prochain bump est disponible
|
||||
embed.setTitle(`${emojis.success} Bump Disponible`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`**Le serveur peut être bumpé maintenant !**`)
|
||||
.addFields(
|
||||
{ name: '👤 Dernier bump par', value: `\`${userTag}\``, inline: true },
|
||||
{ name: '⏰ Il y a', value: `\`${Math.floor(timeSinceBump / (60 * 60 * 1000))}h ${Math.floor((timeSinceBump % (60 * 60 * 1000)) / (60 * 1000))}m\``, inline: true },
|
||||
{ name: '✅ Statut', value: '**Disponible**', inline: true },
|
||||
{ name: '💡 Astuce', value: 'Utilise `/bump` (Disboard) pour bump le serveur !', inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Utilise /bump (Disboard) pour bump le serveur' });
|
||||
}
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la vérification du bump:', err);
|
||||
await interaction.editReply({
|
||||
content: '❌ Erreur lors de la vérification du bump.'
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('bumptime')
|
||||
.setDescription('Vérifier le dernier bump et le temps restant avant le prochain'),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
try {
|
||||
const [bumps] = await db.query(
|
||||
'SELECT * FROM bumps WHERE guildId = ? ORDER BY bumpTime DESC LIMIT 1',
|
||||
[interaction.guild.id]
|
||||
);
|
||||
|
||||
if (bumps.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun bump enregistré pour ce serveur.'
|
||||
});
|
||||
}
|
||||
|
||||
const lastBump = bumps[0];
|
||||
const bumpTime = parseInt(lastBump.bumpTime, 10);
|
||||
const now = Date.now();
|
||||
const timeSinceBump = now - bumpTime;
|
||||
const twoHours = 2 * 60 * 60 * 1000;
|
||||
const timeUntilNextBump = twoHours - timeSinceBump;
|
||||
|
||||
const user = await interaction.client.users.fetch(lastBump.userId).catch(() => null);
|
||||
const userTag = user ? user.tag : `ID: ${lastBump.userId}`;
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: interaction.guild.name,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
if (timeUntilNextBump > 0) {
|
||||
const hours = Math.floor(timeUntilNextBump / (60 * 60 * 1000));
|
||||
const minutes = Math.floor((timeUntilNextBump % (60 * 60 * 1000)) / (60 * 1000));
|
||||
const seconds = Math.floor((timeUntilNextBump % (60 * 1000)) / 1000);
|
||||
|
||||
embed.setTitle('⏳ Bump en Cooldown')
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le dernier bump a été effectué il y a moins de 2 heures.`)
|
||||
.addFields(
|
||||
{ name: '👤 Dernier bump par', value: `\`${userTag}\``, inline: true },
|
||||
{ name: '⏰ Il y a', value: `\`${Math.floor(timeSinceBump / (60 * 60 * 1000))}h ${Math.floor((timeSinceBump % (60 * 60 * 1000)) / (60 * 1000))}m\``, inline: true },
|
||||
{ name: '🕐 Temps restant', value: `\`${hours}h ${minutes}m ${seconds}s\``, inline: true },
|
||||
{ name: '📅 Prochain bump disponible', value: `<t:${Math.floor((bumpTime + twoHours) / 1000)}:R>`, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Utilise /bump (Disboard) pour bump le serveur' });
|
||||
} else {
|
||||
embed.setTitle(`${emojis.success} Bump Disponible`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`**Le serveur peut être bumpé maintenant !**`)
|
||||
.addFields(
|
||||
{ name: '👤 Dernier bump par', value: `\`${userTag}\``, inline: true },
|
||||
{ name: '⏰ Il y a', value: `\`${Math.floor(timeSinceBump / (60 * 60 * 1000))}h ${Math.floor((timeSinceBump % (60 * 60 * 1000)) / (60 * 1000))}m\``, inline: true },
|
||||
{ name: '✅ Statut', value: '**Disponible**', inline: true },
|
||||
{ name: '💡 Astuce', value: 'Utilise `/bump` (Disboard) pour bump le serveur !', inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Utilise /bump (Disboard) pour bump le serveur' });
|
||||
}
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur bumptime:', err);
|
||||
await interaction.editReply({
|
||||
content: '❌ Erreur lors de la vérification du bump.'
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,72 +1,65 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { getLeaderboard, getXPProgress } = require('../../functions/xp/xp.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
module.exports = {
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('leaderboard')
|
||||
.setDescription('Affiche le classement des niveaux')
|
||||
.addIntegerOption(option =>
|
||||
option.setName('limit')
|
||||
.setDescription('Nombre d\'utilisateurs à afficher (par défaut: 10)')
|
||||
.setRequired(false)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(25)),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
|
||||
const limit = interaction.options.getInteger('limit') || 10;
|
||||
const leaderboard = await getLeaderboard(interaction.guild.id, limit);
|
||||
|
||||
if (leaderboard.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun utilisateur trouvé dans le classement.'
|
||||
});
|
||||
}
|
||||
|
||||
// Récupérer les informations des utilisateurs
|
||||
const leaderboardData = [];
|
||||
for (const user of leaderboard) {
|
||||
try {
|
||||
const member = await interaction.guild.members.fetch(user.userId).catch(() => null);
|
||||
if (member) {
|
||||
leaderboardData.push({
|
||||
user: member.user,
|
||||
xp: user.xp,
|
||||
level: user.level
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignorer les erreurs
|
||||
}
|
||||
}
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
// Créer la description du classement
|
||||
const medals = ['🥇', '🥈', '🥉'];
|
||||
const description = leaderboardData.map((data, index) => {
|
||||
const medal = medals[index] || `\`${index + 1}.\``;
|
||||
const progress = getXPProgress(data.xp, data.level);
|
||||
return `${medal} **${data.user.displayName}** • ${emojis.level} Niveau \`${data.level}\` • ${emojis.xp} \`${data.xp.toLocaleString()} XP\` • \`${progress.percentage}%\``;
|
||||
}).join('\n');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${interaction.guild.name}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle(`${emojis.leaderboard} Classement des Niveaux`)
|
||||
.setColor(colors.xp)
|
||||
.setDescription(description || 'Aucun utilisateur dans le classement')
|
||||
.setFooter({
|
||||
text: `Top ${limit} • ${interaction.guild.name}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('leaderboard')
|
||||
.setDescription('Affiche le classement des niveaux')
|
||||
.addIntegerOption(option =>
|
||||
option.setName('limit')
|
||||
.setDescription('Nombre d\'utilisateurs à afficher (par défaut: 10)')
|
||||
.setRequired(false)
|
||||
.setMinValue(1)
|
||||
.setMaxValue(25)),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
|
||||
const limit = interaction.options.getInteger('limit') || 10;
|
||||
const leaderboard = await getLeaderboard(interaction.guild.id, limit);
|
||||
|
||||
if (leaderboard.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun utilisateur trouvé dans le classement.'
|
||||
});
|
||||
}
|
||||
|
||||
const leaderboardData = [];
|
||||
for (const user of leaderboard) {
|
||||
try {
|
||||
const member = await interaction.guild.members.fetch(user.userId).catch(() => null);
|
||||
if (member) {
|
||||
leaderboardData.push({
|
||||
user: member.user,
|
||||
xp: user.xp,
|
||||
level: user.level
|
||||
});
|
||||
}
|
||||
} catch (err) { /* Ignorer */ }
|
||||
}
|
||||
|
||||
const medals = ['🥇', '🥈', '🥉'];
|
||||
const description = leaderboardData.map((data, index) => {
|
||||
const medal = medals[index] || `\`${index + 1}.\``;
|
||||
const progress = getXPProgress(data.xp, data.level);
|
||||
return `${medal} **${data.user.displayName}** • ${emojis.level} Niveau \`${data.level}\` • ${emojis.xp} \`${data.xp.toLocaleString()} XP\` • \`${progress.percentage}%\``;
|
||||
}).join('\n');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: interaction.guild.name,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle(`${emojis.leaderboard} Classement des Niveaux`)
|
||||
.setColor(colors.xp)
|
||||
.setDescription(description || 'Aucun utilisateur dans le classement')
|
||||
.setFooter({
|
||||
text: `Top ${limit} • ${interaction.guild.name}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,96 +1,97 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { getUserXP, getXPProgress, getXPForNextLevel, getUserRank } = require('../../functions/xp/xp.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
module.exports = {
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('level')
|
||||
.setDescription('Affiche ton niveau et ton XP')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux voir le niveau')
|
||||
.setRequired(false)),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
|
||||
if (!userXP) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Erreur lors de la récupération des données XP.'
|
||||
});
|
||||
}
|
||||
|
||||
const progress = getXPProgress(userXP.xp, userXP.level);
|
||||
const xpForNextLevel = getXPForNextLevel(userXP.level);
|
||||
const rank = await getUserRank(target.id, interaction.guild.id);
|
||||
|
||||
// Créer une barre de progression visuelle
|
||||
const barLength = 20;
|
||||
let filled = 0;
|
||||
let empty = barLength;
|
||||
|
||||
// Calculer la barre de progression seulement si progress.needed > 0
|
||||
if (progress.needed > 0 && progress.current >= 0) {
|
||||
const percentage = Math.min(100, Math.max(0, (progress.current / progress.needed) * 100));
|
||||
filled = Math.max(0, Math.min(barLength, Math.floor((percentage / 100) * barLength)));
|
||||
empty = Math.max(0, barLength - filled);
|
||||
}
|
||||
|
||||
const progressBar = '█'.repeat(filled) + '░'.repeat(empty);
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${target.displayName}`,
|
||||
iconURL: target.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.level} Niveau ${userXP.level}`)
|
||||
.setColor(colors.xp)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setDescription(`**${emojis.rank} Rang #${rank}** sur ${interaction.guild.name}`)
|
||||
.addFields(
|
||||
{
|
||||
name: `${emojis.xp} XP Total`,
|
||||
value: `\`${userXP.xp.toLocaleString()} XP\``,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '📈 Progression',
|
||||
value: `\`${progress.current}/${progress.needed} XP\` (${progress.percentage}%)`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🎯 Prochain Niveau',
|
||||
value: `\`${xpForNextLevel} XP\` requis`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '📊 Barre de Progression',
|
||||
value: `\`\`\`${progressBar}\`\`\``,
|
||||
inline: false
|
||||
},
|
||||
{
|
||||
name: '💬 Messages',
|
||||
value: `\`${(userXP.totalMessages || 0).toLocaleString()}\``,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🎤 Temps Vocal',
|
||||
value: `\`${Math.floor((userXP.totalVoiceTime || 0) / 60)}h\``,
|
||||
inline: true
|
||||
}
|
||||
)
|
||||
.setFooter({
|
||||
text: `${interaction.guild.name} • Niveau ${userXP.level}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('level')
|
||||
.setDescription('Affiche ton niveau et ton XP')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux voir le niveau')
|
||||
.setRequired(false)),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
|
||||
const target = interaction.options.getUser('user') || interaction.user;
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
|
||||
if (!userXP) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Erreur lors de la récupération des données XP.'
|
||||
});
|
||||
}
|
||||
|
||||
const progress = getXPProgress(userXP.xp, userXP.level);
|
||||
const xpForNextLevel = getXPForNextLevel(userXP.level);
|
||||
const rank = await getUserRank(target.id, interaction.guild.id);
|
||||
|
||||
const barLength = 20;
|
||||
let filled = 0;
|
||||
let empty = barLength;
|
||||
|
||||
if (progress.needed > 0 && progress.current >= 0) {
|
||||
const percentage = Math.min(100, Math.max(0, (progress.current / progress.needed) * 100));
|
||||
filled = Math.max(0, Math.min(barLength, Math.floor((percentage / 100) * barLength)));
|
||||
empty = Math.max(0, barLength - filled);
|
||||
}
|
||||
|
||||
const progressBar = '█'.repeat(filled) + '░'.repeat(empty);
|
||||
|
||||
// Formatage du temps vocal
|
||||
const totalMinutes = userXP.totalVoiceTime || 0;
|
||||
const hours = Math.floor(totalMinutes / 60);
|
||||
const minutes = totalMinutes % 60;
|
||||
const voiceTimeStr = `${hours}h ${minutes}m`;
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: target.displayName,
|
||||
iconURL: target.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.level} Niveau ${userXP.level}`)
|
||||
.setColor(colors.xp)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true, size: 256 }))
|
||||
.setDescription(`**${emojis.rank} Rang #${rank}** sur ${interaction.guild.name}`)
|
||||
.addFields(
|
||||
{
|
||||
name: `${emojis.xp} XP Total`,
|
||||
value: `\`${userXP.xp.toLocaleString()} XP\``,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '📈 Progression',
|
||||
value: `\`${progress.current}/${progress.needed} XP\` (${progress.percentage}%)`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🎯 Prochain Niveau',
|
||||
value: `\`${xpForNextLevel} XP\` requis`,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '📊 Barre de Progression',
|
||||
value: `\`\`\`${progressBar}\`\`\``,
|
||||
inline: false
|
||||
},
|
||||
{
|
||||
name: '💬 Messages',
|
||||
value: `\`${(userXP.totalMessages || 0).toLocaleString()}\``,
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: '🎤 Temps Vocal',
|
||||
value: `\`${voiceTimeStr}\``,
|
||||
inline: true
|
||||
}
|
||||
)
|
||||
.setFooter({
|
||||
text: `${interaction.guild.name} • Niveau ${userXP.level}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
@@ -1,165 +1,116 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { getUserXP } = require('../../functions/xp/xp.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
const { sendLog } = require('../../utils/helpers');
|
||||
|
||||
module.exports = {
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('resetlevel')
|
||||
.setDescription('Réinitialiser le niveau d\'un utilisateur (Admin/Modérateur uniquement)')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux réinitialiser le niveau')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison du reset (optionnel)')
|
||||
.setRequired(false))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
// Vérifier les permissions (Modérateur ou Admin)
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.ManageMessages) &&
|
||||
!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas la permission d\'utiliser cette commande. (Modérateur/Admin requis)'
|
||||
});
|
||||
}
|
||||
|
||||
const target = interaction.options.getUser('user');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!target) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Aucun utilisateur spécifié !'
|
||||
});
|
||||
}
|
||||
|
||||
// Vérifier que l'utilisateur ne se reset pas lui-même
|
||||
if (target.id === interaction.user.id) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu ne peux pas te reset toi-même.'
|
||||
});
|
||||
}
|
||||
|
||||
// Vérifier que l'utilisateur ne reset pas le propriétaire du serveur
|
||||
if (target.id === interaction.guild.ownerId) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu ne peux pas reset le niveau du propriétaire du serveur.'
|
||||
});
|
||||
}
|
||||
|
||||
// Vérifier les rôles (ne pas permettre de reset quelqu'un avec un rôle supérieur)
|
||||
const targetMember = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
if (targetMember) {
|
||||
if (targetMember.roles.highest.position >= interaction.member.roles.highest.position &&
|
||||
interaction.guild.ownerId !== interaction.user.id) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu ne peux pas reset le niveau d\'un utilisateur avec un rôle supérieur ou égal au tien.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Récupérer les données XP de l'utilisateur avant le reset
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
|
||||
if (!userXP || userXP.xp === 0) {
|
||||
return interaction.editReply({
|
||||
content: `❌ ${target.tag} n'a aucun niveau/XP à réinitialiser.`
|
||||
});
|
||||
}
|
||||
|
||||
const oldLevel = userXP.level;
|
||||
const oldXP = userXP.xp;
|
||||
const oldMessages = userXP.totalMessages || 0;
|
||||
const oldVoiceTime = userXP.totalVoiceTime || 0;
|
||||
|
||||
// Supprimer complètement l'utilisateur de la table user_xp
|
||||
await db.query(
|
||||
'DELETE FROM user_xp WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
// Supprimer aussi les logs XP de l'utilisateur (optionnel, pour un reset complet)
|
||||
await db.query(
|
||||
'DELETE FROM xp_logs WHERE userId = ? AND guildId = ?',
|
||||
[target.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
// Logger l'action dans la table logs
|
||||
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,
|
||||
'Reset de niveau',
|
||||
`Niveau ${oldLevel} (${oldXP} XP) réinitialisé. Raison: ${reason}`,
|
||||
'Permanent',
|
||||
interaction.guild.id,
|
||||
Date.now()
|
||||
]
|
||||
);
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
// Envoyer un log dans le canal de logs
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${interaction.user.displayName}`,
|
||||
iconURL: interaction.user.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.warning} Reset de Niveau`)
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le niveau de ${target.toString()} a été réinitialisé.`)
|
||||
.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: '📊 Niveau avant', value: `\`${oldLevel}\``, inline: true },
|
||||
{ name: '📈 XP avant', value: `\`${oldXP.toLocaleString()} XP\``, inline: true },
|
||||
{ name: '💬 Messages', value: `\`${oldMessages.toLocaleString()}\``, inline: true },
|
||||
{ name: '🎤 Temps vocal', value: `\`${Math.floor(oldVoiceTime / 60)}h\``, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: `ID: ${target.id} • ${interaction.guild.name}` })
|
||||
.setTimestamp();
|
||||
|
||||
await sendLog(interaction.guild, { embeds: [logEmbed] });
|
||||
|
||||
// Réponse à l'utilisateur
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${target.displayName}`,
|
||||
iconURL: target.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.setTitle(`${emojis.success} Niveau Réinitialisé`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le niveau de ${target.toString()} a été complètement réinitialisé.`)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `\`${target.tag}\``, inline: true },
|
||||
{ name: '📊 Niveau avant', value: `\`${oldLevel}\``, inline: true },
|
||||
{ name: '📈 XP avant', value: `\`${oldXP.toLocaleString()} XP\``, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Toutes les données XP ont été supprimées de la base de données.' })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur lors du reset de niveau:', err);
|
||||
await interaction.editReply({
|
||||
content: `❌ Erreur lors du reset de niveau: ${err.message}`
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('resetlevel')
|
||||
.setDescription('Réinitialiser le niveau d\'un utilisateur (Admin/Modérateur uniquement)')
|
||||
.addUserOption(option =>
|
||||
option.setName('user')
|
||||
.setDescription('L\'utilisateur dont tu veux réinitialiser le niveau')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison du reset (optionnel)')
|
||||
.setRequired(false))
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
const target = interaction.options.getUser('user');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!target) return interaction.editReply({ content: '❌ Aucun utilisateur spécifié !' });
|
||||
if (target.id === interaction.user.id) return interaction.editReply({ content: '❌ Tu ne peux pas te reset toi-même.' });
|
||||
if (target.id === interaction.guild.ownerId) return interaction.editReply({ content: '❌ Tu ne peux pas reset le niveau du propriétaire du serveur.' });
|
||||
|
||||
const targetMember = await interaction.guild.members.fetch(target.id).catch(() => null);
|
||||
if (targetMember) {
|
||||
if (targetMember.roles.highest.position >= interaction.member.roles.highest.position &&
|
||||
interaction.guild.ownerId !== interaction.user.id) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu ne peux pas reset le niveau d\'un utilisateur avec un rôle supérieur ou égal au tien.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const userXP = await getUserXP(target.id, interaction.guild.id);
|
||||
|
||||
if (!userXP || userXP.xp === 0) {
|
||||
return interaction.editReply({
|
||||
content: `❌ ${target.tag} n'a aucun niveau/XP à réinitialiser.`
|
||||
});
|
||||
}
|
||||
|
||||
const oldLevel = userXP.level;
|
||||
const oldXP = userXP.xp;
|
||||
const oldMessages = userXP.totalMessages || 0;
|
||||
const oldVoiceTime = userXP.totalVoiceTime || 0;
|
||||
|
||||
await db.query('DELETE FROM user_xp WHERE userId = ? AND guildId = ?', [target.id, interaction.guild.id]);
|
||||
await db.query('DELETE FROM xp_logs 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,
|
||||
'Reset de niveau',
|
||||
`Niveau ${oldLevel} (${oldXP} XP) réinitialisé. Raison: ${reason}`,
|
||||
'Permanent',
|
||||
interaction.guild.id,
|
||||
Date.now()
|
||||
]
|
||||
);
|
||||
|
||||
const logEmbed = new EmbedBuilder()
|
||||
.setAuthor({ name: interaction.user.displayName, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
|
||||
.setTitle(`${emojis.warning} Reset de Niveau`)
|
||||
.setColor(colors.warning)
|
||||
.setDescription(`Le niveau de ${target.toString()} a été réinitialisé.`)
|
||||
.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: '📊 Niveau avant', value: `\`${oldLevel}\``, inline: true },
|
||||
{ name: '📈 XP avant', value: `\`${oldXP.toLocaleString()} XP\``, inline: true },
|
||||
{ name: '💬 Messages', value: `\`${oldMessages.toLocaleString()}\``, inline: true },
|
||||
{ name: '🎤 Temps vocal', value: `\`${Math.floor(oldVoiceTime / 60)}h\``, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: `ID: ${target.id} • ${interaction.guild.name}` })
|
||||
.setTimestamp();
|
||||
|
||||
await sendLog(interaction.guild, { embeds: [logEmbed] });
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({ name: target.displayName, iconURL: target.displayAvatarURL({ dynamic: true }) })
|
||||
.setTitle(`${emojis.success} Niveau Réinitialisé`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le niveau de ${target.toString()} a été complètement réinitialisé.`)
|
||||
.setThumbnail(target.displayAvatarURL({ dynamic: true }))
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `\`${target.tag}\``, inline: true },
|
||||
{ name: '📊 Niveau avant', value: `\`${oldLevel}\``, inline: true },
|
||||
{ name: '📈 XP avant', value: `\`${oldXP.toLocaleString()} XP\``, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: 'Toutes les données XP ont été supprimées de la base de données.' })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur resetlevel:', err);
|
||||
await interaction.editReply({ content: `❌ Erreur lors du reset de niveau: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,154 +1,140 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
|
||||
const db = require('../../functions/database/db.js');
|
||||
const { isChannelExcluded } = require('../../functions/xp/xp.js');
|
||||
const { colors } = require('../../utils/constants');
|
||||
const { colors, emojis } = require('../../utils/constants');
|
||||
|
||||
module.exports = {
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('xpexclude')
|
||||
.setDescription('Gérer les salons exclus de l\'XP (Admin uniquement)')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Exclure un salon de l\'XP')
|
||||
.addChannelOption(option =>
|
||||
option.setName('channel')
|
||||
.setDescription('Le salon à exclure')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison de l\'exclusion')
|
||||
.setRequired(false)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Retirer un salon de l\'exclusion')
|
||||
.addChannelOption(option =>
|
||||
option.setName('channel')
|
||||
.setDescription('Le salon à retirer')
|
||||
.setRequired(true)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Liste des salons exclus')),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
// Vérifier les permissions (Admin uniquement)
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas la permission d\'utiliser cette commande. (Administrateur requis)'
|
||||
});
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
try {
|
||||
if (subcommand === 'add') {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!channel.isTextBased()) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Le salon doit être un canal texte.'
|
||||
});
|
||||
}
|
||||
|
||||
// Vérifier si déjà exclus
|
||||
const excluded = await isChannelExcluded(channel.id, interaction.guild.id);
|
||||
if (excluded) {
|
||||
return interaction.editReply({
|
||||
content: `❌ Le salon ${channel.toString()} est déjà exclu de l'XP.`
|
||||
});
|
||||
}
|
||||
|
||||
// Ajouter l'exclusion
|
||||
await db.query(
|
||||
'INSERT INTO xp_excluded_channels (channelId, guildId, reason, addedBy, addedAt) VALUES (?, ?, ?, ?, ?)',
|
||||
[channel.id, interaction.guild.id, reason, interaction.user.id, Date.now()]
|
||||
);
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`${emojis.success} Salon Exclu`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le salon ${channel.toString()} a été exclu de l'XP.`)
|
||||
.addFields(
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: `${interaction.guild.name} • Système XP` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (subcommand === 'remove') {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
|
||||
// Vérifier si exclu
|
||||
const excluded = await isChannelExcluded(channel.id, interaction.guild.id);
|
||||
if (!excluded) {
|
||||
return interaction.editReply({
|
||||
content: `❌ Le salon ${channel.toString()} n'est pas exclu de l'XP.`
|
||||
});
|
||||
}
|
||||
|
||||
// Retirer l'exclusion
|
||||
await db.query(
|
||||
'DELETE FROM xp_excluded_channels WHERE channelId = ? AND guildId = ?',
|
||||
[channel.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`${emojis.success} Exclusion Retirée`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le salon ${channel.toString()} n'est plus exclu de l'XP.`)
|
||||
.setFooter({ text: `${interaction.guild.name} • Système XP` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (subcommand === 'list') {
|
||||
const [excludedChannels] = await db.query(
|
||||
'SELECT * FROM xp_excluded_channels WHERE guildId = ?',
|
||||
[interaction.guild.id]
|
||||
);
|
||||
|
||||
if (excludedChannels.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '✅ Aucun salon n\'est exclu de l\'XP.'
|
||||
});
|
||||
}
|
||||
|
||||
const description = excludedChannels.map(ch => {
|
||||
const channel = interaction.guild.channels.cache.get(ch.channelId);
|
||||
return `• ${channel ? channel.toString() : `ID: ${ch.channelId}`} - ${ch.reason || 'Aucune raison'}`;
|
||||
}).join('\n');
|
||||
|
||||
const { emojis } = require('../../utils/constants');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: `${interaction.guild.name}`,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle('📋 Salons Exclus de l\'XP')
|
||||
.setColor(colors.xp)
|
||||
.setDescription(description || 'Aucun salon exclu')
|
||||
.setFooter({ text: `${excludedChannels.length} salon(s) exclu(s)` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la gestion des exclusions XP:', err);
|
||||
await interaction.editReply({
|
||||
content: `❌ Erreur: ${err.message}`
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
category: 'xp',
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('xpexclude')
|
||||
.setDescription('Gérer les salons exclus de l\'XP (Admin uniquement)')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Exclure un salon de l\'XP')
|
||||
.addChannelOption(option =>
|
||||
option.setName('channel')
|
||||
.setDescription('Le salon à exclure')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('reason')
|
||||
.setDescription('Raison de l\'exclusion')
|
||||
.setRequired(false)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Retirer un salon de l\'exclusion')
|
||||
.addChannelOption(option =>
|
||||
option.setName('channel')
|
||||
.setDescription('Le salon à retirer')
|
||||
.setRequired(true)))
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('list')
|
||||
.setDescription('Liste des salons exclus')),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Tu n\'as pas la permission d\'utiliser cette commande. (Administrateur requis)'
|
||||
});
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
try {
|
||||
if (subcommand === 'add') {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
const reason = interaction.options.getString('reason') || 'Aucune raison fournie';
|
||||
|
||||
if (!channel.isTextBased()) {
|
||||
return interaction.editReply({
|
||||
content: '❌ Le salon doit être un canal texte.'
|
||||
});
|
||||
}
|
||||
|
||||
const excluded = await isChannelExcluded(channel.id, interaction.guild.id);
|
||||
if (excluded) {
|
||||
return interaction.editReply({
|
||||
content: `❌ Le salon ${channel.toString()} est déjà exclu de l'XP.`
|
||||
});
|
||||
}
|
||||
|
||||
await db.query(
|
||||
'INSERT INTO xp_excluded_channels (channelId, guildId, reason, addedBy, addedAt) VALUES (?, ?, ?, ?, ?)',
|
||||
[channel.id, interaction.guild.id, reason, interaction.user.id, Date.now()]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`${emojis.success} Salon Exclu`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le salon ${channel.toString()} a été exclu de l'XP.`)
|
||||
.addFields(
|
||||
{ name: '📝 Raison', value: reason, inline: false }
|
||||
)
|
||||
.setFooter({ text: `${interaction.guild.name} • Système XP` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (subcommand === 'remove') {
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
|
||||
const excluded = await isChannelExcluded(channel.id, interaction.guild.id);
|
||||
if (!excluded) {
|
||||
return interaction.editReply({
|
||||
content: `❌ Le salon ${channel.toString()} n'est pas exclu de l'XP.`
|
||||
});
|
||||
}
|
||||
|
||||
await db.query(
|
||||
'DELETE FROM xp_excluded_channels WHERE channelId = ? AND guildId = ?',
|
||||
[channel.id, interaction.guild.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`${emojis.success} Exclusion Retirée`)
|
||||
.setColor(colors.success)
|
||||
.setDescription(`Le salon ${channel.toString()} n'est plus exclu de l'XP.`)
|
||||
.setFooter({ text: `${interaction.guild.name} • Système XP` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
|
||||
} else if (subcommand === 'list') {
|
||||
const [excludedChannels] = await db.query(
|
||||
'SELECT * FROM xp_excluded_channels WHERE guildId = ?',
|
||||
[interaction.guild.id]
|
||||
);
|
||||
|
||||
if (excludedChannels.length === 0) {
|
||||
return interaction.editReply({
|
||||
content: '✅ Aucun salon n\'est exclu de l\'XP.'
|
||||
});
|
||||
}
|
||||
|
||||
const description = excludedChannels.map(ch => {
|
||||
const channel = interaction.guild.channels.cache.get(ch.channelId);
|
||||
return `• ${channel ? channel.toString() : `ID: ${ch.channelId}`} - ${ch.reason || 'Aucune raison'}`;
|
||||
}).join('\n');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: interaction.guild.name,
|
||||
iconURL: interaction.guild.iconURL({ dynamic: true }) || undefined
|
||||
})
|
||||
.setTitle('📋 Salons Exclus de l\'XP')
|
||||
.setColor(colors.xp)
|
||||
.setDescription(description || 'Aucun salon exclu')
|
||||
.setFooter({ text: `${excludedChannels.length} salon(s) exclu(s)` })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur xpexclude:', err);
|
||||
await interaction.editReply({ content: `❌ Erreur: ${err.message}` });
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user