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

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

View File

@@ -0,0 +1,56 @@
/**
* Système de reminder pour les bumps
* Vérifie toutes les minutes les bumps qui doivent avoir un reminder 2h après
*/
const chalk = require('chalk');
const { EmbedBuilder } = require('discord.js');
const { colors } = require('../../utils/constants');
module.exports = (client, db) => {
setInterval(async () => {
try {
const now = Date.now();
const twoHours = 2 * 60 * 60 * 1000;
// Récupérer tous les bumps de plus de 2h qui n'ont pas encore eu de rappel
const [bumps] = await db.query(
'SELECT * FROM bumps WHERE reminderSent = ? AND bumpTime <= ?',
[false, now - twoHours]
);
if (!bumps.length) return;
for (const bump of bumps) {
try {
const guild = client.guilds.cache.get(bump.guildId);
if (!guild) continue;
const member = await guild.members.fetch(bump.userId).catch(() => null);
if (!member) continue;
const [bumpChannels] = await db.query('SELECT * FROM bump_channels WHERE guildId = ?', [bump.guildId]);
let channel = bumpChannels.length > 0 ? guild.channels.cache.get(bumpChannels[0].channelId) : guild.systemChannel;
if (!channel) continue;
const reminderEmbed = new EmbedBuilder()
.setTitle('⏰ Rappel de Bump')
.setDescription(`${member.toString()}, tu peux maintenant refaire un bump !`)
.setColor(colors.info)
.setFooter({ text: 'Utilise /bump (Disboard) pour bump le serveur • /bumptime pour vérifier' })
.setTimestamp();
await channel.send({ embeds: [reminderEmbed] });
await db.query('UPDATE bumps SET reminderSent = ? WHERE id = ?', [true, bump.id]);
console.log(chalk.green(`✅ Reminder de bump envoyé à ${member.user.tag} dans la guild ${guild.name}`));
} catch (err) {
console.error(chalk.red(`❌ Erreur reminder bump ${bump.id}:`), err);
}
}
} catch (err) {
console.error(chalk.red('❌ Erreur vérification reminders bump:'), err);
}
}, 60 * 1000);
};

View File

