Update bot

This commit is contained in:
Syxpi
2025-10-18 23:20:19 +02:00
commit c590dd1c64
4459 changed files with 650347 additions and 0 deletions

65
commands/dev/infra.js Normal file
View File

@@ -0,0 +1,65 @@
const os = require('os');
const fs = require('fs');
const cpuInfo = os.cpus()[0].model; // modèle du CPU
const cpuCores = os.cpus().length; // nombre de coeurs
const totalRAM = (os.totalmem() / (1024 ** 3)).toFixed(2); // en Go
const freeRAM = (os.freemem() / (1024 ** 3)).toFixed(2); // en Go
function getOS() {
const platform = os.platform(); // ex: win32, linux
const release = os.release(); // version du kernel / OS
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 uptime = os.uptime(); // en secondes
function formatUptime(seconds) {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `${days}j ${hours}h ${minutes}m ${secs}s`;
}
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
module.exports = {
category: 'dev',
data: new SlashCommandBuilder()
.setName('infra')
.setDescription('Voir linfrastructure du bot'),
async execute(interaction) {
const embed = new EmbedBuilder()
.setTitle('Infrastructure du bot')
.setColor('Blue')
.addFields(
{ name: 'CPU', value: `${cpuInfo} (${cpuCores} coeurs)`, inline: true },
{ name: 'RAM', value: `${freeRAM} Go libres / ${totalRAM} Go totaux`, inline: true },
{ name: 'OS', value: getOS(), inline: true },
{ name: 'Uptime', value: formatUptime(uptime), inline: true }
)
.setTimestamp();
await interaction.reply({ embeds: [embed] });
}
};

37
commands/dev/reload.js Normal file
View File

@@ -0,0 +1,37 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
category: 'dev',
data: new SlashCommandBuilder()
.setName('reload')
.setDescription('Reloads a command.')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addStringOption(option =>
option.setName('command')
.setDescription('The command to reload.')
.setRequired(true)),
async execute(interaction) {
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
return interaction.reply({ content: '❌ Only administrators can use this command.', embeds: [embed] });
}
const commandName = interaction.options.getString('command', true).toLowerCase();
const command = interaction.client.commands.get(commandName);
if (!command) {
return interaction.reply({ content: `There is no command with name \`${commandName}\`!`, embeds: [embed] });
}
delete require.cache[require.resolve(`../${command.category}/${command.data.name}.js`)];
try {
const newCommand = require(`../${command.category}/${command.data.name}.js`);
interaction.client.commands.set(newCommand.data.name, newCommand);
await interaction.reply({ content: `✅ Command \`${newCommand.data.name}\` was reloaded!`, });
} catch (error) {
console.error(error);
await interaction.reply({ content: `❌ Error while reloading \`${command.data.name}\`:\n\`${error.message}\``, embeds: [embed] });
}
},
};

21
commands/fun/cutie.js Normal file
View File

@@ -0,0 +1,21 @@
const {SlashCommandBuilder, EmbedBuilder} = require("discord.js");
module.exports = {
category: 'fun',
data: new SlashCommandBuilder()
.setName('cutie')
.setDescription('Juste montrer qui sont les cuties de ce serveur'),
async execute(interaction) {
const embed = new EmbedBuilder()
.setColor('#00FFFF')
.setTitle('🥰 Liste des Cuties')
.addFields(
{ name: '👑 Reine des Cuties', value: `<@366171099966210049> | Tags: Tellement Cutie que j'suis jalouse 🥺`, inline: true },
{ name: '👑 Roi des Cuties', value: '<@868244823059075102> | Tags: Richou 💵 et Bg 😎', inline: true},
{ name: '👸 Princesses des Cuties', value: `<@361526553940721684> | Titre Refusé, mais ordonné (snif)`, inline: true },
{ name: '😻 Cutie Suprème', value: '<@303149512065548289> | Tags: Cutie Suprème. Aucune négociation'}
)
await interaction.reply({ embeds: [embed] });
},
};

53
commands/info/info.js Normal file
View File

@@ -0,0 +1,53 @@
const {SlashCommandBuilder, EmbedBuilder} = require("discord.js");
module.exports = {
category: 'info',
data: new SlashCommandBuilder()
.setName('info')
.setDescription('Obtenir les informations du bot'),
async execute(interaction) {
const embed = new EmbedBuilder()
.setTitle('📝 Informations du Bot')
.setColor('DarkVividPink')
.addFields(
{
name: '📦 Version',
value: 'v1.0.0',
inline: true
},
{
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.20.0',
'• **Librairies :** <:discordjs:1425179852536938670> Discord.js v14.22',
'• **IDE :** <:webstorm:1429190717066055841> JetBrains WebStorm 2025.2.3',
].join('\n'),
inline: false
},
{
name: '💡 Date de création',
value: '20 Août 2025'
},
{
name: '⏱️ Uptime',
value: `${Math.floor(process.uptime()/3600)}h ${Math.floor(process.uptime()%3600/60)}m`,
inline: true
}
)
.setFooter({
text: 'Fembot • Made with love by Syxpi 💞',
iconURL: interaction.client.user.displayAvatarURL()
})
await interaction.reply({ embeds: [embed] });
},
};