@@ -1,11 +1,10 @@
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: '192.168.1.6',
user: 'bot',
password: 'NxKr63LJB65pHv%t7E$JqgxKRsZMw%VIHEZAjq%^O0KYKjW#cRc^ebIH@%S9kaTh*GIg^D3ai4KBjMeXEh6xwv#9afQIR2$!2UB8C3ToXjnYFmzR%$lfpshnf8g@8229',
database: 'bot',
host: process.env.DB_HOST || '192.168.1.6',
user: process.env.DB_USER || 'bot',
password: process.env.DB_PASSWORD || 'NxKr63LJB65pHv%t7E$JqgxKRsZMw%VIHEZAjq%^O0KYKjW#cRc^ebIH@%S9kaTh*GIg^D3ai4KBjMeXEh6xwv#9afQIR2$!2UB8C3ToXjnYFmzR%$lfpshnf8g@8229',
database: process.env.DB_NAME || 'bot',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
@@ -14,30 +13,219 @@ const pool = mysql.createPool({
async function initDB() {
const conn = await pool.getConnection();
try {
// Tables de modération
await conn.query(`
CREATE TABLE IF NOT EXISTS bans (
userId VARCHAR(32) PRIMARY KEY,
userId VARCHAR(32),
guildId VARCHAR(32),
reason TEXT,
modId VARCHAR(32),
timestamp BIGINT,
type VARCHAR(20) NOT NULL,
unbanDate BIGINT
)
unbanDate BIGINT,
PRIMARY KEY (userId, guildId)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS logs (
id INT AUTO_INCREMENT PRIMARY KEY,
userId VARCHAR(32),
id INT AUTO_INCREMENT PRIMARY KEY,
userId VARCHAR(32),
userTag VARCHAR(100),
modId VARCHAR(32),
modTag VARCHAR(100),
action VARCHAR(20),
action VARCHAR(50),
reason TEXT,
type ENUM('Permanent','Temporary') DEFAULT 'Permanent',
guildId VARCHAR(32),
timestamp BIGINT
)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS mutes (
userId VARCHAR(32),
guildId VARCHAR(32),
reason TEXT,
modId VARCHAR(32),
modTag VARCHAR(100),
timestamp BIGINT,
unmuteDate BIGINT,
type VARCHAR(20) NOT NULL,
PRIMARY KEY (userId, guildId)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS staffbanmessages (
userId VARCHAR(32) PRIMARY KEY,
userTag VARCHAR(100),
message TEXT,
timestamp BIGINT
)
`);
// Tables de tickets
await conn.query(`
CREATE TABLE IF NOT EXISTS tickets (
id INT AUTO_INCREMENT PRIMARY KEY,
ticketId VARCHAR(20) UNIQUE NOT NULL,
channelId VARCHAR(32) UNIQUE NOT NULL,
userId VARCHAR(32) NOT NULL,
userTag VARCHAR(100),
guildId VARCHAR(32) NOT NULL,
type ENUM('Support', 'Plainte', 'Plainte Staff', 'Candidature', 'Problème Technique') NOT NULL,
status ENUM('Ouvert', 'Fermé', 'En attente', 'Supprimé') DEFAULT 'Ouvert',
createdAt BIGINT NOT NULL,
closedAt BIGINT,
closedBy VARCHAR(32),
transcript TEXT,
transcriptPath VARCHAR(255),
claimedBy VARCHAR(32),
claimedAt BIGINT,
claimedByTag VARCHAR(100),
INDEX idx_userId (userId),
INDEX idx_guildId (guildId),
INDEX idx_status (status),
INDEX idx_claimedBy (claimedBy)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS ticket_messages (
id INT AUTO_INCREMENT PRIMARY KEY,
ticketId VARCHAR(20) NOT NULL,
messageId VARCHAR(32) NOT NULL,
userId VARCHAR(32) NOT NULL,
userTag VARCHAR(100),
content TEXT,
attachments TEXT,
timestamp BIGINT NOT NULL,
INDEX idx_ticketId (ticketId),
INDEX idx_messageId (messageId)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS candidature_responses (
id INT AUTO_INCREMENT PRIMARY KEY,
ticketId VARCHAR(20) NOT NULL,
questionNumber INT NOT NULL,
question TEXT NOT NULL,
response TEXT,
timestamp BIGINT NOT NULL,
INDEX idx_ticketId (ticketId)
)
`);
// Tables XP
await conn.query(`
CREATE TABLE IF NOT EXISTS user_xp (
userId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
xp INT DEFAULT 0,
level INT DEFAULT 0,
lastMessageTime BIGINT DEFAULT 0,
totalMessages INT DEFAULT 0,
totalVoiceTime INT DEFAULT 0,
lastVoiceJoin BIGINT DEFAULT 0,
lastBumpTime BIGINT DEFAULT 0,
PRIMARY KEY (userId, guildId),
INDEX idx_guildId (guildId),
INDEX idx_xp (xp),
INDEX idx_level (level)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS xp_excluded_channels (
channelId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
reason VARCHAR(255),
addedBy VARCHAR(32),
addedAt BIGINT,
PRIMARY KEY (channelId, guildId),
INDEX idx_guildId (guildId)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS xp_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
userId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
xpGained INT NOT NULL,
source VARCHAR(50) NOT NULL,
multiplier DECIMAL(3,2) DEFAULT 1.00,
timestamp BIGINT NOT NULL,
INDEX idx_userId (userId),
INDEX idx_guildId (guildId),
INDEX idx_timestamp (timestamp)
)
`);
// Tables Bump
await conn.query(`
CREATE TABLE IF NOT EXISTS bumps (
id INT AUTO_INCREMENT PRIMARY KEY,
userId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
bumpTime BIGINT NOT NULL,
reminderSent BOOLEAN DEFAULT FALSE,
INDEX idx_userId (userId),
INDEX idx_guildId (guildId),
INDEX idx_bumpTime (bumpTime)
)
`);
await conn.query(`
CREATE TABLE IF NOT EXISTS bump_channels (
channelId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
bumpBotId VARCHAR(32),
addedBy VARCHAR(32),
addedAt BIGINT,
PRIMARY KEY (channelId, guildId),
INDEX idx_guildId (guildId)
)
`);
// Tables Profils
await conn.query(`
CREATE TABLE IF NOT EXISTS user_profiles (
userId VARCHAR(32) NOT NULL,
guildId VARCHAR(32) NOT NULL,
signature TEXT,
birthday DATE,
color VARCHAR(7),
gender VARCHAR(50),
location VARCHAR(100),
updatedAt BIGINT,
PRIMARY KEY (userId, guildId),
INDEX idx_guildId (guildId)
)
`);
// Migrations (pour compatibilité existante)
// On utilise la syntaxe standard sans IF NOT EXISTS pour compatibilité maximale
try {
await conn.query('ALTER TABLE bans ADD COLUMN guildId VARCHAR(32)');
await conn.query('ALTER TABLE bans DROP PRIMARY KEY');
await conn.query('ALTER TABLE bans ADD PRIMARY KEY (userId, guildId)');
} catch {}
try { await conn.query('ALTER TABLE logs ADD COLUMN guildId VARCHAR(32)'); } catch {}
try { await conn.query('ALTER TABLE logs MODIFY COLUMN action VARCHAR(50)'); } catch {}
try { await conn.query('ALTER TABLE tickets ADD COLUMN transcriptPath VARCHAR(255)'); } catch {}
try { await conn.query('ALTER TABLE tickets ADD COLUMN claimedBy VARCHAR(32)'); } catch {}
try { await conn.query('ALTER TABLE tickets ADD COLUMN claimedAt BIGINT'); } catch {}
try { await conn.query('ALTER TABLE tickets ADD COLUMN claimedByTag VARCHAR(100)'); } catch {}
// Migrations Profils (Sans IF NOT EXISTS)
try { await conn.query('ALTER TABLE user_profiles ADD COLUMN color VARCHAR(7)'); } catch {}
try { await conn.query('ALTER TABLE user_profiles ADD COLUMN gender VARCHAR(50)'); } catch {}
try { await conn.query('ALTER TABLE user_profiles ADD COLUMN location VARCHAR(100)'); } catch {}
} finally {
conn.release();
}
@@ -45,4 +233,4 @@ async function initDB() {
initDB().catch(console.error);
module.exports = pool;
module.exports = pool;

View File

@@ -1,3 +1,5 @@
const chalk = require('chalk');
module.exports = (client, db) => {
setInterval(async () => {
try {
@@ -7,36 +9,33 @@ module.exports = (client, db) => {
['Temporary', now]
);
if (!expiredBans.length) return; // rien à faire
if (!expiredBans.length) return;
for (const ban of expiredBans) {
const guild = client.guilds.cache.get(ban.guildId);
if (!guild) {
console.warn(`Cannot unban ${ban.userId}: bot is not in guild ${ban.guildId}`);
console.warn(chalk.yellow(`⚠️ Impossible de débannir ${ban.userId}: le bot n'est pas dans la guild ${ban.guildId}`));
continue;
}
try {
await guild.members.unban(ban.userId, 'Temporary ban expired');
console.log(`Auto-unbanned ${ban.userId} from guild ${guild.id}`);
await guild.members.unban(ban.userId, 'Bannissement temporaire expiré');
console.log(chalk.green(`Auto-banni ${ban.userId} de la guild ${guild.name} (${guild.id})`));
} catch (err) {
// Si lutilisateur nest pas ban ou autre erreur
console.error(`Failed to unban ${ban.userId} from guild ${guild.id}:`, err.message);
console.error(chalk.red(`❌ Échec du débannissement de ${ban.userId} de la guild ${guild.id}:`), err.message);
}
// Supprime uniquement le ban correspondant à cette guild
await db.query('DELETE FROM bans WHERE userId = ? AND guildId = ?', [ban.userId, ban.guildId]);
// Log automatique
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, timestamp, guildId)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[ban.userId, null, client.user.id, 'Mod-Bot', 'auto-unban', 'Temporary ban expired', 'Temporary', Date.now(), ban.guildId]
[ban.userId, null, client.user.id, client.user.tag, 'Débannissement automatique', 'Bannissement temporaire expiré', 'Temporary', Date.now(), ban.guildId]
);
}
} catch (err) {
console.error('Error during auto-unban:', err);
console.error(chalk.red('❌ Erreur lors de l\'auto-bannissement:'), err);
}
}, 60_000);
};
};

View File

@@ -0,0 +1,47 @@
const chalk = require('chalk');
module.exports = (client, db) => {
setInterval(async () => {
try {
const now = Date.now();
const [expiredMutes] = await db.query(
'SELECT * FROM mutes WHERE type = ? AND unmuteDate <= ?',
['Temporary', now]
);
if (!expiredMutes.length) return;
for (const mute of expiredMutes) {
const guild = client.guilds.cache.get(mute.guildId);
if (!guild) {
console.warn(chalk.yellow(`⚠️ Impossible de démuter ${mute.userId}: le bot n'est pas dans la guild ${mute.guildId}`));
continue;
}
const member = await guild.members.fetch(mute.userId).catch(() => null);
if (!member) {
await db.query('DELETE FROM mutes WHERE userId = ? AND guildId = ?', [mute.userId, mute.guildId]);
continue;
}
try {
await member.timeout(null, 'Mute temporaire expiré');
console.log(chalk.green(`✅ Auto-démuté ${mute.userId} de la guild ${guild.name} (${guild.id})`));
} catch (err) {
console.error(chalk.red(`❌ Échec du démute de ${mute.userId} de la guild ${guild.id}:`), err.message);
}
await db.query('DELETE FROM mutes WHERE userId = ? AND guildId = ?', [mute.userId, mute.guildId]);
await db.query(
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, timestamp, guildId)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[mute.userId, null, client.user.id, client.user.tag, 'Unmute automatique', 'Mute temporaire expiré', 'Temporary', Date.now(), mute.guildId]
);
}
} catch (err) {
console.error(chalk.red('❌ Erreur lors de l\'auto-unmute:'), err);
}
}, 60_000);
};