133
commands/moderation/ban.js Normal file
View File

@@ -0,0 +1,133 @@
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
const db = require('../../functions/database/db.js'); // instance mysql2/promise
const SPECIAL_BAN_GIFS = {
'256543419252342785': 'https://cdn.discordapp.com/attachments/1280667750213091399/1294481799682199613/Darkgif-ezgif.com-video-to-gif-converter.gif?ex=68f4f0df&is=68f39f5f&hm=4521788383420117fa71419934bb331e269636028088894ffdbc90be302adaa1&'
};
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('ban')
.setDescription('Select a member and ban them.')
.addUserOption(option =>
option.setName('target')
.setDescription('The member to ban')
.setRequired(true))
.addStringOption(option =>
option.setName('reason')
.setDescription('Reason')
.setRequired(false))
.addStringOption(option =>
option.setName('duration')
.setDescription('Ban duration (ex: 1h, 2d, leave empty for permanent)')
.setRequired(false))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
const target = interaction.options.getUser('target');
const reason = interaction.options.getString('reason') || 'No reason provided';
const durationInput = interaction.options.getString('duration');
await interaction.deferReply()
if (!target) return interaction.editReply({ content: 'No user specified!' });
await interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
let type = 'Permanent';
let unbanDate = null;
if (durationInput) {
const regex = /^(\d+)(s|m|h|d|mo|y)$/;
const match = durationInput.match(regex);
if (match) {
const value = parseInt(match[1]);
const unit = match[2];
let multiplier = 1000;
if (unit === 's') multiplier *= 60 / 60;
if (unit === 'm') multiplier *= 60;
if (unit === 'h') multiplier *= 60 * 60;
if (unit === 'd') multiplier *= 60 * 60 * 24;
if (unit === 'mo') multiplier *= 60 * 60 * 24 * 30;
if (unit === 'y') multiplier *= 60 * 60 * 24 * 365;
unbanDate = Date.now() + value * multiplier;
type = 'Temporary';
}
}
// juste avant la création de l'embed
let durationText = type;
if (type === 'Temporary' && unbanDate) {
const initialDurationMs = unbanDate - Date.now() + (Date.now() - Date.now()); // ça reste l'unbanDate - timestamp original
function formatInitialDuration(ms) {
const seconds = Math.floor(ms / 1000) % 60;
const minutes = Math.floor(ms / (1000 * 60)) % 60;
const hours = Math.floor(ms / (1000 * 60 * 60)) % 24;
const days = Math.floor(ms / (1000 * 60 * 60 * 24));
const months = Math.floor(ms / (1000 * 60 * 60 * 24 * 30));
const years = Math.floor(ms / (1000 * 60 * 60 * 24 * 365));
if (years) return `${years} Année${years > 1 ? 's' : ''}`;
if (months) return `${months} Mois`;
if (days) return `${days} Jour${days > 1 ? 's' : ''}`;
if (hours) return `${hours} Heure${hours > 1 ? 's' : ''}`;
if (minutes) return `${minutes} Minute${minutes > 1 ? 's' : ''}`;
if (seconds) return `${seconds} Seconde${seconds > 1 ? 's' : ''}`;
return 'Instantané';
}
durationText = formatInitialDuration(initialDurationMs);
}
try {
// Bannissement
interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
// 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 logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'Bannissement', reason, type, interaction.guild.id, Date.now()]
);
await interaction.editReply(`✅ Successfully banned ${target.tag} (${type})`);
// Embed log
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
if (logChannel) {
const embed = new EmbedBuilder()
.setTitle('User Banned')
.setColor('Red')
.addFields(
{ name: 'Banned User', value: `<@${target.id}> (${target.tag})`, inline: true },
{ name: 'Banned By', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: 'Reason', value: reason, inline: false },
{ name: 'Duration', value: durationText, inline: true },
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
{ name: 'Guild', value: interaction.guild.name, inline: true }
)
.setTimestamp();
let message = '';
if (SPECIAL_BAN_GIFS[interaction.user.id]) {
message = `${SPECIAL_BAN_GIFS[interaction.user.id]}\n (Niku a juste banni quelqu'un)`;
} else {
message = `${target.tag} a été banni (${type})`;
}
await interaction.editReply({ content: message });
logChannel.send({ embeds: [embed] });
}
} catch (err) {
console.error(err);
await interaction.editReply({ content: `❌ Failed to ban ${target.tag}` });
}
}
};