177
functions/xp/xp.js Normal file
View File

@@ -0,0 +1,177 @@
/**
* Fonctions utilitaires pour le système XP
*/
const db = require('../database/db.js');
// Formule de calcul de l'XP nécessaire pour atteindre un niveau
function getXPForLevel(level) {
if (level <= 0) return 0;
return Math.floor(5 * Math.pow(level, 2) + 50 * level + 100);
}
// Calculer le niveau à partir de l'XP total
function getLevelFromXP(xp) {
if (xp < 0) return 0;
let level = 0;
let requiredXP = getXPForLevel(level + 1);
while (xp >= requiredXP) {
level++;
requiredXP = getXPForLevel(level + 1);
}
return level;
}
// Calculer l'XP nécessaire pour le prochain niveau
function getXPForNextLevel(level) {
return getXPForLevel(level + 1);
}
// Calculer l'XP progress dans le niveau actuel
function getXPProgress(xp, level) {
const xpForCurrentLevel = getXPForLevel(level);
const xpForNextLevel = getXPForLevel(level + 1);
const xpInLevel = Math.max(0, xp - xpForCurrentLevel);
const xpNeeded = xpForNextLevel - xpForCurrentLevel;
if (xpNeeded <= 0) {
return { current: xpInLevel, needed: 100, percentage: 0 };
}
const percentage = Math.max(0, Math.min(100, Math.floor((xpInLevel / xpNeeded) * 100)));
return { current: xpInLevel, needed: xpNeeded, percentage };
}
// Obtenir ou créer un utilisateur dans la table XP
async function getUserXP(userId, guildId) {
try {
const [rows] = await db.query(
'SELECT * FROM user_xp WHERE userId = ? AND guildId = ?',
[userId, guildId]
);
if (rows.length === 0) {
await db.query(
'INSERT INTO user_xp (userId, guildId, xp, level) VALUES (?, ?, 0, 0)',
[userId, guildId]
);
return {
userId, guildId, xp: 0, level: 0,
lastMessageTime: 0, totalMessages: 0,
totalVoiceTime: 0, lastVoiceJoin: 0, lastBumpTime: 0
};
}
return rows[0];
} catch (err) {
console.error('Erreur getUserXP:', err);
return null;
}
}
// Ajouter de l'XP à un utilisateur
async function addXP(userId, guildId, xpGained, source = 'message', multiplier = 1.0) {
try {
const userXP = await getUserXP(userId, guildId);
if (!userXP) return null;
const finalXP = Math.floor(xpGained * multiplier);
const newXP = userXP.xp + finalXP;
const newLevel = getLevelFromXP(newXP);
await db.query(
'UPDATE user_xp SET xp = ?, level = ? WHERE userId = ? AND guildId = ?',
[newXP, newLevel, userId, guildId]
);
try {
await db.query(
'INSERT INTO xp_logs (userId, guildId, xpGained, source, multiplier, timestamp) VALUES (?, ?, ?, ?, ?, ?)',
[userId, guildId, finalXP, source, multiplier, Date.now()]
);
} catch {}
return {
oldXP: userXP.xp,
newXP,
oldLevel: userXP.level,
newLevel,
xpGained: finalXP,
levelUp: newLevel > userXP.level
};
} catch (err) {
console.error('Erreur addXP:', err);
return null;
}
}
// Vérifier si un salon est exclus de l'XP
async function isChannelExcluded(channelId, guildId) {
try {
const [rows] = await db.query(
'SELECT * FROM xp_excluded_channels WHERE channelId = ? AND guildId = ?',
[channelId, guildId]
);
return rows.length > 0;
} catch (err) {
console.error('Erreur isChannelExcluded:', err);
return false;
}
}
// Obtenir le multiplicateur XP d'un utilisateur
function getXPMultiplier(member, source = 'message') {
let multiplier = 1.0;
if (member.premiumSince) multiplier *= 1.2;
return multiplier;
}
// Obtenir le classement des utilisateurs par XP
async function getLeaderboard(guildId, limit = 10) {
try {
const [rows] = await db.query(
'SELECT * FROM user_xp WHERE guildId = ? ORDER BY xp DESC LIMIT ?',
[guildId, limit]
);
return rows;
} catch (err) {
console.error('Erreur getLeaderboard:', err);
return [];
}
}
// Obtenir la position d'un utilisateur dans le classement
async function getUserRank(userId, guildId) {
try {
const userXP = await getUserXP(userId, guildId);
if (!userXP) return 0;
const [rows] = await db.query(
'SELECT COUNT(*) as count FROM user_xp WHERE guildId = ? AND xp > ?',
[guildId, userXP.xp]
);
return (rows[0]?.count || 0) + 1;
} catch (err) {
console.error('Erreur getUserRank:', err);
return 0;
}
}
module.exports = {
getXPForLevel,
getLevelFromXP,
getXPForNextLevel,
getXPProgress,
getUserXP,
addXP,
isChannelExcluded,
getXPMultiplier,
getLeaderboard,
getUserRank,
};