View File

@@ -0,0 +1,85 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const db = require('../../functions/database/db.js');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('casier')
.setDescription('Voir toutes les sanctions dun membre.')
.addUserOption(option =>
option.setName('membre')
.setDescription('Le membre dont tu veux voir le casier')
.setRequired(true)),
async execute(interaction) {
await interaction.deferReply();
const member = interaction.options.getUser('membre');
try {
const [rows] = await db.query(
'SELECT * FROM logs WHERE userId = ? ORDER BY timestamp DESC',
[member.id]
);
if (!rows.length) return interaction.editReply(`${member.tag} na aucune sanction.`);
// Découpe les sanctions en pages de 25
const pages = [];
for (let i = 0; i < rows.length; i += 10) {
pages.push(rows.slice(i, i + 10));
}
const embeds = pages.map((page, pageIndex) => {
const embed = new EmbedBuilder()
.setTitle(`Casier de ${member.tag} - Page ${pageIndex + 1}/${pages.length}`)
.setColor('Orange')
.setTimestamp();
page.forEach((row, index) => {
const date = new Date(row.timestamp).toLocaleString();
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1);
const action = row.action || 'N/A';
const guildName = row.guildId
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Unknown Server'
: 'Unknown Server';
embed.addFields({
name: `#${index + 1 + pageIndex * 10} - ${actionCapitalized}`,
value: `**Modérateur:** <@${row.modId}> (${row.modTag || 'N/A'})\n` +
`**Raison:** ${row.reason || 'N/A'}\n` +
`**Serveur:** ${guildName}\n` +
`**Date:** ${date}\n` +
`**Type:** ${action}`,
inline: false
});
});
return embed;
});
// Composants pour naviguer
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder().setCustomId('prev').setLabel('⬅️').setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId('next').setLabel('➡️').setStyle(ButtonStyle.Primary)
);
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', i => {
if (i.user.id !== interaction.user.id) return i.reply({ content: 'Ce nest pas ton menu !', embeds: [embed] });
if (i.customId === 'next') currentPage = (currentPage + 1) % embeds.length;
if (i.customId === 'prev') currentPage = (currentPage - 1 + embeds.length) % embeds.length;
i.update({ embeds: [embeds[currentPage]] });
});
} catch (err) {
console.error(err);
await interaction.editReply('❌ Une erreur est survenue lors de la récupération du casier.');
}
},
};

View File

@@ -0,0 +1,65 @@
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
const db = require('../../functions/database/db.js');
module.exports = {
category: 'moderation',
data: new SlashCommandBuilder()
.setName('unban')
.setDescription('Unban a user by their ID or tag.')
.addStringOption(option =>
option.setName('user')
.setDescription('The ID or tag (Username#1234) of the user to unban')
.setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
await interaction.deferReply();
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()
);
if (!bannedUser) return interaction.editReply('❌ This user is not banned.');
// Récupération depuis MySQL
const [rows] = await db.query('SELECT * FROM bans WHERE userId = ?', [bannedUser.user.id]);
const banRecord = rows[0];
const reason = `Unbanned by ${interaction.user.tag}`;
await interaction.guild.members.unban(bannedUser.user.id, reason);
if (banRecord) await db.query('DELETE FROM bans WHERE userId = ?', [bannedUser.user.id]);
await interaction.editReply(`✅ Successfully unbanned ${bannedUser.user.tag}`);
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
if (logChannel) {
const embed = new EmbedBuilder()
.setTitle('User Unbanned')
.setColor('Green')
.addFields(
{ name: 'Membre Banni', value: `<@${bannedUser.user.id}> (${bannedUser.user.tag})`, inline: true },
{ name: 'Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
{ name: 'Guild', value: interaction.guild.name, inline: true }
)
.setTimestamp();
logChannel.send({ embeds: [embed] });
}
// Log dans la DB avec guildId
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[bannedUser.user.id, bannedUser.user.tag, interaction.user.id, interaction.user.tag, 'unban', reason, null, interaction.guild.id, Date.now()]
);
} catch (err) {
console.error(err);
await interaction.editReply('❌ Failed to unban the user.');
}
}
};

23
commands/utility/ping.js Normal file
View File

@@ -0,0 +1,23 @@
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
module.exports = {
category: 'utility',
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with API latency and ICMP latency!'),
async execute(interaction) {
// --- API latency Discord ---
const apiLatency = Math.round(interaction.client.ws.ping);
// --- Reply final ---
const embed = new EmbedBuilder()
.setColor('#00FFFF')
.setTitle('🏓 Ping Status')
.addFields(
{ name: '🌐 API Latency', value: `${apiLatency}ms`, inline: true }
)
.setTimestamp();
await interaction.reply({ embeds: [embed] });
},
};